damus

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

commit 40d11ad680744a178921771fb1eb8a59769653d2
parent 4d8088d0d0e6e52e5c1cecf00f4417ee5e2b250f
Author: Ben Weeks <ben.weeks@outlook.com>
Date:   Tue, 10 Jan 2023 10:07:35 -0800

Add a Sidebar

Changlog-Added: Sidebar
Closes: #273

Diffstat:
Mdamus.xcodeproj/project.pbxproj | 6+++++-
Mdamus/ContentView.swift | 92++++++++++++++++++++++++++++++++++++++++++-------------------------------------
Mdamus/Views/MainTabView.swift | 12+++++++-----
Mdamus/Views/ProfileName.swift | 2+-
Adamus/Views/SideMenuView.swift | 176+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdamus/damusApp.swift | 3---
6 files changed, 238 insertions(+), 53 deletions(-)

diff --git a/damus.xcodeproj/project.pbxproj b/damus.xcodeproj/project.pbxproj @@ -138,6 +138,7 @@ 4CEE2AF9280B2EAC00AB5EEF /* PowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CEE2AF8280B2EAC00AB5EEF /* PowView.swift */; }; 4CEE2B02280B39E800AB5EEF /* EventActionBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CEE2B01280B39E800AB5EEF /* EventActionBar.swift */; }; 4FE60CDD295E1C5E00105A1F /* Wallet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FE60CDC295E1C5E00105A1F /* Wallet.swift */; }; + 647D9A8D2968520300A295DE /* SideMenuView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 647D9A8C2968520300A295DE /* SideMenuView.swift */; }; 64FBD06F296255C400D9D3B2 /* Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64FBD06E296255C400D9D3B2 /* Theme.swift */; }; 6C7DE41F2955169800E66263 /* Vault in Frameworks */ = {isa = PBXBuildFile; productRef = 6C7DE41E2955169800E66263 /* Vault */; }; BA693074295D649800ADDB87 /* UserSettingsStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA693073295D649800ADDB87 /* UserSettingsStore.swift */; }; @@ -332,6 +333,7 @@ 4CEE2AF8280B2EAC00AB5EEF /* PowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PowView.swift; sourceTree = "<group>"; }; 4CEE2B01280B39E800AB5EEF /* EventActionBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventActionBar.swift; sourceTree = "<group>"; }; 4FE60CDC295E1C5E00105A1F /* Wallet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Wallet.swift; sourceTree = "<group>"; }; + 647D9A8C2968520300A295DE /* SideMenuView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SideMenuView.swift; sourceTree = "<group>"; }; 64FBD06E296255C400D9D3B2 /* Theme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Theme.swift; sourceTree = "<group>"; }; BA693073295D649800ADDB87 /* UserSettingsStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSettingsStore.swift; sourceTree = "<group>"; }; BAB68BEC29543FA3007BA466 /* SelectWalletView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectWalletView.swift; sourceTree = "<group>"; }; @@ -488,7 +490,6 @@ 4C3AC79E2833115300E1F516 /* FollowButtonView.swift */, 4C3AC79C2833036D00E1F516 /* FollowingView.swift */, 4C90BD17283A9EE5008EE7EF /* LoginView.swift */, - 4C3AC7A42836987600E1F516 /* MainTabView.swift */, 4C363A8928236B57006E126D /* MentionView.swift */, 4C363A8D28236FE4006E126D /* NoteContentView.swift */, 4C75EFAC28049CFB0006080F /* PostButton.swift */, @@ -498,6 +499,7 @@ 4C285C892838B985008A31F1 /* ProfilePictureSelector.swift */, 4CEE2AF2280B25C500AB5EEF /* ProfilePicView.swift */, 4C8682862814DE470026224F /* ProfileView.swift */, + 4C3AC7A42836987600E1F516 /* MainTabView.swift */, 4C363A8B28236B92006E126D /* PubkeyView.swift */, 4CB55EF2295E5D59007FD187 /* RecommendedRelayView.swift */, 4C06670028FC7C5900038D2A /* RelayView.swift */, @@ -513,6 +515,7 @@ 4C0A3F96280F8E02000448DE /* ThreadView.swift */, 4CA2EF9F280E37AC0044ACD8 /* TimelineView.swift */, 4CB55EF4295E679D007FD187 /* UserRelaysView.swift */, + 647D9A8C2968520300A295DE /* SideMenuView.swift */, ); path = Views; sourceTree = "<group>"; @@ -824,6 +827,7 @@ 4C363A9028247A1D006E126D /* NostrLink.swift in Sources */, 4C0A3F8C280F5FCA000448DE /* ChatroomView.swift in Sources */, 4C477C9E282C3A4800033AA3 /* TipCounter.swift in Sources */, + 647D9A8D2968520300A295DE /* SideMenuView.swift in Sources */, 4C0A3F91280F6528000448DE /* ChatView.swift in Sources */, 4C216F362870A9A700040376 /* InputDismissKeyboard.swift in Sources */, 4C216F382871EDE300040376 /* DirectMessageModel.swift in Sources */, diff --git a/damus/ContentView.swift b/damus/ContentView.swift @@ -80,6 +80,7 @@ struct ContentView: View { @State var thread_open: Bool = false @State var search_open: Bool = false @State var filter_state : FilterState = .posts_and_replies + @State private var isSideBarOpened = false @StateObject var home: HomeModel = HomeModel() @StateObject var user_settings = UserSettingsStore() @@ -220,46 +221,53 @@ struct ContentView: View { VStack(alignment: .leading, spacing: 0) { if let damus = self.damus_state { NavigationView { - MainContent(damus: damus) - .toolbar { - ToolbarItem(placement: .navigationBarLeading) { - let profile_model = ProfileModel(pubkey: damus_state!.pubkey, damus: damus_state!) - let followers_model = FollowersModel(damus_state: damus_state!, target: damus_state!.pubkey) - let prof_dest = ProfileView(damus_state: damus_state!, profile: profile_model, followers: followers_model) - - NavigationLink(destination: prof_dest) { - /// Verify that the user has a profile picture, if not display a generic SF Symbol - /// (Resolves an in-app error where ``Robohash`` pictures are not generated so the button dissapears - if let picture = damus_state?.profiles.lookup(id: pubkey)?.picture { - ProfilePicView(pubkey: damus_state!.pubkey, size: 32, highlight: .none, profiles: damus_state!.profiles, picture: picture) - } else { - Image(systemName: "person.fill") + ZStack { + VStack { + MainContent(damus: damus) + .toolbar() { + ToolbarItem(placement: .navigationBarLeading) { + let profile_model = ProfileModel(pubkey: damus_state!.pubkey, damus: damus_state!) + let followers_model = FollowersModel(damus_state: damus_state!, target: damus_state!.pubkey) + Button { + isSideBarOpened.toggle() + } label: { + let profile_model = ProfileModel(pubkey: damus_state!.pubkey, damus: damus_state!) + let followers_model = FollowersModel(damus_state: damus_state!, target: damus_state!.pubkey) + + if let picture = damus_state?.profiles.lookup(id: pubkey)?.picture { + ProfilePicView(pubkey: damus_state!.pubkey, size: 32, highlight: .none, profiles: damus_state!.profiles, picture: picture) + } else { + Image(systemName: "person.fill") + } + } } - } - .buttonStyle(PlainButtonStyle()) - } - - ToolbarItem(placement: .navigationBarTrailing) { - HStack(alignment: .center) { - if home.signal.signal != home.signal.max_signal { - Text("\(home.signal.signal)/\(home.signal.max_signal)", comment: "Fraction of how many of the user's relay servers that are operational.") - .font(.callout) - .foregroundColor(.gray) - } - - NavigationLink(destination: ConfigView(state: damus_state!).environmentObject(user_settings)) { - Label("", systemImage: "gear") + + ToolbarItem(placement: .navigationBarTrailing) { + HStack(alignment: .center) { + if home.signal.signal != home.signal.max_signal { + Text("\(home.signal.signal)/\(home.signal.max_signal)", comment: "Fraction of how many of the user's relay servers that are operational.") + .font(.callout) + .foregroundColor(.gray) + } + + } } - .buttonStyle(PlainButtonStyle()) } - } + } + + Color.clear + .overlay( + SideMenuView(damus_state: damus, isSidebarVisible: $isSideBarOpened) + ) + } + .navigationBarHidden(isSideBarOpened ? true: false) // Would prefer a different way of doing this. } .navigationViewStyle(.stack) + + TabBar(new_events: $home.new_events, selected: $selected_timeline, isSidebarVisible: $isSideBarOpened, action: switch_timeline) + .padding([.bottom], 8) } - - TabBar(new_events: $home.new_events, selected: $selected_timeline, action: switch_timeline) - .padding([.bottom], 8) } .onAppear() { self.connect() @@ -300,7 +308,6 @@ struct ContentView: View { guard let privkey = self.privkey else { return } - let ev = notif.object as! NostrEvent let boost = make_boost_event(pubkey: pubkey, privkey: privkey, boosted: ev) self.damus_state?.pool.send(.event(boost)) @@ -333,10 +340,10 @@ struct ContentView: View { let pk = target.pubkey if let ev = unfollow_user(pool: damus.pool, - our_contacts: damus.contacts.event, - pubkey: damus.pubkey, - privkey: privkey, - unfollow: pk) { + our_contacts: damus.contacts.event, + pubkey: damus.pubkey, + privkey: privkey, + unfollow: pk) { notify(.unfollowed, pk) damus.contacts.event = ev @@ -355,10 +362,10 @@ struct ContentView: View { } if let ev = follow_user(pool: damus.pool, - our_contacts: damus.contacts.event, - pubkey: damus.pubkey, - privkey: privkey, - follow: ReferencedId(ref_id: fnotify.pubkey, relay_id: nil, key: "p")) { + our_contacts: damus.contacts.event, + pubkey: damus.pubkey, + privkey: privkey, + follow: ReferencedId(ref_id: fnotify.pubkey, relay_id: nil, key: "p")) { notify(.followed, fnotify.pubkey) damus_state?.contacts.event = ev @@ -448,7 +455,6 @@ struct ContentView_Previews: PreviewProvider { } } - func get_since_time(last_event: NostrEvent?) -> Int64? { if let last_event = last_event { return last_event.created_at - 60 * 10 diff --git a/damus/Views/MainTabView.swift b/damus/Views/MainTabView.swift @@ -31,9 +31,9 @@ func timeline_bit(_ timeline: Timeline) -> Int { struct TabButton: View { let timeline: Timeline let img: String - @Binding var selected: Timeline? @Binding var new_events: NewEventsBits + @Binding var isSidebarVisible: Bool let action: (Timeline) -> () @@ -56,6 +56,7 @@ struct TabButton: View { Button(action: { action(timeline) new_events = NewEventsBits(prev: new_events, unsetting: timeline) + isSidebarVisible = false }) { Label("", systemImage: selected == timeline ? "\(img).fill" : img) .contentShape(Rectangle()) @@ -69,6 +70,7 @@ struct TabButton: View { struct TabBar: View { @Binding var new_events: NewEventsBits @Binding var selected: Timeline? + @Binding var isSidebarVisible: Bool let action: (Timeline) -> () @@ -76,10 +78,10 @@ struct TabBar: View { VStack { Divider() HStack { - TabButton(timeline: .home, img: "house", selected: $selected, new_events: $new_events, action: action).keyboardShortcut("1") - TabButton(timeline: .dms, img: "bubble.left.and.bubble.right", selected: $selected, new_events: $new_events, action: action).keyboardShortcut("2") - TabButton(timeline: .search, img: "magnifyingglass.circle", selected: $selected, new_events: $new_events, action: action).keyboardShortcut("3") - TabButton(timeline: .notifications, img: "bell", selected: $selected, new_events: $new_events, action: action).keyboardShortcut("4") + TabButton(timeline: .home, img: "house", selected: $selected, new_events: $new_events, isSidebarVisible: $isSidebarVisible, action: action).keyboardShortcut("1") + TabButton(timeline: .dms, img: "bubble.left.and.bubble.right", selected: $selected, new_events: $new_events, isSidebarVisible: $isSidebarVisible, action: action).keyboardShortcut("2") + TabButton(timeline: .search, img: "magnifyingglass.circle", selected: $selected, new_events: $new_events, isSidebarVisible: $isSidebarVisible, action: action).keyboardShortcut("3") + TabButton(timeline: .notifications, img: "bell", selected: $selected, new_events: $new_events, isSidebarVisible: $isSidebarVisible, action: action).keyboardShortcut("4") } } } diff --git a/damus/Views/ProfileName.swift b/damus/Views/ProfileName.swift @@ -144,7 +144,7 @@ struct EventProfileName: View { .padding([.trailing], 2) Text("@" + String(display_name ?? Profile.displayName(profile: profile, pubkey: pubkey))) - .foregroundColor(.gray) + .foregroundColor(Color("DamusMediumGrey")) .font(eventviewsize_to_font(size)) } else { Text(String(display_name ?? Profile.displayName(profile: profile, pubkey: pubkey))) diff --git a/damus/Views/SideMenuView.swift b/damus/Views/SideMenuView.swift @@ -0,0 +1,176 @@ +// +// SideMenuView.swift +// damus +// +// Created by Ben Weeks on 1/6/23. +// Ref: https://blog.logrocket.com/create-custom-collapsible-sidebar-swiftui/ + +import SwiftUI + +struct SideMenuView: View { + let damus_state: DamusState + @Binding var isSidebarVisible: Bool + + @State var confirm_logout: Bool = false + @StateObject var user_settings = UserSettingsStore() + + @Environment(\.colorScheme) var colorScheme + + var sideBarWidth = UIScreen.main.bounds.size.width * 0.65 + + func fillColor() -> Color { + colorScheme == .light ? Color("DamusWhite") : Color("DamusBlack") + } + + func textColor() -> Color { + colorScheme == .light ? Color("DamusBlack") : Color("DamusWhite") + } + + var body: some View { + ZStack { + GeometryReader { _ in + EmptyView() + } + .background(Color("DamusDarkGrey").opacity(0.6)) + .opacity(isSidebarVisible ? 1 : 0) + .animation(.easeInOut.delay(0.2), value: isSidebarVisible) + .onTapGesture { + isSidebarVisible.toggle() + } + content + } + .edgesIgnoringSafeArea(.all) + + } + + var content: some View { + HStack(alignment: .top) { + ZStack(alignment: .top) { + fillColor() + + VStack(alignment: .leading, spacing: 20) { + let profile = damus_state.profiles.lookup(id: damus_state.pubkey) + + if let picture = damus_state.profiles.lookup(id: damus_state.pubkey)?.picture { + ProfilePicView(pubkey: damus_state.pubkey, size: 60, highlight: .none, profiles: damus_state.profiles, picture: picture) + } else { + Image(systemName: "person.fill") + } + VStack(alignment: .leading) { + if let display_name = profile?.display_name { + Text(display_name) + .foregroundColor(textColor()) + .font(.title) + } + if let name = profile?.name { + Text("@" + name) + .foregroundColor(Color("DamusMediumGrey")) + .font(.body) + } + } + + Divider() + .padding(.trailing,40) + + /* + HStack(alignment: .bottom) { + Text("69,420") + .foregroundColor(.accentColor) + .font(.largeTitle) + Text("SATS") + .font(.caption) + .padding(.bottom,6) + } + + Divider() + .padding(.trailing,40) + */ + + // THERE IS A LIMIT OF 10 NAVIGATIONLINKS!!! (Consider some in other views) + + let followers = FollowersModel(damus_state: damus_state, target: damus_state.pubkey) + let profile_model = ProfileModel(pubkey: damus_state.pubkey, damus: damus_state) + + NavigationLink(destination: ProfileView(damus_state: damus_state, profile: profile_model, followers: followers)) { + Label("Profile", systemImage: "person") + .font(.title2) + .foregroundColor(textColor()) + } + .simultaneousGesture(TapGesture().onEnded { + isSidebarVisible = false + }) + + /* + NavigationLink(destination: EmptyView()) { + Label("Relays", systemImage: "xserve") + .font(.title2) + .foregroundColor(textColor()) + } + .simultaneousGesture(TapGesture().onEnded { + isSidebarVisible.toggle() + }) + */ + + /* + NavigationLink(destination: EmptyView()) { + Label("Wallet", systemImage: "bolt") + .font(.title2) + .foregroundColor(textColor()) + } + .simultaneousGesture(TapGesture().onEnded { + isSidebarVisible.toggle() + }) + */ + + NavigationLink(destination: ConfigView(state: damus_state).environmentObject(user_settings)) { + Label("App settings", systemImage: "gear") + .font(.title2) + .foregroundColor(textColor()) + } + .simultaneousGesture(TapGesture().onEnded { + isSidebarVisible = false + }) + + Spacer() + + Button(action: { + //ConfigView(state: damus_state) + confirm_logout = true + }, label: { + Label("Sign out", systemImage: "pip.exit") + .font(.title3) + .foregroundColor(textColor()) + }) + } + .padding(.top, 60) + .padding(.bottom, 40) + .padding(.leading, 40) + } + .frame(width: sideBarWidth) + .offset(x: isSidebarVisible ? 0 : -sideBarWidth) + .animation(.default, value: isSidebarVisible) + .onTapGesture { + isSidebarVisible.toggle() + } + .alert("Logout", isPresented: $confirm_logout) { + Button("Cancel") { + confirm_logout = false + } + Button("Logout") { + notify(.logout, ()) + } + } message: { + Text("Make sure your nsec account key is saved before you logout or you will lose access to this account") + } + + Spacer() + } + } +} + +struct Previews_SideMenuView_Previews: PreviewProvider { + static var previews: some View { + let ds = test_damus_state() + SideMenuView(damus_state: ds, isSidebarVisible: .constant(true)) + } +} diff --git a/damus/damusApp.swift b/damus/damusApp.swift @@ -14,10 +14,7 @@ struct damusApp: App { WindowGroup { MainView() } - } - - } struct MainView: View {