Bookmarks Refactor

- Don't do async loading stuff
- Move bookmarkmanager to damus state
- Remove bookmarks update notififcation and switch to observed object
- Switch api to use events explicitly instead of strings
This commit is contained in:
William Casarin
2023-03-03 11:57:18 -05:00
parent 5423704980
commit b58baca227
12 changed files with 76 additions and 60 deletions

View File

@@ -616,7 +616,8 @@ struct ContentView: View {
relay_filters: relay_filters, relay_filters: relay_filters,
relay_metadata: metadatas, relay_metadata: metadatas,
drafts: Drafts(), drafts: Drafts(),
events: EventCache() events: EventCache(),
bookmarks: BookmarksManager(pubkey: pubkey)
) )
home.damus_state = self.damus_state! home.damus_state = self.damus_state!

View File

@@ -7,44 +7,65 @@
import Foundation import Foundation
class BookmarksManager { fileprivate func get_bookmarks_key(pubkey: String) -> String {
pk_setting_key(pubkey, key: "bookmarks")
}
func load_bookmarks(pubkey: String) -> [NostrEvent] {
let key = get_bookmarks_key(pubkey: pubkey)
return (UserDefaults.standard.stringArray(forKey: key) ?? []).compactMap {
event_from_json(dat: $0)
}
}
func save_bookmarks(pubkey: String, current_value: [NostrEvent], value: [NostrEvent]) -> Bool {
let uniq_bookmarks = Array(Set(value))
if uniq_bookmarks != current_value {
let encoded = uniq_bookmarks.map(event_to_json)
UserDefaults.standard.set(encoded, forKey: get_bookmarks_key(pubkey: pubkey))
return true
}
return false
}
class BookmarksManager: ObservableObject {
private let userDefaults = UserDefaults.standard private let userDefaults = UserDefaults.standard
private let pubkey: String private let pubkey: String
init(pubkey: String) { private var _bookmarks: [NostrEvent]
self.pubkey = pubkey var bookmarks: [NostrEvent] {
}
var bookmarks: [String] {
get { get {
return userDefaults.stringArray(forKey: storageKey()) ?? [] return _bookmarks
} }
set { set {
let uniqueBookmarks = Array(Set(newValue)) if save_bookmarks(pubkey: pubkey, current_value: _bookmarks, value: newValue) {
if uniqueBookmarks != bookmarks { self._bookmarks = newValue
userDefaults.set(uniqueBookmarks, forKey: storageKey()) self.objectWillChange.send()
} }
} }
} }
func isBookmarked(_ string: String) -> Bool { init(pubkey: String) {
return bookmarks.contains(string) self._bookmarks = load_bookmarks(pubkey: pubkey)
self.pubkey = pubkey
} }
func updateBookmark(_ string: String) { func isBookmarked(_ ev: NostrEvent) -> Bool {
if isBookmarked(string) { return bookmarks.contains(ev)
bookmarks = bookmarks.filter { $0 != string } }
func updateBookmark(_ ev: NostrEvent) {
if isBookmarked(ev) {
bookmarks = bookmarks.filter { $0 != ev }
} else { } else {
bookmarks.append(string) bookmarks.append(ev)
} }
} }
func clearAll() { func clearAll() {
bookmarks = [] bookmarks = []
} }
private func storageKey() -> String {
pk_setting_key(pubkey, key: "bookmarks")
}
} }

View File

@@ -25,6 +25,7 @@ struct DamusState {
let relay_metadata: RelayMetadatas let relay_metadata: RelayMetadatas
let drafts: Drafts let drafts: Drafts
let events: EventCache let events: EventCache
let bookmarks: BookmarksManager
var pubkey: String { var pubkey: String {
return keypair.pubkey return keypair.pubkey
@@ -35,6 +36,6 @@ struct DamusState {
} }
static var empty: DamusState { static var empty: DamusState {
return DamusState.init(pool: RelayPool(), keypair: Keypair(pubkey: "", privkey: ""), likes: EventCounter(our_pubkey: ""), boosts: EventCounter(our_pubkey: ""), contacts: Contacts(our_pubkey: ""), tips: TipCounter(our_pubkey: ""), profiles: Profiles(), dms: DirectMessagesModel(our_pubkey: ""), previews: PreviewCache(), zaps: Zaps(our_pubkey: ""), lnurls: LNUrls(), settings: UserSettingsStore(), relay_filters: RelayFilters(our_pubkey: ""), relay_metadata: RelayMetadatas(), drafts: Drafts(), events: EventCache()) return DamusState.init(pool: RelayPool(), keypair: Keypair(pubkey: "", privkey: ""), likes: EventCounter(our_pubkey: ""), boosts: EventCounter(our_pubkey: ""), contacts: Contacts(our_pubkey: ""), tips: TipCounter(our_pubkey: ""), profiles: Profiles(), dms: DirectMessagesModel(our_pubkey: ""), previews: PreviewCache(), zaps: Zaps(our_pubkey: ""), lnurls: LNUrls(), settings: UserSettingsStore(), relay_filters: RelayFilters(our_pubkey: ""), relay_metadata: RelayMetadatas(), drafts: Drafts(), events: EventCache(), bookmarks: BookmarksManager(pubkey: ""))
} }
} }

