mirror of https://github.com/oxen-io/session-ios
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
119 lines
4.9 KiB
Swift
119 lines
4.9 KiB
Swift
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
|
|
|
import Foundation
|
|
import GRDB
|
|
import DifferenceKit
|
|
import SignalUtilitiesKit
|
|
|
|
public class HomeViewModel {
|
|
public enum Section: Differentiable {
|
|
case messageRequests
|
|
case threads
|
|
}
|
|
|
|
public struct State: Equatable {
|
|
let showViewedSeedBanner: Bool
|
|
let userProfile: Profile?
|
|
let sections: [ArraySection<Section, SessionThreadViewModel>]
|
|
|
|
func with(
|
|
showViewedSeedBanner: Bool? = nil,
|
|
userProfile: Profile? = nil,
|
|
sections: [ArraySection<Section, SessionThreadViewModel>]? = nil
|
|
) -> State {
|
|
return State(
|
|
showViewedSeedBanner: (showViewedSeedBanner ?? self.showViewedSeedBanner),
|
|
userProfile: (userProfile ?? self.userProfile),
|
|
sections: (sections ?? self.sections)
|
|
)
|
|
}
|
|
}
|
|
|
|
/// This value is the current state of the view
|
|
public private(set) var state: State = State(
|
|
showViewedSeedBanner: !GRDBStorage.shared[.hasViewedSeed],
|
|
userProfile: nil,
|
|
sections: []
|
|
)
|
|
|
|
/// This is all the data the screen needs to populate itself, please see the following link for tips to help optimise
|
|
/// performance https://github.com/groue/GRDB.swift#valueobservation-performance
|
|
///
|
|
/// **Note:** This observation will be triggered twice immediately (and be de-duped by the `removeDuplicates`)
|
|
/// this is due to the behaviour of `ValueConcurrentObserver.asyncStartObservation` which triggers it's own
|
|
/// fetch (after the ones in `ValueConcurrentObserver.asyncStart`/`ValueConcurrentObserver.syncStart`)
|
|
/// just in case the database has changed between the two reads - unfortunately it doesn't look like there is a way to prevent this
|
|
public lazy var observableState = ValueObservation
|
|
.tracking(
|
|
regions: [
|
|
// We explicitly define the regions we want to track as the automatic detection
|
|
// seems to include a bunch of columns we will fetch but probably don't need to
|
|
// track changes for
|
|
SessionThread.select(
|
|
.id,
|
|
.shouldBeVisible,
|
|
.isPinned,
|
|
.mutedUntilTimestamp,
|
|
.onlyNotifyForMentions
|
|
),
|
|
Setting.filter(ids: [
|
|
Setting.BoolKey.hasHiddenMessageRequests.rawValue,
|
|
Setting.BoolKey.hasViewedSeed.rawValue
|
|
]),
|
|
Contact.select(.isBlocked, .isApproved), // 'isApproved' for message requests
|
|
Profile.select(.name, .nickname, .profilePictureFileName),
|
|
ClosedGroup.select(.name),
|
|
OpenGroup.select(.name, .imageData),
|
|
GroupMember.select(.groupId),
|
|
Interaction.select(
|
|
.body,
|
|
.wasRead
|
|
),
|
|
Attachment.select(.state),
|
|
RecipientState.select(.state),
|
|
ThreadTypingIndicator.select(.threadId)
|
|
],
|
|
fetch: { db -> State in
|
|
let hasViewedSeed: Bool = db[.hasViewedSeed]
|
|
let userProfile: Profile = Profile.fetchOrCreateCurrentUser(db)
|
|
let unreadMessageRequestCount: Int = try SessionThread
|
|
.unreadMessageRequestsQuery(userPublicKey: userProfile.id)
|
|
.fetchCount(db)
|
|
let finalUnreadMessageRequestCount: Int = (db[.hasHiddenMessageRequests] ? 0 : unreadMessageRequestCount)
|
|
let threads: [SessionThreadViewModel] = try SessionThreadViewModel
|
|
.homeQuery(userPublicKey: userProfile.id)
|
|
.fetchAll(db)
|
|
|
|
return State(
|
|
showViewedSeedBanner: !hasViewedSeed,
|
|
userProfile: userProfile,
|
|
sections: [
|
|
ArraySection(
|
|
model: .messageRequests,
|
|
elements: [
|
|
// If there are no unread message requests then hide the message request banner
|
|
(finalUnreadMessageRequestCount == 0 ?
|
|
nil :
|
|
SessionThreadViewModel(
|
|
unreadCount: UInt(finalUnreadMessageRequestCount)
|
|
)
|
|
)
|
|
].compactMap { $0 }
|
|
),
|
|
ArraySection(
|
|
model: .threads,
|
|
elements: threads
|
|
)
|
|
]
|
|
)
|
|
}
|
|
)
|
|
.removeDuplicates()
|
|
|
|
// MARK: - Functions
|
|
|
|
public func updateState(_ updatedState: State) {
|
|
self.state = updatedState
|
|
}
|
|
}
|