Merge branch 'master' into add-wallet-modal

This commit is contained in:
Suhail Saqan
2022-12-27 00:55:40 -06:00
committed by GitHub
11 changed files with 99 additions and 40 deletions

View File

@@ -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

View File

@@ -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)

View File

@@ -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;

View File

@@ -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) {
self.remove_handler(sub_id: sub_id) if to == nil {
self.send(.unsubscribe(sub_id)) 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) -> ()) { func subscribe(sub_id: String, filters: [NostrFilter], handler: @escaping (String, NostrConnectionEvent) -> ()) {

View File

@@ -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
let privkey_bech32: String?
var pubkey_bech32: String { init(pubkey: String, privkey: String?) {
return bech32_pubkey(pubkey)! self.pubkey = pubkey
} self.privkey = privkey
self.pubkey_bech32 = bech32_pubkey(pubkey) ?? pubkey
var privkey_bech32: String? { self.privkey_bech32 = privkey.flatMap { bech32_privkey($0) }
return privkey.flatMap { bech32_privkey($0) }
} }
} }

View File

@@ -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") {

View File

@@ -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?")
} }

View File

@@ -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 {

View File

@@ -34,16 +34,16 @@ func pfp_line_width(_ h: Highlight) -> CGFloat {
struct InnerProfilePicView: View { struct InnerProfilePicView: View {
@Environment(\.redactionReasons) private var reasons @Environment(\.redactionReasons) private var reasons
let url: URL? let url: URL?
let pubkey: String let pubkey: String
let size: CGFloat let size: CGFloat
let highlight: Highlight let highlight: Highlight
var PlaceholderColor: Color { var PlaceholderColor: Color {
return id_to_color(pubkey) return id_to_color(pubkey)
} }
var Placeholder: some View { var Placeholder: some View {
PlaceholderColor PlaceholderColor
.frame(width: size, height: size) .frame(width: size, height: size)
@@ -51,7 +51,7 @@ struct InnerProfilePicView: View {
.overlay(Circle().stroke(highlight_color(highlight), lineWidth: pfp_line_width(highlight))) .overlay(Circle().stroke(highlight_color(highlight), lineWidth: pfp_line_width(highlight)))
.padding(2) .padding(2)
} }
var body: some View { var body: some View {
Group { Group {
if reasons.isEmpty { if reasons.isEmpty {
@@ -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 {

View File

@@ -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()

View File

@@ -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 {
MainContent VStack {
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()
} }