View File

@@ -101,9 +101,6 @@ extension Notification.Name {
static var update_stats: Notification.Name { static var update_stats: Notification.Name {
return Notification.Name("update_stats") return Notification.Name("update_stats")
} }
static var update_bookmarks: Notification.Name {
return Notification.Name("update_bookmarks")
}
static var zapping: Notification.Name { static var zapping: Notification.Name {
return Notification.Name("zapping") return Notification.Name("zapping")
} }

View File

@@ -12,15 +12,20 @@ struct BookmarksView: View {
private let noneFilter: (NostrEvent) -> Bool = { _ in true } private let noneFilter: (NostrEvent) -> Bool = { _ in true }
private let bookmarksTitle = NSLocalizedString("Bookmarks", comment: "Title of bookmarks view") private let bookmarksTitle = NSLocalizedString("Bookmarks", comment: "Title of bookmarks view")
@State private var bookmarkEvents: [NostrEvent] = [] @ObservedObject var manager: BookmarksManager
init(state: DamusState) { init(state: DamusState) {
self.state = state self.state = state
self._manager = ObservedObject(initialValue: state.bookmarks)
}
var bookmarks: [NostrEvent] {
manager.bookmarks
} }
var body: some View { var body: some View {
Group { Group {
if bookmarkEvents.isEmpty { if bookmarks.isEmpty {
VStack { VStack {
Image(systemName: "bookmark") Image(systemName: "bookmark")
.resizable() .resizable()
@@ -28,12 +33,9 @@ struct BookmarksView: View {
.frame(width: 32.0, height: 32.0) .frame(width: 32.0, height: 32.0)
Text(NSLocalizedString("You have no bookmarks yet, add them in the context menu", comment: "Text indicating that there are no bookmarks to be viewed")) Text(NSLocalizedString("You have no bookmarks yet, add them in the context menu", comment: "Text indicating that there are no bookmarks to be viewed"))
} }
.task {
updateBookmarks()
}
} else { } else {
ScrollView { ScrollView {
InnerTimelineView(events: EventHolder(events: bookmarkEvents, incoming: []), damus: state, show_friend_icon: true, filter: noneFilter) InnerTimelineView(events: EventHolder(events: bookmarks, incoming: []), damus: state, show_friend_icon: true, filter: noneFilter)
} }
} }
@@ -41,22 +43,12 @@ struct BookmarksView: View {
.navigationBarTitleDisplayMode(.inline) .navigationBarTitleDisplayMode(.inline)
.navigationTitle(bookmarksTitle) .navigationTitle(bookmarksTitle)
.toolbar { .toolbar {
if !bookmarkEvents.isEmpty { if !bookmarks.isEmpty {
Button(NSLocalizedString("Clear All", comment: "Button for clearing bookmarks data.")) { Button(NSLocalizedString("Clear All", comment: "Button for clearing bookmarks data.")) {
BookmarksManager(pubkey: state.pubkey).clearAll() manager.clearAll()
bookmarkEvents = []
} }
} }
} }
.onReceive(handle_notify(.update_bookmarks)) { _ in
updateBookmarks()
}
}
private func updateBookmarks() {
bookmarkEvents = BookmarksManager(pubkey: state.pubkey).bookmarks.compactMap { bookmark_json in
event_from_json(dat: bookmark_json)
}
} }
} }

View File

@@ -24,7 +24,7 @@ struct ChatroomView: View {
next_ev: ind == count-1 ? nil : thread.events[ind+1], next_ev: ind == count-1 ? nil : thread.events[ind+1],
damus_state: damus damus_state: damus
) )
.event_context_menu(ev, keypair: damus.keypair, target_pubkey: ev.pubkey) .event_context_menu(ev, keypair: damus.keypair, target_pubkey: ev.pubkey, bookmarks: damus.bookmarks)
.onTapGesture { .onTapGesture {
if thread.initial_event.id == ev.id { if thread.initial_event.id == ev.id {
//dismiss() //dismiss()

View File

@@ -19,7 +19,7 @@ struct DMChatView: View {
VStack(alignment: .leading) { VStack(alignment: .leading) {
ForEach(Array(zip(dms.events, dms.events.indices)), id: \.0.id) { (ev, ind) in ForEach(Array(zip(dms.events, dms.events.indices)), id: \.0.id) { (ev, ind) in
DMView(event: dms.events[ind], damus_state: damus_state) DMView(event: dms.events[ind], damus_state: damus_state)
.event_context_menu(ev, keypair: damus_state.keypair, target_pubkey: ev.pubkey) .event_context_menu(ev, keypair: damus_state.keypair, target_pubkey: ev.pubkey, bookmarks: damus_state.bookmarks)
} }
EndBlock(height: 80) EndBlock(height: 80)
} }

View File

@@ -126,9 +126,9 @@ extension View {
} }
} }
func event_context_menu(_ event: NostrEvent, keypair: Keypair, target_pubkey: String) -> some View { func event_context_menu(_ event: NostrEvent, keypair: Keypair, target_pubkey: String, bookmarks: BookmarksManager) -> some View {
return self.contextMenu { return self.contextMenu {
EventMenuContext(event: event, keypair: keypair, target_pubkey: target_pubkey) EventMenuContext(event: event, keypair: keypair, target_pubkey: target_pubkey, bookmarks: bookmarks)
} }
} }

View File

@@ -23,7 +23,7 @@ struct EmbeddedEventView: View {
EventBody(damus_state: damus_state, event: event, size: .small) EventBody(damus_state: damus_state, event: event, size: .small)
} }
.event_context_menu(event, keypair: damus_state.keypair, target_pubkey: pubkey) .event_context_menu(event, keypair: damus_state.keypair, target_pubkey: pubkey, bookmarks: damus_state.bookmarks)
} }
} }

