reduce ContentView redraws
Remove observability from the home model, and use inner models for updating specific parts of the UI, such as notification dots on the tab bar.
This commit is contained in:
@@ -171,6 +171,7 @@
|
|||||||
4C90BD1C283AC38E008EE7EF /* Bech32Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C90BD1B283AC38E008EE7EF /* Bech32Tests.swift */; };
|
4C90BD1C283AC38E008EE7EF /* Bech32Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C90BD1B283AC38E008EE7EF /* Bech32Tests.swift */; };
|
||||||
4C987B57283FD07F0042CE38 /* FollowersModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C987B56283FD07F0042CE38 /* FollowersModel.swift */; };
|
4C987B57283FD07F0042CE38 /* FollowersModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C987B56283FD07F0042CE38 /* FollowersModel.swift */; };
|
||||||
4C9AA1482A44442E003F49FD /* CustomizeZapModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C9AA1472A44442E003F49FD /* CustomizeZapModel.swift */; };
|
4C9AA1482A44442E003F49FD /* CustomizeZapModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C9AA1472A44442E003F49FD /* CustomizeZapModel.swift */; };
|
||||||
|
4C9AA14A2A4587A6003F49FD /* NotificationStatusModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C9AA1492A4587A6003F49FD /* NotificationStatusModel.swift */; };
|
||||||
4C9BB83129C0ED4F00FC4E37 /* DisplayName.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C9BB83029C0ED4F00FC4E37 /* DisplayName.swift */; };
|
4C9BB83129C0ED4F00FC4E37 /* DisplayName.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C9BB83029C0ED4F00FC4E37 /* DisplayName.swift */; };
|
||||||
4C9BB83429C12D9900FC4E37 /* EventProfileName.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C9BB83329C12D9900FC4E37 /* EventProfileName.swift */; };
|
4C9BB83429C12D9900FC4E37 /* EventProfileName.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C9BB83329C12D9900FC4E37 /* EventProfileName.swift */; };
|
||||||
4C9F18E229AA9B6C008C55EC /* CustomizeZapView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C9F18E129AA9B6C008C55EC /* CustomizeZapView.swift */; };
|
4C9F18E229AA9B6C008C55EC /* CustomizeZapView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C9F18E129AA9B6C008C55EC /* CustomizeZapView.swift */; };
|
||||||
@@ -615,6 +616,7 @@
|
|||||||
4C90BD1B283AC38E008EE7EF /* Bech32Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Bech32Tests.swift; sourceTree = "<group>"; };
|
4C90BD1B283AC38E008EE7EF /* Bech32Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Bech32Tests.swift; sourceTree = "<group>"; };
|
||||||
4C987B56283FD07F0042CE38 /* FollowersModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FollowersModel.swift; sourceTree = "<group>"; };
|
4C987B56283FD07F0042CE38 /* FollowersModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FollowersModel.swift; sourceTree = "<group>"; };
|
||||||
4C9AA1472A44442E003F49FD /* CustomizeZapModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomizeZapModel.swift; sourceTree = "<group>"; };
|
4C9AA1472A44442E003F49FD /* CustomizeZapModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomizeZapModel.swift; sourceTree = "<group>"; };
|
||||||
|
4C9AA1492A4587A6003F49FD /* NotificationStatusModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationStatusModel.swift; sourceTree = "<group>"; };
|
||||||
4C9BB83029C0ED4F00FC4E37 /* DisplayName.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisplayName.swift; sourceTree = "<group>"; };
|
4C9BB83029C0ED4F00FC4E37 /* DisplayName.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisplayName.swift; sourceTree = "<group>"; };
|
||||||
4C9BB83329C12D9900FC4E37 /* EventProfileName.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventProfileName.swift; sourceTree = "<group>"; };
|
4C9BB83329C12D9900FC4E37 /* EventProfileName.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventProfileName.swift; sourceTree = "<group>"; };
|
||||||
4C9F18E129AA9B6C008C55EC /* CustomizeZapView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomizeZapView.swift; sourceTree = "<group>"; };
|
4C9F18E129AA9B6C008C55EC /* CustomizeZapView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomizeZapView.swift; sourceTree = "<group>"; };
|
||||||
@@ -976,6 +978,7 @@
|
|||||||
children = (
|
children = (
|
||||||
4C54AA0929A55429003E4487 /* EventGroup.swift */,
|
4C54AA0929A55429003E4487 /* EventGroup.swift */,
|
||||||
4C54AA0B29A5543C003E4487 /* ZapGroup.swift */,
|
4C54AA0B29A5543C003E4487 /* ZapGroup.swift */,
|
||||||
|
4C9AA1492A4587A6003F49FD /* NotificationStatusModel.swift */,
|
||||||
);
|
);
|
||||||
path = Notifications;
|
path = Notifications;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -1933,6 +1936,7 @@
|
|||||||
4C1A9A1F29DDD24B00516EAC /* AppearanceSettingsView.swift in Sources */,
|
4C1A9A1F29DDD24B00516EAC /* AppearanceSettingsView.swift in Sources */,
|
||||||
3AA59D1D2999B0400061C48E /* DraftsModel.swift in Sources */,
|
3AA59D1D2999B0400061C48E /* DraftsModel.swift in Sources */,
|
||||||
3169CAED294FCCFC00EE4006 /* Constants.swift in Sources */,
|
3169CAED294FCCFC00EE4006 /* Constants.swift in Sources */,
|
||||||
|
4C9AA14A2A4587A6003F49FD /* NotificationStatusModel.swift in Sources */,
|
||||||
4CB9D4A72992D02B00A9A7E4 /* ProfileNameView.swift in Sources */,
|
4CB9D4A72992D02B00A9A7E4 /* ProfileNameView.swift in Sources */,
|
||||||
4CE4F0F429D779B5005914DB /* PostBox.swift in Sources */,
|
4CE4F0F429D779B5005914DB /* PostBox.swift in Sources */,
|
||||||
4C2859622A12A7F0004746F7 /* GoldSupportGradient.swift in Sources */,
|
4C2859622A12A7F0004746F7 /* GoldSupportGradient.swift in Sources */,
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ struct ContentView: View {
|
|||||||
@State var confirm_overwrite_mutelist: Bool = false
|
@State var confirm_overwrite_mutelist: Bool = false
|
||||||
@SceneStorage("ContentView.filter_state") var filter_state : FilterState = .posts_and_replies
|
@SceneStorage("ContentView.filter_state") var filter_state : FilterState = .posts_and_replies
|
||||||
@State private var isSideBarOpened = false
|
@State private var isSideBarOpened = false
|
||||||
@StateObject var home: HomeModel = HomeModel()
|
var home: HomeModel = HomeModel()
|
||||||
|
|
||||||
let sub_id = UUID().description
|
let sub_id = UUID().description
|
||||||
|
|
||||||
@@ -128,7 +128,7 @@ struct ContentView: View {
|
|||||||
func contentTimelineView(filter: (@escaping (NostrEvent) -> Bool)) -> some View {
|
func contentTimelineView(filter: (@escaping (NostrEvent) -> Bool)) -> some View {
|
||||||
ZStack {
|
ZStack {
|
||||||
if let damus = self.damus_state {
|
if let damus = self.damus_state {
|
||||||
TimelineView(events: home.events, loading: $home.loading, damus: damus, show_friend_icon: false, filter: filter)
|
TimelineView(events: home.events, loading: .constant(false), damus: damus, show_friend_icon: false, filter: filter)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -308,7 +308,7 @@ struct ContentView: View {
|
|||||||
}
|
}
|
||||||
.navigationViewStyle(.stack)
|
.navigationViewStyle(.stack)
|
||||||
|
|
||||||
TabBar(new_events: $home.new_events, selected: $selected_timeline, settings: damus.settings, action: switch_timeline)
|
TabBar(nstatus: home.notification_status, selected: $selected_timeline, settings: damus.settings, action: switch_timeline)
|
||||||
.padding([.bottom], 8)
|
.padding([.bottom], 8)
|
||||||
.background(Color(uiColor: .systemBackground).ignoresSafeArea())
|
.background(Color(uiColor: .systemBackground).ignoresSafeArea())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ struct NewEventsBits: OptionSet {
|
|||||||
static let notifications: NewEventsBits = [.zaps, .likes, .reposts, .mentions]
|
static let notifications: NewEventsBits = [.zaps, .likes, .reposts, .mentions]
|
||||||
}
|
}
|
||||||
|
|
||||||
class HomeModel: ObservableObject {
|
class HomeModel {
|
||||||
// Don't trigger a user notification for events older than a certain age
|
// Don't trigger a user notification for events older than a certain age
|
||||||
static let event_max_age_for_notification: TimeInterval = 12 * 60 * 60
|
static let event_max_age_for_notification: TimeInterval = 12 * 60 * 60
|
||||||
|
|
||||||
@@ -49,9 +49,9 @@ class HomeModel: ObservableObject {
|
|||||||
|
|
||||||
var signal = SignalModel()
|
var signal = SignalModel()
|
||||||
|
|
||||||
@Published var new_events: NewEventsBits = NewEventsBits()
|
var notifications = NotificationsModel()
|
||||||
@Published var notifications = NotificationsModel()
|
var notification_status = NotificationStatusModel()
|
||||||
@Published var events: EventHolder = EventHolder()
|
var events: EventHolder = EventHolder()
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
self.damus_state = DamusState.empty
|
self.damus_state = DamusState.empty
|
||||||
@@ -176,7 +176,7 @@ class HomeModel: ObservableObject {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
guard let new_bits = handle_last_events(new_events: self.new_events, ev: ev, timeline: .notifications, shouldNotify: true) else {
|
guard let new_bits = handle_last_events(new_events: self.notification_status.new_events, ev: ev, timeline: .notifications, shouldNotify: true) else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -195,7 +195,7 @@ class HomeModel: ObservableObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.new_events = new_bits
|
self.notification_status.new_events = new_bits
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -523,8 +523,8 @@ class HomeModel: ObservableObject {
|
|||||||
|
|
||||||
@discardableResult
|
@discardableResult
|
||||||
func handle_last_event(ev: NostrEvent, timeline: Timeline, shouldNotify: Bool = true) -> Bool {
|
func handle_last_event(ev: NostrEvent, timeline: Timeline, shouldNotify: Bool = true) -> Bool {
|
||||||
if let new_bits = handle_last_events(new_events: self.new_events, ev: ev, timeline: timeline, shouldNotify: shouldNotify) {
|
if let new_bits = handle_last_events(new_events: self.notification_status.new_events, ev: ev, timeline: timeline, shouldNotify: shouldNotify) {
|
||||||
new_events = new_bits
|
self.notification_status.new_events = new_bits
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
@@ -556,7 +556,7 @@ class HomeModel: ObservableObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func got_new_dm(notifs: NewEventsBits, ev: NostrEvent) {
|
func got_new_dm(notifs: NewEventsBits, ev: NostrEvent) {
|
||||||
self.new_events = notifs
|
notification_status.new_events = notifs
|
||||||
|
|
||||||
if damus_state.settings.dm_notification && ev.age < HomeModel.event_max_age_for_notification {
|
if damus_state.settings.dm_notification && ev.age < HomeModel.event_max_age_for_notification {
|
||||||
let convo = ev.decrypted(privkey: self.damus_state.keypair.privkey) ?? NSLocalizedString("New encrypted direct message", comment: "Notification that the user has received a new direct message")
|
let convo = ev.decrypted(privkey: self.damus_state.keypair.privkey) ?? NSLocalizedString("New encrypted direct message", comment: "Notification that the user has received a new direct message")
|
||||||
@@ -574,7 +574,7 @@ class HomeModel: ObservableObject {
|
|||||||
|
|
||||||
if !should_debounce_dms {
|
if !should_debounce_dms {
|
||||||
self.incoming_dms.append(ev)
|
self.incoming_dms.append(ev)
|
||||||
if let notifs = handle_incoming_dms(prev_events: self.new_events, dms: self.dms, our_pubkey: self.damus_state.pubkey, evs: self.incoming_dms) {
|
if let notifs = handle_incoming_dms(prev_events: notification_status.new_events, dms: self.dms, our_pubkey: self.damus_state.pubkey, evs: self.incoming_dms) {
|
||||||
got_new_dm(notifs: notifs, ev: ev)
|
got_new_dm(notifs: notifs, ev: ev)
|
||||||
}
|
}
|
||||||
self.incoming_dms = []
|
self.incoming_dms = []
|
||||||
@@ -584,7 +584,7 @@ class HomeModel: ObservableObject {
|
|||||||
incoming_dms.append(ev)
|
incoming_dms.append(ev)
|
||||||
|
|
||||||
dm_debouncer.debounce { [self] in
|
dm_debouncer.debounce { [self] in
|
||||||
if let notifs = handle_incoming_dms(prev_events: self.new_events, dms: self.dms, our_pubkey: self.damus_state.pubkey, evs: self.incoming_dms) {
|
if let notifs = handle_incoming_dms(prev_events: notification_status.new_events, dms: self.dms, our_pubkey: self.damus_state.pubkey, evs: self.incoming_dms) {
|
||||||
got_new_dm(notifs: notifs, ev: ev)
|
got_new_dm(notifs: notifs, ev: ev)
|
||||||
}
|
}
|
||||||
self.incoming_dms = []
|
self.incoming_dms = []
|
||||||
|
|||||||
12
damus/Models/Notifications/NotificationStatusModel.swift
Normal file
12
damus/Models/Notifications/NotificationStatusModel.swift
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
//
|
||||||
|
// NotificationStatusModel.swift
|
||||||
|
// damus
|
||||||
|
//
|
||||||
|
// Created by William Casarin on 2023-06-23.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
class NotificationStatusModel: ObservableObject {
|
||||||
|
@Published var new_events: NewEventsBits = NewEventsBits()
|
||||||
|
}
|
||||||
@@ -29,7 +29,7 @@ struct TabButton: View {
|
|||||||
let timeline: Timeline
|
let timeline: Timeline
|
||||||
let img: String
|
let img: String
|
||||||
@Binding var selected: Timeline
|
@Binding var selected: Timeline
|
||||||
@Binding var new_events: NewEventsBits
|
@ObservedObject var nstatus: NotificationStatusModel
|
||||||
|
|
||||||
let settings: UserSettingsStore
|
let settings: UserSettingsStore
|
||||||
let action: (Timeline) -> ()
|
let action: (Timeline) -> ()
|
||||||
@@ -38,7 +38,7 @@ struct TabButton: View {
|
|||||||
ZStack(alignment: .center) {
|
ZStack(alignment: .center) {
|
||||||
Tab
|
Tab
|
||||||
|
|
||||||
if show_indicator(timeline: timeline, current: new_events, indicator_setting: settings.notification_indicators) {
|
if show_indicator(timeline: timeline, current: nstatus.new_events, indicator_setting: settings.notification_indicators) {
|
||||||
Circle()
|
Circle()
|
||||||
.size(CGSize(width: 8, height: 8))
|
.size(CGSize(width: 8, height: 8))
|
||||||
.frame(width: 10, height: 10, alignment: .topTrailing)
|
.frame(width: 10, height: 10, alignment: .topTrailing)
|
||||||
@@ -53,7 +53,7 @@ struct TabButton: View {
|
|||||||
Button(action: {
|
Button(action: {
|
||||||
action(timeline)
|
action(timeline)
|
||||||
let bits = timeline_to_notification_bits(timeline, ev: nil)
|
let bits = timeline_to_notification_bits(timeline, ev: nil)
|
||||||
new_events = NewEventsBits(rawValue: new_events.rawValue & ~bits.rawValue)
|
nstatus.new_events = NewEventsBits(rawValue: nstatus.new_events.rawValue & ~bits.rawValue)
|
||||||
}) {
|
}) {
|
||||||
Image(selected != timeline ? img : "\(img).fill")
|
Image(selected != timeline ? img : "\(img).fill")
|
||||||
.contentShape(Rectangle())
|
.contentShape(Rectangle())
|
||||||
@@ -65,7 +65,7 @@ struct TabButton: View {
|
|||||||
|
|
||||||
|
|
||||||
struct TabBar: View {
|
struct TabBar: View {
|
||||||
@Binding var new_events: NewEventsBits
|
var nstatus: NotificationStatusModel
|
||||||
@Binding var selected: Timeline
|
@Binding var selected: Timeline
|
||||||
|
|
||||||
let settings: UserSettingsStore
|
let settings: UserSettingsStore
|
||||||
@@ -75,10 +75,10 @@ struct TabBar: View {
|
|||||||
VStack {
|
VStack {
|
||||||
Divider()
|
Divider()
|
||||||
HStack {
|
HStack {
|
||||||
TabButton(timeline: .home, img: "home", selected: $selected, new_events: $new_events, settings: settings, action: action).keyboardShortcut("1")
|
TabButton(timeline: .home, img: "home", selected: $selected, nstatus: nstatus, settings: settings, action: action).keyboardShortcut("1")
|
||||||
TabButton(timeline: .dms, img: "messages", selected: $selected, new_events: $new_events, settings: settings, action: action).keyboardShortcut("2")
|
TabButton(timeline: .dms, img: "messages", selected: $selected, nstatus: nstatus, settings: settings, action: action).keyboardShortcut("2")
|
||||||
TabButton(timeline: .search, img: "search", selected: $selected, new_events: $new_events, settings: settings, action: action).keyboardShortcut("3")
|
TabButton(timeline: .search, img: "search", selected: $selected, nstatus: nstatus, settings: settings, action: action).keyboardShortcut("3")
|
||||||
TabButton(timeline: .notifications, img: "notification-bell", selected: $selected, new_events: $new_events, settings: settings, action: action).keyboardShortcut("4")
|
TabButton(timeline: .notifications, img: "notification-bell", selected: $selected, nstatus: nstatus, settings: settings, action: action).keyboardShortcut("4")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user