Reuse local notification logic with push notifications
Testing ------- Conditional pass Device: iPhone 15 Pro simulator iOS: 17.0.1 Damus: This commit Coverage: 1. Mention notification works (local and push). PASS 2. Thread replies do not appear (but upon code inspection it seems like it was not supported before). PASS? 3. DM notification works with decryption (local and push). PASS 4. Zaps not yet implemented. Coming later. Closes: https://github.com/damus-io/damus/issues/1702 Closes: https://github.com/damus-io/damus/issues/1703 Changelog-Changed: Improve push notification support to match local notification support Signed-off-by: Daniel D’Aquino <daniel@daquino.me> Signed-off-by: William Casarin <jb55@jb55.com>
This commit is contained in:
committed by
William Casarin
parent
5db22ae244
commit
c4f0e833ff
@@ -8,7 +8,7 @@
|
||||
import Foundation
|
||||
import LinkPresentation
|
||||
|
||||
struct DamusState {
|
||||
struct DamusState: HeadlessDamusState {
|
||||
let pool: RelayPool
|
||||
let keypair: Keypair
|
||||
let likes: EventCounter
|
||||
|
||||
21
damus/Models/HeadlessDamusState.swift
Normal file
21
damus/Models/HeadlessDamusState.swift
Normal file
@@ -0,0 +1,21 @@
|
||||
//
|
||||
// HeadlessDamusState.swift
|
||||
// damus
|
||||
//
|
||||
// Created by Daniel D’Aquino on 2023-11-27.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/// HeadlessDamusState
|
||||
///
|
||||
/// A protocl for a lighter headless alternative to DamusState that does not have dependencies on View objects or UI logic.
|
||||
/// This is useful in limited environments (e.g. Notification Service Extension) where we do not want View/UI dependencies
|
||||
protocol HeadlessDamusState {
|
||||
var ndb: Ndb { get }
|
||||
var settings: UserSettingsStore { get }
|
||||
var contacts: Contacts { get }
|
||||
var muted_threads: MutedThreadsManager { get }
|
||||
var keypair: Keypair { get }
|
||||
var profiles: Profiles { get }
|
||||
}
|
||||
@@ -602,7 +602,7 @@ class HomeModel {
|
||||
}
|
||||
|
||||
if handle_last_event(ev: ev, timeline: .notifications) {
|
||||
process_local_notification(damus_state: damus_state, event: ev)
|
||||
process_local_notification(state: damus_state, event: ev)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -644,11 +644,13 @@ class HomeModel {
|
||||
func got_new_dm(notifs: NewEventsBits, ev: NostrEvent) {
|
||||
notification_status.new_events = notifs
|
||||
|
||||
if damus_state.settings.dm_notification && ev.age < HomeModel.event_max_age_for_notification {
|
||||
let convo = ev.decrypted(keypair: self.damus_state.keypair) ?? NSLocalizedString("New encrypted direct message", comment: "Notification that the user has received a new direct message")
|
||||
let notify = LocalNotification(type: .dm, event: ev, target: ev, content: convo)
|
||||
create_local_notification(profiles: damus_state.profiles, notify: notify)
|
||||
guard should_display_notification(state: damus_state, event: ev),
|
||||
let notification_object = generate_local_notification_object(from: ev, state: damus_state)
|
||||
else {
|
||||
return
|
||||
}
|
||||
|
||||
create_local_notification(profiles: damus_state.profiles, notify: notification_object)
|
||||
}
|
||||
|
||||
func handle_dm(_ ev: NostrEvent) {
|
||||
@@ -1161,19 +1163,6 @@ func create_in_app_event_zap_notification(profiles: Profiles, zap: Zap, locale:
|
||||
}
|
||||
}
|
||||
|
||||
func process_local_notification(damus_state: DamusState, event ev: NostrEvent) {
|
||||
process_local_notification(
|
||||
ndb: damus_state.ndb,
|
||||
settings: damus_state.settings,
|
||||
contacts: damus_state.contacts,
|
||||
muted_threads: damus_state.muted_threads,
|
||||
user_keypair: damus_state.keypair,
|
||||
profiles: damus_state.profiles,
|
||||
event: ev
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
enum ProcessZapResult {
|
||||
case already_processed(Zap)
|
||||
case done(Zap)
|
||||
|
||||
@@ -12,68 +12,75 @@ import UIKit
|
||||
|
||||
let EVENT_MAX_AGE_FOR_NOTIFICATION: TimeInterval = 12 * 60 * 60
|
||||
|
||||
func process_local_notification(ndb: Ndb, settings: UserSettingsStore, contacts: Contacts, muted_threads: MutedThreadsManager, user_keypair: Keypair, profiles: Profiles, event ev: NostrEvent) {
|
||||
if ev.known_kind == nil {
|
||||
func process_local_notification(state: HeadlessDamusState, event ev: NostrEvent) {
|
||||
guard should_display_notification(state: state, event: ev) else {
|
||||
// We should not display notification. Exit.
|
||||
return
|
||||
}
|
||||
|
||||
if settings.notification_only_from_following,
|
||||
contacts.follow_state(ev.pubkey) != .follows
|
||||
{
|
||||
guard let local_notification = generate_local_notification_object(from: ev, state: state) else {
|
||||
return
|
||||
}
|
||||
create_local_notification(profiles: state.profiles, notify: local_notification)
|
||||
}
|
||||
|
||||
func should_display_notification(state: HeadlessDamusState, event ev: NostrEvent) -> Bool {
|
||||
if ev.known_kind == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if state.settings.notification_only_from_following,
|
||||
state.contacts.follow_state(ev.pubkey) != .follows
|
||||
{
|
||||
return false
|
||||
}
|
||||
|
||||
// Don't show notifications from muted threads.
|
||||
if muted_threads.isMutedThread(ev, keypair: user_keypair) {
|
||||
return
|
||||
if state.muted_threads.isMutedThread(ev, keypair: state.keypair) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Don't show notifications for old events
|
||||
guard ev.age < EVENT_MAX_AGE_FOR_NOTIFICATION else {
|
||||
return
|
||||
return false
|
||||
}
|
||||
|
||||
guard let local_notification = generate_local_notification_object(
|
||||
ndb: ndb,
|
||||
from: ev,
|
||||
settings: settings,
|
||||
user_keypair: user_keypair,
|
||||
profiles: profiles
|
||||
) else {
|
||||
return
|
||||
}
|
||||
create_local_notification(profiles: profiles, notify: local_notification)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
func generate_local_notification_object(ndb: Ndb, from ev: NostrEvent, settings: UserSettingsStore, user_keypair: Keypair, profiles: Profiles) -> LocalNotification? {
|
||||
func generate_local_notification_object(from ev: NostrEvent, state: HeadlessDamusState) -> LocalNotification? {
|
||||
guard let type = ev.known_kind else {
|
||||
return nil
|
||||
}
|
||||
|
||||
if type == .text, settings.mention_notification {
|
||||
let blocks = ev.blocks(user_keypair).blocks
|
||||
if type == .text, state.settings.mention_notification {
|
||||
let blocks = ev.blocks(state.keypair).blocks
|
||||
for case .mention(let mention) in blocks {
|
||||
guard case .pubkey(let pk) = mention.ref, pk == user_keypair.pubkey else {
|
||||
guard case .pubkey(let pk) = mention.ref, pk == state.keypair.pubkey else {
|
||||
continue
|
||||
}
|
||||
let content_preview = render_notification_content_preview(ev: ev, profiles: profiles, keypair: user_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)
|
||||
}
|
||||
} else if type == .boost,
|
||||
settings.repost_notification,
|
||||
state.settings.repost_notification,
|
||||
let inner_ev = ev.get_inner_event()
|
||||
{
|
||||
let content_preview = render_notification_content_preview(ev: inner_ev, profiles: profiles, keypair: user_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)
|
||||
} else if type == .like,
|
||||
settings.like_notification,
|
||||
state.settings.like_notification,
|
||||
let evid = ev.referenced_ids.last,
|
||||
let liked_event = ndb.lookup_note(evid).unsafeUnownedValue // We are only accessing it temporarily to generate notification content
|
||||
let liked_event = state.ndb.lookup_note(evid).unsafeUnownedValue // We are only accessing it temporarily to generate notification content
|
||||
{
|
||||
let content_preview = render_notification_content_preview(ev: liked_event, profiles: profiles, keypair: user_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)
|
||||
}
|
||||
else if type == .dm,
|
||||
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")
|
||||
return LocalNotification(type: .dm, event: ev, target: ev, content: convo)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user