notifications: add support for tagged mentions

These are text notes that have you tagged but do not have inline
mentions or are replies.

Signed-off-by: William Casarin <jb55@jb55.com>
This commit is contained in:
William Casarin
2024-09-05 16:52:10 -07:00
committed by Daniel D’Aquino
parent d9bbca1005
commit 1dbf7101b9
4 changed files with 43 additions and 15 deletions

View File

@@ -55,6 +55,9 @@ struct NotificationFormatter {
var identifier = "" var identifier = ""
switch notify.type { switch notify.type {
case .tagged:
title = String(format: NSLocalizedString("Tagged by %@", comment: "Tagged by heading in local notification"), displayName)
identifier = "myMentionNotification"
case .mention: case .mention:
title = String(format: NSLocalizedString("Mentioned by %@", comment: "Mentioned by heading in local notification"), displayName) title = String(format: NSLocalizedString("Mentioned by %@", comment: "Mentioned by heading in local notification"), displayName)
identifier = "myMentionNotification" identifier = "myMentionNotification"

View File

@@ -780,7 +780,7 @@ struct ContentView: View {
selected_timeline = .dms selected_timeline = .dms
damus_state.dms.set_active_dm(target.pubkey) damus_state.dms.set_active_dm(target.pubkey)
navigationCoordinator.push(route: Route.DMChat(dms: damus_state.dms.active_model)) navigationCoordinator.push(route: Route.DMChat(dms: damus_state.dms.active_model))
case .like, .zap, .mention, .repost, .reply: case .like, .zap, .mention, .repost, .reply, .tagged:
open_event(ev: target) open_event(ev: target)
case .profile_zap: case .profile_zap:
break break

View File

@@ -61,13 +61,15 @@ func generate_local_notification_object(from ev: NostrEvent, state: HeadlessDamu
if type == .text, state.settings.mention_notification { if type == .text, state.settings.mention_notification {
let blocks = ev.blocks(state.keypair).blocks let blocks = ev.blocks(state.keypair).blocks
for case .mention(let mention) in blocks { for case .mention(let mention) in blocks {
guard case .pubkey(let pk) = mention.ref, pk == state.keypair.pubkey else { guard case .pubkey(let pk) = mention.ref, pk == state.keypair.pubkey else {
continue continue
} }
let content_preview = render_notification_content_preview(ev: ev, profiles: state.profiles, keypair: state.keypair) let content_preview = render_notification_content_preview(ev: ev, profiles: state.profiles, keypair: state.keypair)
return LocalNotification(type: .mention, event: ev, target: ev, content: content_preview) return LocalNotification(type: .mention, event: ev, target: .note(ev), content: content_preview)
} }
if ev.referenced_ids.contains(where: { note_id in if ev.referenced_ids.contains(where: { note_id in
guard let note_author: Pubkey = state.ndb.lookup_note(note_id)?.unsafeUnownedValue?.pubkey else { return false } guard let note_author: Pubkey = state.ndb.lookup_note(note_id)?.unsafeUnownedValue?.pubkey else { return false }
guard note_author == state.keypair.pubkey else { return false } guard note_author == state.keypair.pubkey else { return false }
@@ -75,31 +77,39 @@ func generate_local_notification_object(from ev: NostrEvent, state: HeadlessDamu
}) { }) {
// This is a reply to one of our posts // This is a reply to one of our posts
let content_preview = render_notification_content_preview(ev: ev, profiles: state.profiles, keypair: state.keypair) let content_preview = render_notification_content_preview(ev: ev, profiles: state.profiles, keypair: state.keypair)
return LocalNotification(type: .reply, event: ev, target: ev, content: content_preview) return LocalNotification(type: .reply, event: ev, target: .note(ev), content: content_preview)
} }
if ev.referenced_pubkeys.contains(state.keypair.pubkey) {
// not mentioned or replied to, just tagged
let content_preview = render_notification_content_preview(ev: ev, profiles: state.profiles, keypair: state.keypair)
return LocalNotification(type: .tagged, event: ev, target: .note(ev), content: content_preview)
}
} else if type == .boost, } else if type == .boost,
state.settings.repost_notification, state.settings.repost_notification,
let inner_ev = ev.get_inner_event() let inner_ev = ev.get_inner_event()
{ {
let content_preview = render_notification_content_preview(ev: inner_ev, profiles: state.profiles, keypair: state.keypair) let content_preview = render_notification_content_preview(ev: inner_ev, profiles: state.profiles, keypair: state.keypair)
return LocalNotification(type: .repost, event: ev, target: inner_ev, content: content_preview) return LocalNotification(type: .repost, event: ev, target: .note(inner_ev), content: content_preview)
} else if type == .like, } else if type == .like, state.settings.like_notification, let evid = ev.referenced_ids.last {
state.settings.like_notification, if let txn = state.ndb.lookup_note(evid, txn_name: "local_notification_like"),
let evid = ev.referenced_ids.last, let liked_event = txn.unsafeUnownedValue
let txn = state.ndb.lookup_note(evid, txn_name: "local_notification_like"),
let liked_event = txn.unsafeUnownedValue?.to_owned()
{ {
let content_preview = render_notification_content_preview(ev: liked_event, profiles: state.profiles, keypair: state.keypair) let content_preview = render_notification_content_preview(ev: liked_event, profiles: state.profiles, keypair: state.keypair)
return LocalNotification(type: .like, event: ev, target: liked_event, content: content_preview) return LocalNotification(type: .like, event: ev, target: .note(liked_event), content: content_preview)
} else {
return LocalNotification(type: .like, event: ev, target: .note_id(evid), content: "")
}
} }
else if type == .dm, else if type == .dm,
state.settings.dm_notification { state.settings.dm_notification {
let convo = ev.decrypted(keypair: state.keypair) ?? NSLocalizedString("New encrypted direct message", comment: "Notification that the user has received a new direct message") let convo = ev.decrypted(keypair: state.keypair) ?? NSLocalizedString("New encrypted direct message", comment: "Notification that the user has received a new direct message")
return LocalNotification(type: .dm, event: ev, target: ev, content: convo) return LocalNotification(type: .dm, event: ev, target: .note(ev), content: convo)
} }
else if type == .zap, else if type == .zap,
state.settings.zap_notification { state.settings.zap_notification {
return LocalNotification(type: .zap, event: ev, target: ev, content: ev.content) return LocalNotification(type: .zap, event: ev, target: .note(ev), content: ev.content)
} }
return nil return nil

View File

@@ -48,10 +48,24 @@ struct LossyLocalNotification {
} }
} }
enum NotificationTarget {
case note(NostrEvent)
case note_id(NoteId)
var id: NoteId {
switch self {
case .note(let note):
return note.id
case .note_id(let id):
return id
}
}
}
struct LocalNotification { struct LocalNotification {
let type: LocalNotificationType let type: LocalNotificationType
let event: NostrEvent let event: NostrEvent
let target: NostrEvent let target: NotificationTarget
let content: String let content: String
func to_lossy() -> LossyLocalNotification { func to_lossy() -> LossyLocalNotification {
@@ -64,6 +78,7 @@ enum LocalNotificationType: String {
case like case like
case mention case mention
case reply case reply
case tagged
case repost case repost
case zap case zap
case profile_zap case profile_zap