damus

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

commit 51ee4046a0471369485c4f9838e1958d56245b7d
parent b5a81e2586cb8c20549f40137d7de97c04b49af3
Author: William Casarin <jb55@jb55.com>
Date:   Sun, 22 Sep 2024 10:29:04 +0900

Merge ios18-release_1.10 fixes

Pull some things from the 1.10 release branch into master:

Daniel D’Aquino (1):
      Fix unclickable elements

William Casarin (4):
      relays: add some ping/pong and connection logs
      relay: don't reconnect when we don't have to

Diffstat:
Mdamus.xcodeproj/project.pbxproj | 14++++++++++++++
Mdamus/Components/ImageCarousel.swift | 11+++++++++--
Mdamus/Nostr/RelayConnection.swift | 20++++++++++++++++----
Mdamus/Nostr/RelayPool.swift | 1+
Mdamus/Util/Log.swift | 1+
Mdamus/Views/BannerImageView.swift | 2++
Mdamus/Views/Events/Highlight/HighlightEventRef.swift | 1+
Mdamus/Views/Events/Highlight/HighlightLink.swift | 1+
Mdamus/Views/Events/Longform/LongformPreview.swift | 1+
Mdamus/Views/Images/ImageContainerView.swift | 1+
Mdamus/Views/Images/ProfilePicImageView.swift | 1+
Mdamus/Views/Profile/EditPictureControl.swift | 1+
Mdamus/Views/Profile/ProfilePicView.swift | 1+
Mdamus/Views/Profile/ProfilePictureSelector.swift | 1+
Mdamus/Views/Relays/RelayPicView.swift | 1+
Adamus/Views/Utils/KFClickable.swift | 23+++++++++++++++++++++++
16 files changed, 75 insertions(+), 6 deletions(-)

