Browse Source

Merge branch 'master' into add-wallet-modal

post-button-style
Suhail Saqan 2 years ago
committed by GitHub
parent
commit
65defd6002
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 29
      CHANGELOG.md
  2. 12
      README.md
  3. 6
      damus.xcodeproj/project.pbxproj
  4. 15
      damus/Nostr/RelayPool.swift
  5. 15
      damus/Util/Keys.swift
  6. 21
      damus/Views/EditMetadataView.swift
  7. 6
      damus/Views/EventActionBar.swift
  8. 7
      damus/Views/EventView.swift
  9. 1
      damus/Views/ProfilePicView.swift
  10. 8
      damus/Views/ProfileView.swift
  11. 9
      damus/Views/SearchHomeView.swift

29
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 ### Added
@ -138,3 +163,5 @@
[0.1.2]: https://github.com/damus-io/damus/releases/tag/v0.1.2 [0.1.2]: https://github.com/damus-io/damus/releases/tag/v0.1.2

12
README.md

@ -69,13 +69,11 @@ damus implements the following [Nostr Implementation Possibilities][nips]
- All your notifications except 💬 DMs - All your notifications except 💬 DMs
#### 👤 Change Your Profile (PFP) and Bio #### 👤 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. Go to your Profile Page on Damus app
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. Tap on Edit button at the top
2. Go to https://damus.io/key to convert your nsec key (secret key in ⚙️ Settings) into a hex version 3. You will see text fields to update your information and bio
i. For Alby, right-click the extension, select Options and scroll to the Nostr section to enter your secret hex key 4. For PFP, insert a URL containing your image (support video: https://cdn.jb55.com/vid/pfp-editor.mp4)
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 5. 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
#### ⚡️ Request Sats #### ⚡️ Request Sats
(Sats or Satoshis are the smallest denomination of bitcoin) (Sats or Satoshis are the smallest denomination of bitcoin)

6
damus.xcodeproj/project.pbxproj

@ -129,9 +129,9 @@
4CEE2AF7280B2DEA00AB5EEF /* ProfileName.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CEE2AF6280B2DEA00AB5EEF /* ProfileName.swift */; }; 4CEE2AF7280B2DEA00AB5EEF /* ProfileName.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CEE2AF6280B2DEA00AB5EEF /* ProfileName.swift */; };
4CEE2AF9280B2EAC00AB5EEF /* PowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CEE2AF8280B2EAC00AB5EEF /* PowView.swift */; }; 4CEE2AF9280B2EAC00AB5EEF /* PowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CEE2AF8280B2EAC00AB5EEF /* PowView.swift */; };
4CEE2B02280B39E800AB5EEF /* EventActionBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CEE2B01280B39E800AB5EEF /* EventActionBar.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 */; }; BAB68BED29543FA3007BA466 /* SelectWalletView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BAB68BEC29543FA3007BA466 /* SelectWalletView.swift */; };
6C7DE41F2955169800E66263 /* Vault in Frameworks */ = {isa = PBXBuildFile; productRef = 6C7DE41E2955169800E66263 /* Vault */; }; 6C7DE41F2955169800E66263 /* Vault in Frameworks */ = {isa = PBXBuildFile; productRef = 6C7DE41E2955169800E66263 /* Vault */; };
E990020F2955F837003BBC5A /* EditMetadataView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E990020E2955F837003BBC5A /* EditMetadataView.swift */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */ /* Begin PBXContainerItemProxy section */
@ -1031,7 +1031,7 @@
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = damus/damus.entitlements; CODE_SIGN_ENTITLEMENTS = damus/damus.entitlements;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 3; CURRENT_PROJECT_VERSION = 4;
DEVELOPMENT_ASSET_PATHS = "\"damus/Preview Content\""; DEVELOPMENT_ASSET_PATHS = "\"damus/Preview Content\"";
DEVELOPMENT_TEAM = XK7H4JAB3D; DEVELOPMENT_TEAM = XK7H4JAB3D;
ENABLE_PREVIEWS = YES; ENABLE_PREVIEWS = YES;
@ -1070,7 +1070,7 @@
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = damus/damus.entitlements; CODE_SIGN_ENTITLEMENTS = damus/damus.entitlements;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 3; CURRENT_PROJECT_VERSION = 4;
DEVELOPMENT_ASSET_PATHS = "\"damus/Preview Content\""; DEVELOPMENT_ASSET_PATHS = "\"damus/Preview Content\"";
DEVELOPMENT_TEAM = XK7H4JAB3D; DEVELOPMENT_TEAM = XK7H4JAB3D;
ENABLE_PREVIEWS = YES; ENABLE_PREVIEWS = YES;

