|
|
|
@ -8,7 +8,7 @@ import SessionUIKit
|
|
|
|
|
import SignalUtilitiesKit
|
|
|
|
|
import SessionUtilitiesKit
|
|
|
|
|
|
|
|
|
|
class BlockedContactsViewModel: SessionTableViewModel<NoNav, BlockedContactsViewModel.Section, Profile> {
|
|
|
|
|
class BlockedContactsViewModel: SessionTableViewModel<NoNav, BlockedContactsViewModel.Section, BlockedContactsViewModel.DataModel> {
|
|
|
|
|
// MARK: - Section
|
|
|
|
|
|
|
|
|
|
public enum Section: SessionTableSection {
|
|
|
|
@ -39,10 +39,14 @@ class BlockedContactsViewModel: SessionTableViewModel<NoNav, BlockedContactsView
|
|
|
|
|
// doesn't stutter (it should load basically immediately but without this there is a
|
|
|
|
|
// distinct stutter)
|
|
|
|
|
_pagedDataObserver = PagedDatabaseObserver(
|
|
|
|
|
pagedTable: Profile.self,
|
|
|
|
|
pagedTable: Contact.self,
|
|
|
|
|
pageSize: BlockedContactsViewModel.pageSize,
|
|
|
|
|
idColumn: .id,
|
|
|
|
|
observedChanges: [
|
|
|
|
|
PagedData.ObservedChanges(
|
|
|
|
|
table: Contact.self,
|
|
|
|
|
columns: [.id, .isBlocked]
|
|
|
|
|
),
|
|
|
|
|
PagedData.ObservedChanges(
|
|
|
|
|
table: Profile.self,
|
|
|
|
|
columns: [
|
|
|
|
@ -50,16 +54,12 @@ class BlockedContactsViewModel: SessionTableViewModel<NoNav, BlockedContactsView
|
|
|
|
|
.name,
|
|
|
|
|
.nickname,
|
|
|
|
|
.profilePictureFileName
|
|
|
|
|
]
|
|
|
|
|
),
|
|
|
|
|
PagedData.ObservedChanges(
|
|
|
|
|
table: Contact.self,
|
|
|
|
|
columns: [.isBlocked],
|
|
|
|
|
],
|
|
|
|
|
joinToPagedType: {
|
|
|
|
|
let profile: TypedTableAlias<Profile> = TypedTableAlias()
|
|
|
|
|
let contact: TypedTableAlias<Contact> = TypedTableAlias()
|
|
|
|
|
let profile: TypedTableAlias<Profile> = TypedTableAlias()
|
|
|
|
|
|
|
|
|
|
return SQL("JOIN \(Contact.self) ON \(contact[.id]) = \(profile[.id])")
|
|
|
|
|
return SQL("JOIN \(Profile.self) ON \(profile[.id]) = \(contact[.id])")
|
|
|
|
|
}()
|
|
|
|
|
)
|
|
|
|
|
],
|
|
|
|
@ -101,7 +101,7 @@ class BlockedContactsViewModel: SessionTableViewModel<NoNav, BlockedContactsView
|
|
|
|
|
|
|
|
|
|
private let contactDataSubject: CurrentValueSubject<([SectionModel], StagedChangeset<[SectionModel]>), Never> = CurrentValueSubject(([], StagedChangeset()))
|
|
|
|
|
private let selectedContactIdsSubject: CurrentValueSubject<Set<String>, Never> = CurrentValueSubject([])
|
|
|
|
|
private var _pagedDataObserver: PagedDatabaseObserver<Profile, DataModel>?
|
|
|
|
|
private var _pagedDataObserver: PagedDatabaseObserver<Contact, DataModel>?
|
|
|
|
|
public override var pagedDataObserver: TransactionObserver? { _pagedDataObserver }
|
|
|
|
|
|
|
|
|
|
public override var observableTableData: ObservableData { _observableTableData }
|
|
|
|
@ -138,26 +138,32 @@ class BlockedContactsViewModel: SessionTableViewModel<NoNav, BlockedContactsView
|
|
|
|
|
section: .contacts,
|
|
|
|
|
elements: data
|
|
|
|
|
.sorted { lhs, rhs -> Bool in
|
|
|
|
|
lhs.profile.displayName() < rhs.profile.displayName()
|
|
|
|
|
let lhsValue: String = (lhs.profile?.displayName() ?? lhs.id)
|
|
|
|
|
let rhsValue: String = (rhs.profile?.displayName() ?? rhs.id)
|
|
|
|
|
|
|
|
|
|
return (lhsValue < rhsValue)
|
|
|
|
|
}
|
|
|
|
|
.map { [weak self] model -> SessionCell.Info<Profile> in
|
|
|
|
|
.map { [weak self] model -> SessionCell.Info<DataModel> in
|
|
|
|
|
SessionCell.Info(
|
|
|
|
|
id: model.profile,
|
|
|
|
|
leftAccessory: .profile(id: model.profile.id, profile: model.profile),
|
|
|
|
|
title: model.profile.displayName(),
|
|
|
|
|
id: model,
|
|
|
|
|
leftAccessory: .profile(id: model.id, profile: model.profile),
|
|
|
|
|
title: (
|
|
|
|
|
model.profile?.displayName() ??
|
|
|
|
|
Profile.truncated(id: model.id, truncating: .middle)
|
|
|
|
|
),
|
|
|
|
|
rightAccessory: .radio(
|
|
|
|
|
isSelected: {
|
|
|
|
|
self?.selectedContactIdsSubject.value.contains(model.profile.id) == true
|
|
|
|
|
self?.selectedContactIdsSubject.value.contains(model.id) == true
|
|
|
|
|
}
|
|
|
|
|
),
|
|
|
|
|
onTap: {
|
|
|
|
|
var updatedSelectedIds: Set<String> = (self?.selectedContactIdsSubject.value ?? [])
|
|
|
|
|
|
|
|
|
|
if !updatedSelectedIds.contains(model.profile.id) {
|
|
|
|
|
updatedSelectedIds.insert(model.profile.id)
|
|
|
|
|
if !updatedSelectedIds.contains(model.id) {
|
|
|
|
|
updatedSelectedIds.insert(model.id)
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
updatedSelectedIds.remove(model.profile.id)
|
|
|
|
|
updatedSelectedIds.remove(model.id)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
self?.selectedContactIdsSubject.send(updatedSelectedIds)
|
|
|
|
@ -182,7 +188,7 @@ class BlockedContactsViewModel: SessionTableViewModel<NoNav, BlockedContactsView
|
|
|
|
|
guard
|
|
|
|
|
let section: BlockedContactsViewModel.SectionModel = self.tableData
|
|
|
|
|
.first(where: { section in section.model == .contacts }),
|
|
|
|
|
let info: SessionCell.Info<Profile> = section.elements
|
|
|
|
|
let info: SessionCell.Info<DataModel> = section.elements
|
|
|
|
|
.first(where: { info in info.id.id == contactId })
|
|
|
|
|
else { return contactId }
|
|
|
|
|
|
|
|
|
@ -262,20 +268,22 @@ class BlockedContactsViewModel: SessionTableViewModel<NoNav, BlockedContactsView
|
|
|
|
|
public typealias Columns = CodingKeys
|
|
|
|
|
public enum CodingKeys: String, CodingKey, ColumnExpression, CaseIterable {
|
|
|
|
|
case rowId
|
|
|
|
|
case id
|
|
|
|
|
case profile
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public var differenceIdentifier: String { profile.id }
|
|
|
|
|
public var id: String { profile.id }
|
|
|
|
|
public var differenceIdentifier: String { id }
|
|
|
|
|
|
|
|
|
|
public let rowId: Int64
|
|
|
|
|
public let profile: Profile
|
|
|
|
|
public let id: String
|
|
|
|
|
public let profile: Profile?
|
|
|
|
|
|
|
|
|
|
static func query(
|
|
|
|
|
filterSQL: SQL,
|
|
|
|
|
orderSQL: SQL
|
|
|
|
|
) -> (([Int64]) -> any FetchRequest<DataModel>) {
|
|
|
|
|
return { rowIds -> any FetchRequest<DataModel> in
|
|
|
|
|
let contact: TypedTableAlias<Contact> = TypedTableAlias()
|
|
|
|
|
let profile: TypedTableAlias<Profile> = TypedTableAlias()
|
|
|
|
|
|
|
|
|
|
/// **Note:** The `numColumnsBeforeProfile` value **MUST** match the number of fields before
|
|
|
|
@ -283,15 +291,17 @@ class BlockedContactsViewModel: SessionTableViewModel<NoNav, BlockedContactsView
|
|
|
|
|
/// parse and might throw
|
|
|
|
|
///
|
|
|
|
|
/// Explicitly set default values for the fields ignored for search results
|
|
|
|
|
let numColumnsBeforeProfile: Int = 1
|
|
|
|
|
let numColumnsBeforeProfile: Int = 2
|
|
|
|
|
|
|
|
|
|
let request: SQLRequest<DataModel> = """
|
|
|
|
|
SELECT
|
|
|
|
|
\(profile[.rowId]) AS \(DataModel.Columns.rowId),
|
|
|
|
|
\(contact[.rowId]) AS \(DataModel.Columns.rowId),
|
|
|
|
|
\(contact[.id]),
|
|
|
|
|
\(profile.allColumns)
|
|
|
|
|
|
|
|
|
|
FROM \(Profile.self)
|
|
|
|
|
WHERE \(profile[.rowId]) IN \(rowIds)
|
|
|
|
|
FROM \(Contact.self)
|
|
|
|
|
LEFT JOIN \(Profile.self) ON \(profile[.id]) = \(contact[.id])
|
|
|
|
|
WHERE \(contact[.rowId]) IN \(rowIds)
|
|
|
|
|
ORDER BY \(orderSQL)
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
@ -309,10 +319,10 @@ class BlockedContactsViewModel: SessionTableViewModel<NoNav, BlockedContactsView
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static var optimisedJoinSQL: SQL = {
|
|
|
|
|
let profile: TypedTableAlias<Profile> = TypedTableAlias()
|
|
|
|
|
let contact: TypedTableAlias<Contact> = TypedTableAlias()
|
|
|
|
|
let profile: TypedTableAlias<Profile> = TypedTableAlias()
|
|
|
|
|
|
|
|
|
|
return SQL("JOIN \(Contact.self) ON \(contact[.id]) = \(profile[.id])")
|
|
|
|
|
return SQL("LEFT JOIN \(Profile.self) ON \(profile[.id]) = \(contact[.id])")
|
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
static var filterSQL: SQL = {
|
|
|
|
@ -322,9 +332,10 @@ class BlockedContactsViewModel: SessionTableViewModel<NoNav, BlockedContactsView
|
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
static let orderSQL: SQL = {
|
|
|
|
|
let contact: TypedTableAlias<Contact> = TypedTableAlias()
|
|
|
|
|
let profile: TypedTableAlias<Profile> = TypedTableAlias()
|
|
|
|
|
|
|
|
|
|
return SQL("IFNULL(IFNULL(\(profile[.nickname]), \(profile[.name])), \(profile[.id])) ASC")
|
|
|
|
|
return SQL("IFNULL(IFNULL(\(profile[.nickname]), \(profile[.name])), \(contact[.id])) ASC")
|
|
|
|
|
}()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|