Merge branch 'dev' into standardised-strings

pull/1023/head
Ryan ZHAO 7 months ago
commit 5c18019e6b

@ -7618,7 +7618,7 @@
CLANG_WARN__ARC_BRIDGE_CAST_NONARC = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "iPhone Developer";
CURRENT_PROJECT_VERSION = 475;
CURRENT_PROJECT_VERSION = 476;
ENABLE_BITCODE = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
@ -7655,7 +7655,7 @@
GCC_WARN_UNUSED_VARIABLE = YES;
HEADER_SEARCH_PATHS = "";
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MARKETING_VERSION = 2.7.1;
MARKETING_VERSION = 2.7.2;
ONLY_ACTIVE_ARCH = YES;
OTHER_CFLAGS = (
"-fobjc-arc-exceptions",
@ -7696,7 +7696,7 @@
CLANG_WARN__ARC_BRIDGE_CAST_NONARC = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "iPhone Distribution";
CURRENT_PROJECT_VERSION = 475;
CURRENT_PROJECT_VERSION = 476;
ENABLE_BITCODE = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_NO_COMMON_BLOCKS = YES;
@ -7728,7 +7728,7 @@
GCC_WARN_UNUSED_VARIABLE = YES;
HEADER_SEARCH_PATHS = "";
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MARKETING_VERSION = 2.7.1;
MARKETING_VERSION = 2.7.2;
ONLY_ACTIVE_ARCH = NO;
OTHER_CFLAGS = (
"-DNS_BLOCK_ASSERTIONS=1",

@ -192,7 +192,7 @@ class ThreadDisappearingMessagesSettingsViewModel: SessionTableViewModel, Naviga
(self?.currentSelection.value.type == .disappearAfterSend)
},
accessibility: Accessibility(
identifier: "Disappear After Read - Radio"
identifier: "Disappear After Send - Radio"
)
),
accessibility: Accessibility(

@ -101,11 +101,11 @@ class MessageRequestsViewModel: SessionTableViewModel, NavigatableStateHolder, O
orderSQL: SessionThreadViewModel.messageRequetsOrderSQL
),
onChangeUnsorted: { [weak self] updatedData, updatedPageInfo in
PagedData.processAndTriggerUpdates(
updatedData: self?.process(data: updatedData, for: updatedPageInfo),
currentDataRetriever: { self?.tableData },
valueSubject: self?.pendingTableDataSubject
)
guard let data: [SectionModel] = self?.process(data: updatedData, for: updatedPageInfo) else {
return
}
self?.pendingTableDataSubject.send(data)
}
)
@ -188,7 +188,7 @@ class MessageRequestsViewModel: SessionTableViewModel, NavigatableStateHolder, O
lazy var footerButtonInfo: AnyPublisher<SessionButton.Info?, Never> = observableState
.pendingTableDataSubject
.map { [dependencies] (currentThreadData: [SectionModel], _: StagedChangeset<[SectionModel]>) in
.map { [dependencies] (currentThreadData: [SectionModel]) in
let threadInfo: [(id: String, variant: SessionThread.Variant)] = (currentThreadData
.first(where: { $0.model == .threads })?
.elements

@ -62,12 +62,12 @@ public class BlockedContactsViewModel: SessionTableViewModel, NavigatableStateHo
orderSQL: TableItem.orderSQL
),
onChangeUnsorted: { [weak self] updatedData, updatedPageInfo in
PagedData.processAndTriggerUpdates(
updatedData: self?.process(data: updatedData, for: updatedPageInfo)
.mapToSessionTableViewData(for: self), // Update the cell positions for background rounding
currentDataRetriever: { self?.tableData },
valueSubject: self?.pendingTableDataSubject
)
guard
let data: [SectionModel] = self?.process(data: updatedData, for: updatedPageInfo)
.mapToSessionTableViewData(for: self) // Update the cell positions for background rounding
else { return }
self?.pendingTableDataSubject.send(data)
}
)

@ -226,9 +226,9 @@ class SessionTableViewController<ViewModel>: BaseVC, UITableViewDataSource, UITa
case .finished: break
}
},
receiveValue: { [weak self] updatedData, changeset in
receiveValue: { [weak self] updatedData in
self?.dataStreamJustFailed = false
self?.handleDataUpdates(updatedData, changeset: changeset)
self?.handleDataUpdates(updatedData)
}
)
@ -241,57 +241,28 @@ class SessionTableViewController<ViewModel>: BaseVC, UITableViewDataSource, UITa
dataChangeCancellable?.cancel()
}
private func handleDataUpdates(
_ updatedData: [SectionModel],
changeset: StagedChangeset<[SectionModel]>,
initialLoad: Bool = false
) {
private func handleDataUpdates(_ updatedData: [SectionModel]) {
// Determine if we have any items for the empty state
let itemCount: Int = updatedData
.map { $0.elements.count }
.reduce(0, +)
// Ensure the first load runs without animations (if we don't do this the cells will animate
// in from a frame of CGRect.zero)
guard hasLoadedInitialTableData else {
UIView.performWithoutAnimation {
// Update the initial/empty state
initialLoadLabel.isHidden = true
emptyStateLabel.isHidden = (itemCount > 0)
// Update the content
viewModel.updateTableData(updatedData)
tableView.reloadData()
hasLoadedInitialTableData = true
}
return
}
// Update the empty state
self.emptyStateLabel.isHidden = (itemCount > 0)
CATransaction.begin()
CATransaction.setCompletionBlock { [weak self] in
// Ensure the reloads run without animations (if we don't do this the cells will animate
// in from a frame of CGRect.zero on at least the first load)
UIView.performWithoutAnimation {
// Update the initial/empty state
initialLoadLabel.isHidden = true
emptyStateLabel.isHidden = (itemCount > 0)
// Update the content
viewModel.updateTableData(updatedData)
tableView.reloadData()
hasLoadedInitialTableData = true
// Complete page loading
self?.isLoadingMore = false
self?.autoLoadNextPageIfNeeded()
isLoadingMore = false
autoLoadNextPageIfNeeded()
}
// Reload the table content (animate changes after the first load)
tableView.reload(
using: changeset,
deleteSectionsAnimation: .none,
insertSectionsAnimation: .none,
reloadSectionsAnimation: .none,
deleteRowsAnimation: .fade,
insertRowsAnimation: .fade,
reloadRowsAnimation: .none,
interrupt: { $0.changeCount > 100 } // Prevent too many changes from causing performance issues
) { [weak self] updatedData in
self?.viewModel.updateTableData(updatedData)
}
CATransaction.commit()
}
private func autoLoadNextPageIfNeeded() {

@ -10,7 +10,7 @@ import SessionUtilitiesKit
public protocol ObservableTableSource: AnyObject, SectionedTableData {
typealias TargetObservation = TableObservation<[SectionModel]>
typealias TargetPublisher = AnyPublisher<(([SectionModel], StagedChangeset<[SectionModel]>)), Error>
typealias TargetPublisher = AnyPublisher<[SectionModel], Error>
var dependencies: Dependencies { get }
var state: TableDataState<Section, TableItem> { get }
@ -23,11 +23,11 @@ public protocol ObservableTableSource: AnyObject, SectionedTableData {
}
extension ObservableTableSource {
public var pendingTableDataSubject: CurrentValueSubject<([SectionModel], StagedChangeset<[SectionModel]>), Never> {
public var pendingTableDataSubject: CurrentValueSubject<[SectionModel], Never> {
self.observableState.pendingTableDataSubject
}
public var observation: TargetObservation {
ObservationBuilder.changesetSubject(self.observableState.pendingTableDataSubject)
ObservationBuilder.subject(self.observableState.pendingTableDataSubject)
}
public var tableDataPublisher: TargetPublisher { self.observation.finalPublisher(self, using: dependencies) }
@ -40,7 +40,7 @@ extension ObservableTableSource {
public class ObservableTableSourceState<Section: SessionTableSection, TableItem: Hashable & Differentiable>: SectionedTableData {
public let forcedRefresh: AnyPublisher<Void, Never>
public let pendingTableDataSubject: CurrentValueSubject<([SectionModel], StagedChangeset<[SectionModel]>), Never>
public let pendingTableDataSubject: CurrentValueSubject<[SectionModel], Never>
// MARK: - Internal Variables
@ -52,7 +52,7 @@ public class ObservableTableSourceState<Section: SessionTableSection, TableItem:
init() {
self.hasEmittedInitialData = false
self.forcedRefresh = _forcedRefresh.shareReplay(0)
self.pendingTableDataSubject = CurrentValueSubject(([], StagedChangeset()))
self.pendingTableDataSubject = CurrentValueSubject([])
}
}
@ -78,7 +78,7 @@ public struct TableObservation<T> {
_ source: S,
using dependencies: Dependencies
) -> S.TargetPublisher {
typealias TargetData = (([S.SectionModel], StagedChangeset<[S.SectionModel]>))
typealias TargetData = [S.SectionModel]
switch (self, self.generatePublisherWithChangeset) {
case (_, .some(let generatePublisherWithChangeset)):
@ -177,29 +177,6 @@ public enum ObservationBuilder {
.manualRefreshFrom(source.observableState.forcedRefresh)
}
}
/// The `changesetSubject` will emit immediately when there is a subscriber and store the most recent value to be emitted whenever a new subscriber is
/// added
static func changesetSubject<T>(
_ subject: CurrentValueSubject<([T], StagedChangeset<[T]>), Never>
) -> TableObservation<[T]> {
return TableObservation { viewModel, dependencies in
subject
.withPrevious(([], StagedChangeset()))
.handleEvents(
receiveCancel: {
/// When we unsubscribe we send through the existing data but clear out the `StagedChangeset` value
/// so that resubscribing doesn't result in the UI trying to reapply the same changeset (which would cause a
/// crash due to invalid table view changes)
subject.send((subject.value.0, StagedChangeset()))
}
)
.map { _, current -> ([T], StagedChangeset<[T]>) in current }
.setFailureType(to: Error.self)
.shareReplay(1)
.eraseToAnyPublisher()
}
}
}
// MARK: - Convenience Transforms
@ -249,27 +226,11 @@ public extension Array {
public extension Publisher {
func mapToSessionTableViewData<S: ObservableTableSource>(
for source: S
) -> AnyPublisher<(Output, StagedChangeset<Output>), Failure> where Output == [ArraySection<S.Section, SessionCell.Info<S.TableItem>>] {
) -> AnyPublisher<Output, Failure> where Output == [ArraySection<S.Section, SessionCell.Info<S.TableItem>>] {
return self
.map { [weak source] updatedData -> (Output, StagedChangeset<Output>) in
let updatedDataWithPositions: Output = updatedData
.mapToSessionTableViewData(for: source)
// Generate an updated changeset
let changeset = StagedChangeset(
source: (source?.state.tableData ?? []),
target: updatedDataWithPositions
)
return (updatedDataWithPositions, changeset)
}
.filter { [weak source] _, changeset in
source?.observableState.hasEmittedInitialData == false || // Always emit at least once
!changeset.isEmpty // Do nothing if there were no changes
.map { [weak source] updatedData -> Output in
updatedData.mapToSessionTableViewData(for: source)
}
.handleEvents(receiveOutput: { [weak source] _ in
source?.observableState.hasEmittedInitialData = true
})
.eraseToAnyPublisher()
}
}

@ -169,7 +169,6 @@ public extension UIContextualAction {
calledFromConfigHandling: false
)
}
viewController?.dismiss(animated: true, completion: nil)
completionHandler(true)
},
@ -304,7 +303,6 @@ public extension UIContextualAction {
.optimisticUpdate(
isBlocked: !threadIsBlocked
)
viewController?.dismiss(animated: true, completion: nil)
completionHandler(true)
// Delay the change to give the cell "unswipe" animation some time to complete
@ -440,7 +438,6 @@ public extension UIContextualAction {
}
}
viewController?.dismiss(animated: true, completion: nil)
completionHandler(true)
},
@ -529,7 +526,6 @@ public extension UIContextualAction {
calledFromConfigHandling: false
)
}
viewController?.dismiss(animated: true, completion: nil)
completionHandler(true)
},

@ -54,7 +54,7 @@ class ThreadDisappearingMessagesSettingsViewModelSpec: QuickSpec {
.receive(on: ImmediateScheduler.shared)
.sink(
receiveCompletion: { _ in },
receiveValue: { viewModel.updateTableData($0.0) }
receiveValue: { viewModel.updateTableData($0) }
)
]
@ -88,7 +88,10 @@ class ThreadDisappearingMessagesSettingsViewModelSpec: QuickSpec {
position: .top,
title: "off".localized(),
rightAccessory: .radio(
isSelected: { true }
isSelected: { true },
accessibility: Accessibility(
identifier: "Off - Radio"
)
),
accessibility: Accessibility(
identifier: "Disable disappearing messages (Off option)",
@ -107,7 +110,10 @@ class ThreadDisappearingMessagesSettingsViewModelSpec: QuickSpec {
title: "disappearingMessagesDisappearAfterSend".localized(),
subtitle: "disappearingMessagesDisappearAfterSendDescription".localized(),
rightAccessory: .radio(
isSelected: { false }
isSelected: { false },
accessibility: Accessibility(
identifier: "Disappear After Send - Radio"
)
),
accessibility: Accessibility(
identifier: "Disappear after send option",
@ -144,7 +150,7 @@ class ThreadDisappearingMessagesSettingsViewModelSpec: QuickSpec {
.receive(on: ImmediateScheduler.shared)
.sink(
receiveCompletion: { _ in },
receiveValue: { viewModel.updateTableData($0.0) }
receiveValue: { viewModel.updateTableData($0) }
)
)
@ -160,7 +166,10 @@ class ThreadDisappearingMessagesSettingsViewModelSpec: QuickSpec {
position: .top,
title: "off".localized(),
rightAccessory: .radio(
isSelected: { false }
isSelected: { false },
accessibility: Accessibility(
identifier: "Off - Radio"
)
),
accessibility: Accessibility(
identifier: "Disable disappearing messages (Off option)",
@ -179,7 +188,10 @@ class ThreadDisappearingMessagesSettingsViewModelSpec: QuickSpec {
title: "disappearingMessagesDisappearAfterSend".localized(),
subtitle: "disappearingMessagesDisappearAfterSendDescription".localized(),
rightAccessory: .radio(
isSelected: { true }
isSelected: { true },
accessibility: Accessibility(
identifier: "Disappear After Send - Radio"
)
),
accessibility: Accessibility(
identifier: "Disappear after send option",
@ -200,7 +212,10 @@ class ThreadDisappearingMessagesSettingsViewModelSpec: QuickSpec {
position: .bottom,
title: title,
rightAccessory: .radio(
isSelected: { true }
isSelected: { true },
accessibility: Accessibility(
identifier: "2 weeks - Radio"
)
),
accessibility: Accessibility(
identifier: "Time option",
@ -253,7 +268,7 @@ class ThreadDisappearingMessagesSettingsViewModelSpec: QuickSpec {
.receive(on: ImmediateScheduler.shared)
.sink(
receiveCompletion: { _ in },
receiveValue: { viewModel.updateTableData($0.0) }
receiveValue: { viewModel.updateTableData($0) }
)
)
@ -271,7 +286,10 @@ class ThreadDisappearingMessagesSettingsViewModelSpec: QuickSpec {
title: "disappearingMessagesDisappearAfterSend".localized(),
subtitle: "disappearingMessagesDisappearAfterSendDescription".localized(),
rightAccessory: .radio(
isSelected: { true }
isSelected: { true },
accessibility: Accessibility(
identifier: "Disappear After Send - Radio"
)
),
accessibility: Accessibility(
identifier: "Disappear after send option",
@ -292,7 +310,10 @@ class ThreadDisappearingMessagesSettingsViewModelSpec: QuickSpec {
position: .bottom,
title: title,
rightAccessory: .radio(
isSelected: { true }
isSelected: { true },
accessibility: Accessibility(
identifier: "2 weeks - Radio"
)
),
accessibility: Accessibility(
identifier: "Time option",

@ -67,7 +67,7 @@ class ThreadSettingsViewModelSpec: QuickSpec {
.receive(on: ImmediateScheduler.shared)
.sink(
receiveCompletion: { _ in },
receiveValue: { viewModel.updateTableData($0.0) }
receiveValue: { viewModel.updateTableData($0) }
)
]
@ -160,7 +160,7 @@ class ThreadSettingsViewModelSpec: QuickSpec {
.receive(on: ImmediateScheduler.shared)
.sink(
receiveCompletion: { _ in },
receiveValue: { viewModel.updateTableData($0.0) }
receiveValue: { viewModel.updateTableData($0) }
)
)
}
@ -459,7 +459,7 @@ class ThreadSettingsViewModelSpec: QuickSpec {
.receive(on: ImmediateScheduler.shared)
.sink(
receiveCompletion: { _ in },
receiveValue: { viewModel.updateTableData($0.0) }
receiveValue: { viewModel.updateTableData($0) }
)
)
}
@ -505,7 +505,7 @@ class ThreadSettingsViewModelSpec: QuickSpec {
.receive(on: ImmediateScheduler.shared)
.sink(
receiveCompletion: { _ in },
receiveValue: { viewModel.updateTableData($0.0) }
receiveValue: { viewModel.updateTableData($0) }
)
)
}

@ -39,7 +39,7 @@ class NotificationContentViewModelSpec: QuickSpec {
.receive(on: ImmediateScheduler.shared)
.sink(
receiveCompletion: { _ in },
receiveValue: { viewModel.updateTableData($0.0) }
receiveValue: { viewModel.updateTableData($0) }
)
@TestState var dismissCancellable: AnyCancellable?
@ -101,7 +101,7 @@ class NotificationContentViewModelSpec: QuickSpec {
.receive(on: ImmediateScheduler.shared)
.sink(
receiveCompletion: { _ in },
receiveValue: { viewModel.updateTableData($0.0) }
receiveValue: { viewModel.updateTableData($0) }
)
expect(viewModel.tableData.first?.elements)

Loading…
Cancel
Save