15
damus/Nostr/RelayPool.swift

@ -41,12 +41,19 @@ class RelayPool {
} }
func remove_handler(sub_id: String) { 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) -> ()) { 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)) 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) { func remove_relay(_ relay_id: String) {
@ -125,8 +132,10 @@ class RelayPool {
} }
func unsubscribe(sub_id: String, to: [String]? = nil) { func unsubscribe(sub_id: String, to: [String]? = nil) {
if to == nil {
self.remove_handler(sub_id: sub_id) self.remove_handler(sub_id: sub_id)
self.send(.unsubscribe(sub_id)) }
self.send(.unsubscribe(sub_id), to: to)
} }
func subscribe(sub_id: String, filters: [NostrFilter], handler: @escaping (String, NostrConnectionEvent) -> ()) { func subscribe(sub_id: String, filters: [NostrFilter], handler: @escaping (String, NostrConnectionEvent) -> ()) {

15
damus/Util/Keys.swift

@ -15,13 +15,14 @@ let PRIVKEY_HRP = "nsec"
struct Keypair { struct Keypair {
let pubkey: String let pubkey: String
let privkey: String? let privkey: String?
let pubkey_bech32: String
var pubkey_bech32: String { let privkey_bech32: String?
return bech32_pubkey(pubkey)!
} init(pubkey: String, privkey: String?) {
self.pubkey = pubkey
var privkey_bech32: String? { self.privkey = privkey
return privkey.flatMap { bech32_privkey($0) } self.pubkey_bech32 = bech32_pubkey(pubkey) ?? pubkey
self.privkey_bech32 = privkey.flatMap { bech32_privkey($0) }
} }
} }

21
damus/Views/EditMetadataView.swift

@ -15,6 +15,11 @@ func isHttpsUrl(_ string: String) -> Bool {
return urlTest.evaluate(with: string) return urlTest.evaluate(with: string)
} }
struct NIP05 {
let username: String
let host: String
}
func isImage(_ urlString: String) -> Bool { func isImage(_ urlString: String) -> Bool {
let imageTypes = ["image/jpg", "image/jpeg", "image/png", "image/gif", "image/tiff", "image/bmp", "image/webp"] 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 { var body: some View {
VStack(alignment: .leading) { VStack(alignment: .leading) {
HStack { HStack {
@ -149,13 +162,17 @@ struct EditMetadataView: View {
} }
Section(content: { Section(content: {
TextField("example.com", text: $nip05) TextField("jb55@jb55.com", text: $nip05)
.autocorrectionDisabled(true) .autocorrectionDisabled(true)
.textInputAutocapitalization(.never) .textInputAutocapitalization(.never)
}, header: { }, header: {
Text("NIP-05 Verification") Text("NIP-05 Verification")
}, footer: { }, 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") { Button("Save") {

6
damus/Views/EventActionBar.swift

@ -88,12 +88,12 @@ struct EventActionBar: View {
} }
.padding(.top, 1) .padding(.top, 1)
.alert("Boost", isPresented: $confirm_boost) { .alert("Boost", isPresented: $confirm_boost) {
Button("Boost") {
send_boost()
}
Button("Cancel") { Button("Cancel") {
confirm_boost = false confirm_boost = false
} }
Button("Boost") {
send_boost()
}
} message: { } message: {
Text("Are you sure you want to boost this post?") Text("Are you sure you want to boost this post?")
} }

7
damus/Views/EventView.swift

@ -79,6 +79,7 @@ struct EventView: View {
VStack(alignment: .leading) { VStack(alignment: .leading) {
let prof_model = ProfileModel(pubkey: event.pubkey, damus: damus) let prof_model = ProfileModel(pubkey: event.pubkey, damus: damus)
let follow_model = FollowersModel(damus_state: damus, target: event.pubkey) 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) let booster_profile = ProfileView(damus_state: damus, profile: prof_model, followers: follow_model)
NavigationLink(destination: booster_profile) { NavigationLink(destination: booster_profile) {
@ -86,11 +87,9 @@ struct EventView: View {
Image(systemName: "arrow.2.squarepath") Image(systemName: "arrow.2.squarepath")
.font(.footnote.weight(.bold)) .font(.footnote.weight(.bold))
.foregroundColor(Color.gray) .foregroundColor(Color.gray)
if let prof = damus.profiles.lookup(id: event.pubkey) { ProfileName(pubkey: event.pubkey, profile: prof, contacts: damus.contacts, show_friend_confirmed: true)
Text(Profile.displayName(profile: prof, pubkey: event.pubkey))
.font(.footnote.weight(.bold)) .font(.footnote.weight(.bold))
.foregroundColor(Color.gray) .foregroundColor(Color.gray)
}
Text("Boosted") Text("Boosted")
.font(.footnote.weight(.bold)) .font(.footnote.weight(.bold))
.foregroundColor(Color.gray) .foregroundColor(Color.gray)
@ -214,7 +213,7 @@ extension View {
Button { Button {
UIPasteboard.general.string = event_to_json(ev: event) UIPasteboard.general.string = event_to_json(ev: event)
} label: { } label: {
Label("Copy Note", systemImage: "note") Label("Copy Note JSON", systemImage: "note")
} }
Button { Button {

1
damus/Views/ProfilePicView.swift

@ -74,7 +74,6 @@ struct InnerProfilePicView: View {
.clipShape(Circle()) .clipShape(Circle())
.overlay(Circle().stroke(highlight_color(highlight), lineWidth: pfp_line_width(highlight))) .overlay(Circle().stroke(highlight_color(highlight), lineWidth: pfp_line_width(highlight)))
} }
} }
struct ProfilePicView: View { struct ProfilePicView: View {

8
damus/Views/ProfileView.swift

@ -113,6 +113,7 @@ struct EditButton: View {
struct ProfileView: View { struct ProfileView: View {
let damus_state: DamusState let damus_state: DamusState
let zoom_size: CGFloat = 350
@State private var selected_tab: ProfileTab = .posts @State private var selected_tab: ProfileTab = .posts
@StateObject var profile: ProfileModel @StateObject var profile: ProfileModel
@ -120,6 +121,7 @@ struct ProfileView: View {
@State private var showingEditProfile = false @State private var showingEditProfile = false
@State var showingSelectWallet: Bool = false @State var showingSelectWallet: Bool = false
@State var inv: String = "" @State var inv: String = ""
@State var is_zoomed: Bool = false
@Environment(\.dismiss) var dismiss @Environment(\.dismiss) var dismiss
@Environment(\.colorScheme) var colorScheme @Environment(\.colorScheme) var colorScheme
@ -169,6 +171,12 @@ struct ProfileView: View {
HStack(alignment: .center) { HStack(alignment: .center) {
ProfilePicView(pubkey: profile.pubkey, size: PFP_SIZE, highlight: .custom(Color.black, 2), profiles: damus_state.profiles) 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() Spacer()

9
damus/Views/SearchHomeView.swift

@ -16,12 +16,13 @@ struct SearchHomeView: View {
var SearchInput: some View { var SearchInput: some View {
ZStack(alignment: .leading) { ZStack(alignment: .leading) {
HStack{ HStack{
TextField("", text: $search) TextField("Search...", text: $search)
.padding(8) .padding(8)
.padding(.leading, 35) .padding(.leading, 35)
.autocorrectionDisabled(true) .autocorrectionDisabled(true)
.textInputAutocapitalization(.never) .textInputAutocapitalization(.never)
Label("", systemImage: "xmark.square") Text("Cancel")
.foregroundColor(.blue)
.padding(EdgeInsets(top: 0.0, leading: 0.0, bottom: 0.0, trailing: 10.0)) .padding(EdgeInsets(top: 0.0, leading: 0.0, bottom: 0.0, trailing: 10.0))
.opacity((search == "") ? 0.0 : 1.0) .opacity((search == "") ? 0.0 : 1.0)
.onTapGesture { .onTapGesture {
@ -70,7 +71,9 @@ struct SearchHomeView: View {
@Environment(\.colorScheme) var colorScheme @Environment(\.colorScheme) var colorScheme
var body: some View { var body: some View {
VStack {
MainContent MainContent
}
.safeAreaInset(edge: .top) { .safeAreaInset(edge: .top) {
VStack(spacing: 0) { VStack(spacing: 0) {
SearchInput SearchInput
@ -85,8 +88,6 @@ struct SearchHomeView: View {
print("search change 1") print("search change 1")
} }
.onAppear { .onAppear {
// TODO: This will always be empty when switching between tabs
// We'll need to store these in
if model.events.isEmpty { if model.events.isEmpty {
model.subscribe() model.subscribe()
} }

Loading…
Cancel
Save