damus

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

commit 7372c4847daabcb7b3e4512339b8b69a71ac5d5a
parent b42f0ec5eb864749d204e1222339d896b5ff9c5d
Author: William Casarin <jb55@jb55.com>
Date:   Mon,  2 Jan 2023 15:18:42 -0800

perf: cache link previews

Changelog-Added: Cache link previews

Diffstat:
Mdamus.xcodeproj/project.pbxproj | 4++++
Mdamus/ContentView.swift | 3++-
Mdamus/Models/DamusState.swift | 4+++-
Mdamus/Util/Constants.swift | 2+-
Mdamus/Views/ChatView.swift | 4++--
Mdamus/Views/DMView.swift | 2+-
Mdamus/Views/EventView.swift | 2+-
Mdamus/Views/NoteContentView.swift | 24+++++++++++++++++++-----
Mdamus/Views/ProfileView.swift | 3+--
Mdamus/Views/ReplyQuoteView.swift | 5+++--
Mdamus/damusApp.swift | 1+
11 files changed, 38 insertions(+), 16 deletions(-)

diff --git a/damus.xcodeproj/project.pbxproj b/damus.xcodeproj/project.pbxproj @@ -51,6 +51,7 @@ 4C363AA428296DEE006E126D /* SearchModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C363AA328296DEE006E126D /* SearchModel.swift */; }; 4C363AA828297703006E126D /* InsertSort.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C363AA728297703006E126D /* InsertSort.swift */; }; 4C3A1D332960DB0500558C0F /* Markdown.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C3A1D322960DB0500558C0F /* Markdown.swift */; }; + 4C3A1D3729637E0500558C0F /* PreviewCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C3A1D3629637E0500558C0F /* PreviewCache.swift */; }; 4C3AC79B28306D7B00E1F516 /* Contacts.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C3AC79A28306D7B00E1F516 /* Contacts.swift */; }; 4C3AC79D2833036D00E1F516 /* FollowingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C3AC79C2833036D00E1F516 /* FollowingView.swift */; }; 4C3AC79F2833115300E1F516 /* FollowButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C3AC79E2833115300E1F516 /* FollowButtonView.swift */; }; @@ -207,6 +208,7 @@ 4C363AA328296DEE006E126D /* SearchModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchModel.swift; sourceTree = "<group>"; }; 4C363AA728297703006E126D /* InsertSort.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InsertSort.swift; sourceTree = "<group>"; }; 4C3A1D322960DB0500558C0F /* Markdown.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Markdown.swift; sourceTree = "<group>"; }; + 4C3A1D3629637E0500558C0F /* PreviewCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreviewCache.swift; sourceTree = "<group>"; }; 4C3AC79A28306D7B00E1F516 /* Contacts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Contacts.swift; sourceTree = "<group>"; }; 4C3AC79C2833036D00E1F516 /* FollowingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FollowingView.swift; sourceTree = "<group>"; }; 4C3AC79E2833115300E1F516 /* FollowButtonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FollowButtonView.swift; sourceTree = "<group>"; }; @@ -542,6 +544,7 @@ 4C216F352870A9A700040376 /* InputDismissKeyboard.swift */, 3169CAEC294FCCFC00EE4006 /* Constants.swift */, 3165648A295B70D500C64604 /* LinkView.swift */, + 4C3A1D3629637E0500558C0F /* PreviewCache.swift */, ); path = Util; sourceTree = "<group>"; @@ -842,6 +845,7 @@ 3169CAE6294E69C000EE4006 /* EmptyTimelineView.swift in Sources */, 4C3EA64928FF597700C48A62 /* bech32.c in Sources */, 4C90BD162839DB54008EE7EF /* NostrMetadata.swift in Sources */, + 4C3A1D3729637E0500558C0F /* PreviewCache.swift in Sources */, 4C3EA67528FF7A5A00C48A62 /* take.c in Sources */, 4C3AC7A12835A81400E1F516 /* SetupView.swift in Sources */, 4C06670128FC7C5900038D2A /* RelayView.swift in Sources */, diff --git a/damus/ContentView.swift b/damus/ContentView.swift @@ -423,7 +423,8 @@ struct ContentView: View { contacts: Contacts(), tips: TipCounter(our_pubkey: pubkey), profiles: Profiles(), - dms: home.dms + dms: home.dms, + previews: PreviewCache() ) home.damus_state = self.damus_state! diff --git a/damus/Models/DamusState.swift b/damus/Models/DamusState.swift @@ -6,6 +6,7 @@ // import Foundation +import LinkPresentation struct DamusState { let pool: RelayPool @@ -16,12 +17,13 @@ struct DamusState { let tips: TipCounter let profiles: Profiles let dms: DirectMessagesModel + let previews: PreviewCache var pubkey: String { return keypair.pubkey } static var empty: DamusState { - return DamusState.init(pool: RelayPool(), keypair: Keypair(pubkey: "", privkey: ""), likes: EventCounter(our_pubkey: ""), boosts: EventCounter(our_pubkey: ""), contacts: Contacts(), tips: TipCounter(our_pubkey: ""), profiles: Profiles(), dms: DirectMessagesModel()) + return DamusState.init(pool: RelayPool(), keypair: Keypair(pubkey: "", privkey: ""), likes: EventCounter(our_pubkey: ""), boosts: EventCounter(our_pubkey: ""), contacts: Contacts(), tips: TipCounter(our_pubkey: ""), profiles: Profiles(), dms: DirectMessagesModel(), previews: PreviewCache()) } } diff --git a/damus/Util/Constants.swift b/damus/Util/Constants.swift @@ -11,7 +11,7 @@ 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_DEMOS: DamusState = .empty static let EXAMPLE_EVENTS = [ NostrEvent(id: UUID().description, content: "Nostr - Damus... Haha get it? Bonjour Le Monde mon Ami! C'est la tres importante", pubkey: "3efdaebb1d8923ebd99c9e7ace3b4194ab45512e2be79c1b7d68d9243e0d2681"), diff --git a/damus/Views/ChatView.swift b/damus/Views/ChatView.swift @@ -96,7 +96,7 @@ struct ChatView: View { if let ref_id = thread.replies.lookup(event.id) { if !is_reply_to_prev() { - ReplyQuoteView(privkey: damus_state.keypair.privkey, quoter: event, event_id: ref_id, profiles: damus_state.profiles) + ReplyQuoteView(privkey: damus_state.keypair.privkey, quoter: event, event_id: ref_id, profiles: damus_state.profiles, previews: damus_state.previews) .frame(maxHeight: expand_reply ? nil : 100) .environmentObject(thread) .onTapGesture { @@ -106,7 +106,7 @@ struct ChatView: View { } } - NoteContentView(privkey: damus_state.keypair.privkey, event: event, profiles: damus_state.profiles, show_images: should_show_images(contacts: damus_state.contacts, ev: event, our_pubkey: damus_state.pubkey), artifacts: .just_content(event.content), size: .normal) + NoteContentView(privkey: damus_state.keypair.privkey, event: event, profiles: damus_state.profiles, previews: damus_state.previews, show_images: should_show_images(contacts: damus_state.contacts, ev: event, our_pubkey: damus_state.pubkey), artifacts: .just_content(event.content), size: .normal) if is_active || next_ev == nil || next_ev!.pubkey != event.pubkey { let bar = make_actionbar_model(ev: event, damus: damus_state) diff --git a/damus/Views/DMView.swift b/damus/Views/DMView.swift @@ -23,7 +23,7 @@ struct DMView: View { let should_show_img = should_show_images(contacts: damus_state.contacts, ev: event, our_pubkey: damus_state.pubkey) - NoteContentView(privkey: damus_state.keypair.privkey, event: event, profiles: damus_state.profiles, show_images: should_show_img, artifacts: .just_content(event.get_content(damus_state.keypair.privkey)), size: .normal) + NoteContentView(privkey: damus_state.keypair.privkey, event: event, profiles: damus_state.profiles, previews: damus_state.previews, show_images: should_show_img, artifacts: .just_content(event.get_content(damus_state.keypair.privkey)), size: .normal) .foregroundColor(is_ours ? Color.white : Color.primary) .padding(10) .background(is_ours ? Color.accentColor : Color.secondary.opacity(0.15)) diff --git a/damus/Views/EventView.swift b/damus/Views/EventView.swift @@ -249,7 +249,7 @@ struct EventView: View { let should_show_img = should_show_images(contacts: damus.contacts, ev: event, our_pubkey: damus.pubkey) - NoteContentView(privkey: damus.keypair.privkey, event: event, profiles: damus.profiles, show_images: should_show_img, artifacts: .just_content(content), size: self.size) + NoteContentView(privkey: damus.keypair.privkey, event: event, profiles: damus.profiles, previews: damus.previews, show_images: should_show_img, artifacts: .just_content(content), size: self.size) .frame(maxWidth: .infinity, alignment: .leading) .allowsHitTesting(!embedded) diff --git a/damus/Views/NoteContentView.swift b/damus/Views/NoteContentView.swift @@ -61,12 +61,13 @@ struct NoteContentView: View { let privkey: String? let event: NostrEvent let profiles: Profiles + let previews: PreviewCache let show_images: Bool @State var artifacts: NoteArtifacts - @State var metaData: LPLinkMetadata? = nil + @State var preview: LinkViewRepresentable? = nil let size: EventViewKind func MainContent() -> some View { @@ -89,8 +90,8 @@ struct NoteContentView: View { InvoicesView(invoices: artifacts.invoices) } - if show_images, self.metaData != nil { - LinkViewRepresentable(metadata: self.metaData) + if show_images, self.preview != nil { + self.preview } else { ForEach(artifacts.links, id:\.self) { link in LinkViewRepresentable(url: link) @@ -123,9 +124,22 @@ struct NoteContentView: View { } } .task { + if let preview = previews.lookup(self.event.id) { + switch preview { + case .value(let view): + self.preview = view + case .failed: + // don't try to refetch meta if we've failed + return + } + } + if show_images, artifacts.links.count == 1 { + let meta = await getMetaData(for: artifacts.links.first!) - self.metaData = await getMetaData(for: artifacts.links.first!) + let view = LinkViewRepresentable(metadata: meta) + previews.store(evid: self.event.id, preview: view) + self.preview = view } } } @@ -170,6 +184,6 @@ struct NoteContentView_Previews: PreviewProvider { let state = test_damus_state() let content = "hi there https://jb55.com/s/Oct12-150217.png 5739a762ef6124dd.jpg" let artifacts = NoteArtifacts(content: content, images: [], invoices: [], links: []) - NoteContentView(privkey: "", event: NostrEvent(content: content, pubkey: "pk"), profiles: state.profiles, show_images: true, artifacts: artifacts, size: .normal) + NoteContentView(privkey: "", event: NostrEvent(content: content, pubkey: "pk"), profiles: state.profiles, previews: PreviewCache(), show_images: true, artifacts: artifacts, size: .normal) } } diff --git a/damus/Views/ProfileView.swift b/damus/Views/ProfileView.swift @@ -306,8 +306,7 @@ struct ProfileView_Previews: PreviewProvider { func test_damus_state() -> DamusState { let pubkey = "3efdaebb1d8923ebd99c9e7ace3b4194ab45512e2be79c1b7d68d9243e0d2681" - let damus = DamusState(pool: RelayPool(), keypair: Keypair(pubkey: pubkey, privkey: "privkey"), likes: EventCounter(our_pubkey: pubkey), boosts: EventCounter(our_pubkey: pubkey), contacts: Contacts(), tips: TipCounter(our_pubkey: pubkey), profiles: Profiles(), dms: DirectMessagesModel()) - + let damus: DamusState = .empty let prof = Profile(name: "damus", display_name: "Damus", about: "iOS app!", picture: "https://damus.io/img/logo.png", website: "https://damus.io", lud06: nil, lud16: "jb55@sendsats.lol", nip05: "damus.io") let tsprof = TimestampedProfile(profile: prof, timestamp: 0) damus.profiles.add(id: pubkey, profile: tsprof) diff --git a/damus/Views/ReplyQuoteView.swift b/damus/Views/ReplyQuoteView.swift @@ -12,6 +12,7 @@ struct ReplyQuoteView: View { let quoter: NostrEvent let event_id: String let profiles: Profiles + let previews: PreviewCache @EnvironmentObject var thread: ThreadModel @@ -31,7 +32,7 @@ struct ReplyQuoteView: View { .foregroundColor(.gray) } - NoteContentView(privkey: privkey, event: event, profiles: profiles, show_images: false, artifacts: .just_content(event.content), size: .normal) + NoteContentView(privkey: privkey, event: event, profiles: profiles, previews: previews, show_images: false, artifacts: .just_content(event.content), size: .normal) .font(.callout) .foregroundColor(.accentColor) @@ -58,7 +59,7 @@ struct ReplyQuoteView_Previews: PreviewProvider { static var previews: some View { let s = test_damus_state() let quoter = NostrEvent(content: "a\nb\nc", pubkey: "pubkey") - ReplyQuoteView(privkey: s.keypair.privkey, quoter: quoter, event_id: "pubkey2", profiles: s.profiles) + ReplyQuoteView(privkey: s.keypair.privkey, quoter: quoter, event_id: "pubkey2", profiles: s.profiles, previews: PreviewCache()) .environmentObject(ThreadModel(event: quoter, damus_state: s)) } } diff --git a/damus/damusApp.swift b/damus/damusApp.swift @@ -7,6 +7,7 @@ import SwiftUI + @main struct damusApp: App { var body: some Scene {