mirror of
git://jb55.com/damus
synced 2024-10-01 17:30:44 +00:00
mute: migrate Lists.swift to use new MuteItem
This patch depends on: Adding new structs/enums for new mute list - Rewrites Lists.swift to use new mute list option - This leads to a lot of changes for changing the type from RefId to the new MuteItem - Update & relay new mute list in AddMuteItemView.swift (fixing previous patch TODO) - Renames `list` to `list_deprecated` - We need to keep this since existing users might have an old mute list Related: https://github.com/damus-io/damus/issues/1718 Related: https://github.com/damus-io/damus/issues/856 Lighting Address: fishcharlie@strike.me Signed-off-by: Charlie Fish <contact@charlie.fish> Reviewed-by: William Casarin <jb55@jb55.com> Signed-off-by: William Casarin <jb55@jb55.com>
This commit is contained in:
parent
6d2c382469
commit
f36646116e
@ -545,7 +545,7 @@ struct ContentView: View {
|
|||||||
guard let ds = damus_state,
|
guard let ds = damus_state,
|
||||||
let keypair = ds.keypair.to_full(),
|
let keypair = ds.keypair.to_full(),
|
||||||
let pubkey = muting,
|
let pubkey = muting,
|
||||||
let mutelist = create_or_update_mutelist(keypair: keypair, mprev: nil, to_add: .pubkey(pubkey))
|
let mutelist = create_or_update_mutelist(keypair: keypair, mprev: nil, to_add: .user(pubkey, nil))
|
||||||
else {
|
else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -578,7 +578,7 @@ struct ContentView: View {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
guard let ev = create_or_update_mutelist(keypair: keypair, mprev: ds.contacts.mutelist, to_add: .pubkey(pubkey)) else {
|
guard let ev = create_or_update_mutelist(keypair: keypair, mprev: ds.contacts.mutelist, to_add: .user(pubkey, nil)) else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -157,8 +157,11 @@ class HomeModel {
|
|||||||
case .metadata:
|
case .metadata:
|
||||||
// profile metadata processing is handled by nostrdb
|
// profile metadata processing is handled by nostrdb
|
||||||
break
|
break
|
||||||
case .list:
|
case .list_deprecated:
|
||||||
handle_list_event(ev)
|
handle_list_event(ev)
|
||||||
|
case .mute_list:
|
||||||
|
// @TODO: this will be implemented in a future patch
|
||||||
|
break
|
||||||
case .boost:
|
case .boost:
|
||||||
handle_boost_event(sub_id: sub_id, ev)
|
handle_boost_event(sub_id: sub_id, ev)
|
||||||
case .like:
|
case .like:
|
||||||
@ -461,7 +464,7 @@ class HomeModel {
|
|||||||
var our_contacts_filter = NostrFilter(kinds: [.contacts, .metadata])
|
var our_contacts_filter = NostrFilter(kinds: [.contacts, .metadata])
|
||||||
our_contacts_filter.authors = [damus_state.pubkey]
|
our_contacts_filter.authors = [damus_state.pubkey]
|
||||||
|
|
||||||
var our_blocklist_filter = NostrFilter(kinds: [.list])
|
var our_blocklist_filter = NostrFilter(kinds: [.list_deprecated])
|
||||||
our_blocklist_filter.parameter = ["mute"]
|
our_blocklist_filter.parameter = ["mute"]
|
||||||
our_blocklist_filter.authors = [damus_state.pubkey]
|
our_blocklist_filter.authors = [damus_state.pubkey]
|
||||||
|
|
||||||
|
@ -145,6 +145,13 @@ enum MuteItem: Hashable, Equatable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// - MARK: TagConvertible
|
||||||
|
extension MuteItem: TagConvertible {
|
||||||
|
static func from_tag(tag: TagSequence) -> MuteItem? {
|
||||||
|
return MuteItem(tag.strings())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
extension Collection where Element == MuteItem {
|
extension Collection where Element == MuteItem {
|
||||||
/// Check if an event is muted given a collection of ``MutedItem``.
|
/// Check if an event is muted given a collection of ``MutedItem``.
|
||||||
///
|
///
|
||||||
|
@ -798,3 +798,15 @@ func to_reaction_emoji(ev: NostrEvent) -> String? {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension NostrEvent {
|
||||||
|
/// The mutelist for a given event
|
||||||
|
///
|
||||||
|
/// If the event is not a mutelist it will return `nil`.
|
||||||
|
var mute_list: Set<MuteItem>? {
|
||||||
|
if (self.kind == NostrKind.list_deprecated.rawValue && self.referenced_params.contains(where: { p in p.param.matches_str("mute") })) || self.kind == NostrKind.mute_list.rawValue {
|
||||||
|
return Set(self.referenced_mute_items)
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -17,7 +17,8 @@ enum NostrKind: UInt32, Codable {
|
|||||||
case boost = 6
|
case boost = 6
|
||||||
case like = 7
|
case like = 7
|
||||||
case chat = 42
|
case chat = 42
|
||||||
case list = 30000
|
case mute_list = 10000
|
||||||
|
case list_deprecated = 30000
|
||||||
case longform = 30023
|
case longform = 30023
|
||||||
case zap = 9735
|
case zap = 9735
|
||||||
case zap_request = 9734
|
case zap_request = 9734
|
||||||
|
@ -7,64 +7,30 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
func create_or_update_mutelist(keypair: FullKeypair, mprev: NostrEvent?, to_add: RefId) -> NostrEvent? {
|
func create_or_update_mutelist(keypair: FullKeypair, mprev: NostrEvent?, to_add: Set<MuteItem>) -> NostrEvent? {
|
||||||
return create_or_update_list_event(keypair: keypair, mprev: mprev, to_add: to_add, list_name: "mute", list_type: "p")
|
let muted_items: Set<MuteItem> = (mprev?.mute_list ?? Set<MuteItem>()).union(to_add).filter { !$0.is_expired() }
|
||||||
|
let tags: [[String]] = muted_items.map { $0.tag }
|
||||||
|
return NostrEvent(content: mprev?.content ?? "", keypair: keypair.to_keypair(), kind: NostrKind.mute_list.rawValue, tags: tags)
|
||||||
}
|
}
|
||||||
|
|
||||||
func remove_from_mutelist(keypair: FullKeypair, prev: NostrEvent, to_remove: RefId) -> NostrEvent? {
|
func create_or_update_mutelist(keypair: FullKeypair, mprev: NostrEvent?, to_add: MuteItem) -> NostrEvent? {
|
||||||
return remove_from_list_event(keypair: keypair, prev: prev, to_remove: to_remove)
|
return create_or_update_mutelist(keypair: keypair, mprev: mprev, to_add: [to_add])
|
||||||
}
|
}
|
||||||
|
|
||||||
func create_or_update_list_event(keypair: FullKeypair, mprev: NostrEvent?, to_add: RefId, list_name: String, list_type: String) -> NostrEvent? {
|
func remove_from_mutelist(keypair: FullKeypair, prev: NostrEvent?, to_remove: MuteItem) -> NostrEvent? {
|
||||||
if let prev = mprev,
|
let muted_items: Set<MuteItem> = (prev?.mute_list ?? Set<MuteItem>()).subtracting([to_remove]).filter { !$0.is_expired() }
|
||||||
prev.pubkey == keypair.pubkey,
|
let tags: [[String]] = muted_items.map { $0.tag }
|
||||||
matches_list_name(tags: prev.tags, name: list_name)
|
return NostrEvent(content: "", keypair: keypair.to_keypair(), kind: NostrKind.mute_list.rawValue, tags: tags)
|
||||||
{
|
}
|
||||||
return add_to_list_event(keypair: keypair, prev: prev, to_add: to_add)
|
|
||||||
|
func toggle_from_mutelist(keypair: FullKeypair, prev: NostrEvent?, to_toggle: MuteItem) -> NostrEvent? {
|
||||||
|
let existing_muted_items: Set<MuteItem> = (prev?.mute_list ?? Set<MuteItem>())
|
||||||
|
|
||||||
|
if existing_muted_items.contains(to_toggle) {
|
||||||
|
// Already exists, remove
|
||||||
|
return remove_from_mutelist(keypair: keypair, prev: prev, to_remove: to_toggle)
|
||||||
|
} else {
|
||||||
|
// Doesn't exist, add
|
||||||
|
return create_or_update_mutelist(keypair: keypair, mprev: prev, to_add: to_toggle)
|
||||||
}
|
}
|
||||||
|
|
||||||
let tags = [["d", list_name], [list_type, to_add.description]]
|
|
||||||
return NostrEvent(content: "", keypair: keypair.to_keypair(), kind: 30000, tags: tags)
|
|
||||||
}
|
|
||||||
|
|
||||||
func remove_from_list_event(keypair: FullKeypair, prev: NostrEvent, to_remove: RefId) -> NostrEvent? {
|
|
||||||
var removed = false
|
|
||||||
|
|
||||||
let tags = prev.tags.reduce(into: [[String]](), { acc, tag in
|
|
||||||
if let ref_id = RefId.from_tag(tag: tag), ref_id == to_remove {
|
|
||||||
removed = true
|
|
||||||
return
|
|
||||||
}
|
|
||||||
acc.append(tag.strings())
|
|
||||||
})
|
|
||||||
|
|
||||||
guard removed else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return NostrEvent(content: prev.content, keypair: keypair.to_keypair(), kind: 30000, tags: tags)
|
|
||||||
}
|
|
||||||
|
|
||||||
func add_to_list_event(keypair: FullKeypair, prev: NostrEvent, to_add: RefId) -> NostrEvent? {
|
|
||||||
for tag in prev.tags {
|
|
||||||
// we are already muting this user
|
|
||||||
if let ref = RefId.from_tag(tag: tag), to_add == ref {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var tags = prev.tags.strings()
|
|
||||||
tags.append(to_add.tag)
|
|
||||||
|
|
||||||
return NostrEvent(content: prev.content, keypair: keypair.to_keypair(), kind: 30000, tags: tags)
|
|
||||||
}
|
|
||||||
|
|
||||||
func matches_list_name(tags: Tags, name: String) -> Bool {
|
|
||||||
for tag in tags {
|
|
||||||
if tag.count >= 2 && tag[0].matches_char("d") {
|
|
||||||
return tag[1].matches_str(name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
@ -72,7 +72,19 @@ struct AddMuteItemView: View {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// @TODO: in future patch - actually update & relay the new mute list
|
// Actually update & relay the new mute list
|
||||||
|
if let mute_item {
|
||||||
|
guard
|
||||||
|
let full_keypair = state.keypair.to_full(),
|
||||||
|
let existing_mutelist = state.contacts.mutelist,
|
||||||
|
let mutelist = create_or_update_mutelist(keypair: full_keypair, mprev: existing_mutelist, to_add: mute_item)
|
||||||
|
else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
state.contacts.set_mutelist(mutelist)
|
||||||
|
state.postbox.send(mutelist)
|
||||||
|
}
|
||||||
|
|
||||||
new_text = ""
|
new_text = ""
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ struct MutelistView: View {
|
|||||||
let keypair = damus_state.keypair.to_full(),
|
let keypair = damus_state.keypair.to_full(),
|
||||||
let new_ev = remove_from_mutelist(keypair: keypair,
|
let new_ev = remove_from_mutelist(keypair: keypair,
|
||||||
prev: mutelist,
|
prev: mutelist,
|
||||||
to_remove: .pubkey(pubkey))
|
to_remove: .user(pubkey, nil))
|
||||||
else {
|
else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -188,7 +188,7 @@ struct ProfileView: View {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
guard let new_ev = remove_from_mutelist(keypair: keypair, prev: mutelist, to_remove: .pubkey(profile.pubkey)) else {
|
guard let new_ev = remove_from_mutelist(keypair: keypair, prev: mutelist, to_remove: .user(profile.pubkey, nil)) else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,15 +23,13 @@ final class ListTests: XCTestCase {
|
|||||||
let pubkey = test_keypair_full.pubkey
|
let pubkey = test_keypair_full.pubkey
|
||||||
let to_mute = test_pubkey
|
let to_mute = test_pubkey
|
||||||
let keypair = FullKeypair(pubkey: pubkey, privkey: privkey)
|
let keypair = FullKeypair(pubkey: pubkey, privkey: privkey)
|
||||||
let mutelist = create_or_update_mutelist(keypair: keypair, mprev: nil, to_add: .pubkey(to_mute))!
|
let mutelist = create_or_update_mutelist(keypair: keypair, mprev: nil, to_add: .user(to_mute, nil))!
|
||||||
|
|
||||||
XCTAssertEqual(mutelist.pubkey, pubkey)
|
XCTAssertEqual(mutelist.pubkey, pubkey)
|
||||||
XCTAssertEqual(mutelist.content, "")
|
XCTAssertEqual(mutelist.content, "")
|
||||||
XCTAssertEqual(mutelist.tags.count, 2)
|
XCTAssertEqual(mutelist.tags.count, 1)
|
||||||
XCTAssertEqual(mutelist.tags[0][0].string(), "d")
|
XCTAssertEqual(mutelist.tags[0][0].string(), "p")
|
||||||
XCTAssertEqual(mutelist.tags[0][1].string(), "mute")
|
XCTAssertEqual(mutelist.tags[0][1].string(), to_mute.hex())
|
||||||
XCTAssertEqual(mutelist.tags[1][0].string(), "p")
|
|
||||||
XCTAssertEqual(mutelist.tags[1][1].string(), to_mute.hex())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func testCreateAndRemoveMuteList() throws {
|
func testCreateAndRemoveMuteList() throws {
|
||||||
@ -39,14 +37,12 @@ final class ListTests: XCTestCase {
|
|||||||
let pubkey = test_keypair_full.pubkey
|
let pubkey = test_keypair_full.pubkey
|
||||||
let to_mute = test_pubkey
|
let to_mute = test_pubkey
|
||||||
let keypair = FullKeypair(pubkey: pubkey, privkey: privkey)
|
let keypair = FullKeypair(pubkey: pubkey, privkey: privkey)
|
||||||
let mutelist = create_or_update_mutelist(keypair: keypair, mprev: nil, to_add: .pubkey(to_mute))!
|
let mutelist = create_or_update_mutelist(keypair: keypair, mprev: nil, to_add: .user(to_mute, nil))!
|
||||||
let new = remove_from_mutelist(keypair: keypair, prev: mutelist, to_remove: .pubkey(to_mute))!
|
let new = remove_from_mutelist(keypair: keypair, prev: mutelist, to_remove: .user(to_mute, nil))!
|
||||||
|
|
||||||
XCTAssertEqual(new.pubkey, pubkey)
|
XCTAssertEqual(new.pubkey, pubkey)
|
||||||
XCTAssertEqual(new.content, "")
|
XCTAssertEqual(new.content, "")
|
||||||
XCTAssertEqual(new.tags.count, 1)
|
XCTAssertEqual(new.tags.count, 0)
|
||||||
XCTAssertEqual(new.tags[0][0].string(), "d")
|
|
||||||
XCTAssertEqual(new.tags[0][1].string(), "mute")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func testAddToExistingMutelist() throws {
|
func testAddToExistingMutelist() throws {
|
||||||
@ -55,17 +51,25 @@ final class ListTests: XCTestCase {
|
|||||||
let to_mute = test_pubkey
|
let to_mute = test_pubkey
|
||||||
let to_mute_2 = test_pubkey_2
|
let to_mute_2 = test_pubkey_2
|
||||||
let keypair = FullKeypair(pubkey: pubkey, privkey: privkey)
|
let keypair = FullKeypair(pubkey: pubkey, privkey: privkey)
|
||||||
let mutelist = create_or_update_mutelist(keypair: keypair, mprev: nil, to_add: .pubkey(to_mute))!
|
let mutelist = create_or_update_mutelist(keypair: keypair, mprev: nil, to_add: .user(to_mute, nil))!
|
||||||
let new = create_or_update_mutelist(keypair: keypair, mprev: mutelist, to_add: .pubkey(to_mute_2))!
|
let new = create_or_update_mutelist(keypair: keypair, mprev: mutelist, to_add: .user(to_mute_2, nil))!
|
||||||
|
|
||||||
XCTAssertEqual(new.pubkey, pubkey)
|
XCTAssertEqual(new.pubkey, pubkey)
|
||||||
XCTAssertEqual(new.content, "")
|
XCTAssertEqual(new.content, "")
|
||||||
XCTAssertEqual(new.tags.count, 3)
|
XCTAssertEqual(new.tags.count, 2)
|
||||||
XCTAssertEqual(new.tags[0][0].string(), "d")
|
XCTAssertEqual(new.tags[0][0].string(), "p")
|
||||||
XCTAssertEqual(new.tags[0][1].string(), "mute")
|
|
||||||
XCTAssertEqual(new.tags[1][0].string(), "p")
|
XCTAssertEqual(new.tags[1][0].string(), "p")
|
||||||
XCTAssertEqual(new.tags[1][1].string(), to_mute.hex())
|
// This test failed once out of like 10 tries, due to the tags being in the incorrect order. So I decided to put the elements in an array and sort it. That way if the mutelist tags aren't in the expected order it won't fail the test.
|
||||||
XCTAssertEqual(new.tags[2][0].string(), "p")
|
XCTAssertEqual([new.tags[0][1].string(), new.tags[1][1].string()].sorted(), [to_mute.hex(), to_mute_2.hex()].sorted())
|
||||||
XCTAssertEqual(new.tags[2][1].string(), to_mute_2.hex())
|
}
|
||||||
|
|
||||||
|
func testAddToExistingMutelistShouldNotOverrideContent() throws {
|
||||||
|
let privkey = test_keypair_full.privkey
|
||||||
|
let pubkey = test_keypair_full.pubkey
|
||||||
|
let keypair = FullKeypair(pubkey: pubkey, privkey: privkey)
|
||||||
|
let mutelist = NostrEvent(content: "random", keypair: keypair.to_keypair(), kind: NostrKind.mute_list.rawValue, tags: [])
|
||||||
|
let new = create_or_update_mutelist(keypair: keypair, mprev: mutelist, to_add: .user(test_pubkey, nil))!
|
||||||
|
|
||||||
|
XCTAssertEqual(new.content, "random")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -325,6 +325,10 @@ extension NdbNote {
|
|||||||
References<ReplaceableParam>(tags: self.tags)
|
References<ReplaceableParam>(tags: self.tags)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public var referenced_mute_items: References<MuteItem> {
|
||||||
|
References<MuteItem>(tags: self.tags)
|
||||||
|
}
|
||||||
|
|
||||||
public var references: References<RefId> {
|
public var references: References<RefId> {
|
||||||
References<RefId>(tags: self.tags)
|
References<RefId>(tags: self.tags)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user