From 442a50f9ae8fbe9caadff2fcfd3fbef62a81c236 Mon Sep 17 00:00:00 2001 From: aki-mizu Date: Fri, 6 Jan 2023 17:21:06 +0900 Subject: [PATCH 01/10] update markdown format guides in Formatting Notes In iOS for strikethrough only one tilde symbol is needed before and after words Closes: #269 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 993a208..ce8044c 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ damus implements the following [Nostr Implementation Possibilities][nips] - Formatting Notes (may not format as intended in other web clients) - Italics: 1 asterisk `*italic*` - Bold: 2 asterisk `**bold**` - - Strikethrough: 2 tildes `~~strikethrough~~` + - Strikethrough: 1 tildes `~strikethrough~` - Code: 1 back-tick ``code`` #### 💬 Encrypted DMs (chat app, bottom navigation) From e7d32d9ea76f83a92edea1b2a3b8cb6b60714a20 Mon Sep 17 00:00:00 2001 From: William Casarin Date: Fri, 6 Jan 2023 10:18:31 -0800 Subject: [PATCH 02/10] pool: queue requests if we're disconnected Changelog-Fixed: Don't spin forever if we're temporarily disconnected --- damus/Nostr/RelayPool.swift | 59 +++++++++++++++++++++++++++++++++++-- 1 file changed, 57 insertions(+), 2 deletions(-) diff --git a/damus/Nostr/RelayPool.swift b/damus/Nostr/RelayPool.swift index 59d32e7..ee6fb0a 100644 --- a/damus/Nostr/RelayPool.swift +++ b/damus/Nostr/RelayPool.swift @@ -28,9 +28,20 @@ struct RelayHandler { let callback: (String, NostrConnectionEvent) -> () } +struct QueuedRequest { + let req: NostrRequest + let relay: String +} + +struct NostrRequestId: Equatable, Hashable { + let relay: String? + let sub_id: String +} + class RelayPool { var relays: [Relay] = [] var handlers: [RelayHandler] = [] + var request_queue: [QueuedRequest] = [] var descriptors: [RelayDescriptor] { relays.map { $0.descriptor } @@ -148,13 +159,38 @@ class RelayPool { send(.subscribe(.init(filters: filters, sub_id: sub_id)), to: to) } + func count_queued(relay: String) -> Int { + var c = 0 + for request in request_queue { + if request.relay == relay { + c += 1 + } + } + + return c + } + + func queue_req(r: NostrRequest, relay: String) { + let count = count_queued(relay: relay) + guard count <= 10 else { + print("can't queue, too many queued events for \(relay)") + return + } + + print("queueing request: \(r) for \(relay)") + request_queue.append(QueuedRequest(req: r, relay: relay)) + } + func send(_ req: NostrRequest, to: [String]? = nil) { let relays = to.map{ get_relays($0) } ?? self.relays for relay in relays { - if relay.connection.isConnected { - relay.connection.send(req) + guard relay.connection.isConnected else { + queue_req(r: req, relay: relay.id) + continue } + + relay.connection.send(req) } } @@ -193,9 +229,28 @@ class RelayPool { } } + func run_queue(_ relay_id: String) { + self.request_queue = request_queue.reduce(into: Array()) { (q, req) in + guard req.relay == relay_id else { + q.append(req) + return + } + + print("running queueing request: \(req.req) for \(relay)") + self.send(req.req, to: [relay_id]) + } + } + func handle_event(relay_id: String, event: NostrConnectionEvent) { record_last_pong(relay_id: relay_id, event: event) + // run req queue when we reconnect + if case .ws_event(let ws) = event { + if case .connected = ws { + run_queue(relay_id) + } + } + // handle reconnect logic, etc? for handler in handlers { handler.callback(relay_id, event) From 04e4bc7985a173c6b31855e5751dcef251ebcdaa Mon Sep 17 00:00:00 2001 From: William Casarin Date: Fri, 6 Jan 2023 10:23:39 -0800 Subject: [PATCH 03/10] ocd refactor in load_profiles --- damus/Models/SearchHomeModel.swift | 37 ++++++++++++++++-------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/damus/Models/SearchHomeModel.swift b/damus/Models/SearchHomeModel.swift index 423e8ac..9645a04 100644 --- a/damus/Models/SearchHomeModel.swift +++ b/damus/Models/SearchHomeModel.swift @@ -112,27 +112,30 @@ func load_profiles(profiles_subid: String, relay_id: String, events: [NostrEvent let authors = find_profiles_to_fetch(profiles: damus_state.profiles, events: events) filter.authors = authors - if !authors.isEmpty { - print("loading \(authors.count) profiles from \(relay_id)") - damus_state.pool.subscribe_to(sub_id: profiles_subid, filters: [filter], to: [relay_id]) { sub_id, conn_ev in - let (sid, done) = handle_subid_event(pool: damus_state.pool, relay_id: relay_id, ev: conn_ev) { sub_id, ev in - guard sub_id == profiles_subid else { - return - } - - if ev.known_kind == .metadata { - process_metadata_event(profiles: damus_state.profiles, ev: ev) - } - + guard !authors.isEmpty else { + return + } + + print("loading \(authors.count) profiles from \(relay_id)") + + damus_state.pool.subscribe_to(sub_id: profiles_subid, filters: [filter], to: [relay_id]) { sub_id, conn_ev in + let (sid, done) = handle_subid_event(pool: damus_state.pool, relay_id: relay_id, ev: conn_ev) { sub_id, ev in + guard sub_id == profiles_subid else { + return } - guard done && sid == profiles_subid else { - return + if ev.known_kind == .metadata { + process_metadata_event(profiles: damus_state.profiles, ev: ev) } - - print("done loading \(authors.count) profiles from \(relay_id)") - damus_state.pool.unsubscribe(sub_id: profiles_subid, to: [relay_id]) + } + + guard done && sid == profiles_subid else { + return + } + + print("done loading \(authors.count) profiles from \(relay_id)") + damus_state.pool.unsubscribe(sub_id: profiles_subid, to: [relay_id]) } } From d5857325b1a067dceb0cf2d9ff4041a74c3af4e9 Mon Sep 17 00:00:00 2001 From: William Casarin Date: Fri, 6 Jan 2023 10:24:08 -0800 Subject: [PATCH 04/10] Universal Links: share notes! Changelog-Added: Added universal link sharing of notes --- damus/Nostr/NostrLink.swift | 25 +++++++++++++++++++++++++ damus/Nostr/RelayConnection.swift | 2 +- damus/Views/EventActionBar.swift | 17 ++++++++++++++++- damus/damus.entitlements | 4 ++++ 4 files changed, 46 insertions(+), 2 deletions(-) diff --git a/damus/Nostr/NostrLink.swift b/damus/Nostr/NostrLink.swift index ca6ff31..93ae2e1 100644 --- a/damus/Nostr/NostrLink.swift +++ b/damus/Nostr/NostrLink.swift @@ -80,7 +80,32 @@ func parse_nostr_ref_uri(_ p: Parser) -> ReferencedId? { return ReferencedId(ref_id: pk, relay_id: nil, key: typ) } +func decode_universal_link(_ s: String) -> NostrLink? { + var uri = s.replacingOccurrences(of: "https://damus.io/r/", with: "") + uri = uri.replacingOccurrences(of: "https://damus.io/", with: "") + uri = uri.replacingOccurrences(of: "/", with: "") + + guard let decoded = try? bech32_decode(uri) else { + return nil + } + + let h = hex_encode(decoded.data) + + if decoded.hrp == "note" { + return .ref(ReferencedId(ref_id: h, relay_id: nil, key: "e")) + } else if decoded.hrp == "npub" { + return .ref(ReferencedId(ref_id: h, relay_id: nil, key: "p")) + } + // TODO: handle nprofile, etc + + return nil +} + func decode_nostr_uri(_ s: String) -> NostrLink? { + if s.starts(with: "https://damus.io/") { + return decode_universal_link(s) + } + var uri = s.replacingOccurrences(of: "nostr://", with: "") uri = uri.replacingOccurrences(of: "nostr:", with: "") diff --git a/damus/Nostr/RelayConnection.swift b/damus/Nostr/RelayConnection.swift index eb85057..3f3454e 100644 --- a/damus/Nostr/RelayConnection.swift +++ b/damus/Nostr/RelayConnection.swift @@ -38,7 +38,7 @@ class RelayConnection: WebSocketDelegate { self.connect(force: true) } } - + func connect(force: Bool = false){ if !force && (self.isConnected || self.isConnecting) { return diff --git a/damus/Views/EventActionBar.swift b/damus/Views/EventActionBar.swift index 9afaf9e..a882f44 100644 --- a/damus/Views/EventActionBar.swift +++ b/damus/Views/EventActionBar.swift @@ -24,6 +24,7 @@ struct EventActionBar: View { let generator = UIImpactFeedbackGenerator(style: .medium) @State var sheet: ActionBarSheet? = nil @State var confirm_boost: Bool = false + @State var show_share_sheet: Bool = false @StateObject var bar: ActionBarModel var body: some View { @@ -40,6 +41,7 @@ struct EventActionBar: View { EventActionButton(img: "bubble.left", col: nil) { notify(.reply, event) } + .frame(minWidth: 0, maxWidth: .infinity, alignment: .leading) } HStack(alignment: .bottom) { @@ -55,6 +57,7 @@ struct EventActionBar: View { } } } + .frame(minWidth: 0, maxWidth: .infinity, alignment: .leading) HStack(alignment: .bottom) { Text("\(bar.likes > 0 ? "\(bar.likes)" : "")") @@ -69,6 +72,12 @@ struct EventActionBar: View { } } } + .frame(minWidth: 0, maxWidth: .infinity, alignment: .leading) + + EventActionButton(img: "square.and.arrow.up", col: Color.gray) { + show_share_sheet = true + } + .frame(minWidth: 0, maxWidth: .infinity, alignment: .leading) /* HStack(alignment: .bottom) { @@ -86,6 +95,13 @@ struct EventActionBar: View { } */ } + .sheet(isPresented: $show_share_sheet) { + if let note_id = bech32_note_id(event.id) { + if let url = URL(string: "https://damus.io/" + note_id) { + ShareSheet(activityItems: [url]) + } + } + } .alert("Boost", isPresented: $confirm_boost) { Button("Cancel") { confirm_boost = false @@ -142,7 +158,6 @@ func EventActionButton(img: String, col: Color?, action: @escaping () -> ()) -> .font(.footnote.weight(.medium)) .foregroundColor(col == nil ? Color.gray : col!) } - .padding(.trailing, 40) } struct LikeButton: View { diff --git a/damus/damus.entitlements b/damus/damus.entitlements index f8f9346..723c4f5 100644 --- a/damus/damus.entitlements +++ b/damus/damus.entitlements @@ -4,6 +4,10 @@ aps-environment development + com.apple.developer.associated-domains + + applinks:damus.io + keychain-access-groups $(AppIdentifierPrefix)com.jb55.damus2 From a153515366d27b95630ad795be2e42963ebc8552 Mon Sep 17 00:00:00 2001 From: William Casarin Date: Fri, 6 Jan 2023 10:41:56 -0800 Subject: [PATCH 05/10] Fix compilation --- damus/Nostr/RelayPool.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/damus/Nostr/RelayPool.swift b/damus/Nostr/RelayPool.swift index ee6fb0a..61cf882 100644 --- a/damus/Nostr/RelayPool.swift +++ b/damus/Nostr/RelayPool.swift @@ -236,7 +236,7 @@ class RelayPool { return } - print("running queueing request: \(req.req) for \(relay)") + print("running queueing request: \(req.req) for \(relay_id)") self.send(req.req, to: [relay_id]) } } From b03bef2f8ba9f80db978eb54b8d802ba910c0f70 Mon Sep 17 00:00:00 2001 From: William Casarin Date: Fri, 6 Jan 2023 14:01:57 -0800 Subject: [PATCH 06/10] Profile: Add Universal Link share button Changelog-Added: Added share button to profile --- damus/Views/ProfileView.swift | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/damus/Views/ProfileView.swift b/damus/Views/ProfileView.swift index 24ab862..1636a2f 100644 --- a/damus/Views/ProfileView.swift +++ b/damus/Views/ProfileView.swift @@ -113,6 +113,7 @@ struct ProfileView: View { @State private var showingEditProfile = false @State var showing_select_wallet: Bool = false @State var is_zoomed: Bool = false + @State var show_share_sheet: Bool = false @StateObject var user_settings = UserSettingsStore() @Environment(\.dismiss) var dismiss @@ -165,7 +166,19 @@ struct ProfileView: View { } static let markdown = Markdown() - + + var ShareButton: some View { + Button(action: { + show_share_sheet = true + }) { + Image(systemName: "square.and.arrow.up.circle.fill") + .symbolRenderingMode(.palette) + .font(.system(size: 32)) + .padding() + .foregroundStyle(.white, .black, .black.opacity(0.8)) + } + } + var DMButton: some View { let dm_model = damus_state.dms.lookup_or_create(profile.pubkey) let dmview = DMChatView(damus_state: damus_state, pubkey: profile.pubkey) @@ -186,6 +199,9 @@ struct ProfileView: View { .aspectRatio(contentMode: .fill) .frame(width: geo.size.width, height: 150) .clipped() + + ShareButton + .offset(x: geo.size.width - 80.0, y: 50.0 ) } VStack(alignment: .leading) { let data = damus_state.profiles.lookup(id: profile.pubkey) @@ -200,7 +216,7 @@ struct ProfileView: View { ProfilePicView(pubkey: profile.pubkey, size: zoom_size, highlight: .none, profiles: damus_state.profiles) } .offset(y: -(pfp_size/2.0)) // Increase if set a frame - + Spacer() Group { @@ -223,10 +239,12 @@ struct ProfileView: View { EditButton(damus_state: damus_state) } } + } .offset(y: -15.0) // Increase if set a frame + } - + ProfileNameView(pubkey: profile.pubkey, profile: data, damus: damus_state) //.padding(.bottom) .padding(.top,-(pfp_size/2.0)) @@ -324,6 +342,13 @@ struct ProfileView: View { followers.unsubscribe() // our profilemodel needs a bit more help } + .sheet(isPresented: $show_share_sheet) { + if let npub = bech32_pubkey(profile.pubkey) { + if let url = URL(string: "https://damus.io/" + npub) { + ShareSheet(activityItems: [url]) + } + } + } .ignoresSafeArea() } } From 7081c4ac6968e7eacb0e55fd614204da8292035b Mon Sep 17 00:00:00 2001 From: William Casarin Date: Fri, 6 Jan 2023 14:09:41 -0800 Subject: [PATCH 07/10] Profile: make key high-res and reintroduce key color Changelog-Fixed: High res color pubkey on profile page --- damus/Views/ProfileView.swift | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/damus/Views/ProfileView.swift b/damus/Views/ProfileView.swift index 1636a2f..7cf7a22 100644 --- a/damus/Views/ProfileView.swift +++ b/damus/Views/ProfileView.swift @@ -405,15 +405,11 @@ struct KeyView: View { isCopied = false } } label: { - Label { - Text("Public key") - } icon: { - Image("ic-key") - .contentShape(Rectangle()) - .frame(width: 16, height: 16) - } - .labelStyle(IconOnlyLabelStyle()) - .symbolRenderingMode(.hierarchical) + Label("Public Key", systemImage: "key.fill") + .font(.custom("key", size: 12.0)) + .labelStyle(IconOnlyLabelStyle()) + .foregroundStyle(hex_to_rgb(pubkey)) + .symbolRenderingMode(.palette) } .padding(.leading,4) Text(abbrev_pubkey(bech32, amount: 16)) From d878ff6fdb763dc5efbc9056f94902673d22f514 Mon Sep 17 00:00:00 2001 From: OlegAba Date: Fri, 6 Jan 2023 11:38:11 -0500 Subject: [PATCH 08/10] Add system background color to profile pics Changelog-Fixed: Add system background color to profile pics Closes: #270 --- damus/Views/ProfilePicView.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/damus/Views/ProfilePicView.swift b/damus/Views/ProfilePicView.swift index d32e8bf..305cddd 100644 --- a/damus/Views/ProfilePicView.swift +++ b/damus/Views/ProfilePicView.swift @@ -51,7 +51,9 @@ struct InnerProfilePicView: View { } var body: some View { - Group { + ZStack { + Color(uiColor: .systemBackground) + KFAnimatedImage(url) .callbackQueue(.dispatch(.global(qos: .background))) .processingQueue(.dispatch(.global(qos: .background))) From 5f22a7691ff7202f398fd57d0b4d0c52900ba844 Mon Sep 17 00:00:00 2001 From: OlegAba Date: Fri, 6 Jan 2023 13:07:53 -0500 Subject: [PATCH 09/10] Fix TabView loading new view on scroll --- damus/ContentView.swift | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/damus/ContentView.swift b/damus/ContentView.swift index 2abd09e..a20c90a 100644 --- a/damus/ContentView.swift +++ b/damus/ContentView.swift @@ -93,9 +93,9 @@ struct ContentView: View { var PostingTimelineView: some View { VStack { TabView(selection: $filter_state) { - ContentTimelineView + contentTimelineView(filter: posts_filter_event) .tag(FilterState.posts) - ContentTimelineView + contentTimelineView(filter: posts_and_replies_filter_event) .tag(FilterState.posts_and_replies) } .tabViewStyle(.page(indexDisplayMode: .never)) @@ -113,10 +113,10 @@ struct ContentView: View { .ignoresSafeArea(.keyboard) } - var ContentTimelineView: some View { + func contentTimelineView(filter: (@escaping (NostrEvent) -> Bool)) -> some View { 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: $home.events, loading: $home.loading, damus: damus, show_friend_icon: false, filter: filter) } if privkey != nil { PostButtonContainer { @@ -126,6 +126,14 @@ struct ContentView: View { } } + func posts_and_replies_filter_event(_ ev: NostrEvent) -> Bool { + return true + } + + func posts_filter_event(_ ev: NostrEvent) -> Bool { + return !ev.is_reply(nil) + } + var FiltersView: some View { VStack{ Picker("Filter State", selection: $filter_state) { @@ -136,14 +144,6 @@ struct ContentView: View { } } - func filter_event(_ ev: NostrEvent) -> Bool { - if self.filter_state == .posts { - return !ev.is_reply(nil) - } - - return true - } - func MainContent(damus: DamusState) -> some View { VStack { NavigationLink(destination: MaybeProfileView, isActive: $profile_open) { From ec4790a8fe67e88b36ad021b14d517bed6df5345 Mon Sep 17 00:00:00 2001 From: William Casarin Date: Fri, 6 Jan 2023 14:19:23 -0800 Subject: [PATCH 10/10] v1.0.0 (5) --- damus.xcodeproj/project.pbxproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/damus.xcodeproj/project.pbxproj b/damus.xcodeproj/project.pbxproj index cdfe468..dad6456 100644 --- a/damus.xcodeproj/project.pbxproj +++ b/damus.xcodeproj/project.pbxproj @@ -1094,7 +1094,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = damus/damus.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 4; + CURRENT_PROJECT_VERSION = 5; DEVELOPMENT_ASSET_PATHS = "\"damus/Preview Content\""; DEVELOPMENT_TEAM = XK7H4JAB3D; ENABLE_PREVIEWS = YES; @@ -1134,7 +1134,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = damus/damus.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 4; + CURRENT_PROJECT_VERSION = 5; DEVELOPMENT_ASSET_PATHS = "\"damus/Preview Content\""; DEVELOPMENT_TEAM = XK7H4JAB3D; ENABLE_PREVIEWS = YES;