diff --git a/CHANGELOG.md b/CHANGELOG.md index fa86932..ffeef3e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,30 @@ +## [0.1.8-4] - 2022-12-26 -## [0.1.9] - 2022-12-23 +### Added + + - Long press lightning tip button to copy lnurl + + +### Changed + + - Only reload global view on pulldown refresh + - Save privkey in keychain instead of user defaults + - Also show inline images from friend-of-friends + - Show rounded corners on inline images + + +### Fixed + + - Fix bug where typing the first character in the search box defocuses + - Fixed nip05 identifier format in profile editor + - Fix profile and event loading in global view + - Fix lightning tip button sometimes not working + - Make about me multi-line in profile editor + + +[0.1.8-4]: https://github.com/damus-io/damus/releases/tag/v0.1.8-4 + +## [0.1.8-3] - 2022-12-23 ### Added @@ -138,3 +163,5 @@ [0.1.2]: https://github.com/damus-io/damus/releases/tag/v0.1.2 + + diff --git a/README.md b/README.md index 8275fff..993a208 100644 --- a/README.md +++ b/README.md @@ -69,13 +69,11 @@ damus implements the following [Nostr Implementation Possibilities][nips] - All your notifications except 💬 DMs #### 👤 Change Your Profile (PFP) and Bio -- Currently you can't change your pfp on the Damus app (coming soon!). Here's how to do it on other clients (do at your own risk) -1. Get the [Alby](https://getalby.com/) (Chrome, Brave, Firefox) or [nos2x](https://chrome.google.com/webstore/detail/nos2x/kpgefcfmnafjgpblomihpgmejjdanjjp) browser extension (Chrome, Brave) -2. Go to https://damus.io/key to convert your nsec key (secret key in ⚙️ Settings) into a hex version - i. For Alby, right-click the extension, select Options and scroll to the Nostr section to enter your secret hex key - ii. For nos2x, right-click the extension, select Options, then and add the relay `wss://relay.damus.io` and select both read and write, click Save, then enter your secret hex key and click save -3. Visit https://metadata.nostr.com and your profile data should auto-populate from the extension. If not click the extension or refresh the page -4. Add your image using a hosting site like imgbb.com +1. Go to your Profile Page on Damus app +2. Tap on Edit button at the top +3. You will see text fields to update your information and bio +4. For PFP, insert a URL containing your image (support video: https://cdn.jb55.com/vid/pfp-editor.mp4) +5. Save #### ⚡️ Request Sats (Sats or Satoshis are the smallest denomination of bitcoin) diff --git a/damus.xcodeproj/project.pbxproj b/damus.xcodeproj/project.pbxproj index bd5f60a..0ec35c1 100644 --- a/damus.xcodeproj/project.pbxproj +++ b/damus.xcodeproj/project.pbxproj @@ -129,9 +129,9 @@ 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 /* EditMetadataView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E990020E2955F837003BBC5A /* EditMetadataView.swift */; }; BAB68BED29543FA3007BA466 /* SelectWalletView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BAB68BEC29543FA3007BA466 /* SelectWalletView.swift */; }; 6C7DE41F2955169800E66263 /* Vault in Frameworks */ = {isa = PBXBuildFile; productRef = 6C7DE41E2955169800E66263 /* Vault */; }; + E990020F2955F837003BBC5A /* EditMetadataView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E990020E2955F837003BBC5A /* EditMetadataView.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -1031,7 +1031,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = damus/damus.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 3; + CURRENT_PROJECT_VERSION = 4; DEVELOPMENT_ASSET_PATHS = "\"damus/Preview Content\""; DEVELOPMENT_TEAM = XK7H4JAB3D; ENABLE_PREVIEWS = YES; @@ -1070,7 +1070,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = damus/damus.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 3; + CURRENT_PROJECT_VERSION = 4; DEVELOPMENT_ASSET_PATHS = "\"damus/Preview Content\""; DEVELOPMENT_TEAM = XK7H4JAB3D; ENABLE_PREVIEWS = YES; diff --git a/damus/Nostr/RelayPool.swift b/damus/Nostr/RelayPool.swift index f38a2a9..aa79714 100644 --- a/damus/Nostr/RelayPool.swift +++ b/damus/Nostr/RelayPool.swift @@ -41,12 +41,19 @@ class RelayPool { } func remove_handler(sub_id: String) { - handlers = handlers.filter { $0.sub_id != sub_id } + self.handlers = handlers.filter { $0.sub_id != sub_id } + print("removing \(sub_id) handler, current: \(handlers.count)") } func register_handler(sub_id: String, handler: @escaping (String, NostrConnectionEvent) -> ()) { - + for handler in handlers { + // don't add duplicate handlers + if handler.sub_id == sub_id { + return + } + } self.handlers.append(RelayHandler(sub_id: sub_id, callback: handler)) + print("registering \(sub_id) handler, current: \(self.handlers.count)") } func remove_relay(_ relay_id: String) { @@ -125,8 +132,10 @@ class RelayPool { } func unsubscribe(sub_id: String, to: [String]? = nil) { - self.remove_handler(sub_id: sub_id) - self.send(.unsubscribe(sub_id)) + if to == nil { + self.remove_handler(sub_id: sub_id) + } + self.send(.unsubscribe(sub_id), to: to) } func subscribe(sub_id: String, filters: [NostrFilter], handler: @escaping (String, NostrConnectionEvent) -> ()) { diff --git a/damus/Util/Keys.swift b/damus/Util/Keys.swift index 6f797f8..e7b0009 100644 --- a/damus/Util/Keys.swift +++ b/damus/Util/Keys.swift @@ -15,13 +15,14 @@ let PRIVKEY_HRP = "nsec" struct Keypair { let pubkey: String let privkey: String? + let pubkey_bech32: String + let privkey_bech32: String? - var pubkey_bech32: String { - return bech32_pubkey(pubkey)! - } - - var privkey_bech32: String? { - return privkey.flatMap { bech32_privkey($0) } + init(pubkey: String, privkey: String?) { + self.pubkey = pubkey + self.privkey = privkey + self.pubkey_bech32 = bech32_pubkey(pubkey) ?? pubkey + self.privkey_bech32 = privkey.flatMap { bech32_privkey($0) } } } diff --git a/damus/Views/EditMetadataView.swift b/damus/Views/EditMetadataView.swift index f5e9f73..71b65d3 100644 --- a/damus/Views/EditMetadataView.swift +++ b/damus/Views/EditMetadataView.swift @@ -15,6 +15,11 @@ func isHttpsUrl(_ string: String) -> Bool { return urlTest.evaluate(with: string) } +struct NIP05 { + let username: String + let host: String +} + func isImage(_ urlString: String) -> Bool { let imageTypes = ["image/jpg", "image/jpeg", "image/png", "image/gif", "image/tiff", "image/bmp", "image/webp"] @@ -95,6 +100,14 @@ struct EditMetadataView: View { } } + var nip05_parts: NIP05? { + let parts = nip05.split(separator: "@") + guard parts.count == 2 else { + return nil + } + return NIP05(username: String(parts[0]), host: String(parts[1])) + } + var body: some View { VStack(alignment: .leading) { HStack { @@ -149,13 +162,17 @@ struct EditMetadataView: View { } Section(content: { - TextField("example.com", text: $nip05) + TextField("jb55@jb55.com", text: $nip05) .autocorrectionDisabled(true) .textInputAutocapitalization(.never) }, header: { Text("NIP-05 Verification") }, footer: { - Text("\(name)@\(nip05) will be used for verification") + if let parts = nip05_parts { + Text("'\(parts.username)' at '\(parts.host)' will be used for verification") + } else { + Text("'\(nip05)' is an invalid nip05 identifier. It should look like an email.") + } }) Button("Save") { diff --git a/damus/Views/EventActionBar.swift b/damus/Views/EventActionBar.swift index 4bcfe8e..3e5c76a 100644 --- a/damus/Views/EventActionBar.swift +++ b/damus/Views/EventActionBar.swift @@ -88,12 +88,12 @@ struct EventActionBar: View { } .padding(.top, 1) .alert("Boost", isPresented: $confirm_boost) { - Button("Boost") { - send_boost() - } Button("Cancel") { confirm_boost = false } + Button("Boost") { + send_boost() + } } message: { Text("Are you sure you want to boost this post?") } diff --git a/damus/Views/EventView.swift b/damus/Views/EventView.swift index a17e363..f21b7b6 100644 --- a/damus/Views/EventView.swift +++ b/damus/Views/EventView.swift @@ -79,6 +79,7 @@ struct EventView: View { VStack(alignment: .leading) { let prof_model = ProfileModel(pubkey: event.pubkey, damus: damus) let follow_model = FollowersModel(damus_state: damus, target: event.pubkey) + let prof = damus.profiles.lookup(id: event.pubkey) let booster_profile = ProfileView(damus_state: damus, profile: prof_model, followers: follow_model) NavigationLink(destination: booster_profile) { @@ -86,11 +87,9 @@ struct EventView: View { Image(systemName: "arrow.2.squarepath") .font(.footnote.weight(.bold)) .foregroundColor(Color.gray) - if let prof = damus.profiles.lookup(id: event.pubkey) { - Text(Profile.displayName(profile: prof, pubkey: event.pubkey)) + ProfileName(pubkey: event.pubkey, profile: prof, contacts: damus.contacts, show_friend_confirmed: true) .font(.footnote.weight(.bold)) .foregroundColor(Color.gray) - } Text("Boosted") .font(.footnote.weight(.bold)) .foregroundColor(Color.gray) @@ -214,7 +213,7 @@ extension View { Button { UIPasteboard.general.string = event_to_json(ev: event) } label: { - Label("Copy Note", systemImage: "note") + Label("Copy Note JSON", systemImage: "note") } Button { diff --git a/damus/Views/ProfilePicView.swift b/damus/Views/ProfilePicView.swift index d6bc33b..c631767 100644 --- a/damus/Views/ProfilePicView.swift +++ b/damus/Views/ProfilePicView.swift @@ -34,16 +34,16 @@ func pfp_line_width(_ h: Highlight) -> CGFloat { struct InnerProfilePicView: View { @Environment(\.redactionReasons) private var reasons - + let url: URL? let pubkey: String let size: CGFloat let highlight: Highlight - + var PlaceholderColor: Color { return id_to_color(pubkey) } - + var Placeholder: some View { PlaceholderColor .frame(width: size, height: size) @@ -51,7 +51,7 @@ struct InnerProfilePicView: View { .overlay(Circle().stroke(highlight_color(highlight), lineWidth: pfp_line_width(highlight))) .padding(2) } - + var body: some View { Group { if reasons.isEmpty { @@ -74,7 +74,6 @@ struct InnerProfilePicView: View { .clipShape(Circle()) .overlay(Circle().stroke(highlight_color(highlight), lineWidth: pfp_line_width(highlight))) } - } struct ProfilePicView: View { diff --git a/damus/Views/ProfileView.swift b/damus/Views/ProfileView.swift index 0f2676e..996c622 100644 --- a/damus/Views/ProfileView.swift +++ b/damus/Views/ProfileView.swift @@ -113,6 +113,7 @@ struct EditButton: View { struct ProfileView: View { let damus_state: DamusState + let zoom_size: CGFloat = 350 @State private var selected_tab: ProfileTab = .posts @StateObject var profile: ProfileModel @@ -120,6 +121,7 @@ struct ProfileView: View { @State private var showingEditProfile = false @State var showingSelectWallet: Bool = false @State var inv: String = "" + @State var is_zoomed: Bool = false @Environment(\.dismiss) var dismiss @Environment(\.colorScheme) var colorScheme @@ -169,6 +171,12 @@ struct ProfileView: View { HStack(alignment: .center) { ProfilePicView(pubkey: profile.pubkey, size: PFP_SIZE, highlight: .custom(Color.black, 2), profiles: damus_state.profiles) + .onTapGesture { + is_zoomed.toggle() + } + .sheet(isPresented: $is_zoomed) { + ProfilePicView(pubkey: profile.pubkey, size: zoom_size, highlight: .custom(Color.black, 2), profiles: damus_state.profiles) + } Spacer() diff --git a/damus/Views/SearchHomeView.swift b/damus/Views/SearchHomeView.swift index 27fae88..44fd29d 100644 --- a/damus/Views/SearchHomeView.swift +++ b/damus/Views/SearchHomeView.swift @@ -16,12 +16,13 @@ struct SearchHomeView: View { var SearchInput: some View { ZStack(alignment: .leading) { HStack{ - TextField("", text: $search) + TextField("Search...", text: $search) .padding(8) .padding(.leading, 35) .autocorrectionDisabled(true) .textInputAutocapitalization(.never) - Label("", systemImage: "xmark.square") + Text("Cancel") + .foregroundColor(.blue) .padding(EdgeInsets(top: 0.0, leading: 0.0, bottom: 0.0, trailing: 10.0)) .opacity((search == "") ? 0.0 : 1.0) .onTapGesture { @@ -70,7 +71,9 @@ struct SearchHomeView: View { @Environment(\.colorScheme) var colorScheme var body: some View { - MainContent + VStack { + MainContent + } .safeAreaInset(edge: .top) { VStack(spacing: 0) { SearchInput @@ -85,8 +88,6 @@ struct SearchHomeView: View { print("search change 1") } .onAppear { - // TODO: This will always be empty when switching between tabs - // We'll need to store these in if model.events.isEmpty { model.subscribe() }