SideMenuView.swift (9877B)
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 @State var confirm_logout: Bool = false 15 @State private var showQRCode = false 16 17 @Environment(\.colorScheme) var colorScheme 18 19 var sideBarWidth = min(UIScreen.main.bounds.size.width * 0.65, 400.0) 20 let verticalSpacing: CGFloat = 20 21 let padding: CGFloat = 30 22 23 func fillColor() -> Color { 24 colorScheme == .light ? DamusColors.white : DamusColors.black 25 } 26 27 func textColor() -> Color { 28 colorScheme == .light ? DamusColors.black : DamusColors.white 29 } 30 31 var body: some View { 32 ZStack { 33 GeometryReader { _ in 34 EmptyView() 35 } 36 .background(DamusColors.darkGrey.opacity(0.6)) 37 .opacity(isSidebarVisible ? 1 : 0) 38 .animation(.default, value: isSidebarVisible) 39 .onTapGesture { 40 isSidebarVisible.toggle() 41 } 42 43 content 44 } 45 } 46 47 func SidemenuItems(profile_model: ProfileModel, followers: FollowersModel) -> some View { 48 return VStack(spacing: verticalSpacing) { 49 NavigationLink(value: Route.Profile(profile: profile_model, followers: followers)) { 50 navLabel(title: NSLocalizedString("Profile", comment: "Sidebar menu label for Profile view."), img: "user") 51 } 52 53 NavigationLink(value: Route.Wallet(wallet: damus_state.wallet)) { 54 navLabel(title: NSLocalizedString("Wallet", comment: "Sidebar menu label for Wallet view."), img: "wallet") 55 } 56 57 if damus_state.purple.enable_purple { 58 NavigationLink(destination: DamusPurpleView(damus_state: damus_state)) { 59 HStack(spacing: 13) { 60 Image("nostr-hashtag") 61 Text("Purple") 62 .foregroundColor(DamusColors.purple) 63 .font(.title2.weight(.bold)) 64 } 65 .frame(maxWidth: .infinity, alignment: .leading) 66 } 67 } 68 69 NavigationLink(value: Route.MuteList) { 70 navLabel(title: NSLocalizedString("Muted", comment: "Sidebar menu label for muted users view."), img: "mute") 71 } 72 73 NavigationLink(value: Route.RelayConfig) { 74 navLabel(title: NSLocalizedString("Relays", comment: "Sidebar menu label for Relays view."), img: "world-relays") 75 } 76 77 NavigationLink(value: Route.Bookmarks) { 78 navLabel(title: NSLocalizedString("Bookmarks", comment: "Sidebar menu label for Bookmarks view."), img: "bookmark") 79 } 80 81 Link(destination: URL(string: "https://store.damus.io/?ref=damus_ios_app")!) { 82 navLabel(title: NSLocalizedString("Merch", comment: "Sidebar menu label for merch store link."), img: "basket") 83 } 84 85 NavigationLink(value: Route.Config) { 86 navLabel(title: NSLocalizedString("Settings", comment: "Sidebar menu label for accessing the app settings"), img: "settings") 87 } 88 } 89 } 90 91 var TopProfile: some View { 92 var name: String? = nil 93 var display_name: String? = nil 94 95 do { 96 let profile_txn = damus_state.ndb.lookup_profile(damus_state.pubkey, txn_name: "top_profile") 97 let profile = profile_txn?.unsafeUnownedValue?.profile 98 name = profile?.name 99 display_name = profile?.display_name 100 } 101 102 return VStack(alignment: .leading, spacing: verticalSpacing) { 103 HStack { 104 ProfilePicView(pubkey: damus_state.pubkey, size: 60, highlight: .none, profiles: damus_state.profiles, disable_animation: damus_state.settings.disable_animation) 105 106 VStack(alignment: .leading) { 107 108 if let display_name { 109 Text(display_name) 110 .foregroundColor(textColor()) 111 .font(.title) 112 .lineLimit(1) 113 } 114 if let name { 115 Text("@" + name) 116 .foregroundColor(DamusColors.mediumGrey) 117 .font(.body) 118 .lineLimit(1) 119 } 120 } 121 } 122 123 navLabel(title: NSLocalizedString("Set Status", comment: "Sidebar menu label to set user status"), img: "add-reaction") 124 .font(.title2) 125 .foregroundColor(textColor()) 126 .frame(maxWidth: .infinity, alignment: .leading) 127 .dynamicTypeSize(.xSmall) 128 .onTapGesture { 129 present_sheet(.user_status) 130 } 131 132 UserStatusView(status: damus_state.profiles.profile_data(damus_state.pubkey).status, show_general: true, show_music: true) 133 .dynamicTypeSize(.xSmall) 134 } 135 } 136 137 var MainSidemenu: some View { 138 VStack(alignment: .leading, spacing: 0) { 139 let followers = FollowersModel(damus_state: damus_state, target: damus_state.pubkey) 140 let profile_model = ProfileModel(pubkey: damus_state.pubkey, damus: damus_state) 141 142 NavigationLink(value: Route.Profile(profile: profile_model, followers: followers), label: { 143 144 TopProfile 145 .padding(.bottom, verticalSpacing) 146 }) 147 148 Divider() 149 150 ScrollView { 151 SidemenuItems(profile_model: profile_model, followers: followers) 152 .labelStyle(SideMenuLabelStyle()) 153 .padding([.top, .bottom], verticalSpacing) 154 } 155 } 156 } 157 158 var content: some View { 159 HStack(alignment: .top) { 160 ZStack(alignment: .top) { 161 fillColor() 162 .ignoresSafeArea() 163 164 VStack(alignment: .leading, spacing: 0) { 165 MainSidemenu 166 .simultaneousGesture(TapGesture().onEnded { 167 isSidebarVisible = false 168 }) 169 170 Divider() 171 172 HStack() { 173 Button(action: { 174 //ConfigView(state: damus_state) 175 if damus_state.keypair.privkey == nil { 176 logout(damus_state) 177 } else { 178 confirm_logout = true 179 } 180 }, label: { 181 Label(NSLocalizedString("Sign out", comment: "Sidebar menu label to sign out of the account."), image: "logout") 182 .font(.title3) 183 .foregroundColor(textColor()) 184 .frame(maxWidth: .infinity, alignment: .leading) 185 .dynamicTypeSize(.xSmall) 186 }) 187 188 Spacer() 189 190 Button(action: { 191 showQRCode.toggle() 192 }, label: { 193 Image("qr-code") 194 .font(.title) 195 .foregroundColor(textColor()) 196 .dynamicTypeSize(.xSmall) 197 }).fullScreenCover(isPresented: $showQRCode) { 198 QRCodeView(damus_state: damus_state, pubkey: damus_state.pubkey) 199 } 200 } 201 .padding(.top, verticalSpacing) 202 } 203 .padding(.top, -(padding / 2.0)) 204 .padding([.leading, .trailing, .bottom], padding) 205 } 206 .frame(width: sideBarWidth) 207 .offset(x: isSidebarVisible ? 0 : -(sideBarWidth + padding)) 208 .animation(.default, value: isSidebarVisible) 209 .alert("Logout", isPresented: $confirm_logout) { 210 Button(NSLocalizedString("Cancel", comment: "Cancel out of logging out the user."), role: .cancel) { 211 confirm_logout = false 212 } 213 Button(NSLocalizedString("Logout", comment: "Button for logging out the user."), role: .destructive) { 214 logout(damus_state) 215 } 216 } message: { 217 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.") 218 } 219 220 Spacer() 221 } 222 } 223 224 func navLabel(title: String, img: String) -> some View { 225 HStack { 226 Image(img) 227 .tint(DamusColors.adaptableBlack) 228 229 Text(title) 230 .font(.title2) 231 .foregroundColor(textColor()) 232 .frame(maxWidth: .infinity, alignment: .leading) 233 .dynamicTypeSize(.xSmall) 234 } 235 } 236 237 struct SideMenuLabelStyle: LabelStyle { 238 func makeBody(configuration: Configuration) -> some View { 239 HStack(alignment: .center, spacing: 8) { 240 configuration.icon 241 .frame(width: 24, height: 24) 242 .aspectRatio(contentMode: .fit) 243 configuration.title 244 } 245 } 246 } 247 } 248 249 struct Previews_SideMenuView_Previews: PreviewProvider { 250 static var previews: some View { 251 let ds = test_damus_state 252 SideMenuView(damus_state: ds, isSidebarVisible: .constant(true)) 253 } 254 }