From fbde055fc6e146457e53c4c1538cda5224420ed6 Mon Sep 17 00:00:00 2001 From: Sam DuBois Date: Sun, 18 Dec 2022 16:37:53 -0700 Subject: [PATCH] Making some changes to the view model. braking but we will survive --- damus.xcodeproj/project.pbxproj | 25 +++ .../xcshareddata/swiftpm/Package.resolved | 9 + damus/ContentView.swift | 168 ++---------------- damus/DamusViewModel.swift | 155 ++++++++++++++++ damus/Util/Constants.swift | 25 +++ damus/Views/TimelineView.swift | 31 +++- 6 files changed, 255 insertions(+), 158 deletions(-) create mode 100644 damus/DamusViewModel.swift create mode 100644 damus/Util/Constants.swift diff --git a/damus.xcodeproj/project.pbxproj b/damus.xcodeproj/project.pbxproj index 5b8e3207..1c788c32 100644 --- a/damus.xcodeproj/project.pbxproj +++ b/damus.xcodeproj/project.pbxproj @@ -7,6 +7,9 @@ objects = { /* Begin PBXBuildFile section */ + 3169CAEB294FCABA00EE4006 /* Shimmer in Frameworks */ = {isa = PBXBuildFile; productRef = 3169CAEA294FCABA00EE4006 /* Shimmer */; }; + 3169CAED294FCCFC00EE4006 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3169CAEC294FCCFC00EE4006 /* Constants.swift */; }; + 3169CAEF294FD4C400EE4006 /* DamusViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3169CAEE294FD4C400EE4006 /* DamusViewModel.swift */; }; 4C06670128FC7C5900038D2A /* RelayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C06670028FC7C5900038D2A /* RelayView.swift */; }; 4C06670428FC7EC500038D2A /* Kingfisher in Frameworks */ = {isa = PBXBuildFile; productRef = 4C06670328FC7EC500038D2A /* Kingfisher */; }; 4C06670628FCB08600038D2A /* ImageCarousel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C06670528FCB08600038D2A /* ImageCarousel.swift */; }; @@ -146,6 +149,8 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 3169CAEC294FCCFC00EE4006 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = Constants.swift; path = damus/Util/Constants.swift; sourceTree = SOURCE_ROOT; }; + 3169CAEE294FD4C400EE4006 /* DamusViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DamusViewModel.swift; sourceTree = ""; }; 4C06670028FC7C5900038D2A /* RelayView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayView.swift; sourceTree = ""; }; 4C06670528FCB08600038D2A /* ImageCarousel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageCarousel.swift; sourceTree = ""; }; 4C06670828FDE64700038D2A /* damus-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "damus-Bridging-Header.h"; sourceTree = ""; }; @@ -307,6 +312,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 3169CAEB294FCABA00EE4006 /* Shimmer in Frameworks */, 4C06670428FC7EC500038D2A /* Kingfisher in Frameworks */, 4CE6DF1227F7A2B300C66700 /* Starscream in Frameworks */, 4C649881286E0EE300EAE2B3 /* secp256k1 in Frameworks */, @@ -493,6 +499,7 @@ 4C285C8B28398BC6008A31F1 /* Keys.swift */, 4C90BD19283AA67F008EE7EF /* Bech32.swift */, 4C216F352870A9A700040376 /* InputDismissKeyboard.swift */, + 3169CAEC294FCCFC00EE4006 /* Constants.swift */, ); path = Util; sourceTree = ""; @@ -542,6 +549,7 @@ 4C75EFA72804823E0006080F /* Info.plist */, 4C75EFA227FA576C0006080F /* Views */, 4CE6DEE627F7A08100C66700 /* damusApp.swift */, + 3169CAEE294FD4C400EE4006 /* DamusViewModel.swift */, 4CE6DEE827F7A08100C66700 /* ContentView.swift */, 4CE6DEEA27F7A08200C66700 /* Assets.xcassets */, 4CE6DEEC27F7A08200C66700 /* Preview Content */, @@ -606,6 +614,7 @@ 4CE6DF1127F7A2B300C66700 /* Starscream */, 4C649880286E0EE300EAE2B3 /* secp256k1 */, 4C06670328FC7EC500038D2A /* Kingfisher */, + 3169CAEA294FCABA00EE4006 /* Shimmer */, ); productName = damus; productReference = 4CE6DEE327F7A08100C66700 /* damus.app */; @@ -684,6 +693,7 @@ 4CE6DF1027F7A2B300C66700 /* XCRemoteSwiftPackageReference "Starscream" */, 4C64987F286E0EE300EAE2B3 /* XCRemoteSwiftPackageReference "secp256k1" */, 4C06670228FC7EC500038D2A /* XCRemoteSwiftPackageReference "Kingfisher" */, + 3169CAE9294FCABA00EE4006 /* XCRemoteSwiftPackageReference "Shimmer" */, ); productRefGroup = 4CE6DEE427F7A08100C66700 /* Products */; projectDirPath = ""; @@ -729,6 +739,7 @@ files = ( 4C3AC79D2833036D00E1F516 /* FollowingView.swift in Sources */, 4C363A8A28236B57006E126D /* MentionView.swift in Sources */, + 3169CAEF294FD4C400EE4006 /* DamusViewModel.swift in Sources */, 4CE4F8CD281352B30009DFBB /* Notifications.swift in Sources */, 4C285C8428385690008A31F1 /* CreateAccountView.swift in Sources */, 4C216F34286F5ACD00040376 /* DMView.swift in Sources */, @@ -834,6 +845,7 @@ 4CEE2AF1280B216B00AB5EEF /* EventDetailView.swift in Sources */, 4C75EFBB2804A34C0006080F /* ProofOfWork.swift in Sources */, 4C3AC7A52836987600E1F516 /* MainTabView.swift in Sources */, + 3169CAED294FCCFC00EE4006 /* Constants.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1185,6 +1197,14 @@ /* End XCConfigurationList section */ /* Begin XCRemoteSwiftPackageReference section */ + 3169CAE9294FCABA00EE4006 /* XCRemoteSwiftPackageReference "Shimmer" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/joshuajhomann/Shimmer"; + requirement = { + branch = master; + kind = branch; + }; + }; 4C06670228FC7EC500038D2A /* XCRemoteSwiftPackageReference "Kingfisher" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/onevcat/Kingfisher"; @@ -1212,6 +1232,11 @@ /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ + 3169CAEA294FCABA00EE4006 /* Shimmer */ = { + isa = XCSwiftPackageProductDependency; + package = 3169CAE9294FCABA00EE4006 /* XCRemoteSwiftPackageReference "Shimmer" */; + productName = Shimmer; + }; 4C06670328FC7EC500038D2A /* Kingfisher */ = { isa = XCSwiftPackageProductDependency; package = 4C06670228FC7EC500038D2A /* XCRemoteSwiftPackageReference "Kingfisher" */; diff --git a/damus.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/damus.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 18554801..58468d85 100644 --- a/damus.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/damus.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -17,6 +17,15 @@ "revision" : "40b4b38b3b1c83f7088c76189a742870e0ca06a9" } }, + { + "identity" : "shimmer", + "kind" : "remoteSourceControl", + "location" : "https://github.com/joshuajhomann/Shimmer", + "state" : { + "branch" : "master", + "revision" : "2fde687b3f1d9c5409c53da095d3686361e41343" + } + }, { "identity" : "starscream", "kind" : "remoteSourceControl", diff --git a/damus/ContentView.swift b/damus/ContentView.swift index ac8a863e..cc976717 100644 --- a/damus/ContentView.swift +++ b/damus/ContentView.swift @@ -9,65 +9,9 @@ import SwiftUI import Starscream import Kingfisher -var BOOTSTRAP_RELAYS = [ - "wss://relay.damus.io", - "wss://nostr-relay.wlvs.space", - "wss://nostr.oxtr.dev", -] - -struct TimestampedProfile { - let profile: Profile - let timestamp: Int64 -} - -enum Sheets: Identifiable { - case post - case reply(NostrEvent) - - var id: String { - switch self { - case .post: return "post" - case .reply(let ev): return "reply-" + ev.id - } - } -} - -enum ThreadState { - case event_details - case chatroom -} - -enum FilterState : Int { - case posts_and_replies = 1 - case posts = 0 -} - struct ContentView: View { - let keypair: Keypair - var pubkey: String { - return keypair.pubkey - } - - var privkey: String? { - return keypair.privkey - } - - @State var status: String = "Not connected" - @State var active_sheet: Sheets? = nil - @State var damus_state: DamusState? = nil - @State var selected_timeline: Timeline? = .home - @State var is_thread_open: Bool = false - @State var is_profile_open: Bool = false - @State var event: NostrEvent? = nil - @State var active_profile: String? = nil - @State var active_search: NostrFilter? = nil - @State var active_event_id: String? = nil - @State var profile_open: Bool = false - @State var thread_open: Bool = false - @State var search_open: Bool = false - @State var filter_state : FilterState = .posts_and_replies - @StateObject var home: HomeModel = HomeModel() + @EnvironmentObject var viewModel: DamusViewModel // connect retry timer let timer = Timer.publish(every: 4, on: .main, in: .common).autoconnect() @@ -80,9 +24,9 @@ struct ContentView: View { VStack{ ZStack { if let damus = self.damus_state { - TimelineView(events: $home.events, loading: $home.loading, damus: damus, show_friend_icon: false, filter: filter_event) + TimelineView(events: viewModel.$home.events, loading: viewModel.$home.loading, damus: damus, show_friend_icon: false, filter: filter_event) } - if privkey != nil { + if viewModel.privkey != nil { PostButtonContainer { self.active_sheet = .post } @@ -103,7 +47,7 @@ struct ContentView: View { var FiltersView: some View { VStack{ - Picker("Filter State", selection: $filter_state) { + Picker("Filter State", selection: $viewModel.filter_state) { Text("Posts").tag(FilterState.posts) Text("Posts & Replies").tag(FilterState.posts_and_replies) } @@ -112,7 +56,7 @@ struct ContentView: View { } func filter_event(_ ev: NostrEvent) -> Bool { - if self.filter_state == .posts { + if viewModel.filter_state == .posts { return !ev.is_reply(nil) } @@ -121,18 +65,18 @@ struct ContentView: View { func MainContent(damus: DamusState) -> some View { VStack { - NavigationLink(destination: MaybeProfileView, isActive: $profile_open) { + NavigationLink(destination: MaybeProfileView, isActive: $viewModel.profile_open) { EmptyView() } - NavigationLink(destination: MaybeThreadView, isActive: $thread_open) { + NavigationLink(destination: MaybeThreadView, isActive: $viewModel.thread_open) { EmptyView() } - NavigationLink(destination: MaybeSearchView, isActive: $search_open) { + NavigationLink(destination: MaybeSearchView, isActive: $viewModel.search_open) { EmptyView() } switch selected_timeline { case .search: - SearchHomeView(damus_state: damus_state!, model: SearchHomeModel(damus_state: damus_state!)) + SearchHomeView(damus_state: viewModel.state!, model: SearchHomeModel(damus_state: damus_state!)) case .home: PostingTimelineView @@ -209,18 +153,16 @@ struct ContentView: View { } ToolbarItem(placement: .navigationBarTrailing) { - HStack(alignment: .center) { - if home.signal.signal != home.signal.max_signal { - Text("\(home.signal.signal)/\(home.signal.max_signal)") - .font(.callout) - .foregroundColor(.gray) - } - NavigationLink(destination: ConfigView(state: damus_state!)) { - Label("", systemImage: "gear") + if #available(iOS 16.0, *) { + Image(systemName: "chart.bar.fill", variableValue: Double(home.signal.signal) / Double(home.signal.max_signal)) + .font(.body.weight(.ultraLight)) + .symbolRenderingMode(.hierarchical) + } else { + // Fallback on earlier versions + } } .buttonStyle(PlainButtonStyle()) - } } } } @@ -359,54 +301,6 @@ struct ContentView: View { self.damus_state?.pool.connect_to_disconnected() } } - - func switch_timeline(_ timeline: Timeline) { - NotificationCenter.default.post(name: .switched_timeline, object: timeline) - - if timeline == self.selected_timeline { - NotificationCenter.default.post(name: .scroll_to_top, object: nil) - return - } - - self.selected_timeline = timeline - //NotificationCenter.default.post(name: .switched_timeline, object: timeline) - //self.selected_timeline = timeline - } - - func add_relay(_ pool: RelayPool, _ relay: String) { - //add_rw_relay(pool, "wss://nostr-pub.wellorder.net") - add_rw_relay(pool, relay) - /* - let profile = Profile(name: relay, about: nil, picture: nil) - let ts = Int64(Date().timeIntervalSince1970) - let tsprofile = TimestampedProfile(profile: profile, timestamp: ts) - damus!.profiles.add(id: relay, profile: tsprofile) - */ - } - - func connect() { - let pool = RelayPool() - - for relay in BOOTSTRAP_RELAYS { - add_relay(pool, relay) - } - - pool.register_handler(sub_id: sub_id, handler: home.handle_event) - - self.damus_state = DamusState(pool: pool, keypair: keypair, - likes: EventCounter(our_pubkey: pubkey), - boosts: EventCounter(our_pubkey: pubkey), - contacts: Contacts(), - tips: TipCounter(our_pubkey: pubkey), - profiles: Profiles(), - dms: home.dms - ) - home.damus_state = self.damus_state! - - pool.connect() - } - - } struct ContentView_Previews: PreviewProvider { @@ -424,31 +318,6 @@ func get_since_time(last_event: NostrEvent?) -> Int64? { return nil } -func ws_nostr_event(relay: String, ev: WebSocketEvent) -> NostrEvent? { - switch ev { - case .binary(let dat): - return NostrEvent(content: "binary data? \(dat.count) bytes", pubkey: relay) - case .cancelled: - return NostrEvent(content: "cancelled", pubkey: relay) - case .connected: - return NostrEvent(content: "connected", pubkey: relay) - case .disconnected: - return NostrEvent(content: "disconnected", pubkey: relay) - case .error(let err): - return NostrEvent(content: "error \(err.debugDescription)", pubkey: relay) - case .text(let txt): - return NostrEvent(content: "text \(txt)", pubkey: relay) - case .pong: - return NostrEvent(content: "pong", pubkey: relay) - case .ping: - return NostrEvent(content: "ping", pubkey: relay) - case .viabilityChanged(let b): - return NostrEvent(content: "viabilityChanged \(b)", pubkey: relay) - case .reconnectSuggested(let b): - return NostrEvent(content: "reconnectSuggested \(b)", pubkey: relay) - } -} - func is_notification(ev: NostrEvent, pubkey: String) -> Bool { if ev.pubkey == pubkey { return false @@ -468,11 +337,6 @@ extension UINavigationController: UIGestureRecognizerDelegate { } } -struct LastNotification { - let id: String - let created_at: Int64 -} - func get_last_event(_ timeline: Timeline) -> LastNotification? { let str = timeline.rawValue let last = UserDefaults.standard.string(forKey: "last_\(str)") diff --git a/damus/DamusViewModel.swift b/damus/DamusViewModel.swift new file mode 100644 index 00000000..ea86ffb0 --- /dev/null +++ b/damus/DamusViewModel.swift @@ -0,0 +1,155 @@ +// +// DamusViewModel.swift +// damus +// +// Created by Sam DuBois on 12/18/22. +// + +import SwiftUI +import Starscream +import Kingfisher + +class DamusViewModel: ObservableObject { + + // MARK: Constants and Variables + + /// User Keypair object + let keypair: Keypair + + var pubkey: String { + return keypair.pubkey + } + + var privkey: String? { + return keypair.privkey + } + + /// Default relays to be used when setting up the user's account. + var BOOTSTRAP_RELAYS = [ + "wss://relay.damus.io", + "wss://nostr-relay.wlvs.space", + "wss://nostr.oxtr.dev", + ] + + @Published var status: String = "Not connected" + @Published var state: DamusState? = nil + @Published var active_sheet: Sheets? = nil + @Published var selected_timeline: Timeline? = .home + @Published var is_thread_open: Bool = false + @Published var is_profile_open: Bool = false + @Published var event: NostrEvent? = nil + @Published var active_profile: String? = nil + @Published var active_search: String? = nil + @Published var active_event_id: String? = nil + @Published var profile_open: Bool = false + @Published var thread_open: Bool = false + @Published var search_open: Bool = false + @Published var filter_state: FilterState = .posts_and_replies + @Published var home: HomeModel = HomeModel() + + // MARK: Functionality + + func switch_timeline(_ timeline: Timeline) { + NotificationCenter.default.post(name: .switched_timeline, object: timeline) + + if timeline == self.selected_timeline { + NotificationCenter.default.post(name: .scroll_to_top, object: nil) + return + } + + self.selected_timeline = timeline + //NotificationCenter.default.post(name: .switched_timeline, object: timeline) + //self.selected_timeline = timeline + } + + func add_relay(_ pool: RelayPool, _ relay: String) { + //add_rw_relay(pool, "wss://nostr-pub.wellorder.net") + add_rw_relay(pool, relay) + /* + let profile = Profile(name: relay, about: nil, picture: nil) + let ts = Int64(Date().timeIntervalSince1970) + let tsprofile = TimestampedProfile(profile: profile, timestamp: ts) + damus!.profiles.add(id: relay, profile: tsprofile) + */ + } + + func connect() { + let pool = RelayPool() + + for relay in BOOTSTRAP_RELAYS { + add_relay(pool, relay) + } + + pool.register_handler(sub_id: sub_id, handler: home.handle_event) + + self.state = DamusState(pool: pool, keypair: keypair, + likes: EventCounter(our_pubkey: pubkey), + boosts: EventCounter(our_pubkey: pubkey), + contacts: Contacts(), + tips: TipCounter(our_pubkey: pubkey), + profiles: Profiles(), + dms: home.dms + ) + home.damus_state = self.state! + + pool.connect() + } + +} + +struct TimestampedProfile { + let profile: Profile + let timestamp: Int64 +} + +enum Sheets: Identifiable { + case post + case reply(NostrEvent) + + var id: String { + switch self { + case .post: return "post" + case .reply(let ev): return "reply-" + ev.id + } + } +} + +enum ThreadState { + case event_details + case chatroom +} + +enum FilterState : Int { + case posts_and_replies = 1 + case posts = 0 +} + +func ws_nostr_event(relay: String, ev: WebSocketEvent) -> NostrEvent? { + switch ev { + case .binary(let dat): + return NostrEvent(content: "binary data? \(dat.count) bytes", pubkey: relay) + case .cancelled: + return NostrEvent(content: "cancelled", pubkey: relay) + case .connected: + return NostrEvent(content: "connected", pubkey: relay) + case .disconnected: + return NostrEvent(content: "disconnected", pubkey: relay) + case .error(let err): + return NostrEvent(content: "error \(err.debugDescription)", pubkey: relay) + case .text(let txt): + return NostrEvent(content: "text \(txt)", pubkey: relay) + case .pong: + return NostrEvent(content: "pong", pubkey: relay) + case .ping: + return NostrEvent(content: "ping", pubkey: relay) + case .viabilityChanged(let b): + return NostrEvent(content: "viabilityChanged \(b)", pubkey: relay) + case .reconnectSuggested(let b): + return NostrEvent(content: "reconnectSuggested \(b)", pubkey: relay) + } +} + +struct LastNotification { + let id: String + let created_at: Int64 +} diff --git a/damus/Util/Constants.swift b/damus/Util/Constants.swift new file mode 100644 index 00000000..cca0fb44 --- /dev/null +++ b/damus/Util/Constants.swift @@ -0,0 +1,25 @@ +// +// Constants.swift +// damus +// +// Created by Sam DuBois on 12/18/22. +// + +import Foundation + +public class Constants { + + static let PUB_KEY = "3efdaebb1d8923ebd99c9e7ace3b4194ab45512e2be79c1b7d68d9243e0d2681" + + static let EXAMPLE_DEMOS = DamusState(pool: RelayPool(), keypair: Keypair(pubkey: PUB_KEY, privkey: "privkey"), likes: EventCounter(our_pubkey: PUB_KEY), boosts: EventCounter(our_pubkey: PUB_KEY), contacts: Contacts(), tips: TipCounter(our_pubkey: PUB_KEY), profiles: Profiles(), dms: DirectMessagesModel()) + + static let EXAMPLE_EVENTS = [ + NostrEvent(content: "Icecream", pubkey: "3efdaebb1d8923ebd99c9e7ace3b4194ab45512e2be79c1b7d68d9243e0d2681"), + NostrEvent(content: "This is a test for a really long note that somebody sent because they thought they were super cool or maybe they were just really excited to share something with the world.", pubkey: "3efdaebb1d8923ebd99c9e7ace3b4194ab45512e2be79c1b7d68d9243e0d2681"), + NostrEvent(content: "Bonjour Le Monde", pubkey: "3efdaebb1d8923ebd99c9e7ace3b4194ab45512e2be79c1b7d68d9243e0d2681"), + NostrEvent(content: "Why am I helping on this app? Because it's fun!", pubkey: "3efdaebb1d8923ebd99c9e7ace3b4194ab45512e2be79c1b7d68d9243e0d2681"), + NostrEvent(content: "PIzza", pubkey: "3efdaebb1d8923ebd99c9e7ace3b4194ab45512e2be79c1b7d68d9243e0d2681"), + NostrEvent(content: "Hello World! This is so cool!", pubkey: "3efdaebb1d8923ebd99c9e7ace3b4194ab45512e2be79c1b7d68d9243e0d2681"), + NostrEvent(content: "Nostr - Damus... Haha get it?", pubkey: "3efdaebb1d8923ebd99c9e7ace3b4194ab45512e2be79c1b7d68d9243e0d2681"), + ] +} diff --git a/damus/Views/TimelineView.swift b/damus/Views/TimelineView.swift index d36e33c4..9c5d58eb 100644 --- a/damus/Views/TimelineView.swift +++ b/damus/Views/TimelineView.swift @@ -6,6 +6,7 @@ // import SwiftUI +import Shimmer enum TimelineAction { case chillin @@ -33,6 +34,28 @@ struct InnerTimelineView: View { } } .padding(.horizontal) + .refreshable { + <#code#> + } + } +} + +struct InnerTimelineRedactedView: View { + let events: [NostrEvent] + let damus: DamusState + let show_friend_icon: Bool + + var body: some View { + VStack { + ForEach(events, id: \.id) { event in + EventView(event: event, highlight: .none, has_action_bar: true, damus: damus, show_friend_icon: show_friend_icon) + .buttonStyle(PlainButtonStyle()) + } + } + .shimmer() + .redacted(reason: .placeholder) + .padding(.horizontal) + .disabled(true) } } @@ -52,8 +75,7 @@ struct TimelineView: View { ScrollViewReader { scroller in ScrollView { if loading { - ProgressView() - .progressViewStyle(.circular) + InnerTimelineRedactedView(events: Constants.EXAMPLE_EVENTS, damus: damus, show_friend_icon: true) } InnerTimelineView(events: $events, damus: damus, show_friend_icon: show_friend_icon, filter: filter) } @@ -67,13 +89,11 @@ struct TimelineView: View { } } -/* struct TimelineView_Previews: PreviewProvider { static var previews: some View { - TimelineView() + TimelineView(events: .constant(Constants.EXAMPLE_EVENTS), loading: .constant(true), damus: Constants.EXAMPLE_DEMOS, show_friend_icon: true, filter: { _ in true }) } } - */ struct NavigationLazyView: View { @@ -85,4 +105,3 @@ struct NavigationLazyView: View { build() } } -