damus

nostr ios client
git clone git://jb55.com/damus
Log | Files | Refs | README | LICENSE

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 }