SideMenuView.swift (9493B)
1 // 2 // SideMenuView.swift 3 // damus 4 // 5 // Created by Ben Weeks on 1/6/23. 6 // Ref: https://blog.logrocket.com/create-custom-collapsible-sidebar-swiftui/ 7 8 import SwiftUI 9 10 @MainActor 11 struct SideMenuView: View { 12 let damus_state: DamusState 13 @Binding var isSidebarVisible: Bool 14 @Binding var selected: Timeline 15 @State var confirm_logout: Bool = false 16 @State private var showQRCode = false 17 18 var sideBarWidth = min(UIScreen.main.bounds.size.width * 0.65, 400.0) 19 let verticalSpacing: CGFloat = 25 20 let padding: CGFloat = 30 21 22 var body: some View { 23 ZStack { 24 GeometryReader { _ in 25 EmptyView() 26 } 27 .background(DamusColors.darkGrey.opacity(0.6)) 28 .opacity(isSidebarVisible ? 1 : 0) 29 .animation(.default, value: isSidebarVisible) 30 .onTapGesture { 31 isSidebarVisible.toggle() 32 } 33 34 content 35 } 36 } 37 38 func SidemenuItems(profile_model: ProfileModel, followers: FollowersModel) -> some View { 39 return VStack(spacing: verticalSpacing) { 40 NavigationLink(value: Route.Profile(profile: profile_model, followers: followers)) { 41 navLabel(title: NSLocalizedString("Profile", comment: "Sidebar menu label for Profile view."), img: "user") 42 } 43 .accessibilityIdentifier(AppAccessibilityIdentifiers.side_menu_profile_button.rawValue) 44 45 NavigationLink(value: Route.Wallet(wallet: damus_state.wallet)) { 46 navLabel(title: NSLocalizedString("Wallet", comment: "Sidebar menu label for Wallet view."), img: "wallet") 47 } 48 49 if damus_state.purple.enable_purple { 50 NavigationLink(destination: DamusPurpleView(damus_state: damus_state)) { 51 HStack(spacing: 23) { 52 Image("nostr-hashtag") 53 Text("Purple") 54 .foregroundColor(DamusColors.purple) 55 .font(.title2.weight(.semibold)) 56 } 57 .frame(maxWidth: .infinity, alignment: .leading) 58 } 59 } 60 61 NavigationLink(value: Route.MuteList) { 62 navLabel(title: NSLocalizedString("Muted", comment: "Sidebar menu label for muted users view."), img: "mute") 63 } 64 65 NavigationLink(value: Route.RelayConfig) { 66 navLabel(title: NSLocalizedString("Relays", comment: "Sidebar menu label for Relays view."), img: "world-relays") 67 } 68 69 NavigationLink(value: Route.Bookmarks) { 70 navLabel(title: NSLocalizedString("Bookmarks", comment: "Sidebar menu label for Bookmarks view."), img: "bookmark") 71 } 72 73 Link(destination: URL(string: "https://store.damus.io/?ref=damus_ios_app")!) { 74 navLabel(title: NSLocalizedString("Merch", comment: "Sidebar menu label for merch store link."), img: "shop") 75 } 76 77 NavigationLink(value: Route.Config) { 78 navLabel(title: NSLocalizedString("Settings", comment: "Sidebar menu label for accessing the app settings"), img: "settings") 79 } 80 81 Button(action: { 82 if damus_state.keypair.privkey == nil { 83 logout(damus_state) 84 } else { 85 confirm_logout = true 86 } 87 }, label: { 88 navLabel(title: NSLocalizedString("Logout", comment: "Sidebar menu label to sign out of the account."), img: "logout") 89 }) 90 } 91 } 92 93 var TopProfile: some View { 94 var name: String? = nil 95 var display_name: String? = nil 96 97 do { 98 let profile_txn = damus_state.ndb.lookup_profile(damus_state.pubkey, txn_name: "top_profile") 99 let profile = profile_txn?.unsafeUnownedValue?.profile 100 name = profile?.name 101 display_name = profile?.display_name 102 } 103 104 return VStack(alignment: .leading) { 105 HStack(spacing: 10) { 106 107 ProfilePicView(pubkey: damus_state.pubkey, size: 50, highlight: .none, profiles: damus_state.profiles, disable_animation: damus_state.settings.disable_animation) 108 109 Spacer() 110 111 Button(action: { 112 present_sheet(.user_status) 113 isSidebarVisible = false 114 }, label: { 115 Image("add-reaction") 116 .resizable() 117 .frame(width: 25, height: 25) 118 .padding(5) 119 .foregroundColor(DamusColors.adaptableBlack) 120 .background { 121 Circle() 122 .foregroundColor(DamusColors.neutral3) 123 } 124 }) 125 126 Button(action: { 127 showQRCode.toggle() 128 isSidebarVisible = false 129 }, label: { 130 Image("qr-code") 131 .resizable() 132 .frame(width: 25, height: 25) 133 .padding(5) 134 .foregroundColor(DamusColors.adaptableBlack) 135 .background { 136 Circle() 137 .foregroundColor(DamusColors.neutral3) 138 } 139 }).damus_full_screen_cover($showQRCode, damus_state: damus_state) { 140 QRCodeView(damus_state: damus_state, pubkey: damus_state.pubkey) 141 } 142 } 143 144 VStack(alignment: .leading) { 145 146 if let display_name { 147 Text(display_name) 148 .font(.title2.weight(.bold)) 149 .foregroundColor(DamusColors.adaptableBlack) 150 .frame(maxWidth: .infinity, alignment: .leading) 151 .dynamicTypeSize(.xSmall) 152 .lineLimit(1) 153 } 154 if let name { 155 if !name.isEmpty { 156 Text(verbatim: "@" + name) 157 .foregroundColor(DamusColors.mediumGrey) 158 .font(.body) 159 .lineLimit(1) 160 } 161 } 162 163 PubkeyView(pubkey: damus_state.pubkey, sidemenu: true) 164 .pubkey_context_menu(pubkey: damus_state.pubkey) 165 } 166 } 167 } 168 169 var MainSidemenu: some View { 170 VStack(alignment: .leading, spacing: 0) { 171 let followers = FollowersModel(damus_state: damus_state, target: damus_state.pubkey) 172 let profile_model = ProfileModel(pubkey: damus_state.pubkey, damus: damus_state) 173 174 NavigationLink(value: Route.Profile(profile: profile_model, followers: followers), label: { 175 TopProfile 176 .padding(.bottom, verticalSpacing) 177 }) 178 .simultaneousGesture(TapGesture().onEnded { 179 isSidebarVisible = false 180 }) 181 182 ScrollView { 183 SidemenuItems(profile_model: profile_model, followers: followers) 184 .simultaneousGesture(TapGesture().onEnded { 185 isSidebarVisible = false 186 }) 187 } 188 .scrollIndicators(.hidden) 189 } 190 } 191 192 var content: some View { 193 HStack(alignment: .top) { 194 ZStack(alignment: .top) { 195 DamusColors.adaptableWhite 196 .ignoresSafeArea() 197 198 MainSidemenu 199 .padding([.leading, .trailing], padding) 200 } 201 .frame(width: sideBarWidth) 202 .offset(x: isSidebarVisible ? 0 : -(sideBarWidth + padding)) 203 .animation(.default, value: isSidebarVisible) 204 .alert("Logout", isPresented: $confirm_logout) { 205 Button(NSLocalizedString("Cancel", comment: "Cancel out of logging out the user."), role: .cancel) { 206 confirm_logout = false 207 } 208 Button(NSLocalizedString("Logout", comment: "Button for logging out the user."), role: .destructive) { 209 logout(damus_state) 210 } 211 } message: { 212 Text("Make sure your nsec account key is saved before you logout or you will lose access to this account", comment: "Reminder message in alert to get customer to verify that their private security account key is saved saved before logging out.") 213 } 214 215 Spacer() 216 } 217 } 218 219 func navLabel(title: String, img: String) -> some View { 220 HStack(spacing: 20) { 221 Image(img) 222 .tint(DamusColors.adaptableBlack) 223 224 Text(title) 225 .font(.title2.weight(.semibold)) 226 .foregroundColor(DamusColors.adaptableBlack) 227 .frame(maxWidth: .infinity, alignment: .leading) 228 .dynamicTypeSize(.xSmall) 229 .minimumScaleFactor(0.5) 230 .lineLimit(1) 231 } 232 } 233 } 234 235 struct Previews_SideMenuView_Previews: PreviewProvider { 236 static var previews: some View { 237 let ds = test_damus_state 238 SideMenuView(damus_state: ds, isSidebarVisible: .constant(true), selected: .constant(.home)) 239 } 240 }