From 68e9c127f971bfdd0f74f897c0edad2e3cc86b46 Mon Sep 17 00:00:00 2001 From: William Casarin Date: Thu, 22 Dec 2022 11:02:40 -0800 Subject: [PATCH 01/24] Release v0.1.8 --- CHANGELOG.md | 44 +++++++++++++++++++++++++++++++++ damus.xcodeproj/project.pbxproj | 8 +++--- 2 files changed, 48 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 64f308f..2950463 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,47 @@ +## [0.1.8] - 2022-12-21 + +### Changed + + - Lots of overall design polish (Sam DuBois) + - Added loading shimmering effect (Sam DuBois) + - Show real name next to username in timelines (Sam DuBois) + +### Added + + - Animated gif are now shown inline and in profile pictures (@futurepaul) + - Added ability to copy and share image (@futurepaul) + - Haptic feedback when liking for that sweet dopamine hit (radixrat) + - Hide private key in config, make it easier to copy keys (Nitesh Balusu) + +### Fixed + + - Disable autocorrection for username when creating account + - Fixed issues with the post placeholder + - Disable autocorrection on search + - Disable autocorrection on add relay field + - Parse lightning: prefixes on lightning invoice + - Resize images to fill the space + + +## [0.1.7] - 2022-12-21 + +### Changed + + - Only show inline images from your friends + - Improved look of profile view + + +### Fixed + + - Added ability to dismiss keyboard during account creation + - Fixed crashed on lightning invoices with empty descriptions + - Make dm chat area visible again + + + +[0.1.7]: https://github.com/damus-io/damus/releases/tag/v0.1.7 + ## [0.1.6] - 2022-10-30 ### Added @@ -78,3 +121,4 @@ [0.1.2]: https://github.com/damus-io/damus/releases/tag/v0.1.2 + diff --git a/damus.xcodeproj/project.pbxproj b/damus.xcodeproj/project.pbxproj index 2a30c20..883eda4 100644 --- a/damus.xcodeproj/project.pbxproj +++ b/damus.xcodeproj/project.pbxproj @@ -1019,7 +1019,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = damus/damus.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"damus/Preview Content\""; DEVELOPMENT_TEAM = XK7H4JAB3D; ENABLE_PREVIEWS = YES; @@ -1039,7 +1039,7 @@ "$(inherited)", "$(PROJECT_DIR)", ); - MARKETING_VERSION = 0.1.7; + MARKETING_VERSION = 0.1.8; PRODUCT_BUNDLE_IDENTIFIER = com.jb55.damus2; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = YES; @@ -1058,7 +1058,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = damus/damus.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"damus/Preview Content\""; DEVELOPMENT_TEAM = XK7H4JAB3D; ENABLE_PREVIEWS = YES; @@ -1078,7 +1078,7 @@ "$(inherited)", "$(PROJECT_DIR)", ); - MARKETING_VERSION = 0.1.7; + MARKETING_VERSION = 0.1.8; PRODUCT_BUNDLE_IDENTIFIER = com.jb55.damus2; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = YES; From 514d9f42013d4b8e53e4c1612c7d95c31a808419 Mon Sep 17 00:00:00 2001 From: William Casarin Date: Thu, 22 Dec 2022 16:07:52 -0800 Subject: [PATCH 02/24] bolt11: fix another crash on unknown fields --- damus-c/bolt11.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/damus-c/bolt11.c b/damus-c/bolt11.c index 027b0bd..14e078b 100644 --- a/damus-c/bolt11.c +++ b/damus-c/bolt11.c @@ -553,7 +553,7 @@ struct bolt11 *bolt11_decode_nosig(const tal_t *ctx, const char *str, u5 **sig, case 'n': problem = decode_n(b11, &hu5, &data, &data_len, data_length, - have_n); + &have_n); break; case 'x': From 549cbb9bce76443aedaec9394f3926acd546667e Mon Sep 17 00:00:00 2001 From: Sam DuBois Date: Thu, 22 Dec 2022 17:23:55 -0700 Subject: [PATCH 03/24] Fix issue with loading view using the redacted modifier Also tried to make some adjusmtents to the refresh subscribtion to see if this fixes bugs people were having Closes: #119 Changelog-Fixed: Fixed issues when refreshing global view --- damus/Components/Shimmer.swift | 10 +++++++-- damus/Nostr/NostrEvent.swift | 14 +++++++++++++ damus/Util/Constants.swift | 16 +++++++------- damus/Views/ProfilePicView.swift | 36 +++++++++++++++++++------------- damus/Views/SearchHomeView.swift | 10 ++++----- damus/Views/TimelineView.swift | 30 ++++---------------------- 6 files changed, 62 insertions(+), 54 deletions(-) diff --git a/damus/Components/Shimmer.swift b/damus/Components/Shimmer.swift index 38956fc..1ae1c2e 100644 --- a/damus/Components/Shimmer.swift +++ b/damus/Components/Shimmer.swift @@ -41,6 +41,7 @@ struct ShimmeringView: View { _startPoint = .init(wrappedValue: configuration.initialLocation.start) _endPoint = .init(wrappedValue: configuration.initialLocation.end) } + var body: some View { ZStack { content() @@ -71,7 +72,12 @@ public struct ShimmerModifier: ViewModifier { public extension View { - func shimmer(configuration: ShimmerConfiguration = .default) -> some View { - modifier(ShimmerModifier(configuration: configuration)) + + @ViewBuilder func shimmer(configuration: ShimmerConfiguration = .default, _ loading: Bool) -> some View { + if loading { + modifier(ShimmerModifier(configuration: configuration)) + } else { + self + } } } diff --git a/damus/Nostr/NostrEvent.swift b/damus/Nostr/NostrEvent.swift index fe7ce89..2bbf9d7 100644 --- a/damus/Nostr/NostrEvent.swift +++ b/damus/Nostr/NostrEvent.swift @@ -274,6 +274,20 @@ class NostrEvent: Codable, Identifiable, CustomStringConvertible, Equatable { self.tags = tags self.created_at = Int64(Date().timeIntervalSince1970) } + + /// Intiialization statement used to specificy ID + /// + /// This is mainly used for contant and testing data + init(id: String, content: String, pubkey: String, kind: Int = 1, tags: [[String]] = []) { + self.id = id + self.sig = "" + + self.content = content + self.pubkey = pubkey + self.kind = kind + self.tags = tags + self.created_at = Int64(Date().timeIntervalSince1970) + } init(from: NostrEvent, content: String? = nil) { self.id = from.id diff --git a/damus/Util/Constants.swift b/damus/Util/Constants.swift index cca0fb4..63199b1 100644 --- a/damus/Util/Constants.swift +++ b/damus/Util/Constants.swift @@ -14,12 +14,14 @@ public class Constants { 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"), + NostrEvent(id: UUID().description, content: "Nostr - Damus... Haha get it? Bonjour Le Monde mon Ami! C'est la tres importante", pubkey: "3efdaebb1d8923ebd99c9e7ace3b4194ab45512e2be79c1b7d68d9243e0d2681"), + NostrEvent(id: UUID().description, 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(id: UUID().description, content: "Bonjour Le Monde", pubkey: "3efdaebb1d8923ebd99c9e7ace3b4194ab45512e2be79c1b7d68d9243e0d2681"), + NostrEvent(id: UUID().description, content: "Why am I helping on this app? Because it's fun!", pubkey: "3efdaebb1d8923ebd99c9e7ace3b4194ab45512e2be79c1b7d68d9243e0d2681"), + NostrEvent(id: UUID().description, content: "Pizza and Icecream! Pizza and Icecream! Testing Testing! 1 .. 2.. 3..", pubkey: "3efdaebb1d8923ebd99c9e7ace3b4194ab45512e2be79c1b7d68d9243e0d2681"), + NostrEvent(id: UUID().description, content: "Hello World! This is so cool!", pubkey: "3efdaebb1d8923ebd99c9e7ace3b4194ab45512e2be79c1b7d68d9243e0d2681"), + NostrEvent(id: UUID().description, content: "Nostr - Damus... Haha get it?", pubkey: "3efdaebb1d8923ebd99c9e7ace3b4194ab45512e2be79c1b7d68d9243e0d2681"), + NostrEvent(id: UUID().description, content: "Hello World! This is so cool!", pubkey: "3efdaebb1d8923ebd99c9e7ace3b4194ab45512e2be79c1b7d68d9243e0d2681"), + NostrEvent(id: UUID().description, content: "Bonjour Le Monde", pubkey: "3efdaebb1d8923ebd99c9e7ace3b4194ab45512e2be79c1b7d68d9243e0d2681"), ] } diff --git a/damus/Views/ProfilePicView.swift b/damus/Views/ProfilePicView.swift index d7c0f55..d614a7f 100644 --- a/damus/Views/ProfilePicView.swift +++ b/damus/Views/ProfilePicView.swift @@ -33,10 +33,14 @@ func pfp_line_width(_ h: Highlight) -> CGFloat { } struct ProfilePicView: View { + + @Environment(\.redactionReasons) private var reasons + let pubkey: String let size: CGFloat let highlight: Highlight let profiles: Profiles + let isPlaceholder: Bool = false @State var picture: String? = nil @@ -57,21 +61,25 @@ struct ProfilePicView: View { let pic = picture ?? profiles.lookup(id: pubkey)?.picture ?? robohash(pubkey) let url = URL(string: pic) - KFAnimatedImage(url) - .configure { view in - view.framePreloadCount = 1 - } - .placeholder { _ in - Placeholder - } - .cacheOriginalImage() - .scaleFactor(UIScreen.main.scale) - .loadDiskFileSynchronously() - .fade(duration: 0.1) - .frame(width: size, height: size) - .clipShape(Circle()) - .overlay(Circle().stroke(highlight_color(highlight), lineWidth: pfp_line_width(highlight))) + if reasons.isEmpty { + KFAnimatedImage(url) + .configure { view in + view.framePreloadCount = 1 + } + .placeholder { _ in + Placeholder + } + .cacheOriginalImage() + .scaleFactor(UIScreen.main.scale) + .loadDiskFileSynchronously() + .fade(duration: 0.1) + } else { + KFImage(url) + } } + .frame(width: size, height: size) + .clipShape(Circle()) + .overlay(Circle().stroke(highlight_color(highlight), lineWidth: pfp_line_width(highlight))) } var body: some View { diff --git a/damus/Views/SearchHomeView.swift b/damus/Views/SearchHomeView.swift index f68bec0..27fae88 100644 --- a/damus/Views/SearchHomeView.swift +++ b/damus/Views/SearchHomeView.swift @@ -42,7 +42,8 @@ struct SearchHomeView: View { var GlobalContent: some View { return TimelineView(events: $model.events, loading: $model.loading, damus: damus_state, show_friend_icon: true, filter: { _ in true }) .refreshable { - // Fetch new information by resubscribing to the relay + // Fetch new information by unsubscribing and resubscribing to the relay + model.unsubscribe() model.subscribe() } } @@ -50,7 +51,8 @@ struct SearchHomeView: View { var SearchContent: some View { SearchResultsView(damus_state: damus_state, search: $search) .refreshable { - // Fetch new information by resubscribing to the relay + // Fetch new information by unsubscribing and resubscribing to the relay + model.unsubscribe() model.subscribe() } } @@ -68,9 +70,7 @@ struct SearchHomeView: View { @Environment(\.colorScheme) var colorScheme var body: some View { - VStack { - MainContent - } + MainContent .safeAreaInset(edge: .top) { VStack(spacing: 0) { SearchInput diff --git a/damus/Views/TimelineView.swift b/damus/Views/TimelineView.swift index 83fa3b7..61ecf4b 100644 --- a/damus/Views/TimelineView.swift +++ b/damus/Views/TimelineView.swift @@ -40,25 +40,6 @@ struct InnerTimelineView: View { } } -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) - } -} - struct TimelineView: View { @Binding var events: [NostrEvent] @@ -75,13 +56,10 @@ struct TimelineView: View { var MainContent: some View { ScrollViewReader { scroller in ScrollView { - if loading { - InnerTimelineRedactedView(events: Constants.EXAMPLE_EVENTS, damus: damus, show_friend_icon: true) - ProgressView() - .progressViewStyle(.circular) - } else { - InnerTimelineView(events: $events, damus: damus, show_friend_icon: show_friend_icon, filter: filter) - } + InnerTimelineView(events: loading ? .constant(Constants.EXAMPLE_EVENTS) : $events, damus: damus, show_friend_icon: show_friend_icon, filter: loading ? { _ in true } : filter) + .redacted(reason: loading ? .placeholder : []) + .shimmer(loading) + .disabled(loading) } .onReceive(NotificationCenter.default.publisher(for: .scroll_to_top)) { _ in guard let event = events.filter(self.filter).first else { From dff450282b38de84bf534b52b6280447e169b6f3 Mon Sep 17 00:00:00 2001 From: Thomas <31560900+0xtlt@users.noreply.github.com> Date: Fri, 23 Dec 2022 16:12:50 +0100 Subject: [PATCH 04/24] Metadata screen --- damus.xcodeproj/project.pbxproj | 4 ++ damus/Models/ProfileModel.swift | 10 ++- damus/Nostr/Nostr.swift | 6 +- damus/Nostr/NostrMetadata.swift | 6 +- damus/Views/ConfigView.swift | 18 +++-- damus/Views/MetadataView.swift | 113 +++++++++++++++++++++++++++++++ damus/Views/PostView.swift | 1 + damus/Views/ProfilePicView.swift | 2 +- damus/Views/ProfileView.swift | 2 +- 9 files changed, 150 insertions(+), 12 deletions(-) create mode 100644 damus/Views/MetadataView.swift diff --git a/damus.xcodeproj/project.pbxproj b/damus.xcodeproj/project.pbxproj index 883eda4..74f09d9 100644 --- a/damus.xcodeproj/project.pbxproj +++ b/damus.xcodeproj/project.pbxproj @@ -129,6 +129,7 @@ 4CEE2AF7280B2DEA00AB5EEF /* ProfileName.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CEE2AF6280B2DEA00AB5EEF /* ProfileName.swift */; }; 4CEE2AF9280B2EAC00AB5EEF /* PowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CEE2AF8280B2EAC00AB5EEF /* PowView.swift */; }; 4CEE2B02280B39E800AB5EEF /* EventActionBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CEE2B01280B39E800AB5EEF /* EventActionBar.swift */; }; + E990020F2955F837003BBC5A /* MetadataView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E990020E2955F837003BBC5A /* MetadataView.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -306,6 +307,7 @@ 4CEE2AF6280B2DEA00AB5EEF /* ProfileName.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileName.swift; sourceTree = ""; }; 4CEE2AF8280B2EAC00AB5EEF /* PowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PowView.swift; sourceTree = ""; }; 4CEE2B01280B39E800AB5EEF /* EventActionBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventActionBar.swift; sourceTree = ""; }; + E990020E2955F837003BBC5A /* MetadataView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MetadataView.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -473,6 +475,7 @@ 4C216F31286E388800040376 /* DMChatView.swift */, 4C216F33286F5ACD00040376 /* DMView.swift */, 4C06670028FC7C5900038D2A /* RelayView.swift */, + E990020E2955F837003BBC5A /* MetadataView.swift */, ); path = Views; sourceTree = ""; @@ -845,6 +848,7 @@ 4C3EA67D28FFBBA300C48A62 /* InvoicesView.swift in Sources */, 4C363A8E28236FE4006E126D /* NoteContentView.swift in Sources */, 4C90BD1A283AA67F008EE7EF /* Bech32.swift in Sources */, + E990020F2955F837003BBC5A /* MetadataView.swift in Sources */, 4CACA9D5280C31E100D9BBE8 /* ReplyView.swift in Sources */, 4C0A3F97280F8E02000448DE /* ThreadView.swift in Sources */, 4C06670B28FDE64700038D2A /* damus.c in Sources */, diff --git a/damus/Models/ProfileModel.swift b/damus/Models/ProfileModel.swift index 20b59c5..1f89b0b 100644 --- a/damus/Models/ProfileModel.swift +++ b/damus/Models/ProfileModel.swift @@ -7,7 +7,7 @@ import Foundation -class ProfileModel: ObservableObject { +class ProfileModel: ObservableObject, Equatable { @Published var events: [NostrEvent] = [] @Published var contacts: NostrEvent? = nil @Published var following: Int = 0 @@ -31,6 +31,14 @@ class ProfileModel: ObservableObject { self.damus = damus } + static func == (lhs: ProfileModel, rhs: ProfileModel) -> Bool { + return lhs.pubkey == rhs.pubkey + } + + func hash(into hasher: inout Hasher) { + hasher.combine(pubkey) + } + func unsubscribe() { print("unsubscribing from profile \(pubkey) with sub_id \(sub_id)") damus.pool.unsubscribe(sub_id: sub_id) diff --git a/damus/Nostr/Nostr.swift b/damus/Nostr/Nostr.swift index cc1289c..b13a339 100644 --- a/damus/Nostr/Nostr.swift +++ b/damus/Nostr/Nostr.swift @@ -8,12 +8,13 @@ import Foundation -struct Profile: Decodable { +struct Profile: Decodable, Equatable { let name: String? let display_name: String? let about: String? let picture: String? let website: String? + let nip05: String? let lud06: String? let lud16: String? @@ -34,6 +35,3 @@ struct NostrSubscription { let sub_id: String let filter: NostrFilter } - - - diff --git a/damus/Nostr/NostrMetadata.swift b/damus/Nostr/NostrMetadata.swift index faf5963..0c69f42 100644 --- a/damus/Nostr/NostrMetadata.swift +++ b/damus/Nostr/NostrMetadata.swift @@ -13,8 +13,12 @@ struct NostrMetadata: Codable { let name: String? let about: String? let website: String? + let nip05: String? + let picture: String? + let lud06: String? + let lud16: String? } func create_account_to_metadata(_ model: CreateAccountModel) -> NostrMetadata { - return NostrMetadata(display_name: model.real_name, name: model.nick_name, about: model.about, website: nil) + return NostrMetadata(display_name: model.real_name, name: model.nick_name, about: model.about, website: nil, nip05: nil, picture: nil, lud06: nil, lud16: nil) } diff --git a/damus/Views/ConfigView.swift b/damus/Views/ConfigView.swift index 2f54ca1..5e61df7 100644 --- a/damus/Views/ConfigView.swift +++ b/damus/Views/ConfigView.swift @@ -73,11 +73,19 @@ struct ConfigView: View { CopyButton(is_pk: false) } - + Toggle("Show", isOn: $show_privkey) } } - + + Section("Account settings") { + NavigationLink { + MetadataView(damus_state: state, profileModel: ProfileModel(pubkey: state.pubkey, damus: state)) + } label: { + Text("Profile metadata") + } + } + Section("Reset") { Button("Logout") { confirm_logout = true @@ -129,7 +137,7 @@ struct ConfigView: View { guard let privkey = state.keypair.privkey else { return } - + let info = RelayInfo.rw guard (try? state.pool.add_relay(url, info: info)) != nil else { @@ -154,6 +162,8 @@ struct ConfigView: View { struct ConfigView_Previews: PreviewProvider { static var previews: some View { - ConfigView(state: test_damus_state()) + NavigationView { + ConfigView(state: test_damus_state()) + } } } diff --git a/damus/Views/MetadataView.swift b/damus/Views/MetadataView.swift new file mode 100644 index 0000000..546fcf7 --- /dev/null +++ b/damus/Views/MetadataView.swift @@ -0,0 +1,113 @@ +// +// MetadataView.swift +// damus +// +// Created by Thomas Tastet on 23/12/2022. +// + +import SwiftUI + +struct MetadataView: View { + let damus_state: DamusState + @State var name: String = "" + @State var about: String = "" + @State var picture: String = "" + @State var nip05: String = "" + @State var nickname: String = "" + @State var lud06: String = "" + @State var lud16: String = "" + @State private var showAlert = false + @State private var isFocused = false + + @StateObject var profileModel: ProfileModel + + func save() { + let metadata = NostrMetadata( + display_name: name, + name: nickname, + about: about, + website: nil, + nip05: nip05.isEmpty ? nil : nip05, + picture: picture.isEmpty ? nil : picture, + lud06: lud06.isEmpty ? nil : lud06, + lud16: lud16.isEmpty ? nil : lud16 + ); + + let m_metadata_ev = make_metadata_event(keypair: damus_state.keypair, metadata: metadata) + + if let metadata_ev = m_metadata_ev { + damus_state.pool.send(.event(metadata_ev)) + } + } + + var body: some View { + VStack(alignment: .leading) { + Form { + Section("Your Nostr Profile") { + TextField("Your username", text: $name) + .textInputAutocapitalization(.never) + TextField("Your @", text: $nickname) + .textInputAutocapitalization(.never) + TextField("Profile Picture Url", text: $picture) + .autocorrectionDisabled(true) + .textInputAutocapitalization(.never) + TextField("NIP 05 (@)", text: $nip05) + .autocorrectionDisabled(true) + .textInputAutocapitalization(.never) + } + + Section("Description") { + ZStack(alignment: .topLeading) { + TextEditor(text: $about) + .textInputAutocapitalization(.sentences) + if about.isEmpty { + Text("Type your description here...") + .offset(x: 0, y: 7) + .foregroundColor(Color(uiColor: .placeholderText)) + } + } + } + + Section("Advanced") { + TextField("Lud06", text: $lud06) + .autocorrectionDisabled(true) + .textInputAutocapitalization(.never) + TextField("Lud16", text: $lud16) + .autocorrectionDisabled(true) + .textInputAutocapitalization(.never) + } + + Button("Save") { + save() + showAlert = true + }.alert(isPresented: $showAlert) { + Alert(title: Text("Saved"), message: Text("Your metadata has been saved."), dismissButton: .default(Text("OK"))) + } + } + } + .onAppear() { + profileModel.subscribe() + + let data = damus_state.profiles.lookup(id: profileModel.pubkey) + + name = data?.display_name ?? name + nickname = data?.name ?? name + about = data?.about ?? about + picture = data?.picture ?? picture + nip05 = data?.nip05 ?? nip05 + lud06 = data?.lud06 ?? lud06 + lud16 = data?.lud16 ?? lud16 + } + .onDisappear { + profileModel.unsubscribe() + } + } +} + +struct MetadataView_Previews: PreviewProvider { + static var previews: some View { + let ds = test_damus_state() + let profile_model = ProfileModel(pubkey: ds.pubkey, damus: ds) + MetadataView(damus_state: ds, profileModel: profile_model) + } +} diff --git a/damus/Views/PostView.swift b/damus/Views/PostView.swift index bc14d92..ef73014 100644 --- a/damus/Views/PostView.swift +++ b/damus/Views/PostView.swift @@ -79,6 +79,7 @@ struct PostView: View { .padding(.top, 8) .padding(.leading, 10) .foregroundColor(Color(uiColor: .placeholderText)) + .allowsHitTesting(false) } } } diff --git a/damus/Views/ProfilePicView.swift b/damus/Views/ProfilePicView.swift index d614a7f..e917918 100644 --- a/damus/Views/ProfilePicView.swift +++ b/damus/Views/ProfilePicView.swift @@ -101,7 +101,7 @@ struct ProfilePicView: View { func make_preview_profiles(_ pubkey: String) -> Profiles { let profiles = Profiles() let picture = "http://cdn.jb55.com/img/red-me.jpg" - let profile = Profile(name: "jb55", display_name: "William Casarin", about: "It's me", picture: picture, website: "https://jb55.com", lud06: nil, lud16: nil) + let profile = Profile(name: "jb55", display_name: "William Casarin", about: "It's me", picture: picture, website: "https://jb55.com", nip05: "jb55@damus.io", lud06: nil, lud16: nil) let ts_profile = TimestampedProfile(profile: profile, timestamp: 0) profiles.add(id: pubkey, profile: ts_profile) return profiles diff --git a/damus/Views/ProfileView.swift b/damus/Views/ProfileView.swift index b924f2e..711db8b 100644 --- a/damus/Views/ProfileView.swift +++ b/damus/Views/ProfileView.swift @@ -211,7 +211,7 @@ 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 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") + let prof = Profile(name: "damus", display_name: "Damus", about: "iOS app!", picture: "https://damus.io/img/logo.png", website: "https://damus.io", nip05: "jb@damus.io", lud06: nil, lud16: "jb55@sendsats.lol") let tsprof = TimestampedProfile(profile: prof, timestamp: 0) damus.profiles.add(id: pubkey, profile: tsprof) return damus From 969ec820c9455356dbf0bcf3a3d4628e00122b16 Mon Sep 17 00:00:00 2001 From: Artyom Kazak Date: Thu, 22 Dec 2022 23:37:08 +0100 Subject: [PATCH 05/24] Better post editor placeholder --- damus/Views/PostView.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/damus/Views/PostView.swift b/damus/Views/PostView.swift index bc14d92..df75aeb 100644 --- a/damus/Views/PostView.swift +++ b/damus/Views/PostView.swift @@ -77,8 +77,9 @@ struct PostView: View { if post.isEmpty { Text(POST_PLACEHOLDER) .padding(.top, 8) - .padding(.leading, 10) + .padding(.leading, 4) .foregroundColor(Color(uiColor: .placeholderText)) + .allowsHitTesting(false) } } } From 81f3764e4bdcaf25787832d88019e7567d3afea3 Mon Sep 17 00:00:00 2001 From: William Casarin Date: Fri, 23 Dec 2022 08:32:23 -0800 Subject: [PATCH 06/24] profile: don't overwrite profile info This will be important for profile editing --- damus/Nostr/Nostr.swift | 69 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/damus/Nostr/Nostr.swift b/damus/Nostr/Nostr.swift index cc1289c..9845ecb 100644 --- a/damus/Nostr/Nostr.swift +++ b/damus/Nostr/Nostr.swift @@ -7,7 +7,75 @@ import Foundation +struct Profile: Codable { + var value: [String: String] + + init (name: String?, display_name: String?, about: String?, picture: String?, website: String?, lud06: String?, lud16: String?) { + self.value = [:] + self.name = name + self.display_name = display_name + self.about = about + self.picture = picture + self.website = website + self.lud06 = lud06 + self.lud16 = lud16 + } + + var display_name: String? { + get { return value["display_name"]; } + set(s) { value["display_name"] = s } + } + + var name: String? { + get { return value["name"]; } + set(s) { value["name"] = s } + } + + var about: String? { + get { return value["about"]; } + set(s) { value["about"] = s } + } + + var picture: String? { + get { return value["picture"]; } + set(s) { value["picture"] = s } + } + + var website: String? { + get { return value["website"]; } + set(s) { value["website"] = s } + } + + var lud06: String? { + get { return value["lud06"]; } + set(s) { value["lud06"] = s } + } + + var lud16: String? { + get { return value["lud16"]; } + set(s) { value["lud16"] = s } + } + + var lightning_uri: URL? { + return make_ln_url(self.lud06) ?? make_ln_url(self.lud16) + } + + init(from decoder: Decoder) throws { + let container = try decoder.singleValueContainer() + self.value = try container.decode([String: String].self) + } + + func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + try container.encode(value) + } + + static func displayName(profile: Profile?, pubkey: String) -> String { + return profile?.name ?? abbrev_pubkey(pubkey) + } +} +/* struct Profile: Decodable { let name: String? let display_name: String? @@ -25,6 +93,7 @@ struct Profile: Decodable { return profile?.name ?? abbrev_pubkey(pubkey) } } + */ func make_ln_url(_ str: String?) -> URL? { return str.flatMap { URL(string: "lightning:" + $0) } From 749b97e663628389cb83c5c38c6f17e5cecb6c55 Mon Sep 17 00:00:00 2001 From: Thomas <31560900+0xtlt@users.noreply.github.com> Date: Fri, 23 Dec 2022 17:37:21 +0100 Subject: [PATCH 07/24] Edit button on account page --- damus/Views/FollowButtonView.swift | 18 ++++++++++++++---- damus/Views/FollowingView.swift | 2 +- damus/Views/ProfileView.swift | 18 ++++++++++++++++-- 3 files changed, 31 insertions(+), 7 deletions(-) diff --git a/damus/Views/FollowButtonView.swift b/damus/Views/FollowButtonView.swift index d7de02d..6bc6b4b 100644 --- a/damus/Views/FollowButtonView.swift +++ b/damus/Views/FollowButtonView.swift @@ -13,9 +13,14 @@ struct FollowButtonView: View { let target: FollowTarget @State var follow_state: FollowState + let perform: (() -> Void)? var body: some View { Button { + if perform != nil { + perform!() + } + follow_state = perform_follow_btn_action(follow_state, target: target) } label: { Text(follow_btn_txt(follow_state)) @@ -66,16 +71,19 @@ struct FollowButtonPreviews: View { var body: some View { VStack { Text("Unfollows") - FollowButtonView(target: target, follow_state: .unfollows) + FollowButtonView(target: target, follow_state: .unfollows, perform: nil) Text("Following") - FollowButtonView(target: target, follow_state: .following) + FollowButtonView(target: target, follow_state: .following, perform: nil) Text("Follows") - FollowButtonView(target: target, follow_state: .follows) + FollowButtonView(target: target, follow_state: .follows, perform: nil) Text("Unfollowing") - FollowButtonView(target: target, follow_state: .unfollowing) + FollowButtonView(target: target, follow_state: .unfollowing, perform: nil) + + Text("Edit") + FollowButtonView(target: target, follow_state: .edit, perform: nil) } } } @@ -98,6 +106,8 @@ func perform_follow_btn_action(_ fs: FollowState, target: FollowTarget) -> Follo case .unfollows: notify(.follow, target) return .unfollowing + case .edit: + return .edit } } diff --git a/damus/Views/FollowingView.swift b/damus/Views/FollowingView.swift index 243eeb1..bff37fa 100644 --- a/damus/Views/FollowingView.swift +++ b/damus/Views/FollowingView.swift @@ -32,7 +32,7 @@ struct FollowUserView: View { } .buttonStyle(PlainButtonStyle()) - FollowButtonView(target: target, follow_state: damus_state.contacts.follow_state(target.pubkey)) + FollowButtonView(target: target, follow_state: damus_state.contacts.follow_state(target.pubkey), perform: nil) } } } diff --git a/damus/Views/ProfileView.swift b/damus/Views/ProfileView.swift index 711db8b..681399a 100644 --- a/damus/Views/ProfileView.swift +++ b/damus/Views/ProfileView.swift @@ -17,6 +17,7 @@ enum FollowState { case following case unfollowing case unfollows + case edit } func follow_btn_txt(_ fs: FollowState) -> String { @@ -29,6 +30,8 @@ func follow_btn_txt(_ fs: FollowState) -> String { return "Unfollowing..." case .unfollows: return "Follow" + case .edit: + return "Edit" } } @@ -42,6 +45,8 @@ func follow_btn_enabled_state(_ fs: FollowState) -> Bool { return false case .unfollows: return true + case .edit: + return true } } @@ -83,6 +88,7 @@ struct ProfileView: View { @State private var selected_tab: ProfileTab = .posts @StateObject var profile: ProfileModel @StateObject var followers: FollowersModel + @State private var showingSheet = false @Environment(\.dismiss) var dismiss @Environment(\.colorScheme) var colorScheme @@ -127,7 +133,15 @@ struct ProfileView: View { DMButton - FollowButtonView(target: profile.get_follow_target(), follow_state: damus_state.contacts.follow_state(profile.pubkey)) + + FollowButtonView( + target: profile.get_follow_target(), + follow_state: profile.pubkey == damus_state.pubkey ? .edit : damus_state.contacts.follow_state(profile.pubkey), + perform: profile.pubkey == damus_state.pubkey ? { showingSheet.toggle() } : nil + ) + + }.sheet(isPresented: $showingSheet) { + MetadataView(damus_state: damus_state, profileModel: profile) } ProfileNameView(pubkey: profile.pubkey, profile: data, contacts: damus_state.contacts) @@ -211,7 +225,7 @@ 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 prof = Profile(name: "damus", display_name: "Damus", about: "iOS app!", picture: "https://damus.io/img/logo.png", website: "https://damus.io", nip05: "jb@damus.io", lud06: nil, lud16: "jb55@sendsats.lol") + let prof = Profile(name: "damus", display_name: "Damus", about: "iOS app!", picture: "https://damus.io/img/logo.png", website: "https://damus.io", nip05: nil, lud06: nil, lud16: "jb55@sendsats.lol") let tsprof = TimestampedProfile(profile: prof, timestamp: 0) damus.profiles.add(id: pubkey, profile: tsprof) return damus From 35f7215154c84a8f87b1088bf38a2f1d8a610e1e Mon Sep 17 00:00:00 2001 From: Thomas <31560900+0xtlt@users.noreply.github.com> Date: Fri, 23 Dec 2022 17:40:35 +0100 Subject: [PATCH 08/24] Better naming --- damus/Views/ProfileView.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/damus/Views/ProfileView.swift b/damus/Views/ProfileView.swift index 681399a..78fde60 100644 --- a/damus/Views/ProfileView.swift +++ b/damus/Views/ProfileView.swift @@ -88,7 +88,7 @@ struct ProfileView: View { @State private var selected_tab: ProfileTab = .posts @StateObject var profile: ProfileModel @StateObject var followers: FollowersModel - @State private var showingSheet = false + @State private var showingEditProfile = false @Environment(\.dismiss) var dismiss @Environment(\.colorScheme) var colorScheme @@ -137,10 +137,10 @@ struct ProfileView: View { FollowButtonView( target: profile.get_follow_target(), follow_state: profile.pubkey == damus_state.pubkey ? .edit : damus_state.contacts.follow_state(profile.pubkey), - perform: profile.pubkey == damus_state.pubkey ? { showingSheet.toggle() } : nil + perform: profile.pubkey == damus_state.pubkey ? { showingEditProfile.toggle() } : nil ) - }.sheet(isPresented: $showingSheet) { + }.sheet(isPresented: $showingEditProfile) { MetadataView(damus_state: damus_state, profileModel: profile) } From 3346dd13a75aa1f4e084302ec52352f2900bc39d Mon Sep 17 00:00:00 2001 From: Thomas <31560900+0xtlt@users.noreply.github.com> Date: Fri, 23 Dec 2022 17:41:47 +0100 Subject: [PATCH 09/24] Cleaning --- damus/Views/MetadataView.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/damus/Views/MetadataView.swift b/damus/Views/MetadataView.swift index 546fcf7..051ed3d 100644 --- a/damus/Views/MetadataView.swift +++ b/damus/Views/MetadataView.swift @@ -17,7 +17,6 @@ struct MetadataView: View { @State var lud06: String = "" @State var lud16: String = "" @State private var showAlert = false - @State private var isFocused = false @StateObject var profileModel: ProfileModel From eaf00afa7580c7a8f14da437efeaea96801aeb61 Mon Sep 17 00:00:00 2001 From: Thomas <31560900+0xtlt@users.noreply.github.com> Date: Fri, 23 Dec 2022 18:19:46 +0100 Subject: [PATCH 10/24] Image preview --- damus/Views/MetadataView.swift | 66 ++++++++++++++++++++++++++++++++-- 1 file changed, 64 insertions(+), 2 deletions(-) diff --git a/damus/Views/MetadataView.swift b/damus/Views/MetadataView.swift index 051ed3d..eec89d0 100644 --- a/damus/Views/MetadataView.swift +++ b/damus/Views/MetadataView.swift @@ -7,6 +7,50 @@ import SwiftUI +let PPM_SIZE: CGFloat = 80.0 + +func isHttpsUrl(_ string: String) -> Bool { + let urlRegEx = "^https://.*$" + let urlTest = NSPredicate(format:"SELF MATCHES %@", urlRegEx) + return urlTest.evaluate(with: string) +} + +func isImage(_ urlString: String) -> Bool { + let imageTypes = ["image/jpg", "image/jpeg", "image/png", "image/gif", "image/tiff", "image/bmp"] + + guard let url = URL(string: urlString) else { + return false + } + + var result = false + let semaphore = DispatchSemaphore(value: 0) + + let task = URLSession.shared.dataTask(with: url) { data, response, error in + if let error = error { + print(error) + semaphore.signal() + return + } + + guard let httpResponse = response as? HTTPURLResponse, + let contentType = httpResponse.allHeaderFields["Content-Type"] as? String else { + semaphore.signal() + return + } + + if imageTypes.contains(contentType.lowercased()) { + result = true + } + + semaphore.signal() + } + + task.resume() + semaphore.wait() + + return result +} + struct MetadataView: View { let damus_state: DamusState @State var name: String = "" @@ -18,6 +62,10 @@ struct MetadataView: View { @State var lud16: String = "" @State private var showAlert = false + // Image preview + @State var profiles = Profiles() + @State private var timer: Timer? + @StateObject var profileModel: ProfileModel func save() { @@ -42,6 +90,11 @@ struct MetadataView: View { var body: some View { VStack(alignment: .leading) { Form { + HStack { + Spacer() + ProfilePicView(pubkey: "0", size: PPM_SIZE, highlight: .none, profiles: profiles) + Spacer() + } Section("Your Nostr Profile") { TextField("Your username", text: $name) .textInputAutocapitalization(.never) @@ -50,6 +103,15 @@ struct MetadataView: View { TextField("Profile Picture Url", text: $picture) .autocorrectionDisabled(true) .textInputAutocapitalization(.never) + .onChange(of: picture) { newValue in + self.timer?.invalidate() + self.timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: false) { _ in + profiles = Profiles() + let tmp_profile = Profile(name: "0", display_name: "0", about: "0", picture: isHttpsUrl(picture) && isImage(picture) ? picture : nil, website: nil, nip05: "", lud06: nil, lud16: nil) + let ts_profile = TimestampedProfile(profile: tmp_profile, timestamp: 0) + profiles.add(id: "0", profile: ts_profile) + } + } TextField("NIP 05 (@)", text: $nip05) .autocorrectionDisabled(true) .textInputAutocapitalization(.never) @@ -68,10 +130,10 @@ struct MetadataView: View { } Section("Advanced") { - TextField("Lud06", text: $lud06) + TextField("LNURL", text: $lud06) .autocorrectionDisabled(true) .textInputAutocapitalization(.never) - TextField("Lud16", text: $lud16) + TextField("LN Address", text: $lud16) .autocorrectionDisabled(true) .textInputAutocapitalization(.never) } From 85914b3cbe474c7a0d377dbdebc396161a7e11d2 Mon Sep 17 00:00:00 2001 From: Thomas <31560900+0xtlt@users.noreply.github.com> Date: Fri, 23 Dec 2022 18:20:28 +0100 Subject: [PATCH 11/24] Update MetadataView.swift --- damus/Views/MetadataView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/damus/Views/MetadataView.swift b/damus/Views/MetadataView.swift index eec89d0..4588d42 100644 --- a/damus/Views/MetadataView.swift +++ b/damus/Views/MetadataView.swift @@ -112,7 +112,7 @@ struct MetadataView: View { profiles.add(id: "0", profile: ts_profile) } } - TextField("NIP 05 (@)", text: $nip05) + TextField("NIP-05 Verification Domain (eg: example.com)", text: $nip05) .autocorrectionDisabled(true) .textInputAutocapitalization(.never) } From 972716882c12c6367e08edad780c101dd4b974aa Mon Sep 17 00:00:00 2001 From: William Casarin Date: Fri, 23 Dec 2022 09:20:44 -0800 Subject: [PATCH 12/24] profile: add ability to gets/set nip05 --- damus/Nostr/Nostr.swift | 8 +++++++- damus/Views/ProfilePicView.swift | 2 +- damus/Views/ProfileView.swift | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/damus/Nostr/Nostr.swift b/damus/Nostr/Nostr.swift index 9845ecb..d809341 100644 --- a/damus/Nostr/Nostr.swift +++ b/damus/Nostr/Nostr.swift @@ -10,7 +10,7 @@ import Foundation struct Profile: Codable { var value: [String: String] - init (name: String?, display_name: String?, about: String?, picture: String?, website: String?, lud06: String?, lud16: String?) { + init (name: String?, display_name: String?, about: String?, picture: String?, website: String?, lud06: String?, lud16: String?, nip05: String?) { self.value = [:] self.name = name self.display_name = display_name @@ -19,6 +19,7 @@ struct Profile: Codable { self.website = website self.lud06 = lud06 self.lud16 = lud16 + self.nip05 = nip05 } var display_name: String? { @@ -56,6 +57,11 @@ struct Profile: Codable { set(s) { value["lud16"] = s } } + var nip05: String? { + get { return value["nip05"]; } + set(s) { value["nip05"] = s } + } + var lightning_uri: URL? { return make_ln_url(self.lud06) ?? make_ln_url(self.lud16) } diff --git a/damus/Views/ProfilePicView.swift b/damus/Views/ProfilePicView.swift index d614a7f..e0e266b 100644 --- a/damus/Views/ProfilePicView.swift +++ b/damus/Views/ProfilePicView.swift @@ -101,7 +101,7 @@ struct ProfilePicView: View { func make_preview_profiles(_ pubkey: String) -> Profiles { let profiles = Profiles() let picture = "http://cdn.jb55.com/img/red-me.jpg" - let profile = Profile(name: "jb55", display_name: "William Casarin", about: "It's me", picture: picture, website: "https://jb55.com", lud06: nil, lud16: nil) + let profile = Profile(name: "jb55", display_name: "William Casarin", about: "It's me", picture: picture, website: "https://jb55.com", lud06: nil, lud16: nil, nip05: "jb55.com") let ts_profile = TimestampedProfile(profile: profile, timestamp: 0) profiles.add(id: pubkey, profile: ts_profile) return profiles diff --git a/damus/Views/ProfileView.swift b/damus/Views/ProfileView.swift index b924f2e..f450824 100644 --- a/damus/Views/ProfileView.swift +++ b/damus/Views/ProfileView.swift @@ -211,7 +211,7 @@ 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 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") + 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) return damus From 49082683473108b8211e991830750240bb014707 Mon Sep 17 00:00:00 2001 From: Thomas <31560900+0xtlt@users.noreply.github.com> Date: Fri, 23 Dec 2022 18:26:19 +0100 Subject: [PATCH 13/24] Rebased tmp profile --- damus/Views/MetadataView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/damus/Views/MetadataView.swift b/damus/Views/MetadataView.swift index 4588d42..1d818d6 100644 --- a/damus/Views/MetadataView.swift +++ b/damus/Views/MetadataView.swift @@ -107,7 +107,7 @@ struct MetadataView: View { self.timer?.invalidate() self.timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: false) { _ in profiles = Profiles() - let tmp_profile = Profile(name: "0", display_name: "0", about: "0", picture: isHttpsUrl(picture) && isImage(picture) ? picture : nil, website: nil, nip05: "", lud06: nil, lud16: nil) + let tmp_profile = Profile(name: "0", display_name: "0", about: "0", picture: isHttpsUrl(picture) && isImage(picture) ? picture : nil, website: nil, lud06: "", lud16: nil, nip05: nil) let ts_profile = TimestampedProfile(profile: tmp_profile, timestamp: 0) profiles.add(id: "0", profile: ts_profile) } From 444f76a233085480764a96e218d466ca40c2c599 Mon Sep 17 00:00:00 2001 From: Thomas <31560900+0xtlt@users.noreply.github.com> Date: Fri, 23 Dec 2022 18:27:39 +0100 Subject: [PATCH 14/24] MetadataView -> EditMetadataView --- damus.xcodeproj/project.pbxproj | 8 ++++---- damus/Views/ConfigView.swift | 2 +- .../Views/{MetadataView.swift => EditMetadataView.swift} | 8 ++++---- damus/Views/ProfileView.swift | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) rename damus/Views/{MetadataView.swift => EditMetadataView.swift} (97%) diff --git a/damus.xcodeproj/project.pbxproj b/damus.xcodeproj/project.pbxproj index 74f09d9..10e0e62 100644 --- a/damus.xcodeproj/project.pbxproj +++ b/damus.xcodeproj/project.pbxproj @@ -129,7 +129,7 @@ 4CEE2AF7280B2DEA00AB5EEF /* ProfileName.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CEE2AF6280B2DEA00AB5EEF /* ProfileName.swift */; }; 4CEE2AF9280B2EAC00AB5EEF /* PowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CEE2AF8280B2EAC00AB5EEF /* PowView.swift */; }; 4CEE2B02280B39E800AB5EEF /* EventActionBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CEE2B01280B39E800AB5EEF /* EventActionBar.swift */; }; - E990020F2955F837003BBC5A /* MetadataView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E990020E2955F837003BBC5A /* MetadataView.swift */; }; + E990020F2955F837003BBC5A /* EditMetadataView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E990020E2955F837003BBC5A /* EditMetadataView.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -307,7 +307,7 @@ 4CEE2AF6280B2DEA00AB5EEF /* ProfileName.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileName.swift; sourceTree = ""; }; 4CEE2AF8280B2EAC00AB5EEF /* PowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PowView.swift; sourceTree = ""; }; 4CEE2B01280B39E800AB5EEF /* EventActionBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventActionBar.swift; sourceTree = ""; }; - E990020E2955F837003BBC5A /* MetadataView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MetadataView.swift; sourceTree = ""; }; + E990020E2955F837003BBC5A /* EditMetadataView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditMetadataView.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -475,7 +475,7 @@ 4C216F31286E388800040376 /* DMChatView.swift */, 4C216F33286F5ACD00040376 /* DMView.swift */, 4C06670028FC7C5900038D2A /* RelayView.swift */, - E990020E2955F837003BBC5A /* MetadataView.swift */, + E990020E2955F837003BBC5A /* EditMetadataView.swift */, ); path = Views; sourceTree = ""; @@ -848,7 +848,7 @@ 4C3EA67D28FFBBA300C48A62 /* InvoicesView.swift in Sources */, 4C363A8E28236FE4006E126D /* NoteContentView.swift in Sources */, 4C90BD1A283AA67F008EE7EF /* Bech32.swift in Sources */, - E990020F2955F837003BBC5A /* MetadataView.swift in Sources */, + E990020F2955F837003BBC5A /* EditMetadataView.swift in Sources */, 4CACA9D5280C31E100D9BBE8 /* ReplyView.swift in Sources */, 4C0A3F97280F8E02000448DE /* ThreadView.swift in Sources */, 4C06670B28FDE64700038D2A /* damus.c in Sources */, diff --git a/damus/Views/ConfigView.swift b/damus/Views/ConfigView.swift index 5e61df7..8095356 100644 --- a/damus/Views/ConfigView.swift +++ b/damus/Views/ConfigView.swift @@ -80,7 +80,7 @@ struct ConfigView: View { Section("Account settings") { NavigationLink { - MetadataView(damus_state: state, profileModel: ProfileModel(pubkey: state.pubkey, damus: state)) + EditMetadataView(damus_state: state, profileModel: ProfileModel(pubkey: state.pubkey, damus: state)) } label: { Text("Profile metadata") } diff --git a/damus/Views/MetadataView.swift b/damus/Views/EditMetadataView.swift similarity index 97% rename from damus/Views/MetadataView.swift rename to damus/Views/EditMetadataView.swift index 1d818d6..7ecd34a 100644 --- a/damus/Views/MetadataView.swift +++ b/damus/Views/EditMetadataView.swift @@ -1,5 +1,5 @@ // -// MetadataView.swift +// EditMetadataView.swift // damus // // Created by Thomas Tastet on 23/12/2022. @@ -51,7 +51,7 @@ func isImage(_ urlString: String) -> Bool { return result } -struct MetadataView: View { +struct EditMetadataView: View { let damus_state: DamusState @State var name: String = "" @State var about: String = "" @@ -165,10 +165,10 @@ struct MetadataView: View { } } -struct MetadataView_Previews: PreviewProvider { +struct EditMetadataView_Previews: PreviewProvider { static var previews: some View { let ds = test_damus_state() let profile_model = ProfileModel(pubkey: ds.pubkey, damus: ds) - MetadataView(damus_state: ds, profileModel: profile_model) + EditMetadataView(damus_state: ds, profileModel: profile_model) } } diff --git a/damus/Views/ProfileView.swift b/damus/Views/ProfileView.swift index 789b98a..c302a41 100644 --- a/damus/Views/ProfileView.swift +++ b/damus/Views/ProfileView.swift @@ -141,7 +141,7 @@ struct ProfileView: View { ) }.sheet(isPresented: $showingEditProfile) { - MetadataView(damus_state: damus_state, profileModel: profile) + EditMetadataView(damus_state: damus_state, profileModel: profile) } ProfileNameView(pubkey: profile.pubkey, profile: data, contacts: damus_state.contacts) From e6a512649486fe5bbf6e4be992423d1daa7d793f Mon Sep 17 00:00:00 2001 From: William Casarin Date: Fri, 23 Dec 2022 12:07:48 -0800 Subject: [PATCH 15/24] profile: many fixes and tweaks --- damus/Views/ConfigView.swift | 8 -- damus/Views/EditMetadataView.swift | 118 +++++++++++++++-------------- damus/Views/ProfileView.swift | 2 +- 3 files changed, 61 insertions(+), 67 deletions(-) diff --git a/damus/Views/ConfigView.swift b/damus/Views/ConfigView.swift index 8095356..eea1e05 100644 --- a/damus/Views/ConfigView.swift +++ b/damus/Views/ConfigView.swift @@ -78,14 +78,6 @@ struct ConfigView: View { } } - Section("Account settings") { - NavigationLink { - EditMetadataView(damus_state: state, profileModel: ProfileModel(pubkey: state.pubkey, damus: state)) - } label: { - Text("Profile metadata") - } - } - Section("Reset") { Button("Logout") { confirm_logout = true diff --git a/damus/Views/EditMetadataView.swift b/damus/Views/EditMetadataView.swift index 7ecd34a..0b27f61 100644 --- a/damus/Views/EditMetadataView.swift +++ b/damus/Views/EditMetadataView.swift @@ -53,25 +53,32 @@ func isImage(_ urlString: String) -> Bool { struct EditMetadataView: View { let damus_state: DamusState - @State var name: String = "" - @State var about: String = "" - @State var picture: String = "" - @State var nip05: String = "" - @State var nickname: String = "" - @State var lud06: String = "" - @State var lud16: String = "" + @State var display_name: String + @State var about: String + @State var picture: String + @State var nip05: String + @State var name: String + @State var lud06: String + @State var lud16: String @State private var showAlert = false - // Image preview - @State var profiles = Profiles() - @State private var timer: Timer? - - @StateObject var profileModel: ProfileModel + init (damus_state: DamusState) { + self.damus_state = damus_state + let data = damus_state.profiles.lookup(id: damus_state.pubkey) + + name = data?.name ?? "" + display_name = data?.display_name ?? "" + about = data?.about ?? "" + picture = data?.picture ?? "" + nip05 = data?.nip05 ?? "" + lud06 = data?.lud06 ?? "" + lud16 = data?.lud16 ?? "" + } func save() { let metadata = NostrMetadata( - display_name: name, - name: nickname, + display_name: display_name, + name: name, about: about, website: nil, nip05: nip05.isEmpty ? nil : nip05, @@ -89,54 +96,67 @@ struct EditMetadataView: View { var body: some View { VStack(alignment: .leading) { + HStack { + Spacer() + ProfilePicView(pubkey: damus_state.pubkey, size: PPM_SIZE, highlight: .none, profiles: damus_state.profiles, picture: picture) + Spacer() + } + .padding([.top], 30) Form { - HStack { - Spacer() - ProfilePicView(pubkey: "0", size: PPM_SIZE, highlight: .none, profiles: profiles) - Spacer() - } - Section("Your Nostr Profile") { - TextField("Your username", text: $name) - .textInputAutocapitalization(.never) - TextField("Your @", text: $nickname) + Section("Your Name") { + TextField("Satoshi Nakamoto", text: $display_name) + .autocorrectionDisabled(true) .textInputAutocapitalization(.never) - TextField("Profile Picture Url", text: $picture) + } + + Section("Username") { + TextField("satoshi", text: $name) .autocorrectionDisabled(true) .textInputAutocapitalization(.never) - .onChange(of: picture) { newValue in - self.timer?.invalidate() - self.timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: false) { _ in - profiles = Profiles() - let tmp_profile = Profile(name: "0", display_name: "0", about: "0", picture: isHttpsUrl(picture) && isImage(picture) ? picture : nil, website: nil, lud06: "", lud16: nil, nip05: nil) - let ts_profile = TimestampedProfile(profile: tmp_profile, timestamp: 0) - profiles.add(id: "0", profile: ts_profile) - } - } - TextField("NIP-05 Verification Domain (eg: example.com)", text: $nip05) + + } + + Section ("Profile Picture") { + TextField("https://example.com/pic.jpg", text: $picture) .autocorrectionDisabled(true) .textInputAutocapitalization(.never) + } - Section("Description") { + Section("About Me") { ZStack(alignment: .topLeading) { TextEditor(text: $about) .textInputAutocapitalization(.sentences) if about.isEmpty { - Text("Type your description here...") + Text("Absolute boss") .offset(x: 0, y: 7) .foregroundColor(Color(uiColor: .placeholderText)) } } } - Section("Advanced") { + Section(content: { + TextField("Lightning Address", text: $lud16) + .autocorrectionDisabled(true) + .textInputAutocapitalization(.never) TextField("LNURL", text: $lud06) .autocorrectionDisabled(true) .textInputAutocapitalization(.never) - TextField("LN Address", text: $lud16) + }, header: { + Text("Bitcoin Lightning Tips") + }, footer: { + Text("Only one needs to be set") + }) + + Section(content: { + TextField("example.com", text: $nip05) .autocorrectionDisabled(true) .textInputAutocapitalization(.never) - } + }, header: { + Text("NIP-05 Verification") + }, footer: { + Text("\(name)@\(nip05) will be used for verification") + }) Button("Save") { save() @@ -146,29 +166,11 @@ struct EditMetadataView: View { } } } - .onAppear() { - profileModel.subscribe() - - let data = damus_state.profiles.lookup(id: profileModel.pubkey) - - name = data?.display_name ?? name - nickname = data?.name ?? name - about = data?.about ?? about - picture = data?.picture ?? picture - nip05 = data?.nip05 ?? nip05 - lud06 = data?.lud06 ?? lud06 - lud16 = data?.lud16 ?? lud16 - } - .onDisappear { - profileModel.unsubscribe() - } } } struct EditMetadataView_Previews: PreviewProvider { static var previews: some View { - let ds = test_damus_state() - let profile_model = ProfileModel(pubkey: ds.pubkey, damus: ds) - EditMetadataView(damus_state: ds, profileModel: profile_model) + EditMetadataView(damus_state: test_damus_state()) } } diff --git a/damus/Views/ProfileView.swift b/damus/Views/ProfileView.swift index c302a41..aeead69 100644 --- a/damus/Views/ProfileView.swift +++ b/damus/Views/ProfileView.swift @@ -141,7 +141,7 @@ struct ProfileView: View { ) }.sheet(isPresented: $showingEditProfile) { - EditMetadataView(damus_state: damus_state, profileModel: profile) + EditMetadataView(damus_state: damus_state) } ProfileNameView(pubkey: profile.pubkey, profile: data, contacts: damus_state.contacts) From db763d07fea0d2d75f0a4d1abc2f6c6a57762a3e Mon Sep 17 00:00:00 2001 From: Thomas <31560900+0xtlt@users.noreply.github.com> Date: Fri, 23 Dec 2022 19:44:52 +0100 Subject: [PATCH 16/24] WEBP --- damus/Views/EditMetadataView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/damus/Views/EditMetadataView.swift b/damus/Views/EditMetadataView.swift index 0b27f61..6f71425 100644 --- a/damus/Views/EditMetadataView.swift +++ b/damus/Views/EditMetadataView.swift @@ -16,7 +16,7 @@ func isHttpsUrl(_ string: String) -> Bool { } func isImage(_ urlString: String) -> Bool { - let imageTypes = ["image/jpg", "image/jpeg", "image/png", "image/gif", "image/tiff", "image/bmp"] + let imageTypes = ["image/jpg", "image/jpeg", "image/png", "image/gif", "image/tiff", "image/bmp", "image/webp"] guard let url = URL(string: urlString) else { return false From 79f422397704d73140f7d375e0bcb715709a0156 Mon Sep 17 00:00:00 2001 From: William Casarin Date: Fri, 23 Dec 2022 13:13:30 -0800 Subject: [PATCH 17/24] Profile Editor Changelog-Added: Added profile edit view Changelog-Changed: Don't fetch followers right away --- damus/Views/EditMetadataView.swift | 55 +++++++++++++-------------- damus/Views/FollowButtonView.swift | 18 ++------- damus/Views/FollowingView.swift | 8 +++- damus/Views/ProfilePicView.swift | 42 +++++++++++++++------ damus/Views/ProfileView.swift | 60 +++++++++++++++++++++++------- 5 files changed, 114 insertions(+), 69 deletions(-) diff --git a/damus/Views/EditMetadataView.swift b/damus/Views/EditMetadataView.swift index 6f71425..4bbe868 100644 --- a/damus/Views/EditMetadataView.swift +++ b/damus/Views/EditMetadataView.swift @@ -58,21 +58,22 @@ struct EditMetadataView: View { @State var picture: String @State var nip05: String @State var name: String - @State var lud06: String - @State var lud16: String - @State private var showAlert = false + @State var ln: String + @State var website: String + + @Environment(\.dismiss) var dismiss init (damus_state: DamusState) { self.damus_state = damus_state let data = damus_state.profiles.lookup(id: damus_state.pubkey) - name = data?.name ?? "" - display_name = data?.display_name ?? "" - about = data?.about ?? "" - picture = data?.picture ?? "" - nip05 = data?.nip05 ?? "" - lud06 = data?.lud06 ?? "" - lud16 = data?.lud16 ?? "" + _name = State(initialValue: data?.name ?? "") + _display_name = State(initialValue: data?.display_name ?? "") + _about = State(initialValue: data?.about ?? "") + _website = State(initialValue: data?.website ?? "") + _picture = State(initialValue: data?.picture ?? "") + _nip05 = State(initialValue: data?.nip05 ?? "") + _ln = State(initialValue: data?.lud16 ?? data?.lud06 ?? "") } func save() { @@ -80,11 +81,11 @@ struct EditMetadataView: View { display_name: display_name, name: name, about: about, - website: nil, + website: website, nip05: nip05.isEmpty ? nil : nip05, picture: picture.isEmpty ? nil : picture, - lud06: lud06.isEmpty ? nil : lud06, - lud16: lud16.isEmpty ? nil : lud16 + lud06: ln.contains("@") ? ln : nil, + lud16: ln.contains("@") ? nil : ln ); let m_metadata_ev = make_metadata_event(keypair: damus_state.keypair, metadata: metadata) @@ -98,10 +99,9 @@ struct EditMetadataView: View { VStack(alignment: .leading) { HStack { Spacer() - ProfilePicView(pubkey: damus_state.pubkey, size: PPM_SIZE, highlight: .none, profiles: damus_state.profiles, picture: picture) + InnerProfilePicView(url: URL(string: picture), pubkey: damus_state.pubkey, size: PPM_SIZE, highlight: .none) Spacer() } - .padding([.top], 30) Form { Section("Your Name") { TextField("Satoshi Nakamoto", text: $display_name) @@ -120,7 +120,12 @@ struct EditMetadataView: View { TextField("https://example.com/pic.jpg", text: $picture) .autocorrectionDisabled(true) .textInputAutocapitalization(.never) - + } + + Section("Website") { + TextField("https://jb55.com", text: $website) + .autocorrectionDisabled(true) + .textInputAutocapitalization(.never) } Section("About Me") { @@ -135,18 +140,11 @@ struct EditMetadataView: View { } } - Section(content: { - TextField("Lightning Address", text: $lud16) + Section("Bitcoin Lightning Tips") { + TextField("Lightning Address or LNURL", text: $ln) .autocorrectionDisabled(true) .textInputAutocapitalization(.never) - TextField("LNURL", text: $lud06) - .autocorrectionDisabled(true) - .textInputAutocapitalization(.never) - }, header: { - Text("Bitcoin Lightning Tips") - }, footer: { - Text("Only one needs to be set") - }) + } Section(content: { TextField("example.com", text: $nip05) @@ -160,12 +158,11 @@ struct EditMetadataView: View { Button("Save") { save() - showAlert = true - }.alert(isPresented: $showAlert) { - Alert(title: Text("Saved"), message: Text("Your metadata has been saved."), dismissButton: .default(Text("OK"))) + dismiss() } } } + .navigationTitle("Edit Profile") } } diff --git a/damus/Views/FollowButtonView.swift b/damus/Views/FollowButtonView.swift index 6bc6b4b..d7de02d 100644 --- a/damus/Views/FollowButtonView.swift +++ b/damus/Views/FollowButtonView.swift @@ -13,14 +13,9 @@ struct FollowButtonView: View { let target: FollowTarget @State var follow_state: FollowState - let perform: (() -> Void)? var body: some View { Button { - if perform != nil { - perform!() - } - follow_state = perform_follow_btn_action(follow_state, target: target) } label: { Text(follow_btn_txt(follow_state)) @@ -71,19 +66,16 @@ struct FollowButtonPreviews: View { var body: some View { VStack { Text("Unfollows") - FollowButtonView(target: target, follow_state: .unfollows, perform: nil) + FollowButtonView(target: target, follow_state: .unfollows) Text("Following") - FollowButtonView(target: target, follow_state: .following, perform: nil) + FollowButtonView(target: target, follow_state: .following) Text("Follows") - FollowButtonView(target: target, follow_state: .follows, perform: nil) + FollowButtonView(target: target, follow_state: .follows) Text("Unfollowing") - FollowButtonView(target: target, follow_state: .unfollowing, perform: nil) - - Text("Edit") - FollowButtonView(target: target, follow_state: .edit, perform: nil) + FollowButtonView(target: target, follow_state: .unfollowing) } } } @@ -106,8 +98,6 @@ func perform_follow_btn_action(_ fs: FollowState, target: FollowTarget) -> Follo case .unfollows: notify(.follow, target) return .unfollowing - case .edit: - return .edit } } diff --git a/damus/Views/FollowingView.swift b/damus/Views/FollowingView.swift index bff37fa..4d14df6 100644 --- a/damus/Views/FollowingView.swift +++ b/damus/Views/FollowingView.swift @@ -32,7 +32,7 @@ struct FollowUserView: View { } .buttonStyle(PlainButtonStyle()) - FollowButtonView(target: target, follow_state: damus_state.contacts.follow_state(target.pubkey), perform: nil) + FollowButtonView(target: target, follow_state: damus_state.contacts.follow_state(target.pubkey)) } } } @@ -53,6 +53,12 @@ struct FollowersView: View { } } .navigationBarTitle("\(Profile.displayName(profile: profile, pubkey: whos))'s Followers") + .onAppear { + followers.subscribe() + } + .onDisappear { + followers.unsubscribe() + } } } diff --git a/damus/Views/ProfilePicView.swift b/damus/Views/ProfilePicView.swift index e0e266b..d6bc33b 100644 --- a/damus/Views/ProfilePicView.swift +++ b/damus/Views/ProfilePicView.swift @@ -32,17 +32,13 @@ func pfp_line_width(_ h: Highlight) -> CGFloat { } } -struct ProfilePicView: View { - +struct InnerProfilePicView: View { @Environment(\.redactionReasons) private var reasons + let url: URL? let pubkey: String let size: CGFloat let highlight: Highlight - let profiles: Profiles - let isPlaceholder: Bool = false - - @State var picture: String? = nil var PlaceholderColor: Color { return id_to_color(pubkey) @@ -56,11 +52,8 @@ struct ProfilePicView: View { .padding(2) } - var MainContent: some View { + var body: some View { Group { - let pic = picture ?? profiles.lookup(id: pubkey)?.picture ?? robohash(pubkey) - let url = URL(string: pic) - if reasons.isEmpty { KFAnimatedImage(url) .configure { view in @@ -82,8 +75,27 @@ struct ProfilePicView: View { .overlay(Circle().stroke(highlight_color(highlight), lineWidth: pfp_line_width(highlight))) } +} + +struct ProfilePicView: View { + + let pubkey: String + let size: CGFloat + let highlight: Highlight + let profiles: Profiles + + @State var picture: String? + + init (pubkey: String, size: CGFloat, highlight: Highlight, profiles: Profiles, picture: String? = nil) { + self.pubkey = pubkey + self.profiles = profiles + self.size = size + self.highlight = highlight + self._picture = State(initialValue: picture) + } + var body: some View { - MainContent + InnerProfilePicView(url: get_profile_url(picture: picture, pubkey: pubkey, profiles: profiles), pubkey: pubkey, size: size, highlight: highlight) .onReceive(handle_notify(.profile_updated)) { notif in let updated = notif.object as! ProfileUpdate @@ -98,6 +110,14 @@ struct ProfilePicView: View { } } +func get_profile_url(picture: String?, pubkey: String, profiles: Profiles) -> URL { + let pic = picture ?? profiles.lookup(id: pubkey)?.picture ?? robohash(pubkey) + if let url = URL(string: pic) { + return url + } + return URL(string: robohash(pubkey))! +} + func make_preview_profiles(_ pubkey: String) -> Profiles { let profiles = Profiles() let picture = "http://cdn.jb55.com/img/red-me.jpg" diff --git a/damus/Views/ProfileView.swift b/damus/Views/ProfileView.swift index aeead69..33db599 100644 --- a/damus/Views/ProfileView.swift +++ b/damus/Views/ProfileView.swift @@ -17,7 +17,6 @@ enum FollowState { case following case unfollowing case unfollows - case edit } func follow_btn_txt(_ fs: FollowState) -> String { @@ -30,8 +29,6 @@ func follow_btn_txt(_ fs: FollowState) -> String { return "Unfollowing..." case .unfollows: return "Follow" - case .edit: - return "Edit" } } @@ -45,8 +42,6 @@ func follow_btn_enabled_state(_ fs: FollowState) -> Bool { return false case .unfollows: return true - case .edit: - return true } } @@ -82,6 +77,40 @@ struct ProfileNameView: View { } } +struct EditButton: View { + let damus_state: DamusState + + @Environment(\.colorScheme) var colorScheme + + var body: some View { + NavigationLink(destination: EditMetadataView(damus_state: damus_state)) { + Text("Edit") + .padding(.horizontal, 25) + .padding(.vertical, 10) + .font(.caption.weight(.bold)) + .foregroundColor(fillColor()) + .background(emptyColor()) + .cornerRadius(20) + .overlay { + RoundedRectangle(cornerRadius: 16) + .stroke(borderColor(), lineWidth: 1) + } + } + } + + func fillColor() -> Color { + colorScheme == .light ? .black : .white + } + + func emptyColor() -> Color { + colorScheme == .light ? .white : .black + } + + func borderColor() -> Color { + colorScheme == .light ? .black.opacity(0.1) : .white.opacity(0.2) + } +} + struct ProfileView: View { let damus_state: DamusState @@ -134,14 +163,17 @@ struct ProfileView: View { DMButton - FollowButtonView( - target: profile.get_follow_target(), - follow_state: profile.pubkey == damus_state.pubkey ? .edit : damus_state.contacts.follow_state(profile.pubkey), - perform: profile.pubkey == damus_state.pubkey ? { showingEditProfile.toggle() } : nil - ) + if profile.pubkey != damus_state.pubkey { + FollowButtonView( + target: profile.get_follow_target(), + follow_state: damus_state.contacts.follow_state(profile.pubkey) + ) + } else { + NavigationLink(destination: EditMetadataView(damus_state: damus_state)) { + EditButton(damus_state: damus_state) + } + } - }.sheet(isPresented: $showingEditProfile) { - EditMetadataView(damus_state: damus_state) } ProfileNameView(pubkey: profile.pubkey, profile: data, contacts: damus_state.contacts) @@ -201,11 +233,11 @@ struct ProfileView: View { } .onAppear() { profile.subscribe() - followers.subscribe() + //followers.subscribe() } .onDisappear { profile.unsubscribe() - followers.unsubscribe() + //followers.unsubscribe() // our profilemodel needs a bit more help } } From 85ba7bbed0fceabfdd20ee55b88dd7593cb7d5f7 Mon Sep 17 00:00:00 2001 From: Thomas Rademaker Date: Thu, 22 Dec 2022 15:46:35 -0500 Subject: [PATCH 18/24] ContentTimelineView inside a page tabView To get a nice swipe gesture Closes: #103 Changelog-Changed: Add swipe gesture between posts and post & replies --- damus/ContentView.swift | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/damus/ContentView.swift b/damus/ContentView.swift index 9b05b38..f5d0af7 100644 --- a/damus/ContentView.swift +++ b/damus/ContentView.swift @@ -78,17 +78,14 @@ struct ContentView: View { @Environment(\.colorScheme) var colorScheme var PostingTimelineView: some 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) - } - if privkey != nil { - PostButtonContainer { - self.active_sheet = .post - } - } + VStack { + TabView(selection: $filter_state) { + ContentTimelineView + .tag(FilterState.posts) + ContentTimelineView + .tag(FilterState.posts_and_replies) } + .tabViewStyle(.page(indexDisplayMode: .never)) } .safeAreaInset(edge: .top) { VStack(spacing: 0) { @@ -102,6 +99,19 @@ struct ContentView: View { } } + var ContentTimelineView: 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) + } + if privkey != nil { + PostButtonContainer { + self.active_sheet = .post + } + } + } + } + var FiltersView: some View { VStack{ Picker("Filter State", selection: $filter_state) { From acb204964171b611da86595d8cba79a9253a034a Mon Sep 17 00:00:00 2001 From: William Casarin Date: Fri, 23 Dec 2022 15:17:19 -0800 Subject: [PATCH 19/24] profile: don't auto-load followers This is a bandwidth-intensive operation Changelog-Changed: Don't auto-load follower count --- damus/Models/FollowersModel.swift | 13 ++++++++++--- damus/Views/FollowingView.swift | 2 +- damus/Views/ProfileView.swift | 31 ++++++++++++++++++++++--------- 3 files changed, 33 insertions(+), 13 deletions(-) diff --git a/damus/Models/FollowersModel.swift b/damus/Models/FollowersModel.swift index a361111..b5b69a4 100644 --- a/damus/Models/FollowersModel.swift +++ b/damus/Models/FollowersModel.swift @@ -12,12 +12,19 @@ class FollowersModel: ObservableObject { let target: String var needs_sub: Bool = true - @Published var contacts: [String] = [] + @Published var contacts: [String]? = nil var has_contact: Set = Set() let sub_id: String = UUID().description let profiles_id: String = UUID().description + var count_display: String { + guard let contacts = self.contacts else { + return "?" + } + return "\(contacts.count)"; + } + init(damus_state: DamusState, target: String) { self.damus_state = damus_state self.target = target @@ -49,13 +56,13 @@ class FollowersModel: ObservableObject { contacts: damus_state.contacts, pubkey: damus_state.pubkey, ev: ev ) - contacts.append(ev.pubkey) + contacts?.append(ev.pubkey) has_contact.insert(ev.pubkey) } func load_profiles(relay_id: String) { var filter = NostrFilter.filter_profiles - let authors = find_profiles_to_fetch_pk(profiles: damus_state.profiles, event_pubkeys: contacts) + let authors = find_profiles_to_fetch_pk(profiles: damus_state.profiles, event_pubkeys: contacts ?? []) if authors.isEmpty { return } diff --git a/damus/Views/FollowingView.swift b/damus/Views/FollowingView.swift index 4d14df6..3caf22a 100644 --- a/damus/Views/FollowingView.swift +++ b/damus/Views/FollowingView.swift @@ -47,7 +47,7 @@ struct FollowersView: View { let profile = damus_state.profiles.lookup(id: whos) ScrollView { LazyVStack(alignment: .leading) { - ForEach(followers.contacts, id: \.self) { pk in + ForEach(followers.contacts ?? [], id: \.self) { pk in FollowUserView(target: .pubkey(pk), damus_state: damus_state) } } diff --git a/damus/Views/ProfileView.swift b/damus/Views/ProfileView.swift index 33db599..4b0ccf8 100644 --- a/damus/Views/ProfileView.swift +++ b/damus/Views/ProfileView.swift @@ -201,20 +201,33 @@ struct ProfileView: View { } let fview = FollowersView(damus_state: damus_state, whos: profile.pubkey) .environmentObject(followers) - NavigationLink(destination: fview) { - HStack { - Text("\(followers.contacts.count)") - .font(.subheadline.weight(.medium)) - Text("Followers") - .font(.subheadline) - .foregroundColor(.gray) + if followers.contacts != nil { + NavigationLink(destination: fview) { + FollowersCount } + .buttonStyle(PlainButtonStyle()) + } else { + FollowersCount + .onTapGesture { + UIImpactFeedbackGenerator(style: .light).impactOccurred() + followers.contacts = [] + followers.subscribe() + } } - .buttonStyle(PlainButtonStyle()) } } } + var FollowersCount: some View { + HStack { + Text("\(followers.count_display)") + .font(.subheadline.weight(.medium)) + Text("Followers") + .font(.subheadline) + .foregroundColor(.gray) + } + } + var body: some View { VStack(alignment: .leading) { ScrollView { @@ -237,7 +250,7 @@ struct ProfileView: View { } .onDisappear { profile.unsubscribe() - //followers.unsubscribe() + followers.unsubscribe() // our profilemodel needs a bit more help } } From a7aee616c74ad1c242027723be3115c946dd02f1 Mon Sep 17 00:00:00 2001 From: William Casarin Date: Fri, 23 Dec 2022 15:17:59 -0800 Subject: [PATCH 20/24] like: increase boop intensity Changelog-Changed: Increase like boop intensity --- damus/Views/EventActionBar.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/damus/Views/EventActionBar.swift b/damus/Views/EventActionBar.swift index 872aadb..4bcfe8e 100644 --- a/damus/Views/EventActionBar.swift +++ b/damus/Views/EventActionBar.swift @@ -21,7 +21,7 @@ enum ActionBarSheet: Identifiable { struct EventActionBar: View { let damus_state: DamusState let event: NostrEvent - let generator = UIImpactFeedbackGenerator(style: .light) + let generator = UIImpactFeedbackGenerator(style: .medium) @State var sheet: ActionBarSheet? = nil @State var confirm_boost: Bool = false @StateObject var bar: ActionBarModel From 6079b2f62060e55a9cfa0342d5600fbd28b25efa Mon Sep 17 00:00:00 2001 From: William Casarin Date: Fri, 23 Dec 2022 15:24:55 -0800 Subject: [PATCH 21/24] remove old comment --- damus/Views/EventDetailView.swift | 33 ------------------------------- 1 file changed, 33 deletions(-) diff --git a/damus/Views/EventDetailView.swift b/damus/Views/EventDetailView.swift index 79a1c45..94f1919 100644 --- a/damus/Views/EventDetailView.swift +++ b/damus/Views/EventDetailView.swift @@ -308,39 +308,6 @@ func scroll_to_event(scroller: ScrollViewProxy, id: String, delay: Double, anima } } - - /* - func OldEventView(proxy: ScrollViewProxy, ev: NostrEvent, highlight: Highlight, collapsed_events: [CollapsedEvent]) -> some View { - Group { - if ev.id == thread.event.id { - EventView(event: ev, highlight: .main, has_action_bar: true) - .onAppear() { - scroll_to_event(scroller: proxy, id: ev.id, delay: 0.5, animate: true) - } - .onTapGesture { - print_event(ev) - let any = any_collapsed(collapsed_events) - if (collapsed && any) || (!collapsed && !any) { - toggle_collapse_thread(scroller: proxy, id: ev.id) - } - } - } else { - if !(self.collapsed && highlight.is_none) { - EventView(event: ev, highlight: collapsed ? .none : highlight, has_action_bar: true) - .onTapGesture { - print_event(ev) - if !collapsed { - toggle_collapse_thread(scroller: proxy, id: ev.id) - } - thread.event = ev - } - } - } - } - } - */ - - extension Collection { /// Returns the element at the specified index if it is within bounds, otherwise nil. From e64ccb96b3ad2172aa07bdcf2d165983074ac174 Mon Sep 17 00:00:00 2001 From: William Casarin Date: Fri, 23 Dec 2022 16:51:47 -0800 Subject: [PATCH 22/24] v0.1.9 --- CHANGELOG.md | 21 +++++++++++++++++++-- damus.xcodeproj/project.pbxproj | 4 ++-- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2950463..5aa783a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,23 @@ +## [0.1.9] - 2022-12-23 + +### Added + + - Added profile edit view + +### Changed + + - Increase like boop intensity + - Don't auto-load follower count + - Add swipe gesture between posts and post & replies + - Don't fetch followers right away + +### Fixed + + - Fix crash on some bolt11 invoices + - Fixed issues when refreshing global view + + ## [0.1.8] - 2022-12-21 ### Changed @@ -120,5 +139,3 @@ [0.1.2]: https://github.com/damus-io/damus/releases/tag/v0.1.2 - - diff --git a/damus.xcodeproj/project.pbxproj b/damus.xcodeproj/project.pbxproj index 10e0e62..cd4abe1 100644 --- a/damus.xcodeproj/project.pbxproj +++ b/damus.xcodeproj/project.pbxproj @@ -1043,7 +1043,7 @@ "$(inherited)", "$(PROJECT_DIR)", ); - MARKETING_VERSION = 0.1.8; + MARKETING_VERSION = 0.1.9; PRODUCT_BUNDLE_IDENTIFIER = com.jb55.damus2; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = YES; @@ -1082,7 +1082,7 @@ "$(inherited)", "$(PROJECT_DIR)", ); - MARKETING_VERSION = 0.1.8; + MARKETING_VERSION = 0.1.9; PRODUCT_BUNDLE_IDENTIFIER = com.jb55.damus2; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = YES; From d55a47a6a8f667adc3df5cbd500d3fd7525f9a0d Mon Sep 17 00:00:00 2001 From: William Casarin Date: Fri, 23 Dec 2022 17:02:02 -0800 Subject: [PATCH 23/24] Revert "ContentTimelineView inside a page tabView" Noticed this was crashing This reverts commit 85ba7bbed0fceabfdd20ee55b88dd7593cb7d5f7. --- damus/ContentView.swift | 30 ++++++++++-------------------- 1 file changed, 10 insertions(+), 20 deletions(-) diff --git a/damus/ContentView.swift b/damus/ContentView.swift index f5d0af7..9b05b38 100644 --- a/damus/ContentView.swift +++ b/damus/ContentView.swift @@ -78,14 +78,17 @@ struct ContentView: View { @Environment(\.colorScheme) var colorScheme var PostingTimelineView: some View { - VStack { - TabView(selection: $filter_state) { - ContentTimelineView - .tag(FilterState.posts) - ContentTimelineView - .tag(FilterState.posts_and_replies) + VStack{ + ZStack { + if let damus = self.damus_state { + TimelineView(events: $home.events, loading: $home.loading, damus: damus, show_friend_icon: false, filter: filter_event) + } + if privkey != nil { + PostButtonContainer { + self.active_sheet = .post + } + } } - .tabViewStyle(.page(indexDisplayMode: .never)) } .safeAreaInset(edge: .top) { VStack(spacing: 0) { @@ -99,19 +102,6 @@ struct ContentView: View { } } - var ContentTimelineView: 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) - } - if privkey != nil { - PostButtonContainer { - self.active_sheet = .post - } - } - } - } - var FiltersView: some View { VStack{ Picker("Filter State", selection: $filter_state) { From a6f11fcc8b7d0ff2587506cc8348588f19c6e70d Mon Sep 17 00:00:00 2001 From: William Casarin Date: Fri, 23 Dec 2022 17:21:06 -0800 Subject: [PATCH 24/24] Fix changelog --- CHANGELOG.md | 1 - damus.xcodeproj/project.pbxproj | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5aa783a..fa86932 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,6 @@ - Increase like boop intensity - Don't auto-load follower count - - Add swipe gesture between posts and post & replies - Don't fetch followers right away ### Fixed diff --git a/damus.xcodeproj/project.pbxproj b/damus.xcodeproj/project.pbxproj index cd4abe1..ec4e33c 100644 --- a/damus.xcodeproj/project.pbxproj +++ b/damus.xcodeproj/project.pbxproj @@ -1023,7 +1023,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = damus/damus.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEVELOPMENT_ASSET_PATHS = "\"damus/Preview Content\""; DEVELOPMENT_TEAM = XK7H4JAB3D; ENABLE_PREVIEWS = YES; @@ -1062,7 +1062,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = damus/damus.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEVELOPMENT_ASSET_PATHS = "\"damus/Preview Content\""; DEVELOPMENT_TEAM = XK7H4JAB3D; ENABLE_PREVIEWS = YES;