View File

@@ -11,9 +11,20 @@ struct EventMenuContext: View {
let event: NostrEvent let event: NostrEvent
let keypair: Keypair let keypair: Keypair
let target_pubkey: String let target_pubkey: String
let bookmarks: BookmarksManager
@State private var isBookmarked: Bool = false @State private var isBookmarked: Bool = false
init(event: NostrEvent, keypair: Keypair, target_pubkey: String, bookmarks: BookmarksManager) {
let bookmarked = bookmarks.isBookmarked(event)
self._isBookmarked = State(initialValue: bookmarked)
self.bookmarks = bookmarks
self.event = event
self.keypair = keypair
self.target_pubkey = target_pubkey
}
var body: some View { var body: some View {
Button { Button {
@@ -41,21 +52,14 @@ struct EventMenuContext: View {
} }
Button { Button {
let event_json = event_to_json(ev: event) self.bookmarks.updateBookmark(event)
BookmarksManager(pubkey: keypair.pubkey).updateBookmark(event_json) isBookmarked = self.bookmarks.isBookmarked(event)
isBookmarked = BookmarksManager(pubkey: keypair.pubkey).isBookmarked(event_json)
notify(.update_bookmarks, event)
} label: { } label: {
let imageName = isBookmarked ? "bookmark.fill" : "bookmark" let imageName = isBookmarked ? "bookmark.fill" : "bookmark"
let removeBookmarkString = NSLocalizedString("Remove Bookmark", comment: "Context menu option for removing a note bookmark.") let removeBookmarkString = NSLocalizedString("Remove Bookmark", comment: "Context menu option for removing a note bookmark.")
let addBookmarkString = NSLocalizedString("Add Bookmark", comment: "Context menu option for adding a note bookmark.") let addBookmarkString = NSLocalizedString("Add Bookmark", comment: "Context menu option for adding a note bookmark.")
Label(isBookmarked ? removeBookmarkString : addBookmarkString, systemImage: imageName) Label(isBookmarked ? removeBookmarkString : addBookmarkString, systemImage: imageName)
} }
.onAppear {
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
isBookmarked = BookmarksManager(pubkey: keypair.pubkey).isBookmarked(event_to_json(ev: event))
}
}
Button { Button {
NotificationCenter.default.post(name: .broadcast_event, object: event) NotificationCenter.default.post(name: .broadcast_event, object: event)

View File

@@ -60,7 +60,7 @@ struct SelectedEventView: View {
self.bar.update(damus: self.damus, evid: target) self.bar.update(damus: self.damus, evid: target)
} }
.padding([.leading], 2) .padding([.leading], 2)
.event_context_menu(event, keypair: damus.keypair, target_pubkey: event.pubkey) .event_context_menu(event, keypair: damus.keypair, target_pubkey: event.pubkey, bookmarks: damus.bookmarks)
} }
} }
} }

View File

@@ -66,7 +66,7 @@ struct TextEvent: View {
.id(event.id) .id(event.id)
.frame(maxWidth: .infinity, minHeight: PFP_SIZE) .frame(maxWidth: .infinity, minHeight: PFP_SIZE)
.padding([.bottom], 2) .padding([.bottom], 2)
.event_context_menu(event, keypair: damus.keypair, target_pubkey: pubkey) .event_context_menu(event, keypair: damus.keypair, target_pubkey: pubkey, bookmarks: damus.bookmarks)
} }
} }