Make RelayPool private to NostrNetworkManager and migrate usages
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This commit is contained in:
@@ -58,7 +58,7 @@ class ActionBarModel: ObservableObject {
|
||||
self.our_zap = damus.zaps.our_zaps[evid]?.first
|
||||
self.our_reply = damus.replies.our_reply(evid)
|
||||
self.our_quote_repost = damus.quote_reposts.our_events[evid]
|
||||
self.relays = (damus.nostrNetwork.pool.seen[evid] ?? []).count
|
||||
self.relays = (damus.nostrNetwork.relayURLsThatSawNote(id: evid) ?? []).count
|
||||
self.objectWillChange.send()
|
||||
}
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@ struct EventDetailBar: View {
|
||||
}
|
||||
|
||||
if bar.relays > 0 {
|
||||
let relays = Array(state.nostrNetwork.pool.seen[target] ?? [])
|
||||
let relays = Array(state.nostrNetwork.relayURLsThatSawNote(id: target) ?? [])
|
||||
NavigationLink(value: Route.UserRelays(relays: relays)) {
|
||||
let nounString = pluralizedString(key: "relays_count", count: bar.relays)
|
||||
let noun = Text(nounString).foregroundColor(.gray)
|
||||
|
||||
@@ -56,12 +56,7 @@ class ThreadModel: ObservableObject {
|
||||
/// The damus state, needed to access the relay pool and load the thread events
|
||||
let damus_state: DamusState
|
||||
|
||||
private let profiles_subid = UUID().description
|
||||
private let base_subid = UUID().description
|
||||
private let meta_subid = UUID().description
|
||||
private var subids: [String] {
|
||||
return [profiles_subid, base_subid, meta_subid]
|
||||
}
|
||||
private var listener: Task<Void, Never>?
|
||||
|
||||
|
||||
// MARK: Initialization
|
||||
@@ -86,17 +81,6 @@ class ThreadModel: ObservableObject {
|
||||
|
||||
// MARK: Relay pool subscription management
|
||||
|
||||
/// Unsubscribe from events in the relay pool. Call this when unloading the view
|
||||
func unsubscribe() {
|
||||
self.damus_state.nostrNetwork.pool.remove_handler(sub_id: base_subid)
|
||||
self.damus_state.nostrNetwork.pool.remove_handler(sub_id: meta_subid)
|
||||
self.damus_state.nostrNetwork.pool.remove_handler(sub_id: profiles_subid)
|
||||
self.damus_state.nostrNetwork.pool.unsubscribe(sub_id: base_subid)
|
||||
self.damus_state.nostrNetwork.pool.unsubscribe(sub_id: meta_subid)
|
||||
self.damus_state.nostrNetwork.pool.unsubscribe(sub_id: profiles_subid)
|
||||
Log.info("unsubscribing to thread %s with sub_id %s", for: .render, original_event.id.hex(), base_subid)
|
||||
}
|
||||
|
||||
/// Subscribe to events in this thread. Call this when loading the view.
|
||||
func subscribe() {
|
||||
var meta_events = NostrFilter()
|
||||
@@ -127,10 +111,27 @@ class ThreadModel: ObservableObject {
|
||||
|
||||
let base_filters = [event_filter, ref_events]
|
||||
let meta_filters = [meta_events, quote_events]
|
||||
|
||||
Log.info("subscribing to thread %s with sub_id %s", for: .render, original_event.id.hex(), base_subid)
|
||||
damus_state.nostrNetwork.pool.subscribe(sub_id: base_subid, filters: base_filters, handler: handle_event)
|
||||
damus_state.nostrNetwork.pool.subscribe(sub_id: meta_subid, filters: meta_filters, handler: handle_event)
|
||||
|
||||
self.listener?.cancel()
|
||||
self.listener = Task {
|
||||
Log.info("subscribing to thread %s ", for: .render, original_event.id.hex())
|
||||
for await item in damus_state.nostrNetwork.reader.subscribe(filters: base_filters + meta_filters) {
|
||||
switch item {
|
||||
case .event(let borrow):
|
||||
try? borrow { event in
|
||||
handle_event(ev: event.toOwned())
|
||||
}
|
||||
case .eose:
|
||||
guard let txn = NdbTxn(ndb: damus_state.ndb) else { return }
|
||||
load_profiles(context: "thread", load: .from_events(Array(event_map.events)), damus_state: damus_state, txn: txn)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func unsubscribe() {
|
||||
self.listener?.cancel()
|
||||
self.listener = nil
|
||||
}
|
||||
|
||||
/// Adds an event to this thread.
|
||||
@@ -175,35 +176,20 @@ class ThreadModel: ObservableObject {
|
||||
///
|
||||
/// Marked as private because it is this class' responsibility to load events, not the view's. Simplify the interface
|
||||
@MainActor
|
||||
private func handle_event(relay_id: RelayURL, ev: NostrConnectionEvent) {
|
||||
let (sub_id, done) = handle_subid_event(pool: damus_state.nostrNetwork.pool, relay_id: relay_id, ev: ev) { sid, ev in
|
||||
guard subids.contains(sid) else {
|
||||
return
|
||||
private func handle_event(ev: NostrEvent) {
|
||||
if ev.known_kind == .zap {
|
||||
process_zap_event(state: damus_state, ev: ev) { zap in
|
||||
|
||||
}
|
||||
|
||||
if ev.known_kind == .zap {
|
||||
process_zap_event(state: damus_state, ev: ev) { zap in
|
||||
|
||||
}
|
||||
} else if ev.is_textlike {
|
||||
// handle thread quote reposts, we just count them instead of
|
||||
// adding them to the thread
|
||||
if let target = ev.is_quote_repost, target == self.selected_event.id {
|
||||
//let _ = self.damus_state.quote_reposts.add_event(ev, target: target)
|
||||
} else {
|
||||
self.add_event(ev, keypair: damus_state.keypair)
|
||||
}
|
||||
} else if ev.is_textlike {
|
||||
// handle thread quote reposts, we just count them instead of
|
||||
// adding them to the thread
|
||||
if let target = ev.is_quote_repost, target == self.selected_event.id {
|
||||
//let _ = self.damus_state.quote_reposts.add_event(ev, target: target)
|
||||
} else {
|
||||
self.add_event(ev, keypair: damus_state.keypair)
|
||||
}
|
||||
}
|
||||
|
||||
guard done, let sub_id, subids.contains(sub_id) else {
|
||||
return
|
||||
}
|
||||
|
||||
if sub_id == self.base_subid {
|
||||
guard let txn = NdbTxn(ndb: damus_state.ndb) else { return }
|
||||
load_profiles(context: "thread", profiles_subid: self.profiles_subid, relay_id: relay_id, load: .from_events(Array(event_map.events)), damus_state: damus_state, txn: txn)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: External control interface
|
||||
|
||||
@@ -13,6 +13,7 @@ struct EventLoaderView<Content: View>: View {
|
||||
let event_id: NoteId
|
||||
@State var event: NostrEvent?
|
||||
@State var subscription_uuid: String = UUID().description
|
||||
@State var loadingTask: Task<Void, Never>? = nil
|
||||
let content: (NostrEvent) -> Content
|
||||
|
||||
init(damus_state: DamusState, event_id: NoteId, @ViewBuilder content: @escaping (NostrEvent) -> Content) {
|
||||
@@ -24,34 +25,24 @@ struct EventLoaderView<Content: View>: View {
|
||||
}
|
||||
|
||||
func unsubscribe() {
|
||||
damus_state.nostrNetwork.pool.unsubscribe(sub_id: subscription_uuid)
|
||||
self.loadingTask?.cancel()
|
||||
}
|
||||
|
||||
func subscribe(filters: [NostrFilter]) {
|
||||
damus_state.nostrNetwork.pool.register_handler(sub_id: subscription_uuid, handler: handle_event)
|
||||
damus_state.nostrNetwork.pool.send(.subscribe(.init(filters: filters, sub_id: subscription_uuid)))
|
||||
}
|
||||
|
||||
func handle_event(relay_id: RelayURL, ev: NostrConnectionEvent) {
|
||||
guard case .nostr_event(let nostr_response) = ev else {
|
||||
return
|
||||
self.loadingTask?.cancel()
|
||||
self.loadingTask = Task {
|
||||
for await item in await damus_state.nostrNetwork.reader.subscribe(filters: filters) {
|
||||
switch item {
|
||||
case .event(let borrow):
|
||||
try? borrow { ev in
|
||||
event = ev.toOwned()
|
||||
}
|
||||
break
|
||||
case .eose:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
guard case .event(let id, let nostr_event) = nostr_response else {
|
||||
return
|
||||
}
|
||||
|
||||
guard id == subscription_uuid else {
|
||||
return
|
||||
}
|
||||
|
||||
if event != nil {
|
||||
return
|
||||
}
|
||||
|
||||
event = nostr_event
|
||||
|
||||
unsubscribe()
|
||||
}
|
||||
|
||||
func load() {
|
||||
|
||||
@@ -152,7 +152,7 @@ struct MenuItems: View {
|
||||
profileModel.subscribeToFindRelays()
|
||||
}
|
||||
.onDisappear() {
|
||||
profileModel.unsubscribeFindRelays()
|
||||
profileModel.findRelaysListener?.cancel()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,10 +11,10 @@ class EventsModel: ObservableObject {
|
||||
let state: DamusState
|
||||
let target: NoteId
|
||||
let kind: QueryKind
|
||||
let sub_id = UUID().uuidString
|
||||
let profiles_id = UUID().uuidString
|
||||
var events: EventHolder
|
||||
@Published var loading: Bool
|
||||
var loadingTask: Task<Void, Never>?
|
||||
|
||||
enum QueryKind {
|
||||
case kind(NostrKind)
|
||||
@@ -68,13 +68,29 @@ class EventsModel: ObservableObject {
|
||||
}
|
||||
|
||||
func subscribe() {
|
||||
state.nostrNetwork.pool.subscribe(sub_id: sub_id,
|
||||
filters: [get_filter()],
|
||||
handler: handle_nostr_event)
|
||||
loadingTask?.cancel()
|
||||
loadingTask = Task {
|
||||
for await item in state.nostrNetwork.reader.subscribe(filters: [get_filter()]) {
|
||||
switch item {
|
||||
case .event(let borrow):
|
||||
var event: NostrEvent? = nil
|
||||
try? borrow { ev in
|
||||
event = ev.toOwned()
|
||||
}
|
||||
guard let event else { return }
|
||||
if events.insert(event) { objectWillChange.send() }
|
||||
case .eose:
|
||||
break
|
||||
}
|
||||
}
|
||||
self.loading = false
|
||||
guard let txn = NdbTxn(ndb: self.state.ndb) else { return }
|
||||
load_profiles(context: "events_model", load: .from_events(events.all_events), damus_state: state, txn: txn)
|
||||
}
|
||||
}
|
||||
|
||||
func unsubscribe() {
|
||||
state.nostrNetwork.pool.unsubscribe(sub_id: sub_id)
|
||||
loadingTask?.cancel()
|
||||
}
|
||||
|
||||
private func handle_event(relay_id: RelayURL, ev: NostrEvent) {
|
||||
@@ -82,28 +98,4 @@ class EventsModel: ObservableObject {
|
||||
objectWillChange.send()
|
||||
}
|
||||
}
|
||||
|
||||
func handle_nostr_event(relay_id: RelayURL, ev: NostrConnectionEvent) {
|
||||
guard case .nostr_event(let nev) = ev, nev.subid == self.sub_id
|
||||
else {
|
||||
return
|
||||
}
|
||||
|
||||
switch nev {
|
||||
case .event(_, let ev):
|
||||
handle_event(relay_id: relay_id, ev: ev)
|
||||
case .notice:
|
||||
break
|
||||
case .ok:
|
||||
break
|
||||
case .auth:
|
||||
break
|
||||
case .eose:
|
||||
self.loading = false
|
||||
guard let txn = NdbTxn(ndb: self.state.ndb) else {
|
||||
return
|
||||
}
|
||||
load_profiles(context: "events_model", profiles_subid: profiles_id, relay_id: relay_id, load: .from_events(events.all_events), damus_state: state, txn: txn)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ class LoadableNostrEventViewModel: ObservableObject {
|
||||
|
||||
/// Asynchronously find an event from NostrDB or from the network (if not available on NostrDB)
|
||||
private func loadEvent(noteId: NoteId) async -> NostrEvent? {
|
||||
let res = await find_event(state: damus_state, query: .event(evid: noteId))
|
||||
let res = await damus_state.nostrNetwork.findEvent(query: .event(evid: noteId))
|
||||
guard let res, case .event(let ev) = res else { return nil }
|
||||
return ev
|
||||
}
|
||||
@@ -78,7 +78,7 @@ class LoadableNostrEventViewModel: ObservableObject {
|
||||
return .unknown_or_unsupported_kind
|
||||
}
|
||||
case .naddr(let naddr):
|
||||
guard let event = await naddrLookup(damus_state: damus_state, naddr: naddr) else { return .not_found }
|
||||
guard let event = await damus_state.nostrNetwork.lookup(naddr: naddr) else { return .not_found }
|
||||
return .loaded(route: Route.Thread(thread: ThreadModel(event: event, damus_state: damus_state)))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ class FollowPackModel: ObservableObject {
|
||||
@Published var loading: Bool = false
|
||||
|
||||
let damus_state: DamusState
|
||||
let subid = UUID().description
|
||||
var listener: Task<Void, Never>? = nil
|
||||
let limit: UInt32 = 500
|
||||
|
||||
init(damus_state: DamusState) {
|
||||
@@ -25,52 +25,40 @@ class FollowPackModel: ObservableObject {
|
||||
|
||||
func subscribe(follow_pack_users: [Pubkey]) {
|
||||
loading = true
|
||||
let to_relays = determine_to_relays(pool: damus_state.nostrNetwork.pool, filters: damus_state.relay_filters)
|
||||
self.listener = Task {
|
||||
await self.listenForUpdates(follow_pack_users: follow_pack_users)
|
||||
}
|
||||
}
|
||||
|
||||
func unsubscribe(to: RelayURL? = nil) {
|
||||
loading = false
|
||||
self.listener?.cancel()
|
||||
}
|
||||
|
||||
func listenForUpdates(follow_pack_users: [Pubkey]) async {
|
||||
let to_relays = damus_state.nostrNetwork.determineToRelays(filters: damus_state.relay_filters)
|
||||
var filter = NostrFilter(kinds: [.text, .chat])
|
||||
filter.until = UInt32(Date.now.timeIntervalSince1970)
|
||||
filter.authors = follow_pack_users
|
||||
filter.limit = 500
|
||||
|
||||
damus_state.nostrNetwork.pool.subscribe(sub_id: subid, filters: [filter], handler: handle_event, to: to_relays)
|
||||
}
|
||||
|
||||
func unsubscribe(to: RelayURL? = nil) {
|
||||
loading = false
|
||||
damus_state.nostrNetwork.pool.unsubscribe(sub_id: subid, to: to.map { [$0] })
|
||||
}
|
||||
|
||||
func handle_event(relay_id: RelayURL, conn_ev: NostrConnectionEvent) {
|
||||
guard case .nostr_event(let event) = conn_ev else {
|
||||
return
|
||||
}
|
||||
|
||||
switch event {
|
||||
case .event(let sub_id, let ev):
|
||||
guard sub_id == self.subid else {
|
||||
return
|
||||
}
|
||||
if ev.is_textlike && should_show_event(state: damus_state, ev: ev) && !ev.is_reply()
|
||||
{
|
||||
if self.events.insert(ev) {
|
||||
self.objectWillChange.send()
|
||||
for await item in damus_state.nostrNetwork.reader.subscribe(filters: [filter], to: to_relays) {
|
||||
switch item {
|
||||
case .event(borrow: let borrow):
|
||||
var event: NostrEvent? = nil
|
||||
try? borrow { ev in
|
||||
event = ev.toOwned()
|
||||
}
|
||||
guard let event else { return }
|
||||
if event.is_textlike && should_show_event(state: damus_state, ev: event) && !event.is_reply()
|
||||
{
|
||||
if self.events.insert(event) {
|
||||
self.objectWillChange.send()
|
||||
}
|
||||
}
|
||||
case .eose:
|
||||
continue
|
||||
}
|
||||
case .notice(let msg):
|
||||
print("follow pack notice: \(msg)")
|
||||
case .ok:
|
||||
break
|
||||
case .eose(let sub_id):
|
||||
loading = false
|
||||
|
||||
if sub_id == self.subid {
|
||||
unsubscribe(to: relay_id)
|
||||
|
||||
guard let txn = NdbTxn(ndb: damus_state.ndb) else { return }
|
||||
}
|
||||
|
||||
break
|
||||
case .auth:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,8 +14,8 @@ class FollowersModel: ObservableObject {
|
||||
@Published var contacts: [Pubkey]? = nil
|
||||
var has_contact: Set<Pubkey> = Set()
|
||||
|
||||
let sub_id: String = UUID().description
|
||||
let profiles_id: String = UUID().description
|
||||
var listener: Task<Void, Never>? = nil
|
||||
var profilesListener: Task<Void, Never>? = nil
|
||||
|
||||
var count: Int? {
|
||||
guard let contacts = self.contacts else {
|
||||
@@ -36,12 +36,27 @@ class FollowersModel: ObservableObject {
|
||||
func subscribe() {
|
||||
let filter = get_filter()
|
||||
let filters = [filter]
|
||||
//print_filters(relay_id: "following", filters: [filters])
|
||||
self.damus_state.nostrNetwork.pool.subscribe(sub_id: sub_id, filters: filters, handler: handle_event)
|
||||
self.listener?.cancel()
|
||||
self.listener = Task {
|
||||
for await item in await damus_state.nostrNetwork.reader.subscribe(filters: filters) {
|
||||
switch item {
|
||||
case .event(let borrow):
|
||||
try? borrow { event in
|
||||
self.handle_event(ev: event.toOwned())
|
||||
}
|
||||
case .eose:
|
||||
guard let txn = NdbTxn(ndb: self.damus_state.ndb) else { return }
|
||||
load_profiles(txn: txn)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func unsubscribe() {
|
||||
self.damus_state.nostrNetwork.pool.unsubscribe(sub_id: sub_id)
|
||||
self.listener?.cancel()
|
||||
self.profilesListener?.cancel()
|
||||
self.listener = nil
|
||||
self.profilesListener = nil
|
||||
}
|
||||
|
||||
func handle_contact_event(_ ev: NostrEvent) {
|
||||
@@ -53,7 +68,7 @@ class FollowersModel: ObservableObject {
|
||||
has_contact.insert(ev.pubkey)
|
||||
}
|
||||
|
||||
func load_profiles<Y>(relay_id: RelayURL, txn: NdbTxn<Y>) {
|
||||
func load_profiles<Y>(txn: NdbTxn<Y>) {
|
||||
let authors = find_profiles_to_fetch_from_keys(profiles: damus_state.profiles, pks: contacts ?? [], txn: txn)
|
||||
if authors.isEmpty {
|
||||
return
|
||||
@@ -61,38 +76,24 @@ class FollowersModel: ObservableObject {
|
||||
|
||||
let filter = NostrFilter(kinds: [.metadata],
|
||||
authors: authors)
|
||||
damus_state.nostrNetwork.pool.subscribe_to(sub_id: profiles_id, filters: [filter], to: [relay_id], handler: handle_event)
|
||||
}
|
||||
|
||||
func handle_event(relay_id: RelayURL, ev: NostrConnectionEvent) {
|
||||
guard case .nostr_event(let nev) = ev else {
|
||||
return
|
||||
}
|
||||
|
||||
switch nev {
|
||||
case .event(let sub_id, let ev):
|
||||
guard sub_id == self.sub_id || sub_id == self.profiles_id else {
|
||||
return
|
||||
self.profilesListener?.cancel()
|
||||
self.profilesListener = Task {
|
||||
for await item in await damus_state.nostrNetwork.reader.subscribe(filters: [filter]) {
|
||||
switch item {
|
||||
case .event(let borrow):
|
||||
try? borrow { event in
|
||||
self.handle_event(ev: event.toOwned())
|
||||
}
|
||||
case .eose: break
|
||||
}
|
||||
}
|
||||
|
||||
if ev.known_kind == .contacts {
|
||||
handle_contact_event(ev)
|
||||
}
|
||||
case .notice(let msg):
|
||||
print("followingmodel notice: \(msg)")
|
||||
|
||||
case .eose(let sub_id):
|
||||
if sub_id == self.sub_id {
|
||||
guard let txn = NdbTxn(ndb: self.damus_state.ndb) else { return }
|
||||
load_profiles(relay_id: relay_id, txn: txn)
|
||||
} else if sub_id == self.profiles_id {
|
||||
damus_state.nostrNetwork.pool.unsubscribe(sub_id: profiles_id, to: [relay_id])
|
||||
}
|
||||
|
||||
case .ok:
|
||||
break
|
||||
case .auth:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
func handle_event(ev: NostrEvent) {
|
||||
if ev.known_kind == .contacts {
|
||||
handle_contact_event(ev)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ class FollowingModel {
|
||||
let contacts: [Pubkey]
|
||||
let hashtags: [Hashtag]
|
||||
|
||||
let sub_id: String = UUID().description
|
||||
private var listener: Task<Void, Never>? = nil
|
||||
|
||||
init(damus_state: DamusState, contacts: [Pubkey], hashtags: [Hashtag]) {
|
||||
self.damus_state = damus_state
|
||||
@@ -41,19 +41,17 @@ class FollowingModel {
|
||||
return
|
||||
}
|
||||
let filters = [filter]
|
||||
//print_filters(relay_id: "following", filters: [filters])
|
||||
self.damus_state.nostrNetwork.pool.subscribe(sub_id: sub_id, filters: filters, handler: handle_event)
|
||||
self.listener?.cancel()
|
||||
self.listener = Task {
|
||||
for await item in self.damus_state.nostrNetwork.reader.subscribe(filters: filters) {
|
||||
// don't need to do anything here really
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func unsubscribe() {
|
||||
if !needs_sub {
|
||||
return
|
||||
}
|
||||
print("unsubscribing from following \(sub_id)")
|
||||
self.damus_state.nostrNetwork.pool.unsubscribe(sub_id: sub_id)
|
||||
}
|
||||
|
||||
func handle_event(relay_id: RelayURL, ev: NostrConnectionEvent) {
|
||||
// don't need to do anything here really
|
||||
self.listener?.cancel()
|
||||
self.listener = nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,8 +15,7 @@ class NIP05DomainEventsModel: ObservableObject {
|
||||
|
||||
let domain: String
|
||||
var filter: NostrFilter
|
||||
let sub_id = UUID().description
|
||||
let profiles_subid = UUID().description
|
||||
var loadingTask: Task<Void, Never>?
|
||||
let limit: UInt32 = 500
|
||||
|
||||
init(state: DamusState, domain: String) {
|
||||
@@ -29,6 +28,20 @@ class NIP05DomainEventsModel: ObservableObject {
|
||||
}
|
||||
|
||||
@MainActor func subscribe() {
|
||||
print("subscribing to notes from friends of friends with '\(domain)' NIP-05 domain")
|
||||
loadingTask = Task {
|
||||
await streamItems()
|
||||
}
|
||||
loading = true
|
||||
}
|
||||
|
||||
func unsubscribe() {
|
||||
loadingTask?.cancel()
|
||||
loading = false
|
||||
print("unsubscribing from notes from friends of friends with '\(domain)' NIP-05 domain")
|
||||
}
|
||||
|
||||
func streamItems() async {
|
||||
filter.limit = self.limit
|
||||
filter.kinds = [.text, .longform, .highlight]
|
||||
|
||||
@@ -50,16 +63,19 @@ class NIP05DomainEventsModel: ObservableObject {
|
||||
}
|
||||
filter.authors = Array(authors)
|
||||
|
||||
print("subscribing to notes from friends of friends with '\(domain)' NIP-05 domain with sub_id \(sub_id)")
|
||||
state.nostrNetwork.pool.register_handler(sub_id: sub_id, handler: handle_event)
|
||||
loading = true
|
||||
state.nostrNetwork.pool.send(.subscribe(.init(filters: [filter], sub_id: sub_id)))
|
||||
}
|
||||
|
||||
func unsubscribe() {
|
||||
state.nostrNetwork.pool.unsubscribe(sub_id: sub_id)
|
||||
loading = false
|
||||
print("unsubscribing from notes from friends of friends with '\(domain)' NIP-05 domain with sub_id \(sub_id)")
|
||||
|
||||
for await item in state.nostrNetwork.reader.subscribe(filters: [filter]) {
|
||||
switch item {
|
||||
case .event(borrow: let borrow):
|
||||
try? borrow { event in
|
||||
self.add_event(event.toOwned())
|
||||
guard let txn = NdbTxn(ndb: state.ndb) else { return }
|
||||
load_profiles(context: "search", load: .from_events(self.events.all_events), damus_state: state, txn: txn)
|
||||
}
|
||||
case .eose:
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func add_event(_ ev: NostrEvent) {
|
||||
@@ -75,23 +91,4 @@ class NIP05DomainEventsModel: ObservableObject {
|
||||
objectWillChange.send()
|
||||
}
|
||||
}
|
||||
|
||||
func handle_event(relay_id: RelayURL, ev: NostrConnectionEvent) {
|
||||
let (sub_id, done) = handle_subid_event(pool: state.nostrNetwork.pool, relay_id: relay_id, ev: ev) { sub_id, ev in
|
||||
if sub_id == self.sub_id && ev.is_textlike && ev.should_show_event {
|
||||
self.add_event(ev)
|
||||
}
|
||||
}
|
||||
|
||||
guard done else {
|
||||
return
|
||||
}
|
||||
|
||||
self.loading = false
|
||||
|
||||
if sub_id == self.sub_id {
|
||||
guard let txn = NdbTxn(ndb: state.ndb) else { return }
|
||||
load_profiles(context: "search", profiles_subid: self.profiles_subid, relay_id: relay_id, load: .from_events(self.events.all_events), damus_state: state, txn: txn)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ struct OnboardingSuggestionsView: View {
|
||||
// - We don't have other mechanisms to allow the user to edit this yet
|
||||
//
|
||||
// Therefore, it is better to just save it locally, and retrieve this once we build out https://github.com/damus-io/damus/issues/3042
|
||||
model.damus_state.nostrNetwork.pool.send_raw_to_local_ndb(.typical(.event(event)))
|
||||
model.damus_state.nostrNetwork.sendToNostrDB(event: event)
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
|
||||
@@ -224,7 +224,7 @@ class Drafts: ObservableObject {
|
||||
}
|
||||
|
||||
/// Saves the drafts tracked by this class persistently using NostrDB + UserDefaults
|
||||
func save(damus_state: DamusState) {
|
||||
func save(damus_state: DamusState) async {
|
||||
var draft_events: [NdbNote] = []
|
||||
post_artifact_block: if let post_artifacts = self.post {
|
||||
let nip37_draft = try? post_artifacts.to_nip37_draft(action: .posting(.user(damus_state.pubkey)), damus_state: damus_state)
|
||||
@@ -254,7 +254,7 @@ class Drafts: ObservableObject {
|
||||
// TODO: Once it is time to implement draft syncing with relays, please consider the following:
|
||||
// - Privacy: Sending drafts to the network leaks metadata about app activity, and may break user expectations
|
||||
// - Down-sync conflict resolution: Consider how to solve conflicts for different draft versions holding the same ID (e.g. edited in Damus, then another client, then Damus again)
|
||||
damus_state.nostrNetwork.pool.send_raw_to_local_ndb(.typical(.event(draft_event)))
|
||||
damus_state.nostrNetwork.sendToNostrDB(event: draft_event)
|
||||
}
|
||||
|
||||
damus_state.settings.draft_event_ids = draft_events.map({ $0.id.hex() })
|
||||
|
||||
@@ -105,7 +105,7 @@ struct PostView: View {
|
||||
self.prompt_view = prompt_view
|
||||
self.placeholder_messages = placeholder_messages ?? [POST_PLACEHOLDER]
|
||||
self.initial_text_suffix = initial_text_suffix
|
||||
self.autoSaveModel = AutoSaveIndicatorView.AutoSaveViewModel(save: { damus_state.drafts.save(damus_state: damus_state) })
|
||||
self.autoSaveModel = AutoSaveIndicatorView.AutoSaveViewModel(save: { await damus_state.drafts.save(damus_state: damus_state) })
|
||||
}
|
||||
|
||||
@Environment(\.dismiss) var dismiss
|
||||
@@ -231,7 +231,7 @@ struct PostView: View {
|
||||
damus_state.drafts.post = nil
|
||||
}
|
||||
|
||||
damus_state.drafts.save(damus_state: damus_state)
|
||||
Task{ await damus_state.drafts.save(damus_state: damus_state) }
|
||||
}
|
||||
|
||||
func load_draft() -> Bool {
|
||||
|
||||
@@ -23,17 +23,21 @@ class ProfileModel: ObservableObject, Equatable {
|
||||
return nil
|
||||
}
|
||||
|
||||
private let MAX_SHARE_RELAYS = 4
|
||||
|
||||
var events: EventHolder
|
||||
let pubkey: Pubkey
|
||||
let damus: DamusState
|
||||
|
||||
var seen_event: Set<NoteId> = Set()
|
||||
var sub_id = UUID().description
|
||||
var prof_subid = UUID().description
|
||||
var conversations_subid = UUID().description
|
||||
var findRelay_subid = UUID().description
|
||||
|
||||
var findRelaysListener: Task<Void, Never>? = nil
|
||||
var listener: Task<Void, Never>? = nil
|
||||
var profileListener: Task<Void, Never>? = nil
|
||||
var conversationListener: Task<Void, Never>? = nil
|
||||
|
||||
var conversation_events: Set<NoteId> = Set()
|
||||
|
||||
|
||||
init(pubkey: Pubkey, damus: DamusState) {
|
||||
self.pubkey = pubkey
|
||||
self.damus = damus
|
||||
@@ -46,7 +50,7 @@ class ProfileModel: ObservableObject, Equatable {
|
||||
guard let contacts = self.contacts else {
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
return contacts.referenced_pubkeys.contains(pubkey)
|
||||
}
|
||||
|
||||
@@ -60,39 +64,53 @@ class ProfileModel: ObservableObject, Equatable {
|
||||
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.nostrNetwork.pool.unsubscribe(sub_id: sub_id)
|
||||
damus.nostrNetwork.pool.unsubscribe(sub_id: prof_subid)
|
||||
if pubkey != damus.pubkey {
|
||||
damus.nostrNetwork.pool.unsubscribe(sub_id: conversations_subid)
|
||||
func subscribe() {
|
||||
print("subscribing to profile \(pubkey)")
|
||||
listener?.cancel()
|
||||
listener = Task {
|
||||
var text_filter = NostrFilter(kinds: [.text, .longform, .highlight])
|
||||
text_filter.authors = [pubkey]
|
||||
text_filter.limit = 500
|
||||
for await item in damus.nostrNetwork.reader.subscribe(filters: [text_filter]) {
|
||||
switch item {
|
||||
case .event(let borrow):
|
||||
try? borrow { event in
|
||||
handleNostrEvent(event.toOwned())
|
||||
}
|
||||
case .eose: break
|
||||
}
|
||||
}
|
||||
guard let txn = NdbTxn(ndb: damus.ndb) else { return }
|
||||
load_profiles(context: "profile", load: .from_events(events.events), damus_state: damus, txn: txn)
|
||||
progress += 1
|
||||
}
|
||||
profileListener?.cancel()
|
||||
profileListener = Task {
|
||||
var profile_filter = NostrFilter(kinds: [.contacts, .metadata, .boost])
|
||||
profile_filter.authors = [pubkey]
|
||||
for await item in damus.nostrNetwork.reader.subscribe(filters: [profile_filter]) {
|
||||
switch item {
|
||||
case .event(let borrow):
|
||||
try? borrow { event in
|
||||
handleNostrEvent(event.toOwned())
|
||||
}
|
||||
case .eose: break
|
||||
}
|
||||
}
|
||||
progress += 1
|
||||
}
|
||||
conversationListener?.cancel()
|
||||
conversationListener = Task {
|
||||
await listenToConversations()
|
||||
}
|
||||
}
|
||||
|
||||
func subscribe() {
|
||||
var text_filter = NostrFilter(kinds: [.text, .longform, .highlight])
|
||||
var profile_filter = NostrFilter(kinds: [.contacts, .metadata, .boost])
|
||||
var relay_list_filter = NostrFilter(kinds: [.relay_list], authors: [pubkey])
|
||||
|
||||
profile_filter.authors = [pubkey]
|
||||
|
||||
text_filter.authors = [pubkey]
|
||||
text_filter.limit = 500
|
||||
|
||||
print("subscribing to textlike events from profile \(pubkey) with sub_id \(sub_id)")
|
||||
//print_filters(relay_id: "profile", filters: [[text_filter], [profile_filter]])
|
||||
damus.nostrNetwork.pool.subscribe(sub_id: sub_id, filters: [text_filter], handler: handle_event)
|
||||
damus.nostrNetwork.pool.subscribe(sub_id: prof_subid, filters: [profile_filter, relay_list_filter], handler: handle_event)
|
||||
|
||||
subscribe_to_conversations()
|
||||
}
|
||||
|
||||
private func subscribe_to_conversations() {
|
||||
|
||||
func listenToConversations() async {
|
||||
// Only subscribe to conversation events if the profile is not us.
|
||||
guard pubkey != damus.pubkey else {
|
||||
return
|
||||
@@ -102,10 +120,35 @@ class ProfileModel: ObservableObject, Equatable {
|
||||
let limit: UInt32 = 500
|
||||
let conversations_filter_them = NostrFilter(kinds: conversation_kinds, pubkeys: [damus.pubkey], limit: limit, authors: [pubkey])
|
||||
let conversations_filter_us = NostrFilter(kinds: conversation_kinds, pubkeys: [pubkey], limit: limit, authors: [damus.pubkey])
|
||||
print("subscribing to conversation events from and to profile \(pubkey) with sub_id \(conversations_subid)")
|
||||
damus.nostrNetwork.pool.subscribe(sub_id: conversations_subid, filters: [conversations_filter_them, conversations_filter_us], handler: handle_event)
|
||||
print("subscribing to conversation events from and to profile \(pubkey)")
|
||||
for await item in self.damus.nostrNetwork.reader.subscribe(filters: [conversations_filter_them, conversations_filter_us]) {
|
||||
switch item {
|
||||
case .event(borrow: let borrow):
|
||||
try? borrow { ev in
|
||||
if !seen_event.contains(ev.id) {
|
||||
let event = ev.toOwned()
|
||||
Task { await self.add_event(event) }
|
||||
conversation_events.insert(ev.id)
|
||||
}
|
||||
else if !conversation_events.contains(ev.id) {
|
||||
conversation_events.insert(ev.id)
|
||||
}
|
||||
}
|
||||
case .eose:
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func unsubscribe() {
|
||||
listener?.cancel()
|
||||
listener = nil
|
||||
profileListener?.cancel()
|
||||
profileListener = nil
|
||||
conversationListener?.cancel()
|
||||
conversationListener = nil
|
||||
}
|
||||
|
||||
func handle_profile_contact_event(_ ev: NostrEvent) {
|
||||
process_contact_event(state: damus, ev: ev)
|
||||
|
||||
@@ -120,8 +163,13 @@ class ProfileModel: ObservableObject, Equatable {
|
||||
self.following = count_pubkeys(ev.tags)
|
||||
self.legacy_relay_list = decode_json_relays(ev.content)
|
||||
}
|
||||
|
||||
@MainActor
|
||||
func add_event(_ ev: NostrEvent) {
|
||||
guard ev.should_show_event else {
|
||||
return
|
||||
}
|
||||
|
||||
private func add_event(_ ev: NostrEvent) {
|
||||
if ev.is_textlike || ev.known_kind == .boost {
|
||||
if self.events.insert(ev) {
|
||||
self.objectWillChange.send()
|
||||
@@ -134,72 +182,13 @@ class ProfileModel: ObservableObject, Equatable {
|
||||
}
|
||||
seen_event.insert(ev.id)
|
||||
}
|
||||
|
||||
// Ensure the event public key matches the public key(s) we are querying.
|
||||
// This is done to protect against a relay not properly filtering events by the pubkey
|
||||
// See https://github.com/damus-io/damus/issues/1846 for more information
|
||||
private func relay_filtered_correctly(_ ev: NostrEvent, subid: String?) -> Bool {
|
||||
if subid == self.conversations_subid {
|
||||
switch ev.pubkey {
|
||||
case self.pubkey:
|
||||
return ev.referenced_pubkeys.contains(damus.pubkey)
|
||||
case damus.pubkey:
|
||||
return ev.referenced_pubkeys.contains(self.pubkey)
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return self.pubkey == ev.pubkey
|
||||
}
|
||||
|
||||
private func handle_event(relay_id: RelayURL, ev: NostrConnectionEvent) {
|
||||
switch ev {
|
||||
case .ws_connection_event:
|
||||
return
|
||||
case .nostr_event(let resp):
|
||||
guard resp.subid == self.sub_id || resp.subid == self.prof_subid || resp.subid == self.conversations_subid else {
|
||||
return
|
||||
}
|
||||
switch resp {
|
||||
case .ok:
|
||||
break
|
||||
case .event(_, let ev):
|
||||
guard ev.should_show_event else {
|
||||
break
|
||||
}
|
||||
|
||||
if !seen_event.contains(ev.id) {
|
||||
guard relay_filtered_correctly(ev, subid: resp.subid) else {
|
||||
break
|
||||
}
|
||||
|
||||
add_event(ev)
|
||||
|
||||
if resp.subid == self.conversations_subid {
|
||||
conversation_events.insert(ev.id)
|
||||
}
|
||||
} else if resp.subid == self.conversations_subid && !conversation_events.contains(ev.id) {
|
||||
guard relay_filtered_correctly(ev, subid: resp.subid) else {
|
||||
break
|
||||
}
|
||||
|
||||
conversation_events.insert(ev.id)
|
||||
}
|
||||
case .notice:
|
||||
break
|
||||
//notify(.notice, notice)
|
||||
case .eose:
|
||||
guard let txn = NdbTxn(ndb: damus.ndb) else { return }
|
||||
if resp.subid == sub_id {
|
||||
load_profiles(context: "profile", profiles_subid: prof_subid, relay_id: relay_id, load: .from_events(events.events), damus_state: damus, txn: txn)
|
||||
}
|
||||
progress += 1
|
||||
break
|
||||
case .auth:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
private func handleNostrEvent(_ ev: NostrEvent) {
|
||||
// Ensure the event public key matches this profiles public key
|
||||
// This is done to protect against a relay not properly filtering events by the pubkey
|
||||
// See https://github.com/damus-io/damus/issues/1846 for more information
|
||||
guard self.pubkey == ev.pubkey else { return }
|
||||
Task { await add_event(ev) }
|
||||
}
|
||||
|
||||
private func findRelaysHandler(relay_id: RelayURL, ev: NostrConnectionEvent) {
|
||||
@@ -211,12 +200,27 @@ class ProfileModel: ObservableObject, Equatable {
|
||||
func subscribeToFindRelays() {
|
||||
var profile_filter = NostrFilter(kinds: [.contacts])
|
||||
profile_filter.authors = [pubkey]
|
||||
|
||||
damus.nostrNetwork.pool.subscribe(sub_id: findRelay_subid, filters: [profile_filter], handler: findRelaysHandler)
|
||||
self.findRelaysListener?.cancel()
|
||||
self.findRelaysListener = Task {
|
||||
for await item in await damus.nostrNetwork.reader.subscribe(filters: [profile_filter]) {
|
||||
switch item {
|
||||
case .event(let borrow):
|
||||
try? borrow { event in
|
||||
if case .contacts = event.known_kind {
|
||||
// TODO: Is this correct?
|
||||
self.legacy_relay_list = decode_json_relays(event.content)
|
||||
}
|
||||
}
|
||||
case .eose:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func unsubscribeFindRelays() {
|
||||
damus.nostrNetwork.pool.unsubscribe(sub_id: findRelay_subid)
|
||||
self.findRelaysListener?.cancel()
|
||||
self.findRelaysListener = nil
|
||||
}
|
||||
|
||||
func getCappedRelays() -> [RelayURL] {
|
||||
|
||||
@@ -283,7 +283,7 @@ fileprivate struct ProfileActionSheetZapButton: View {
|
||||
VStack(alignment: .center, spacing: 10) {
|
||||
Button(
|
||||
action: {
|
||||
send_zap(damus_state: damus_state, target: .profile(self.profile.pubkey), lnurl: lnurl, is_custom: false, comment: nil, amount_sats: nil, zap_type: damus_state.settings.default_zap_type)
|
||||
Task { await send_zap(damus_state: damus_state, target: .profile(self.profile.pubkey), lnurl: lnurl, is_custom: false, comment: nil, amount_sats: nil, zap_type: damus_state.settings.default_zap_type) }
|
||||
zap_state = .zapping
|
||||
},
|
||||
label: {
|
||||
|
||||
@@ -588,3 +588,4 @@ func check_nip05_validity(pubkey: Pubkey, profiles: Profiles) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -84,9 +84,3 @@ func load_relay_filters(_ pubkey: Pubkey) -> Set<RelayFilter>? {
|
||||
s.insert(filter)
|
||||
}
|
||||
}
|
||||
|
||||
func determine_to_relays(pool: RelayPool, filters: RelayFilters) -> [RelayURL] {
|
||||
return pool.our_descriptors
|
||||
.map { $0.url }
|
||||
.filter { !filters.is_filtered(timeline: .search, relay_id: $0) }
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ struct RelayConfigView: View {
|
||||
|
||||
init(state: DamusState) {
|
||||
self.state = state
|
||||
_relays = State(initialValue: state.nostrNetwork.pool.our_descriptors)
|
||||
_relays = State(initialValue: state.nostrNetwork.ourRelayDescriptors)
|
||||
UITabBar.appearance().isHidden = true
|
||||
}
|
||||
|
||||
@@ -98,7 +98,7 @@ struct RelayConfigView: View {
|
||||
}
|
||||
}
|
||||
.onReceive(handle_notify(.relays_changed)) { _ in
|
||||
self.relays = state.nostrNetwork.pool.our_descriptors
|
||||
self.relays = state.nostrNetwork.ourRelayDescriptors
|
||||
}
|
||||
.onAppear {
|
||||
notify(.display_tabbar(false))
|
||||
|
||||
@@ -30,7 +30,7 @@ struct RelayDetailView: View {
|
||||
|
||||
func RemoveRelayButton(_ keypair: FullKeypair) -> some View {
|
||||
Button(action: {
|
||||
self.removeRelay()
|
||||
Task { await self.removeRelay() }
|
||||
}) {
|
||||
HStack {
|
||||
Text("Disconnect", comment: "Button to disconnect from the relay.")
|
||||
@@ -43,7 +43,7 @@ struct RelayDetailView: View {
|
||||
|
||||
func ConnectRelayButton(_ keypair: FullKeypair) -> some View {
|
||||
Button(action: {
|
||||
self.connectRelay()
|
||||
Task { await self.connectRelay() }
|
||||
}) {
|
||||
HStack {
|
||||
Text("Connect", comment: "Button to connect to the relay.")
|
||||
@@ -177,16 +177,18 @@ struct RelayDetailView: View {
|
||||
}
|
||||
|
||||
private var relay_object: RelayPool.Relay? {
|
||||
state.nostrNetwork.pool.get_relay(relay)
|
||||
// TODO: Concurrency problems?
|
||||
state.nostrNetwork.connectedRelays.first(where: { $0.descriptor.url == relay })
|
||||
}
|
||||
|
||||
private var relay_connection: RelayConnection? {
|
||||
relay_object?.connection
|
||||
}
|
||||
|
||||
func removeRelay() {
|
||||
func removeRelay() async {
|
||||
do {
|
||||
try state.nostrNetwork.userRelayList.remove(relayURL: self.relay)
|
||||
// TODO: Concurrency problems?
|
||||
try await state.nostrNetwork.userRelayList.remove(relayURL: self.relay)
|
||||
dismiss()
|
||||
}
|
||||
catch {
|
||||
@@ -194,9 +196,10 @@ struct RelayDetailView: View {
|
||||
}
|
||||
}
|
||||
|
||||
func connectRelay() {
|
||||
func connectRelay() async {
|
||||
do {
|
||||
try state.nostrNetwork.userRelayList.insert(relay: NIP65.RelayList.RelayItem(url: relay, rwConfiguration: .readWrite))
|
||||
// TODO: Concurrency problems?
|
||||
try await state.nostrNetwork.userRelayList.insert(relay: NIP65.RelayList.RelayItem(url: relay, rwConfiguration: .readWrite))
|
||||
dismiss()
|
||||
}
|
||||
catch {
|
||||
|
||||
@@ -19,7 +19,7 @@ struct RelayFilterView: View {
|
||||
}
|
||||
|
||||
var relays: [RelayPool.RelayDescriptor] {
|
||||
return state.nostrNetwork.pool.our_descriptors
|
||||
return state.nostrNetwork.ourRelayDescriptors
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
|
||||
@@ -56,7 +56,7 @@ struct RelayStatusView: View {
|
||||
|
||||
struct RelayStatusView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
let connection = test_damus_state.nostrNetwork.pool.get_relay(RelayURL("wss://relay.damus.io")!)!.connection
|
||||
let connection = test_damus_state.nostrNetwork.getRelay(RelayURL("wss://relay.damus.io")!)!.connection
|
||||
RelayStatusView(connection: connection)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ struct RelayToggle: View {
|
||||
}
|
||||
|
||||
private var relay_connection: RelayConnection? {
|
||||
state.nostrNetwork.pool.get_relay(relay_id)?.connection
|
||||
state.nostrNetwork.getRelay(relay_id)?.connection
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,13 +24,13 @@ struct RelayView: View {
|
||||
self.recommended = recommended
|
||||
self.model_cache = state.relay_model_cache
|
||||
_showActionButtons = showActionButtons
|
||||
let relay_state = RelayView.get_relay_state(pool: state.nostrNetwork.pool, relay: relay)
|
||||
let relay_state = RelayView.get_relay_state(state: state, relay: relay)
|
||||
self._relay_state = State(initialValue: relay_state)
|
||||
self.disableNavLink = disableNavLink
|
||||
}
|
||||
|
||||
static func get_relay_state(pool: RelayPool, relay: RelayURL) -> Bool {
|
||||
return pool.get_relay(relay) == nil
|
||||
static func get_relay_state(state: DamusState, relay: RelayURL) -> Bool {
|
||||
return state.nostrNetwork.getRelay(relay) == nil
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
@@ -110,7 +110,7 @@ struct RelayView: View {
|
||||
.contentShape(Rectangle())
|
||||
}
|
||||
.onReceive(handle_notify(.relays_changed)) { _ in
|
||||
self.relay_state = RelayView.get_relay_state(pool: state.nostrNetwork.pool, relay: self.relay)
|
||||
self.relay_state = RelayView.get_relay_state(state: state, relay: self.relay)
|
||||
}
|
||||
.onTapGesture {
|
||||
if !disableNavLink {
|
||||
@@ -120,7 +120,7 @@ struct RelayView: View {
|
||||
}
|
||||
|
||||
private var relay_connection: RelayConnection? {
|
||||
state.nostrNetwork.pool.get_relay(relay)?.connection
|
||||
state.nostrNetwork.getRelay(relay)?.connection
|
||||
}
|
||||
|
||||
func add_action(keypair: FullKeypair) async {
|
||||
|
||||
@@ -22,7 +22,7 @@ struct UserRelaysView: View {
|
||||
|
||||
static func make_relay_state(state: DamusState, relays: [RelayURL]) -> [(RelayURL, Bool)] {
|
||||
return relays.map({ r in
|
||||
return (r, state.nostrNetwork.pool.get_relay(r) == nil)
|
||||
return (r, state.nostrNetwork.getRelay(r) == nil)
|
||||
}).sorted { (a, b) in a.0 < b.0 }
|
||||
}
|
||||
|
||||
|
||||
@@ -39,63 +39,41 @@ class SearchHomeModel: ObservableObject {
|
||||
self.objectWillChange.send()
|
||||
}
|
||||
|
||||
func subscribe() {
|
||||
func load() async {
|
||||
loading = true
|
||||
let to_relays = determine_to_relays(pool: damus_state.nostrNetwork.pool, filters: damus_state.relay_filters)
|
||||
|
||||
var follow_list_filter = NostrFilter(kinds: [.follow_list])
|
||||
follow_list_filter.until = UInt32(Date.now.timeIntervalSince1970)
|
||||
let to_relays = damus_state.nostrNetwork.ourRelayDescriptors
|
||||
.map { $0.url }
|
||||
.filter { !damus_state.relay_filters.is_filtered(timeline: .search, relay_id: $0) }
|
||||
|
||||
damus_state.nostrNetwork.pool.subscribe(sub_id: base_subid, filters: [get_base_filter()], handler: handle_event, to: to_relays)
|
||||
damus_state.nostrNetwork.pool.subscribe(sub_id: follow_pack_subid, filters: [follow_list_filter], handler: handle_event, to: to_relays)
|
||||
}
|
||||
|
||||
func unsubscribe(to: RelayURL? = nil) {
|
||||
loading = false
|
||||
damus_state.nostrNetwork.pool.unsubscribe(sub_id: base_subid, to: to.map { [$0] })
|
||||
damus_state.nostrNetwork.pool.unsubscribe(sub_id: follow_pack_subid, to: to.map { [$0] })
|
||||
}
|
||||
|
||||
func handle_event(relay_id: RelayURL, conn_ev: NostrConnectionEvent) {
|
||||
guard case .nostr_event(let event) = conn_ev else {
|
||||
return
|
||||
for await item in damus_state.nostrNetwork.reader.subscribe(filters: [get_base_filter()], to: to_relays) {
|
||||
switch item {
|
||||
case .event(let borrow):
|
||||
var event: NostrEvent? = nil
|
||||
try? borrow { ev in
|
||||
event = ev.toOwned()
|
||||
}
|
||||
guard let event else { return }
|
||||
await self.handleEvent(event)
|
||||
case .eose: break
|
||||
}
|
||||
}
|
||||
loading = false
|
||||
|
||||
switch event {
|
||||
case .event(let sub_id, let ev):
|
||||
guard sub_id == self.base_subid || sub_id == self.profiles_subid || sub_id == self.follow_pack_subid else {
|
||||
guard let txn = NdbTxn(ndb: damus_state.ndb) else { return }
|
||||
load_profiles(context: "universe", load: .from_events(events.all_events), damus_state: damus_state, txn: txn)
|
||||
}
|
||||
|
||||
@MainActor
|
||||
func handleEvent(_ ev: NostrEvent) {
|
||||
if ev.is_textlike && should_show_event(state: damus_state, ev: ev) && !ev.is_reply() {
|
||||
if !damus_state.settings.multiple_events_per_pubkey && seen_pubkey.contains(ev.pubkey) {
|
||||
return
|
||||
}
|
||||
if ev.is_textlike && should_show_event(state: damus_state, ev: ev) && !ev.is_reply()
|
||||
{
|
||||
if !damus_state.settings.multiple_events_per_pubkey && seen_pubkey.contains(ev.pubkey) {
|
||||
return
|
||||
}
|
||||
seen_pubkey.insert(ev.pubkey)
|
||||
|
||||
if self.events.insert(ev) {
|
||||
self.objectWillChange.send()
|
||||
}
|
||||
}
|
||||
case .notice(let msg):
|
||||
print("search home notice: \(msg)")
|
||||
case .ok:
|
||||
break
|
||||
case .eose(let sub_id):
|
||||
loading = false
|
||||
seen_pubkey.insert(ev.pubkey)
|
||||
|
||||
if sub_id == self.base_subid {
|
||||
// Make sure we unsubscribe after we've fetched the global events
|
||||
// global events are not realtime
|
||||
unsubscribe(to: relay_id)
|
||||
|
||||
guard let txn = NdbTxn(ndb: damus_state.ndb) else { return }
|
||||
load_profiles(context: "universe", profiles_subid: profiles_subid, relay_id: relay_id, load: .from_events(events.all_events), damus_state: damus_state, txn: txn)
|
||||
if self.events.insert(ev) {
|
||||
self.objectWillChange.send()
|
||||
}
|
||||
|
||||
break
|
||||
case .auth:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -135,44 +113,35 @@ enum PubkeysToLoad {
|
||||
case from_keys([Pubkey])
|
||||
}
|
||||
|
||||
func load_profiles<Y>(context: String, profiles_subid: String, relay_id: RelayURL, load: PubkeysToLoad, damus_state: DamusState, txn: NdbTxn<Y>) {
|
||||
func load_profiles<Y>(context: String, load: PubkeysToLoad, damus_state: DamusState, txn: NdbTxn<Y>) {
|
||||
let authors = find_profiles_to_fetch(profiles: damus_state.profiles, load: load, cache: damus_state.events, txn: txn)
|
||||
|
||||
guard !authors.isEmpty else {
|
||||
return
|
||||
}
|
||||
|
||||
print("load_profiles[\(context)]: requesting \(authors.count) profiles from \(relay_id)")
|
||||
|
||||
let filter = NostrFilter(kinds: [.metadata], authors: authors)
|
||||
|
||||
damus_state.nostrNetwork.pool.subscribe_to(sub_id: profiles_subid, filters: [filter], to: [relay_id]) { rid, conn_ev in
|
||||
Task {
|
||||
print("load_profiles[\(context)]: requesting \(authors.count) profiles from relay pool")
|
||||
let filter = NostrFilter(kinds: [.metadata], authors: authors)
|
||||
|
||||
let now = UInt64(Date.now.timeIntervalSince1970)
|
||||
switch conn_ev {
|
||||
case .ws_connection_event:
|
||||
break
|
||||
case .nostr_event(let ev):
|
||||
guard ev.subid == profiles_subid, rid == relay_id else { return }
|
||||
|
||||
switch ev {
|
||||
case .event(_, let ev):
|
||||
if ev.known_kind == .metadata {
|
||||
damus_state.ndb.write_profile_last_fetched(pubkey: ev.pubkey, fetched_at: now)
|
||||
for await item in damus_state.nostrNetwork.reader.subscribe(filters: [filter]) {
|
||||
let now = UInt64(Date.now.timeIntervalSince1970)
|
||||
switch item {
|
||||
case .event(let borrow):
|
||||
var event: NostrEvent? = nil
|
||||
try? borrow { ev in
|
||||
event = ev.toOwned()
|
||||
}
|
||||
guard let event else { return }
|
||||
if event.known_kind == .metadata {
|
||||
damus_state.ndb.write_profile_last_fetched(pubkey: event.pubkey, fetched_at: now)
|
||||
}
|
||||
case .eose:
|
||||
print("load_profiles[\(context)]: done loading \(authors.count) profiles from \(relay_id)")
|
||||
damus_state.nostrNetwork.pool.unsubscribe(sub_id: profiles_subid, to: [relay_id])
|
||||
case .ok:
|
||||
break
|
||||
case .notice:
|
||||
break
|
||||
case .auth:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
print("load_profiles[\(context)]: done loading \(authors.count) profiles from relay pool")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,8 +14,8 @@ class SearchModel: ObservableObject {
|
||||
@Published var loading: Bool = false
|
||||
|
||||
var search: NostrFilter
|
||||
let sub_id = UUID().description
|
||||
let profiles_subid = UUID().description
|
||||
var listener: Task<Void, Never>? = nil
|
||||
let limit: UInt32 = 500
|
||||
|
||||
init(state: DamusState, search: NostrFilter) {
|
||||
@@ -39,17 +39,32 @@ class SearchModel: ObservableObject {
|
||||
search.kinds = [.text, .like, .longform, .highlight, .follow_list]
|
||||
|
||||
//likes_filter.ids = ref_events.referenced_ids!
|
||||
|
||||
print("subscribing to search '\(search)' with sub_id \(sub_id)")
|
||||
state.nostrNetwork.pool.register_handler(sub_id: sub_id, handler: handle_event)
|
||||
loading = true
|
||||
state.nostrNetwork.pool.send(.subscribe(.init(filters: [search], sub_id: sub_id)))
|
||||
listener?.cancel()
|
||||
listener = Task {
|
||||
self.loading = true
|
||||
print("subscribing to search")
|
||||
for await item in await state.nostrNetwork.reader.subscribe(filters: [search]) {
|
||||
switch item {
|
||||
case .event(let borrow):
|
||||
try? borrow { ev in
|
||||
let event = ev.toOwned()
|
||||
if event.is_textlike && event.should_show_event {
|
||||
self.add_event(event)
|
||||
}
|
||||
}
|
||||
case .eose:
|
||||
break
|
||||
}
|
||||
guard let txn = NdbTxn(ndb: state.ndb) else { return }
|
||||
load_profiles(context: "search", load: .from_events(self.events.all_events), damus_state: state, txn: txn)
|
||||
}
|
||||
self.loading = false
|
||||
}
|
||||
}
|
||||
|
||||
func unsubscribe() {
|
||||
state.nostrNetwork.pool.unsubscribe(sub_id: sub_id)
|
||||
loading = false
|
||||
print("unsubscribing from search '\(search)' with sub_id \(sub_id)")
|
||||
listener?.cancel()
|
||||
listener = nil
|
||||
}
|
||||
|
||||
func add_event(_ ev: NostrEvent) {
|
||||
@@ -65,25 +80,6 @@ class SearchModel: ObservableObject {
|
||||
objectWillChange.send()
|
||||
}
|
||||
}
|
||||
|
||||
func handle_event(relay_id: RelayURL, ev: NostrConnectionEvent) {
|
||||
let (sub_id, done) = handle_subid_event(pool: state.nostrNetwork.pool, relay_id: relay_id, ev: ev) { sub_id, ev in
|
||||
if ev.is_textlike && ev.should_show_event {
|
||||
self.add_event(ev)
|
||||
}
|
||||
}
|
||||
|
||||
guard done else {
|
||||
return
|
||||
}
|
||||
|
||||
self.loading = false
|
||||
|
||||
if sub_id == self.sub_id {
|
||||
guard let txn = NdbTxn(ndb: state.ndb) else { return }
|
||||
load_profiles(context: "search", profiles_subid: self.profiles_subid, relay_id: relay_id, load: .from_events(self.events.all_events), damus_state: state, txn: txn)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func event_matches_hashtag(_ ev: NostrEvent, hashtags: [String]) -> Bool {
|
||||
@@ -106,33 +102,3 @@ func event_matches_filter(_ ev: NostrEvent, filter: NostrFilter) -> Bool {
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func handle_subid_event(pool: RelayPool, relay_id: RelayURL, ev: NostrConnectionEvent, handle: (String, NostrEvent) -> ()) -> (String?, Bool) {
|
||||
switch ev {
|
||||
case .ws_connection_event:
|
||||
return (nil, false)
|
||||
|
||||
case .nostr_event(let res):
|
||||
switch res {
|
||||
case .event(let ev_subid, let ev):
|
||||
handle(ev_subid, ev)
|
||||
return (ev_subid, false)
|
||||
|
||||
case .ok:
|
||||
return (nil, false)
|
||||
|
||||
case .notice(let note):
|
||||
if note.contains("Too many subscription filters") {
|
||||
// TODO: resend filters?
|
||||
pool.reconnect(to: [relay_id])
|
||||
}
|
||||
return (nil, false)
|
||||
|
||||
case .eose(let subid):
|
||||
return (subid, true)
|
||||
|
||||
case .auth:
|
||||
return (nil, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ struct SearchHomeView: View {
|
||||
@StateObject var model: SearchHomeModel
|
||||
@State var search: String = ""
|
||||
@FocusState private var isFocused: Bool
|
||||
@State var loadingTask: Task<Void, Never>?
|
||||
|
||||
func content_filter(_ fstate: FilterState) -> ((NostrEvent) -> Bool) {
|
||||
var filters = ContentFilters.defaults(damus_state: damus_state)
|
||||
@@ -84,8 +85,8 @@ struct SearchHomeView: View {
|
||||
)
|
||||
.refreshable {
|
||||
// Fetch new information by unsubscribing and resubscribing to the relay
|
||||
model.unsubscribe()
|
||||
model.subscribe()
|
||||
loadingTask?.cancel()
|
||||
loadingTask = Task { await model.load() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,8 +94,8 @@ struct SearchHomeView: View {
|
||||
SearchResultsView(damus_state: damus_state, search: $search)
|
||||
.refreshable {
|
||||
// Fetch new information by unsubscribing and resubscribing to the relay
|
||||
model.unsubscribe()
|
||||
model.subscribe()
|
||||
loadingTask?.cancel()
|
||||
loadingTask = Task { await model.load() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,11 +130,11 @@ struct SearchHomeView: View {
|
||||
}
|
||||
.onAppear {
|
||||
if model.events.events.isEmpty {
|
||||
model.subscribe()
|
||||
loadingTask = Task { await model.load() }
|
||||
}
|
||||
}
|
||||
.onDisappear {
|
||||
model.unsubscribe()
|
||||
loadingTask?.cancel()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,7 +77,8 @@ struct SearchingEventView: View {
|
||||
}
|
||||
|
||||
case .event(let note_id):
|
||||
find_event(state: state, query: .event(evid: note_id)) { res in
|
||||
Task {
|
||||
let res = await state.nostrNetwork.findEvent(query: .event(evid: note_id))
|
||||
guard case .event(let ev) = res else {
|
||||
self.search_state = .not_found
|
||||
return
|
||||
@@ -85,7 +86,8 @@ struct SearchingEventView: View {
|
||||
self.search_state = .found(ev)
|
||||
}
|
||||
case .profile(let pubkey):
|
||||
find_event(state: state, query: .profile(pubkey: pubkey)) { res in
|
||||
Task {
|
||||
let res = await state.nostrNetwork.findEvent(query: .profile(pubkey: pubkey))
|
||||
guard case .profile(let pubkey) = res else {
|
||||
self.search_state = .not_found
|
||||
return
|
||||
@@ -93,7 +95,8 @@ struct SearchingEventView: View {
|
||||
self.search_state = .found_profile(pubkey)
|
||||
}
|
||||
case .naddr(let naddr):
|
||||
naddrLookup(damus_state: state, naddr: naddr) { res in
|
||||
Task {
|
||||
let res = await state.nostrNetwork.lookup(naddr: naddr)
|
||||
guard let res = res else {
|
||||
self.search_state = .not_found
|
||||
return
|
||||
|
||||
@@ -68,7 +68,7 @@ struct FirstAidSettingsView: View {
|
||||
guard let new_contact_list_event = make_first_contact_event(keypair: damus_state.keypair) else {
|
||||
throw FirstAidError.cannotMakeFirstContactEvent
|
||||
}
|
||||
damus_state.nostrNetwork.pool.send(.event(new_contact_list_event))
|
||||
damus_state.nostrNetwork.send(event: new_contact_list_event)
|
||||
damus_state.settings.latest_contact_event_id_hex = new_contact_list_event.id.hex()
|
||||
}
|
||||
|
||||
|
||||
@@ -65,12 +65,11 @@ class HomeModel: ContactsDelegate {
|
||||
let resub_debouncer = Debouncer(interval: 3.0)
|
||||
var should_debounce_dms = true
|
||||
|
||||
let home_subid = UUID().description
|
||||
let contacts_subid = UUID().description
|
||||
let notifications_subid = UUID().description
|
||||
let dms_subid = UUID().description
|
||||
let init_subid = UUID().description
|
||||
let profiles_subid = UUID().description
|
||||
var homeHandlerTask: Task<Void, Never>?
|
||||
var contactsHandlerTask: Task<Void, Never>?
|
||||
var notificationsHandlerTask: Task<Void, Never>?
|
||||
var dmsHandlerTask: Task<Void, Never>?
|
||||
var nwcHandlerTask: Task<Void, Never>?
|
||||
|
||||
var loading: Bool = false
|
||||
|
||||
@@ -94,23 +93,10 @@ class HomeModel: ContactsDelegate {
|
||||
preload_events(state: self.damus_state, events: [ev])
|
||||
}
|
||||
|
||||
var pool: RelayPool {
|
||||
self.damus_state.nostrNetwork.pool
|
||||
}
|
||||
|
||||
var dms: DirectMessagesModel {
|
||||
return damus_state.dms
|
||||
}
|
||||
|
||||
func has_sub_id_event(sub_id: String, ev_id: NoteId) -> Bool {
|
||||
if !has_event.keys.contains(sub_id) {
|
||||
has_event[sub_id] = Set()
|
||||
return false
|
||||
}
|
||||
|
||||
return has_event[sub_id]!.contains(ev_id)
|
||||
}
|
||||
|
||||
func setup_debouncer() {
|
||||
// turn off debouncer after initial load
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 5.0) {
|
||||
@@ -140,6 +126,28 @@ class HomeModel: ContactsDelegate {
|
||||
damus_state.drafts.load(from: damus_state)
|
||||
}
|
||||
|
||||
enum RelayListLoadingError: Error {
|
||||
case noRelayList
|
||||
case relayListParseError
|
||||
|
||||
var humanReadableError: ErrorView.UserPresentableError {
|
||||
switch self {
|
||||
case .noRelayList:
|
||||
return ErrorView.UserPresentableError(
|
||||
user_visible_description: NSLocalizedString("Your relay list could not be found, so we cannot connect you to your Nostr network.", comment: "Human readable error description for a failure to find the relay list"),
|
||||
tip: NSLocalizedString("Please check your internet connection and restart the app. If the error persists, please go to Settings > First Aid.", comment: "Human readable tips for what to do for a failure to find the relay list"),
|
||||
technical_info: "No NIP-65 relay list or legacy kind:3 contact event could be found."
|
||||
)
|
||||
case .relayListParseError:
|
||||
return ErrorView.UserPresentableError(
|
||||
user_visible_description: NSLocalizedString("Your relay list appears to be broken, so we cannot connect you to your Nostr network.", comment: "Human readable error description for a failure to parse the relay list due to a bad relay list"),
|
||||
tip: NSLocalizedString("Please contact support for further help.", comment: "Human readable tips for what to do for a failure to find the relay list"),
|
||||
technical_info: "Relay list could not be parsed."
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - ContactsDelegate functions
|
||||
|
||||
func latest_contact_event_changed(new_event: NostrEvent) {
|
||||
@@ -158,9 +166,6 @@ class HomeModel: ContactsDelegate {
|
||||
print("hit resub debouncer")
|
||||
|
||||
resub_debouncer.debounce {
|
||||
print("resub")
|
||||
self.unsubscribe_to_home_filters()
|
||||
|
||||
switch resubbing {
|
||||
case .following:
|
||||
break
|
||||
@@ -175,25 +180,16 @@ class HomeModel: ContactsDelegate {
|
||||
}
|
||||
|
||||
@MainActor
|
||||
func process_event(sub_id: String, relay_id: RelayURL, ev: NostrEvent) {
|
||||
if has_sub_id_event(sub_id: sub_id, ev_id: ev.id) {
|
||||
return
|
||||
}
|
||||
|
||||
let last_k = get_last_event_of_kind(relay_id: relay_id, kind: ev.kind)
|
||||
if last_k == nil || ev.created_at > last_k!.created_at {
|
||||
last_event_of_kind[relay_id]?[ev.kind] = ev
|
||||
}
|
||||
|
||||
func process_event(ev: NostrEvent, context: SubscriptionContext) {
|
||||
guard let kind = ev.known_kind else {
|
||||
return
|
||||
}
|
||||
|
||||
switch kind {
|
||||
case .chat, .longform, .text, .highlight:
|
||||
handle_text_event(sub_id: sub_id, ev)
|
||||
handle_text_event(ev, context: context)
|
||||
case .contacts:
|
||||
handle_contact_event(sub_id: sub_id, relay_id: relay_id, ev: ev)
|
||||
handle_contact_event(ev: ev)
|
||||
case .metadata:
|
||||
// profile metadata processing is handled by nostrdb
|
||||
break
|
||||
@@ -202,7 +198,7 @@ class HomeModel: ContactsDelegate {
|
||||
case .mute_list:
|
||||
handle_mute_list_event(ev)
|
||||
case .boost:
|
||||
handle_boost_event(sub_id: sub_id, ev)
|
||||
handle_boost_event(ev, context: context)
|
||||
case .like:
|
||||
handle_like_event(ev)
|
||||
case .dm:
|
||||
@@ -216,7 +212,7 @@ class HomeModel: ContactsDelegate {
|
||||
case .nwc_request:
|
||||
break
|
||||
case .nwc_response:
|
||||
handle_nwc_response(ev, relay: relay_id)
|
||||
handle_nwc_response(ev)
|
||||
case .http_auth:
|
||||
break
|
||||
case .status:
|
||||
@@ -261,7 +257,7 @@ class HomeModel: ContactsDelegate {
|
||||
pdata.status.update_status(st)
|
||||
}
|
||||
|
||||
func handle_nwc_response(_ ev: NostrEvent, relay: RelayURL) {
|
||||
func handle_nwc_response(_ ev: NostrEvent) {
|
||||
Task { @MainActor in
|
||||
// TODO: Adapt KeychainStorage to StringCodable and instead of parsing to WalletConnectURL every time
|
||||
guard let nwc_str = damus_state.settings.nostr_wallet_connect,
|
||||
@@ -269,7 +265,6 @@ class HomeModel: ContactsDelegate {
|
||||
return
|
||||
}
|
||||
|
||||
guard nwc.relay == relay else { return } // Don't process NWC responses coming from relays other than our designated one
|
||||
guard ev.referenced_pubkeys.first == nwc.keypair.pubkey else {
|
||||
return // This message is not for us. Ignore it.
|
||||
}
|
||||
@@ -289,9 +284,9 @@ class HomeModel: ContactsDelegate {
|
||||
// since command results are not returned for ephemeral events,
|
||||
// remove the request from the postbox which is likely failing over and over
|
||||
if damus_state.nostrNetwork.postbox.remove_relayer(relay_id: nwc.relay, event_id: resp.req_id) {
|
||||
Log.debug("HomeModel: got NWC response, removed %s from the postbox [%s]", for: .nwc, resp.req_id.hex(), relay.absoluteString)
|
||||
Log.debug("HomeModel: got NWC response, removed %s from the postbox", for: .nwc, resp.req_id.hex())
|
||||
} else {
|
||||
Log.debug("HomeModel: got NWC response, %s not found in the postbox, nothing to remove [%s]", for: .nwc, resp.req_id.hex(), relay.absoluteString)
|
||||
Log.debug("HomeModel: got NWC response, %s not found in the postbox, nothing to remove", for: .nwc, resp.req_id.hex())
|
||||
}
|
||||
|
||||
damus_state.wallet.handle_nwc_response(response: resp) // This can handle success or error cases
|
||||
@@ -303,7 +298,6 @@ class HomeModel: ContactsDelegate {
|
||||
return
|
||||
}
|
||||
|
||||
print("nwc success: \(resp.response.result.debugDescription) [\(relay)]")
|
||||
WalletConnect.handle_zap_success(state: self.damus_state, resp: resp)
|
||||
}
|
||||
}
|
||||
@@ -382,19 +376,11 @@ class HomeModel: ContactsDelegate {
|
||||
self.deleted_events.insert(ev.id)
|
||||
}
|
||||
|
||||
func handle_contact_event(sub_id: String, relay_id: RelayURL, ev: NostrEvent) {
|
||||
func handle_contact_event(ev: NostrEvent) {
|
||||
process_contact_event(state: self.damus_state, ev: ev)
|
||||
|
||||
if sub_id == init_subid {
|
||||
pool.send(.unsubscribe(init_subid), to: [relay_id])
|
||||
if !done_init {
|
||||
done_init = true
|
||||
send_home_filters(relay_id: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func handle_boost_event(sub_id: String, _ ev: NostrEvent) {
|
||||
func handle_boost_event(_ ev: NostrEvent, context: SubscriptionContext) {
|
||||
var boost_ev_id = ev.last_refid()
|
||||
|
||||
if let inner_ev = ev.get_inner_event(cache: damus_state.events) {
|
||||
@@ -409,7 +395,7 @@ class HomeModel: ContactsDelegate {
|
||||
|
||||
if inner_ev.is_textlike {
|
||||
DispatchQueue.main.async {
|
||||
self.handle_text_event(sub_id: sub_id, ev)
|
||||
self.handle_text_event(ev, context: context)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -457,94 +443,50 @@ class HomeModel: ContactsDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
@MainActor
|
||||
func handle_event(relay_id: RelayURL, conn_event: NostrConnectionEvent) {
|
||||
switch conn_event {
|
||||
case .ws_connection_event(let ev):
|
||||
switch ev {
|
||||
case .connected:
|
||||
if !done_init {
|
||||
self.loading = true
|
||||
send_initial_filters(relay_id: relay_id)
|
||||
} else {
|
||||
//remove_bootstrap_nodes(damus_state)
|
||||
send_home_filters(relay_id: relay_id)
|
||||
/// Send the initial filters, just our contact list and relay list mostly
|
||||
func send_initial_filters() {
|
||||
Task {
|
||||
let filter = NostrFilter(kinds: [.contacts], limit: 1, authors: [damus_state.pubkey])
|
||||
for await item in damus_state.nostrNetwork.reader.subscribe(filters: [filter]) {
|
||||
switch item {
|
||||
case .event(let borrow):
|
||||
var event: NostrEvent? = nil
|
||||
try? borrow { ev in
|
||||
event = ev.toOwned()
|
||||
}
|
||||
guard let event else { return }
|
||||
await process_event(ev: event, context: .initialContactList)
|
||||
continue
|
||||
case .eose:
|
||||
if !done_init {
|
||||
done_init = true
|
||||
send_home_filters()
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
// connect to nwc relays when connected
|
||||
if let nwc_str = damus_state.settings.nostr_wallet_connect,
|
||||
let r = pool.get_relay(relay_id),
|
||||
r.descriptor.variant == .nwc,
|
||||
let nwc = WalletConnectURL(str: nwc_str),
|
||||
nwc.relay == relay_id
|
||||
{
|
||||
WalletConnect.subscribe(url: nwc, pool: pool)
|
||||
}
|
||||
case .error(let merr):
|
||||
let desc = String(describing: merr)
|
||||
if desc.contains("Software caused connection abort") {
|
||||
pool.reconnect(to: [relay_id])
|
||||
}
|
||||
case .disconnected:
|
||||
pool.reconnect(to: [relay_id])
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
update_signal_from_pool(signal: self.signal, pool: damus_state.nostrNetwork.pool)
|
||||
case .nostr_event(let ev):
|
||||
switch ev {
|
||||
case .event(let sub_id, let ev):
|
||||
// globally handle likes
|
||||
/*
|
||||
let always_process = sub_id == notifications_subid || sub_id == contacts_subid || sub_id == home_subid || sub_id == dms_subid || sub_id == init_subid || ev.known_kind == .like || ev.known_kind == .boost || ev.known_kind == .zap || ev.known_kind == .contacts || ev.known_kind == .metadata
|
||||
if !always_process {
|
||||
// TODO: other views like threads might have their own sub ids, so ignore those events... or should we?
|
||||
return
|
||||
}
|
||||
*/
|
||||
|
||||
self.process_event(sub_id: sub_id, relay_id: relay_id, ev: ev)
|
||||
case .notice(let msg):
|
||||
print(msg)
|
||||
|
||||
case .eose(let sub_id):
|
||||
guard let txn = NdbTxn(ndb: damus_state.ndb) else {
|
||||
return
|
||||
}
|
||||
|
||||
if sub_id == dms_subid {
|
||||
var dms = dms.dms.flatMap { $0.events }
|
||||
dms.append(contentsOf: incoming_dms)
|
||||
load_profiles(context: "dms", profiles_subid: profiles_subid, relay_id: relay_id, load: .from_events(dms), damus_state: damus_state, txn: txn)
|
||||
} else if sub_id == notifications_subid {
|
||||
load_profiles(context: "notifications", profiles_subid: profiles_subid, relay_id: relay_id, load: .from_keys(notifications.uniq_pubkeys()), damus_state: damus_state, txn: txn)
|
||||
} else if sub_id == home_subid {
|
||||
load_profiles(context: "home", profiles_subid: profiles_subid, relay_id: relay_id, load: .from_events(events.events), damus_state: damus_state, txn: txn)
|
||||
}
|
||||
|
||||
self.loading = false
|
||||
break
|
||||
|
||||
case .ok:
|
||||
break
|
||||
case .auth:
|
||||
break
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Send the initial filters, just our contact list mostly
|
||||
func send_initial_filters(relay_id: RelayURL) {
|
||||
let filter = NostrFilter(kinds: [.contacts], limit: 1, authors: [damus_state.pubkey])
|
||||
let subscription = NostrSubscribe(filters: [filter], sub_id: init_subid)
|
||||
pool.send(.subscribe(subscription), to: [relay_id])
|
||||
|
||||
Task {
|
||||
let relayListFilter = NostrFilter(kinds: [.relay_list], limit: 1, authors: [damus_state.pubkey])
|
||||
for await item in damus_state.nostrNetwork.reader.subscribe(filters: [relayListFilter]) {
|
||||
switch item {
|
||||
case .event(let borrow):
|
||||
var event: NostrEvent? = nil
|
||||
try? borrow { ev in
|
||||
event = ev.toOwned()
|
||||
}
|
||||
guard let event else { return }
|
||||
await process_event(ev: event, context: .initialRelayList)
|
||||
case .eose: break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// After initial connection or reconnect, send subscription filters for the home timeline, DMs, and notifications
|
||||
func send_home_filters(relay_id: RelayURL?) {
|
||||
func send_home_filters() {
|
||||
// TODO: since times should be based on events from a specific relay
|
||||
// perhaps we could mark this in the relay pool somehow
|
||||
|
||||
@@ -589,38 +531,99 @@ class HomeModel: ContactsDelegate {
|
||||
let contacts_filter_chunks = contacts_filter.chunked(on: .authors, into: MAX_CONTACTS_ON_FILTER)
|
||||
var contacts_filters = contacts_filter_chunks + [our_contacts_filter, our_blocklist_filter, our_old_blocklist_filter]
|
||||
var dms_filters = [dms_filter, our_dms_filter]
|
||||
let last_of_kind = get_last_of_kind(relay_id: relay_id)
|
||||
|
||||
contacts_filters = update_filters_with_since(last_of_kind: last_of_kind, filters: contacts_filters)
|
||||
notifications_filters = update_filters_with_since(last_of_kind: last_of_kind, filters: notifications_filters)
|
||||
dms_filters = update_filters_with_since(last_of_kind: last_of_kind, filters: dms_filters)
|
||||
|
||||
//print_filters(relay_id: relay_id, filters: [home_filters, contacts_filters, notifications_filters, dms_filters])
|
||||
|
||||
subscribe_to_home_filters(relay_id: relay_id)
|
||||
subscribe_to_home_filters()
|
||||
|
||||
let relay_ids = relay_id.map { [$0] }
|
||||
|
||||
pool.send(.subscribe(.init(filters: contacts_filters, sub_id: contacts_subid)), to: relay_ids)
|
||||
pool.send(.subscribe(.init(filters: notifications_filters, sub_id: notifications_subid)), to: relay_ids)
|
||||
pool.send(.subscribe(.init(filters: dms_filters, sub_id: dms_subid)), to: relay_ids)
|
||||
|
||||
self.contactsHandlerTask?.cancel()
|
||||
self.contactsHandlerTask = Task {
|
||||
for await item in damus_state.nostrNetwork.reader.subscribe(filters: contacts_filters) {
|
||||
switch item {
|
||||
case .event(let borrow):
|
||||
var event: NostrEvent? = nil
|
||||
try? borrow { ev in
|
||||
var event = ev.toOwned()
|
||||
}
|
||||
guard let event else { return }
|
||||
await self.process_event(ev: event, context: .contacts)
|
||||
case .eose: continue
|
||||
}
|
||||
}
|
||||
}
|
||||
self.notificationsHandlerTask?.cancel()
|
||||
self.notificationsHandlerTask = Task {
|
||||
for await item in damus_state.nostrNetwork.reader.subscribe(filters: notifications_filters) {
|
||||
switch item {
|
||||
case .event(let borrow):
|
||||
var event: NostrEvent? = nil
|
||||
try? borrow { ev in
|
||||
event = ev.toOwned()
|
||||
}
|
||||
guard let event else { return }
|
||||
await self.process_event(ev: event, context: .notifications)
|
||||
case .eose:
|
||||
guard let txn = NdbTxn(ndb: damus_state.ndb) else { return }
|
||||
load_profiles(context: "notifications", load: .from_keys(notifications.uniq_pubkeys()), damus_state: damus_state, txn: txn)
|
||||
}
|
||||
}
|
||||
}
|
||||
self.dmsHandlerTask?.cancel()
|
||||
self.dmsHandlerTask = Task {
|
||||
for await item in damus_state.nostrNetwork.reader.subscribe(filters: dms_filters) {
|
||||
switch item {
|
||||
case .event(let borrow):
|
||||
var event: NostrEvent? = nil
|
||||
try? borrow { ev in
|
||||
event = ev.toOwned()
|
||||
}
|
||||
guard let event else { return }
|
||||
await self.process_event(ev: event, context: .dms)
|
||||
case .eose:
|
||||
guard let txn = NdbTxn(ndb: damus_state.ndb) else { return }
|
||||
var dms = dms.dms.flatMap { $0.events }
|
||||
dms.append(contentsOf: incoming_dms)
|
||||
load_profiles(context: "dms", load: .from_events(dms), damus_state: damus_state, txn: txn)
|
||||
}
|
||||
}
|
||||
}
|
||||
self.nwcHandlerTask?.cancel()
|
||||
self.nwcHandlerTask = Task {
|
||||
if let nwc_str = damus_state.settings.nostr_wallet_connect,
|
||||
let nwc = WalletConnectURL(str: nwc_str)
|
||||
{
|
||||
var filter = NostrFilter(kinds: [.nwc_response])
|
||||
filter.authors = [nwc.pubkey]
|
||||
filter.limit = 0
|
||||
for await item in damus_state.nostrNetwork.reader.subscribe(filters: [filter], to: [nwc.relay]) {
|
||||
switch item {
|
||||
case .event(let borrow):
|
||||
var event: NostrEvent? = nil
|
||||
try? borrow { ev in
|
||||
event = ev.toOwned()
|
||||
}
|
||||
guard let event else { return }
|
||||
await self.process_event(ev: event, context: .nwc)
|
||||
case .eose: continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func get_last_of_kind(relay_id: RelayURL?) -> [UInt32: NostrEvent] {
|
||||
return relay_id.flatMap { last_event_of_kind[$0] } ?? [:]
|
||||
}
|
||||
|
||||
func unsubscribe_to_home_filters() {
|
||||
pool.send(.unsubscribe(home_subid))
|
||||
}
|
||||
|
||||
func get_friends() -> [Pubkey] {
|
||||
var friends = damus_state.contacts.get_friend_list()
|
||||
friends.insert(damus_state.pubkey)
|
||||
return Array(friends)
|
||||
}
|
||||
|
||||
func subscribe_to_home_filters(friends fs: [Pubkey]? = nil, relay_id: RelayURL? = nil) {
|
||||
func subscribe_to_home_filters(friends fs: [Pubkey]? = nil) {
|
||||
// TODO: separate likes?
|
||||
var home_filter_kinds: [NostrKind] = [
|
||||
.text, .longform, .boost, .highlight
|
||||
@@ -649,11 +652,34 @@ class HomeModel: ContactsDelegate {
|
||||
home_filters.append(hashtag_filter)
|
||||
}
|
||||
|
||||
let relay_ids = relay_id.map { [$0] }
|
||||
home_filters = update_filters_with_since(last_of_kind: get_last_of_kind(relay_id: relay_id), filters: home_filters)
|
||||
let sub = NostrSubscribe(filters: home_filters, sub_id: home_subid)
|
||||
|
||||
pool.send(.subscribe(sub), to: relay_ids)
|
||||
self.homeHandlerTask?.cancel()
|
||||
self.homeHandlerTask = Task {
|
||||
for await item in damus_state.nostrNetwork.reader.subscribe(filters: home_filters) {
|
||||
switch item {
|
||||
case .event(let borrow):
|
||||
var event: NostrEvent? = nil
|
||||
try? borrow { ev in
|
||||
event = ev.toOwned()
|
||||
}
|
||||
guard let event else { return }
|
||||
await self.process_event(ev: event, context: .home)
|
||||
case .eose:
|
||||
guard let txn = NdbTxn(ndb: damus_state.ndb) else { return }
|
||||
load_profiles(context: "home", load: .from_events(events.events), damus_state: damus_state, txn: txn)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Adapter pattern to make migration easier
|
||||
enum SubscriptionContext {
|
||||
case initialContactList
|
||||
case initialRelayList
|
||||
case home
|
||||
case notifications
|
||||
case dms
|
||||
case contacts
|
||||
case nwc
|
||||
}
|
||||
|
||||
func handle_mute_list_event(_ ev: NostrEvent) {
|
||||
@@ -746,7 +772,7 @@ class HomeModel: ContactsDelegate {
|
||||
}
|
||||
|
||||
|
||||
func handle_text_event(sub_id: String, _ ev: NostrEvent) {
|
||||
func handle_text_event(_ ev: NostrEvent, context: SubscriptionContext) {
|
||||
guard should_show_event(state: damus_state, ev: ev) else {
|
||||
return
|
||||
}
|
||||
@@ -770,10 +796,13 @@ class HomeModel: ContactsDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
if sub_id == home_subid {
|
||||
switch context {
|
||||
case .home:
|
||||
insert_home_event(ev)
|
||||
} else if sub_id == notifications_subid {
|
||||
case .notifications:
|
||||
handle_notification(ev: ev)
|
||||
case .dms, .contacts, .initialRelayList, .initialContactList, .nwc:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1191,3 +1220,24 @@ func create_in_app_event_zap_notification(profiles: Profiles, zap: Zap, locale:
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Extension to bridge NIP-65 relay list structs with app-native objects
|
||||
// TODO: Do we need this??
|
||||
|
||||
//extension NIP65.RelayList {
|
||||
// static func fromLegacyContactList(_ contactList: NdbNote) throws(BridgeError) -> Self {
|
||||
// guard let relayListInfo = decode_json_relays(contactList.content) else { throw .couldNotDecodeRelayListInfo }
|
||||
// let relayItems = relayListInfo.map({ url, rwConfiguration in
|
||||
// return RelayItem(url: url, rwConfiguration: rwConfiguration.toNIP65RWConfiguration() ?? .readWrite)
|
||||
// })
|
||||
// return NIP65.RelayList(relays: relayItems)
|
||||
// }
|
||||
//
|
||||
// static func fromLegacyContactList(_ contactList: NdbNote?) throws(BridgeError) -> Self? {
|
||||
// guard let contactList = contactList else { return nil }
|
||||
// return try fromLegacyContactList(contactList)
|
||||
// }
|
||||
//
|
||||
// enum BridgeError: Error {
|
||||
// case couldNotDecodeRelayListInfo
|
||||
// }
|
||||
//}
|
||||
|
||||
@@ -123,8 +123,8 @@ extension WalletConnect {
|
||||
|
||||
let delay = 0.0 // We don't need a delay when fetching a transaction list or balance
|
||||
|
||||
WalletConnect.request_transaction_list(url: nwc, pool: damus_state.nostrNetwork.pool, post: damus_state.nostrNetwork.postbox, delay: delay, on_flush: flusher)
|
||||
WalletConnect.request_balance_information(url: nwc, pool: damus_state.nostrNetwork.pool, post: damus_state.nostrNetwork.postbox, delay: delay, on_flush: flusher)
|
||||
damus_state.nostrNetwork.requestTransactionList(url: nwc, delay: delay, on_flush: flusher)
|
||||
damus_state.nostrNetwork.requestBalanceInformation(url: nwc, delay: delay, on_flush: flusher)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -153,22 +153,6 @@ extension WalletConnect {
|
||||
}
|
||||
}
|
||||
|
||||
/// Send a donation zap to the Damus team
|
||||
static func send_donation_zap(pool: RelayPool, postbox: PostBox, nwc: WalletConnectURL, percent: Int, base_msats: Int64) async {
|
||||
let percent_f = Double(percent) / 100.0
|
||||
let donations_msats = Int64(percent_f * Double(base_msats))
|
||||
|
||||
let payreq = LNUrlPayRequest(allowsNostr: true, commentAllowed: nil, nostrPubkey: "", callback: "https://sendsats.lol/@damus")
|
||||
guard let invoice = await fetch_zap_invoice(payreq, zapreq: nil, msats: donations_msats, zap_type: .non_zap, comment: nil) else {
|
||||
// we failed... oh well. no donation for us.
|
||||
print("damus-donation failed to fetch invoice")
|
||||
return
|
||||
}
|
||||
|
||||
print("damus-donation donating...")
|
||||
WalletConnect.pay(url: nwc, pool: pool, post: postbox, invoice: invoice, zap_request: nil, delay: nil)
|
||||
}
|
||||
|
||||
/// Handles a received Nostr Wallet Connect error
|
||||
static func handle_error(zapcache: Zaps, evcache: EventCache, resp: WalletConnect.FullWalletResponse) {
|
||||
// find a pending zap with the nwc request id associated with this response and remove it
|
||||
|
||||
@@ -185,7 +185,7 @@ struct SendPaymentView: View {
|
||||
sendState = .processing
|
||||
|
||||
// Process payment
|
||||
guard let payRequestEv = WalletConnect.pay(url: nwc, pool: damus_state.nostrNetwork.pool, post: damus_state.nostrNetwork.postbox, invoice: invoice.string, zap_request: nil, delay: nil) else {
|
||||
guard let payRequestEv = damus_state.nostrNetwork.nwcPay(url: nwc, post: damus_state.nostrNetwork.postbox, invoice: invoice.string, zap_request: nil) else {
|
||||
sendState = .failed(error: .init(
|
||||
user_visible_description: NSLocalizedString("The payment request could not be made to your wallet provider.", comment: "A human-readable error message"),
|
||||
tip: NSLocalizedString("Check if your wallet looks configured correctly and try again. If the error persists, please contact support.", comment: "A human-readable tip for an error when a payment request cannot be made to a wallet."),
|
||||
|
||||
@@ -11,7 +11,7 @@ class ZapsModel: ObservableObject {
|
||||
let state: DamusState
|
||||
let target: ZapTarget
|
||||
|
||||
let zaps_subid = UUID().description
|
||||
var zapCommsListener: Task<Void, Never>? = nil
|
||||
let profiles_subid = UUID().description
|
||||
|
||||
init(state: DamusState, target: ZapTarget) {
|
||||
@@ -31,46 +31,40 @@ class ZapsModel: ObservableObject {
|
||||
case .note(let note_target):
|
||||
filter.referenced_ids = [note_target.note_id]
|
||||
}
|
||||
state.nostrNetwork.pool.subscribe(sub_id: zaps_subid, filters: [filter], handler: handle_event)
|
||||
zapCommsListener?.cancel()
|
||||
zapCommsListener = Task {
|
||||
for await item in state.nostrNetwork.reader.subscribe(filters: [filter]) {
|
||||
switch item {
|
||||
case .event(let borrow):
|
||||
var event: NostrEvent? = nil
|
||||
try? borrow { ev in
|
||||
event = ev.toOwned()
|
||||
}
|
||||
guard let event else { return }
|
||||
await self.handle_event(ev: event)
|
||||
case .eose:
|
||||
let events = state.events.lookup_zaps(target: target).map { $0.request.ev }
|
||||
guard let txn = NdbTxn(ndb: state.ndb) else { return }
|
||||
load_profiles(context: "zaps_model", load: .from_events(events), damus_state: state, txn: txn)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func unsubscribe() {
|
||||
state.nostrNetwork.pool.unsubscribe(sub_id: zaps_subid)
|
||||
zapCommsListener?.cancel()
|
||||
zapCommsListener = nil
|
||||
}
|
||||
|
||||
|
||||
@MainActor
|
||||
func handle_event(relay_id: RelayURL, conn_ev: NostrConnectionEvent) {
|
||||
guard case .nostr_event(let resp) = conn_ev else {
|
||||
func handle_event(ev: NostrEvent) {
|
||||
guard ev.kind == 9735,
|
||||
let zapper = state.profiles.lookup_zapper(pubkey: target.pubkey),
|
||||
let zap = Zap.from_zap_event(zap_ev: ev, zapper: zapper, our_privkey: state.keypair.privkey)
|
||||
else {
|
||||
return
|
||||
}
|
||||
|
||||
guard resp.subid == zaps_subid else {
|
||||
return
|
||||
}
|
||||
|
||||
switch resp {
|
||||
case .ok:
|
||||
break
|
||||
case .notice:
|
||||
break
|
||||
case .eose:
|
||||
let events = state.events.lookup_zaps(target: target).map { $0.request.ev }
|
||||
guard let txn = NdbTxn(ndb: state.ndb) else { return }
|
||||
load_profiles(context: "zaps_model", profiles_subid: profiles_subid, relay_id: relay_id, load: .from_events(events), damus_state: state, txn: txn)
|
||||
case .event(_, let ev):
|
||||
guard ev.kind == 9735,
|
||||
let zapper = state.profiles.lookup_zapper(pubkey: target.pubkey),
|
||||
let zap = Zap.from_zap_event(zap_ev: ev, zapper: zapper, our_privkey: state.keypair.privkey)
|
||||
else {
|
||||
return
|
||||
}
|
||||
|
||||
self.state.add_zap(zap: .zap(zap))
|
||||
case .auth:
|
||||
break
|
||||
}
|
||||
|
||||
|
||||
|
||||
self.state.add_zap(zap: .zap(zap))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,7 +175,9 @@ struct CustomizeZapView: View {
|
||||
} else {
|
||||
Button(action: {
|
||||
let amount = model.custom_amount_sats
|
||||
send_zap(damus_state: state, target: target, lnurl: lnurl, is_custom: true, comment: model.comment, amount_sats: amount, zap_type: model.zap_type)
|
||||
Task {
|
||||
await send_zap(damus_state: state, target: target, lnurl: lnurl, is_custom: true, comment: model.comment, amount_sats: amount, zap_type: model.zap_type)
|
||||
}
|
||||
model.zapping = true
|
||||
}) {
|
||||
HStack {
|
||||
|
||||
@@ -72,7 +72,7 @@ struct NoteZapButton: View {
|
||||
|
||||
func tap() {
|
||||
guard let our_zap else {
|
||||
send_zap(damus_state: damus_state, target: target, lnurl: lnurl, is_custom: false, comment: nil, amount_sats: nil, zap_type: damus_state.settings.default_zap_type)
|
||||
Task { await send_zap(damus_state: damus_state, target: target, lnurl: lnurl, is_custom: false, comment: nil, amount_sats: nil, zap_type: damus_state.settings.default_zap_type) }
|
||||
return
|
||||
}
|
||||
|
||||
@@ -173,13 +173,13 @@ func initial_pending_zap_state(settings: UserSettingsStore) -> PendingZapState {
|
||||
return .external(ExtPendingZapState(state: .fetching_invoice))
|
||||
}
|
||||
|
||||
func send_zap(damus_state: DamusState, target: ZapTarget, lnurl: String, is_custom: Bool, comment: String?, amount_sats: Int?, zap_type: ZapType) {
|
||||
func send_zap(damus_state: DamusState, target: ZapTarget, lnurl: String, is_custom: Bool, comment: String?, amount_sats: Int?, zap_type: ZapType) async {
|
||||
guard let keypair = damus_state.keypair.to_full() else {
|
||||
return
|
||||
}
|
||||
|
||||
// Only take the first 10 because reasons
|
||||
let relays = Array(damus_state.nostrNetwork.pool.our_descriptors.prefix(10))
|
||||
let relays = Array(damus_state.nostrNetwork.ourRelayDescriptors.prefix(10))
|
||||
let content = comment ?? ""
|
||||
|
||||
guard let mzapreq = make_zap_request_event(keypair: keypair, content: content, relays: relays, target: target, zap_type: zap_type) else {
|
||||
@@ -232,7 +232,7 @@ func send_zap(damus_state: DamusState, target: ZapTarget, lnurl: String, is_cust
|
||||
flusher = .once({ pe in
|
||||
// send donation zap when the pending zap is flushed, this allows user to cancel and not send a donation
|
||||
Task { @MainActor in
|
||||
await WalletConnect.send_donation_zap(pool: damus_state.nostrNetwork.pool, postbox: damus_state.nostrNetwork.postbox, nwc: nwc_state.url, percent: damus_state.settings.donation_percent, base_msats: amount_msat)
|
||||
await damus_state.nostrNetwork.send_donation_zap(nwc: nwc_state.url, percent: damus_state.settings.donation_percent, base_msats: amount_msat)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -240,7 +240,7 @@ func send_zap(damus_state: DamusState, target: ZapTarget, lnurl: String, is_cust
|
||||
// we don't have a delay on one-tap nozaps (since this will be from customize zap view)
|
||||
let delay = damus_state.settings.nozaps ? nil : 5.0
|
||||
|
||||
let nwc_req = WalletConnect.pay(url: nwc_state.url, pool: damus_state.nostrNetwork.pool, post: damus_state.nostrNetwork.postbox, invoice: inv, zap_request: zapreq, delay: delay, on_flush: flusher)
|
||||
let nwc_req = damus_state.nostrNetwork.nwcPay(url: nwc_state.url, post: damus_state.nostrNetwork.postbox, invoice: inv, delay: delay, on_flush: flusher)
|
||||
|
||||
guard let nwc_req, case .nwc(let pzap_state) = pending_zap_state else {
|
||||
print("nwc: failed to send nwc request for zapreq \(reqid.reqid)")
|
||||
|
||||
Reference in New Issue
Block a user