From 02cd514370c2d2ba68c0cf2b042ad8c6aa05734b Mon Sep 17 00:00:00 2001 From: Nora Trapp Date: Thu, 11 Aug 2022 13:06:46 -0700 Subject: [PATCH] Cleanup page view controller's access to multiple contexts --- .../StoryPageViewController.swift | 25 +++++++++---------- .../Stories/MyStoriesViewController.swift | 7 +++++- .../Stories/StoriesViewController.swift | 18 +++++++++---- 3 files changed, 31 insertions(+), 19 deletions(-) diff --git a/Signal/src/ViewControllers/HomeView/Stories/Context View/StoryPageViewController.swift b/Signal/src/ViewControllers/HomeView/Stories/Context View/StoryPageViewController.swift index 2bdd5fd801..af037a6f51 100644 --- a/Signal/src/ViewControllers/HomeView/Stories/Context View/StoryPageViewController.swift +++ b/Signal/src/ViewControllers/HomeView/Stories/Context View/StoryPageViewController.swift @@ -22,10 +22,8 @@ class StoryPageViewController: UIPageViewController { } let onlyRenderMyStories: Bool - weak var contextDataSource: StoryPageViewControllerDataSource? { - didSet { initiallyAvailableContexts = contextDataSource?.storyPageViewControllerAvailableContexts(self) ?? [currentContext] } - } - lazy var initiallyAvailableContexts: [StoryContext] = [currentContext] + weak var contextDataSource: StoryPageViewControllerDataSource? + let viewableContexts: [StoryContext] private var interactiveDismissCoordinator: StoryInteractiveTransitionCoordinator? private let audioActivity = AudioActivity(audioDescription: "StoriesViewer", behavior: .playbackMixWithOthers) @@ -40,8 +38,9 @@ class StoryPageViewController: UIPageViewController { // MARK: - Init - required init(context: StoryContext, loadMessage: StoryMessage? = nil, onlyRenderMyStories: Bool = false) { + required init(context: StoryContext, viewableContexts: [StoryContext]? = nil, loadMessage: StoryMessage? = nil, onlyRenderMyStories: Bool = false) { self.onlyRenderMyStories = onlyRenderMyStories + self.viewableContexts = viewableContexts ?? [context] super.init(transitionStyle: .scroll, navigationOrientation: .vertical, options: nil) self.currentContext = context currentContextViewController.loadMessage = loadMessage @@ -283,22 +282,22 @@ extension StoryPageViewController: UIPageViewControllerDataSource { extension StoryPageViewController: StoryContextViewControllerDelegate { var availableContexts: [StoryContext] { - guard let contextDataSource = contextDataSource else { return initiallyAvailableContexts } + guard let contextDataSource = contextDataSource else { return viewableContexts } let availableContexts = contextDataSource.storyPageViewControllerAvailableContexts(self) - return initiallyAvailableContexts.filter { availableContexts.contains($0) } + return viewableContexts.filter { availableContexts.contains($0) } } var previousStoryContext: StoryContext? { - guard let contextIndex = availableContexts.firstIndex(of: currentContext), - let contextBefore = availableContexts[safe: contextIndex.advanced(by: -1)] else { + guard let contextIndex = viewableContexts.firstIndex(of: currentContext), + let contextBefore = viewableContexts[safe: contextIndex.advanced(by: -1)] else { return nil } return contextBefore } var nextStoryContext: StoryContext? { - guard let contextIndex = availableContexts.firstIndex(of: currentContext), - let contextAfter = availableContexts[safe: contextIndex.advanced(by: 1)] else { + guard let contextIndex = viewableContexts.firstIndex(of: currentContext), + let contextAfter = viewableContexts[safe: contextIndex.advanced(by: 1)] else { return nil } return contextAfter @@ -335,8 +334,8 @@ extension StoryPageViewController: StoryContextViewControllerDelegate { } func storyContextViewController(_ storyContextViewController: StoryContextViewController, contextAfter context: StoryContext) -> StoryContext? { - guard let contextIndex = availableContexts.firstIndex(of: context), - let contextAfter = availableContexts[safe: contextIndex.advanced(by: 1)] else { + guard let contextIndex = viewableContexts.firstIndex(of: context), + let contextAfter = viewableContexts[safe: contextIndex.advanced(by: 1)] else { return nil } return contextAfter diff --git a/Signal/src/ViewControllers/HomeView/Stories/MyStoriesViewController.swift b/Signal/src/ViewControllers/HomeView/Stories/MyStoriesViewController.swift index 9c667289b1..3484078f15 100644 --- a/Signal/src/ViewControllers/HomeView/Stories/MyStoriesViewController.swift +++ b/Signal/src/ViewControllers/HomeView/Stories/MyStoriesViewController.swift @@ -125,7 +125,12 @@ extension MyStoriesViewController: UITableViewDelegate { tableView.deselectRow(at: indexPath, animated: true) guard let thread = thread(for: indexPath.section), let item = item(for: indexPath) else { return } - let vc = StoryPageViewController(context: thread.storyContext, loadMessage: item.message, onlyRenderMyStories: true) + let vc = StoryPageViewController( + context: thread.storyContext, + viewableContexts: items.orderedKeys.map { $0.storyContext }, + loadMessage: item.message, + onlyRenderMyStories: true + ) vc.contextDataSource = self present(vc, animated: true) } diff --git a/Signal/src/ViewControllers/HomeView/Stories/StoriesViewController.swift b/Signal/src/ViewControllers/HomeView/Stories/StoriesViewController.swift index d0b7206aff..5ff3c34176 100644 --- a/Signal/src/ViewControllers/HomeView/Stories/StoriesViewController.swift +++ b/Signal/src/ViewControllers/HomeView/Stories/StoriesViewController.swift @@ -13,7 +13,6 @@ class StoriesViewController: OWSViewController { let tableView = UITableView() private let syncingModels = SyncingStoryViewModelArray() private var myStoryModel = AtomicOptional(nil) - private var viewerContexts: [StoryContext]? private lazy var emptyStateLabel: UILabel = { let label = UILabel() @@ -403,13 +402,14 @@ extension StoriesViewController: UITableViewDelegate { // If we tap on a story with unviewed stories, we only want the viewer // to page through unviewed contexts. + let viewableContexts: [StoryContext] if model.hasUnviewedMessages { - viewerContexts = syncingModels.get().lazy.filter { $0.hasUnviewedMessages }.map { $0.context } + viewableContexts = syncingModels.get().lazy.filter { $0.hasUnviewedMessages }.map { $0.context } } else { - viewerContexts = syncingModels.get().map { $0.context } + viewableContexts = syncingModels.allContexts } - let vc = StoryPageViewController(context: model.context) + let vc = StoryPageViewController(context: model.context, viewableContexts: viewableContexts) vc.contextDataSource = self presentFullScreen(vc, animated: true) case .hiddenStories: @@ -494,7 +494,7 @@ extension StoriesViewController: UITableViewDataSource { extension StoriesViewController: StoryPageViewControllerDataSource { func storyPageViewControllerAvailableContexts(_ storyPageViewController: StoryPageViewController) -> [StoryContext] { - viewerContexts ?? [] + syncingModels.allContexts } } @@ -688,6 +688,8 @@ private class SyncingStoryViewModelArray { // This may lag behind `trueModels` and is eventually consistent. It is exposed to UITableView. @ThreadBoundValue(wrappedValue: [], queue: .main) private var exposedModels: [StoryViewModel] + private var contexts = AtomicArray() + /// Safely modify the list of models. This method must be called on the loading queue. /// /// - Parameters @@ -708,6 +710,7 @@ private class SyncingStoryViewModelArray { } trueModels = newModels DispatchQueue.main.async { + self.contexts.set(newModels.map { $0.context }) self.exposedModels = newModels sync(newModels, userInfo) } @@ -717,4 +720,9 @@ private class SyncingStoryViewModelArray { func get() -> [StoryViewModel] { return exposedModels } + + /// Thread safe list of all contexts currently exposed + var allContexts: [StoryContext] { + contexts.get() + } }