diff --git a/damus.xcodeproj/project.pbxproj b/damus.xcodeproj/project.pbxproj @@ -1113,6 +1113,8 @@ D7CE1B482B0BE719002EDAD4 /* Message.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C32B93D2A9AD44700DC3548 /* Message.swift */; }; D7CE1B492B0BE729002EDAD4 /* DisplayName.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C9BB83029C0ED4F00FC4E37 /* DisplayName.swift */; }; D7D2A3812BF815D000E4B42B /* PushNotificationClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7D2A3802BF815D000E4B42B /* PushNotificationClient.swift */; }; + D7D68FF92C9E01BE0015A515 /* KFClickable.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7D68FF82C9E01B60015A515 /* KFClickable.swift */; }; + D7D68FFA2C9E01BE0015A515 /* KFClickable.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7D68FF82C9E01B60015A515 /* KFClickable.swift */; }; D7DBD41F2B02F15E002A6197 /* NostrKind.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C3BEFD32819DE8F00B3DE84 /* NostrKind.swift */; }; D7DEEF2F2A8C021E00E0C99F /* NostrEventTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7DEEF2E2A8C021E00E0C99F /* NostrEventTests.swift */; }; D7EDED152B11776B0018B19C /* LibreTranslateServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AE45AF5297BB2E700C1D842 /* LibreTranslateServer.swift */; }; @@ -1962,6 +1964,7 @@ D7CBD1D32B8D21DC00BFD889 /* DamusPurpleNotificationManagement.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DamusPurpleNotificationManagement.swift; sourceTree = "<group>"; }; D7CBD1D52B8D509800BFD889 /* DamusPurpleImpendingExpirationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DamusPurpleImpendingExpirationTests.swift; sourceTree = "<group>"; }; D7D2A3802BF815D000E4B42B /* PushNotificationClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PushNotificationClient.swift; sourceTree = "<group>"; }; + D7D68FF82C9E01B60015A515 /* KFClickable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KFClickable.swift; sourceTree = "<group>"; }; D7DEEF2E2A8C021E00E0C99F /* NostrEventTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NostrEventTests.swift; sourceTree = "<group>"; }; D7EDED1B2B1178FE0018B19C /* NoteContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoteContent.swift; sourceTree = "<group>"; }; D7EDED1D2B11797D0018B19C /* LongformEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LongformEvent.swift; sourceTree = "<group>"; }; @@ -2543,6 +2546,7 @@ 4C75EFA227FA576C0006080F /* Views */ = { isa = PBXGroup; children = ( + D7D68FF72C9E01A80015A515 /* Utils */, D78DB85D2C20FE9E00F0AB12 /* Chat */, D71AC4CA2BA8E3320076268E /* Extensions */, BA3759952ABCCF360018D73B /* Camera */, @@ -3372,6 +3376,14 @@ path = Extensions; sourceTree = "<group>"; }; + D7D68FF72C9E01A80015A515 /* Utils */ = { + isa = PBXGroup; + children = ( + D7D68FF82C9E01B60015A515 /* KFClickable.swift */, + ); + path = Utils; + sourceTree = "<group>"; + }; E06336A72B7582D600A88E6B /* Assets */ = { isa = PBXGroup; children = ( @@ -3808,6 +3820,7 @@ B51C1CEA2B55A60A00E312A9 /* AddMuteItemView.swift in Sources */, 4C5D5C992A6AF8F80024563C /* NdbNote.swift in Sources */, 4CF0ABF029857E9200D66079 /* Bech32Object.swift in Sources */, + D7D68FFA2C9E01BE0015A515 /* KFClickable.swift in Sources */, 4C3D52B8298DB5C6001C5831 /* TextEvent.swift in Sources */, 4C216F362870A9A700040376 /* InputDismissKeyboard.swift in Sources */, D74AAFCF2B155D8C006CF0F4 /* ZapDataModel.swift in Sources */, @@ -4523,6 +4536,7 @@ D73E5F332C6A97F4007EB227 /* ZapEvent.swift in Sources */, D73E5F342C6A97F4007EB227 /* TextEvent.swift in Sources */, D73E5F352C6A97F4007EB227 /* WideEventView.swift in Sources */, + D7D68FF92C9E01BE0015A515 /* KFClickable.swift in Sources */, D73E5F8A2C6AA69C007EB227 /* SideMenuView.swift in Sources */, D73E5F362C6A97F4007EB227 /* LongformView.swift in Sources */, D73E5F372C6A97F4007EB227 /* LongformPreview.swift in Sources */, diff --git a/damus/Components/ImageCarousel.swift b/damus/Components/ImageCarousel.swift @@ -236,6 +236,7 @@ struct ImageCarousel<Content: View>: View { Placeholder(url: url, geo_size: geo.size, num_urls: urls.count) } .aspectRatio(contentMode: filling ? .fill : .fit) + .kfClickable() .position(x: geo.size.width / 2, y: geo.size.height / 2) .tabItem { Text(url.absoluteString) @@ -274,8 +275,14 @@ struct ImageCarousel<Content: View>: View { var body: some View { VStack { - Medias - .onTapGesture { } + if #available(iOS 18.0, *) { + Medias + } else { + // An empty tap gesture recognizer is needed on iOS 17 and below to suppress other overlapping tap recognizers + // Otherwise it will both open the carousel and go to a note at the same time + Medias.onTapGesture { } + } + if urls.count > 1 { PageControlView(currentPage: $model.selectedIndex, numberOfPages: urls.count) diff --git a/damus/Nostr/RelayConnection.swift b/damus/Nostr/RelayConnection.swift @@ -46,9 +46,10 @@ final class RelayConnection: ObservableObject { if err == nil { self.last_pong = .now + Log.info("Got pong from '%s'", for: .networking, self.relay_url.absoluteString) self.log?.add("Successful ping") } else { - print("pong failed, reconnecting \(self.relay_url.id)") + Log.info("Ping failed, reconnecting to '%s'", for: .networking, self.relay_url.absoluteString) self.isConnected = false self.isConnecting = false self.reconnect_with_backoff() @@ -126,7 +127,7 @@ final class RelayConnection: ObservableObject { self.receive(message: message) case .disconnected(let closeCode, let reason): if closeCode != .normalClosure { - print("⚠️ Warning: RelayConnection (\(self.relay_url)) closed with code \(closeCode), reason: \(String(describing: reason))") + Log.error("⚠️ Warning: RelayConnection (%d) closed with code: %s", for: .networking, String(describing: closeCode), String(describing: reason)) } DispatchQueue.main.async { self.isConnected = false @@ -134,12 +135,16 @@ final class RelayConnection: ObservableObject { self.reconnect() } case .error(let error): - print("⚠️ Warning: RelayConnection (\(self.relay_url)) error: \(error)") + Log.error("⚠️ Warning: RelayConnection (%s) error: %s", for: .networking, self.relay_url.absoluteString, error.localizedDescription) let nserr = error as NSError if nserr.domain == NSPOSIXErrorDomain && nserr.code == 57 { // ignore socket not connected? return } + if nserr.domain == NSURLErrorDomain && nserr.code == -999 { + // these aren't real error, it just means task was cancelled + return + } DispatchQueue.main.async { self.isConnected = false self.isConnecting = false @@ -156,14 +161,21 @@ final class RelayConnection: ObservableObject { } func reconnect_with_backoff() { - self.backoff *= 1.5 + self.backoff *= 2.0 self.reconnect_in(after: self.backoff) } func reconnect() { guard !isConnecting && !isDisabled else { + self.log?.add("Cancelling reconnect, already connecting") return // we're already trying to connect or we're disabled } + + guard !self.isConnected else { + self.log?.add("Cancelling reconnect, already connected") + return + } + disconnect() connect() log?.add("Reconnecting...") diff --git a/damus/Nostr/RelayPool.swift b/damus/Nostr/RelayPool.swift @@ -89,6 +89,7 @@ class RelayPool { } func ping() { + Log.info("Pinging %d relays", for: .networking, relays.count) for relay in relays { relay.connection.ping() } diff --git a/damus/Util/Log.swift b/damus/Util/Log.swift @@ -13,6 +13,7 @@ enum LogCategory: String { case nav case render case storage + case networking case push_notifications case damus_purple case image_uploading diff --git a/damus/Views/BannerImageView.swift b/damus/Views/BannerImageView.swift @@ -29,6 +29,7 @@ struct EditBannerImageView: View { Color(uiColor: .secondarySystemBackground) } .onFailureImage(defaultImage) + .kfClickable() EditPictureControl(uploader: damus_state.settings.default_media_uploader, pubkey: damus_state.pubkey, image_url: $banner_image, uploadObserver: viewModel, callback: callback) } @@ -54,6 +55,7 @@ struct InnerBannerImageView: View { Color(uiColor: .secondarySystemBackground) } .onFailureImage(defaultImage) + .kfClickable() } else { Image(uiImage: defaultImage).resizable() } diff --git a/damus/Views/Events/Highlight/HighlightEventRef.swift b/damus/Views/Events/Highlight/HighlightEventRef.swift @@ -50,6 +50,7 @@ struct HighlightEventRef: View { FailedImage() } .frame(width: 35, height: 35) + .kfClickable() .clipShape(RoundedRectangle(cornerRadius: 10)) .overlay(RoundedRectangle(cornerRadius: 10).stroke(.gray.opacity(0.5), lineWidth: 0.5)) .scaledToFit() diff --git a/damus/Views/Events/Highlight/HighlightLink.swift b/damus/Views/Events/Highlight/HighlightLink.swift @@ -61,6 +61,7 @@ struct HighlightLink: View { .background(DamusColors.adaptableWhite) } .frame(width: 35, height: 35) + .kfClickable() .clipShape(RoundedRectangle(cornerRadius: 10)) .scaledToFit() } else { diff --git a/damus/Views/Events/Longform/LongformPreview.swift b/damus/Views/Events/Longform/LongformPreview.swift @@ -98,6 +98,7 @@ struct LongformPreviewBody: View { } .aspectRatio(contentMode: .fill) .frame(maxWidth: .infinity, maxHeight: header ? .infinity : 150) + .kfClickable() .cornerRadius(1) } diff --git a/damus/Views/Images/ImageContainerView.swift b/damus/Views/Images/ImageContainerView.swift @@ -33,6 +33,7 @@ struct ImageContainerView: View { view.framePreloadCount = 3 } .imageModifier(ImageHandler(handler: $image)) + .kfClickable() .clipped() .modifier(ImageContextMenuModifier(url: url, image: image, settings: settings, showShareSheet: $showShareSheet)) .sheet(isPresented: $showShareSheet) { diff --git a/damus/Views/Images/ProfilePicImageView.swift b/damus/Views/Images/ProfilePicImageView.swift @@ -33,6 +33,7 @@ struct ProfileImageContainerView: View { .imageModifier(ImageHandler(handler: $image)) .clipShape(Circle()) .modifier(ImageContextMenuModifier(url: url, image: image, settings: settings, showShareSheet: $showShareSheet)) + .kfClickable() .sheet(isPresented: $showShareSheet) { ShareSheet(activityItems: [url]) } diff --git a/damus/Views/Profile/EditPictureControl.swift b/damus/Views/Profile/EditPictureControl.swift @@ -71,6 +71,7 @@ struct EditPictureControl: View { } .scaledToFill() .frame(width: (size ?? 25) + 10, height: (size ?? 25) + 10) + .kfClickable() .foregroundColor(DamusColors.white) .clipShape(Circle()) .overlay(Circle().stroke(.white, lineWidth: 4)) diff --git a/damus/Views/Profile/ProfilePicView.swift b/damus/Views/Profile/ProfilePicView.swift @@ -57,6 +57,7 @@ struct InnerProfilePicView: View { } .scaledToFill() .frame(width: size, height: size) + .kfClickable() .clipShape(Circle()) .overlay(Circle().stroke(highlight_color(highlight), lineWidth: pfp_line_width(highlight))) } diff --git a/damus/Views/Profile/ProfilePictureSelector.swift b/damus/Views/Profile/ProfilePictureSelector.swift @@ -31,6 +31,7 @@ struct EditProfilePictureView: View { view.framePreloadCount = 3 } .scaledToFill() + .kfClickable() EditPictureControl(uploader: damus_state?.settings.default_media_uploader ?? .nostrBuild, pubkey: pubkey, image_url: $profile_url, uploadObserver: uploadObserver, callback: callback) } diff --git a/damus/Views/Relays/RelayPicView.swift b/damus/Views/Relays/RelayPicView.swift @@ -55,6 +55,7 @@ struct InnerRelayPicView: View { Placeholder(url: url) } .scaledToFit() + .kfClickable() } else { FailedRelayImage(url: nil) } diff --git a/damus/Views/Utils/KFClickable.swift b/damus/Views/Utils/KFClickable.swift @@ -0,0 +1,23 @@ +// +// ClickableOverlay.swift +// damus +// +// Created by Daniel D’Aquino on 2024-09-20. +// + +import SwiftUI + +/// Applies a property that makes `KFAnimatedImage` clickable again on iOS 18+ +fileprivate struct KFClickable: ViewModifier { + func body(content: Content) -> some View { + content + .contentShape(Rectangle()) + } +} + +extension View { + /// Applies a property that makes `KFAnimatedImage` clickable again on iOS 18+ + func kfClickable() -> some View { + return self.modifier(KFClickable()) + } +}