15
damus/Models/ChatroomMetadata.swift
Normal file
15
damus/Models/ChatroomMetadata.swift
Normal file
@@ -0,0 +1,15 @@
|
||||
//
|
||||
// ChatroomMetadata.swift
|
||||
// damus
|
||||
//
|
||||
// Created by William Casarin on 2022-09-07.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
|
||||
struct ChatroomMetadata: Decodable {
|
||||
let name: String?
|
||||
let about: String?
|
||||
let picture: String?
|
||||
}
|
||||
@@ -34,6 +34,8 @@ class HomeModel: ObservableObject {
|
||||
var damus_state: DamusState
|
||||
|
||||
var has_event: [String: Set<String>] = [:]
|
||||
var deleted_events: Set<String> = Set()
|
||||
var channels: [String: NostrEvent] = [:]
|
||||
var last_event_of_kind: [String: [Int: NostrEvent]] = [:]
|
||||
var done_init: Bool = false
|
||||
|
||||
@@ -100,9 +102,32 @@ class HomeModel: ObservableObject {
|
||||
case .dm:
|
||||
handle_dm(ev)
|
||||
case .delete:
|
||||
break
|
||||
handle_delete_event(ev)
|
||||
case .channel_create:
|
||||
handle_channel_create(ev)
|
||||
case .channel_meta:
|
||||
handle_channel_meta(ev)
|
||||
}
|
||||
}
|
||||
|
||||
func handle_channel_create(_ ev: NostrEvent) {
|
||||
guard ev.is_valid else {
|
||||
return
|
||||
}
|
||||
|
||||
self.channels[ev.id] = ev
|
||||
}
|
||||
|
||||
func handle_channel_meta(_ ev: NostrEvent) {
|
||||
}
|
||||
|
||||
func handle_delete_event(_ ev: NostrEvent) {
|
||||
guard ev.is_valid else {
|
||||
return
|
||||
}
|
||||
|
||||
self.deleted_events.insert(ev.id)
|
||||
}
|
||||
|
||||
func handle_contact_event(sub_id: String, relay_id: String, ev: NostrEvent) {
|
||||
process_contact_event(pool: damus_state.pool, contacts: damus_state.contacts, pubkey: damus_state.pubkey, ev: ev)
|
||||
@@ -119,11 +144,14 @@ class HomeModel: ObservableObject {
|
||||
func handle_boost_event(sub_id: String, _ ev: NostrEvent) {
|
||||
var boost_ev_id = ev.last_refid()?.ref_id
|
||||
|
||||
// CHECK SIGS ON THESE
|
||||
if let inner_ev = ev.inner_event {
|
||||
boost_ev_id = inner_ev.id
|
||||
|
||||
guard inner_ev.is_valid else {
|
||||
return
|
||||
}
|
||||
|
||||
if inner_ev.kind == 1 {
|
||||
if inner_ev.is_textlike {
|
||||
handle_text_event(sub_id: sub_id, ev)
|
||||
}
|
||||
}
|
||||
@@ -259,6 +287,7 @@ class HomeModel: ObservableObject {
|
||||
// TODO: separate likes?
|
||||
var home_filter = NostrFilter.filter_kinds([
|
||||
NostrKind.text.rawValue,
|
||||
NostrKind.chat.rawValue,
|
||||
NostrKind.like.rawValue,
|
||||
NostrKind.boost.rawValue,
|
||||
])
|
||||
@@ -268,6 +297,7 @@ class HomeModel: ObservableObject {
|
||||
|
||||
var notifications_filter = NostrFilter.filter_kinds([
|
||||
NostrKind.text.rawValue,
|
||||
NostrKind.chat.rawValue,
|
||||
NostrKind.like.rawValue,
|
||||
NostrKind.boost.rawValue,
|
||||
])
|
||||
|
||||
@@ -39,7 +39,8 @@ class ProfileModel: ObservableObject {
|
||||
|
||||
func subscribe() {
|
||||
var text_filter = NostrFilter.filter_kinds([
|
||||
NostrKind.text.rawValue
|
||||
NostrKind.text.rawValue,
|
||||
NostrKind.chat.rawValue,
|
||||
])
|
||||
|
||||
var profile_filter = NostrFilter.filter_kinds([
|
||||
@@ -75,7 +76,7 @@ class ProfileModel: ObservableObject {
|
||||
if seen_event.contains(ev.id) {
|
||||
return
|
||||
}
|
||||
if ev.known_kind == .text || ev.known_kind == .boost {
|
||||
if ev.is_textlike || ev.known_kind == .boost {
|
||||
let _ = insert_uniq_sorted_event(events: &self.events, new_ev: ev, cmp: { $0.created_at > $1.created_at})
|
||||
} else if ev.known_kind == .contacts {
|
||||
handle_profile_contact_event(ev)
|
||||
|
||||
@@ -40,16 +40,6 @@ class SearchHomeModel: ObservableObject {
|
||||
damus_state.pool.unsubscribe(sub_id: base_subid)
|
||||
}
|
||||
|
||||
func load_profiles(relay_id: String) {
|
||||
var filter = NostrFilter.filter_profiles
|
||||
let authors = find_profiles_to_fetch(profiles: damus_state.profiles, events: events)
|
||||
filter.authors = authors
|
||||
|
||||
if !authors.isEmpty {
|
||||
damus_state.pool.subscribe(sub_id: profiles_subid, filters: [filter], handler: handle_event)
|
||||
}
|
||||
}
|
||||
|
||||
func handle_event(relay_id: String, conn_ev: NostrConnectionEvent) {
|
||||
switch conn_ev {
|
||||
case .ws_event:
|
||||
@@ -68,8 +58,6 @@ class SearchHomeModel: ObservableObject {
|
||||
let _ = insert_uniq_sorted_event(events: &events, new_ev: ev) {
|
||||
$0.created_at > $1.created_at
|
||||
}
|
||||
} else if ev.known_kind == .metadata {
|
||||
process_metadata_event(image_cache: damus_state.image_cache, profiles: damus_state.profiles, ev: ev)
|
||||
}
|
||||
case .notice(let msg):
|
||||
print("search home notice: \(msg)")
|
||||
@@ -77,9 +65,7 @@ class SearchHomeModel: ObservableObject {
|
||||
loading = false
|
||||
|
||||
if sub_id == self.base_subid {
|
||||
load_profiles(relay_id: relay_id)
|
||||
} else if sub_id == self.profiles_subid {
|
||||
damus_state.pool.unsubscribe(sub_id: self.profiles_subid)
|
||||
load_profiles(profiles_subid: profiles_subid, relay_id: relay_id, events: events, damus_state: damus_state)
|
||||
}
|
||||
|
||||
break
|
||||
@@ -115,3 +101,33 @@ func find_profiles_to_fetch(profiles: Profiles, events: [NostrEvent]) -> [String
|
||||
|
||||
return Array(pubkeys)
|
||||
}
|
||||
|
||||
func load_profiles(profiles_subid: String, relay_id: String, events: [NostrEvent], damus_state: DamusState) {
|
||||
var filter = NostrFilter.filter_profiles
|
||||
let authors = find_profiles_to_fetch(profiles: damus_state.profiles, events: events)
|
||||
filter.authors = authors
|
||||
|
||||
if !authors.isEmpty {
|
||||
print("loading \(authors.count) profiles from \(relay_id)")
|
||||
damus_state.pool.subscribe_to(sub_id: profiles_subid, filters: [filter], to: [relay_id]) { sub_id, conn_ev in
|
||||
let (sid, done) = handle_subid_event(pool: damus_state.pool, relay_id: relay_id, ev: conn_ev) { sub_id, ev in
|
||||
guard sub_id == profiles_subid else {
|
||||
return
|
||||
}
|
||||
|
||||
if ev.known_kind == .metadata {
|
||||
process_metadata_event(image_cache: damus_state.image_cache, profiles: damus_state.profiles, ev: ev)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
guard done && sid == profiles_subid else {
|
||||
return
|
||||
}
|
||||
|
||||
print("done loading \(authors.count) profiles from \(relay_id)")
|
||||
damus_state.pool.unsubscribe(sub_id: profiles_subid, to: [relay_id])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,8 @@ import Foundation
|
||||
class SearchModel: ObservableObject {
|
||||
@Published var events: [NostrEvent] = []
|
||||
@Published var loading: Bool = false
|
||||
@Published var channel_name: String? = nil
|
||||
|
||||
let pool: RelayPool
|
||||
var search: NostrFilter
|
||||
let sub_id = UUID().description
|
||||
@@ -50,10 +52,24 @@ class SearchModel: ObservableObject {
|
||||
}
|
||||
}
|
||||
|
||||
func handle_channel_create(_ ev: NostrEvent) {
|
||||
self.channel_name = ev.content
|
||||
return
|
||||
}
|
||||
|
||||
func handle_channel_meta(_ ev: NostrEvent) {
|
||||
self.channel_name = ev.content
|
||||
return
|
||||
}
|
||||
|
||||
func handle_event(relay_id: String, ev: NostrConnectionEvent) {
|
||||
let done = handle_subid_event(pool: pool, sub_id: sub_id, relay_id: relay_id, ev: ev) { ev in
|
||||
if ev.known_kind == .text && ev.should_show_event {
|
||||
let (_, done) = handle_subid_event(pool: pool, relay_id: relay_id, ev: ev) { sub_id, ev in
|
||||
if ev.is_textlike && ev.should_show_event {
|
||||
self.add_event(ev)
|
||||
} else if ev.known_kind == .channel_create {
|
||||
handle_channel_create(ev)
|
||||
} else if ev.known_kind == .channel_meta {
|
||||
handle_channel_meta(ev)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,29 +95,26 @@ func event_matches_filter(_ ev: NostrEvent, filter: NostrFilter) -> Bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func handle_subid_event(pool: RelayPool, sub_id: String, relay_id: String, ev: NostrConnectionEvent, handle: (NostrEvent) -> ()) -> Bool {
|
||||
func handle_subid_event(pool: RelayPool, relay_id: String, ev: NostrConnectionEvent, handle: (String, NostrEvent) -> ()) -> (String?, Bool) {
|
||||
switch ev {
|
||||
case .ws_event:
|
||||
break
|
||||
return (nil, false)
|
||||
|
||||
case .nostr_event(let res):
|
||||
switch res {
|
||||
case .event(let ev_subid, let ev):
|
||||
if ev_subid == sub_id {
|
||||
handle(ev)
|
||||
}
|
||||
break
|
||||
handle(ev_subid, ev)
|
||||
return (ev_subid, false)
|
||||
|
||||
case .notice(let note):
|
||||
if note.contains("Too many subscription filters") {
|
||||
// TODO: resend filters?
|
||||
pool.reconnect(to: [relay_id])
|
||||
}
|
||||
break
|
||||
return (nil, false)
|
||||
|
||||
case .eose:
|
||||
return true
|
||||
case .eose(let subid):
|
||||
return (subid, true)
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -30,13 +30,13 @@ enum InitialEvent {
|
||||
|
||||
/// manages the lifetime of a thread
|
||||
class ThreadModel: ObservableObject {
|
||||
let privkey: String?
|
||||
let kind: Int
|
||||
|
||||
@Published var initial_event: InitialEvent
|
||||
@Published var events: [NostrEvent] = []
|
||||
@Published var event_map: [String: Int] = [:]
|
||||
@Published var loading: Bool = false
|
||||
|
||||
|
||||
var replies: ReplyMap = ReplyMap()
|
||||
|
||||
var event: NostrEvent? {
|
||||
@@ -53,33 +53,32 @@ class ThreadModel: ObservableObject {
|
||||
}
|
||||
}
|
||||
|
||||
let pool: RelayPool
|
||||
var sub_id = UUID().description
|
||||
let damus_state: DamusState
|
||||
|
||||
let profiles_subid = UUID().description
|
||||
var base_subid = UUID().description
|
||||
|
||||
init(evid: String, pool: RelayPool, privkey: String?) {
|
||||
self.pool = pool
|
||||
init(evid: String, damus_state: DamusState) {
|
||||
self.damus_state = damus_state
|
||||
self.initial_event = .event_id(evid)
|
||||
self.privkey = privkey
|
||||
self.kind = NostrKind.text.rawValue
|
||||
}
|
||||
|
||||
init(event: NostrEvent, pool: RelayPool, privkey: String?) {
|
||||
self.pool = pool
|
||||
init(event: NostrEvent, damus_state: DamusState) {
|
||||
self.damus_state = damus_state
|
||||
self.initial_event = .event(event)
|
||||
self.privkey = privkey
|
||||
self.kind = NostrKind.text.rawValue
|
||||
}
|
||||
|
||||
init(event: NostrEvent, pool: RelayPool, privkey: String?, kind: Int) {
|
||||
self.pool = pool
|
||||
init(event: NostrEvent, damus_state: DamusState, kind: Int) {
|
||||
self.damus_state = damus_state
|
||||
self.initial_event = .event(event)
|
||||
self.privkey = privkey
|
||||
self.kind = kind
|
||||
}
|
||||
|
||||
func unsubscribe() {
|
||||
self.pool.unsubscribe(sub_id: sub_id)
|
||||
print("unsubscribing from thread \(initial_event.id) with sub_id \(sub_id)")
|
||||
self.damus_state.pool.unsubscribe(sub_id: base_subid)
|
||||
print("unsubscribing from thread \(initial_event.id) with sub_id \(base_subid)")
|
||||
}
|
||||
|
||||
func reset_events() {
|
||||
@@ -125,19 +124,23 @@ class ThreadModel: ObservableObject {
|
||||
case .event(let ev):
|
||||
ref_events.referenced_ids = ev.referenced_ids.map { $0.ref_id }
|
||||
ref_events.referenced_ids?.append(ev.id)
|
||||
ref_events.limit = 50
|
||||
events_filter.ids = ref_events.referenced_ids!
|
||||
events_filter.limit = 100
|
||||
events_filter.ids?.append(ev.id)
|
||||
case .event_id(let evid):
|
||||
events_filter.ids = [evid]
|
||||
events_filter.limit = 100
|
||||
ref_events.referenced_ids = [evid]
|
||||
ref_events.limit = 50
|
||||
}
|
||||
|
||||
//likes_filter.ids = ref_events.referenced_ids!
|
||||
|
||||
print("subscribing to thread \(initial_event.id) with sub_id \(sub_id)")
|
||||
pool.register_handler(sub_id: sub_id, handler: handle_event)
|
||||
print("subscribing to thread \(initial_event.id) with sub_id \(base_subid)")
|
||||
damus_state.pool.register_handler(sub_id: base_subid, handler: handle_event)
|
||||
loading = true
|
||||
pool.send(.subscribe(.init(filters: [ref_events, events_filter], sub_id: sub_id)))
|
||||
damus_state.pool.send(.subscribe(.init(filters: [ref_events, events_filter], sub_id: base_subid)))
|
||||
}
|
||||
|
||||
func lookup(_ event_id: String) -> NostrEvent? {
|
||||
@@ -180,18 +183,41 @@ class ThreadModel: ObservableObject {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
func handle_channel_meta(_ ev: NostrEvent) {
|
||||
guard let meta: ChatroomMetadata = decode_json(ev.content) else {
|
||||
return
|
||||
}
|
||||
|
||||
notify(.chatroom_meta, meta)
|
||||
}
|
||||
|
||||
func handle_event(relay_id: String, ev: NostrConnectionEvent) {
|
||||
let done = handle_subid_event(pool: pool, sub_id: sub_id, relay_id: relay_id, ev: ev) { ev in
|
||||
if ev.is_textlike {
|
||||
self.add_event(ev, privkey: self.privkey)
|
||||
|
||||
let (sub_id, done) = handle_subid_event(pool: damus_state.pool, relay_id: relay_id, ev: ev) { sid, ev in
|
||||
guard sid == base_subid || sid == profiles_subid else {
|
||||
return
|
||||
}
|
||||
|
||||
if ev.known_kind == .metadata {
|
||||
process_metadata_event(image_cache: damus_state.image_cache, profiles: damus_state.profiles, ev: ev)
|
||||
} else if ev.is_textlike {
|
||||
self.add_event(ev, privkey: self.damus_state.keypair.privkey)
|
||||
} else if ev.known_kind == .channel_meta || ev.known_kind == .channel_create {
|
||||
handle_channel_meta(ev)
|
||||
}
|
||||
}
|
||||
|
||||
if done {
|
||||
if (events.contains { ev in ev.id == initial_event.id }) {
|
||||
loading = false
|
||||
}
|
||||
guard done && (sub_id == base_subid || sub_id == profiles_subid) else {
|
||||
return
|
||||
}
|
||||
|
||||
if (events.contains { ev in ev.id == initial_event.id }) {
|
||||
loading = false
|
||||
}
|
||||
|
||||
if sub_id == self.base_subid {
|
||||
load_profiles(profiles_subid: self.profiles_subid, relay_id: relay_id, events: events, damus_state: damus_state)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user