forked from zelo72/mastodon-ios
chore: remove orig files
This commit is contained in:
parent
f755a0eb2d
commit
277d574254
|
@ -1,353 +0,0 @@
|
||||||
//
|
|
||||||
// HomeTimelineViewController+DebugAction.swift
|
|
||||||
// Mastodon
|
|
||||||
//
|
|
||||||
// Created by MainasuK Cirno on 2021-2-5.
|
|
||||||
//
|
|
||||||
|
|
||||||
import os.log
|
|
||||||
import UIKit
|
|
||||||
import CoreData
|
|
||||||
import CoreDataStack
|
|
||||||
|
|
||||||
#if DEBUG
|
|
||||||
extension HomeTimelineViewController {
|
|
||||||
var debugMenu: UIMenu {
|
|
||||||
let menu = UIMenu(
|
|
||||||
title: "Debug Tools",
|
|
||||||
image: nil,
|
|
||||||
identifier: nil,
|
|
||||||
options: .displayInline,
|
|
||||||
children: [
|
|
||||||
moveMenu,
|
|
||||||
dropMenu,
|
|
||||||
UIAction(title: "Show Welcome", image: UIImage(systemName: "figure.walk"), attributes: []) { [weak self] action in
|
|
||||||
guard let self = self else { return }
|
|
||||||
self.showWelcomeAction(action)
|
|
||||||
},
|
|
||||||
UIAction(title: "Show Or Remove EmptyView", image: UIImage(systemName: "clear"), attributes: []) { [weak self] action in
|
|
||||||
guard let self = self else { return }
|
|
||||||
if self.emptyView.superview != nil {
|
|
||||||
self.emptyView.removeFromSuperview()
|
|
||||||
} else {
|
|
||||||
self.showEmptyView()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
UIAction(title: "Show Public Timeline", image: UIImage(systemName: "list.dash"), attributes: []) { [weak self] action in
|
|
||||||
guard let self = self else { return }
|
|
||||||
self.showPublicTimelineAction(action)
|
|
||||||
},
|
|
||||||
UIAction(title: "Show Profile", image: UIImage(systemName: "person.crop.circle"), attributes: []) { [weak self] action in
|
|
||||||
guard let self = self else { return }
|
|
||||||
self.showProfileAction(action)
|
|
||||||
},
|
|
||||||
UIAction(title: "Show Thread", image: UIImage(systemName: "bubble.left.and.bubble.right"), attributes: []) { [weak self] action in
|
|
||||||
guard let self = self else { return }
|
|
||||||
self.showThreadAction(action)
|
|
||||||
},
|
|
||||||
UIAction(title: "Settings", image: UIImage(systemName: "gear"), attributes: []) { [weak self] action in
|
|
||||||
guard let self = self else { return }
|
|
||||||
self.showSettings(action)
|
|
||||||
},
|
|
||||||
UIAction(title: "Sign Out", image: UIImage(systemName: "escape"), attributes: .destructive) { [weak self] action in
|
|
||||||
guard let self = self else { return }
|
|
||||||
self.signOutAction(action)
|
|
||||||
}
|
|
||||||
]
|
|
||||||
)
|
|
||||||
return menu
|
|
||||||
}
|
|
||||||
|
|
||||||
var moveMenu: UIMenu {
|
|
||||||
return UIMenu(
|
|
||||||
title: "Move to…",
|
|
||||||
image: UIImage(systemName: "arrow.forward.circle"),
|
|
||||||
identifier: nil,
|
|
||||||
options: [],
|
|
||||||
children: [
|
|
||||||
UIAction(title: "First Gap", image: nil, attributes: [], handler: { [weak self] action in
|
|
||||||
guard let self = self else { return }
|
|
||||||
self.moveToTopGapAction(action)
|
|
||||||
}),
|
|
||||||
UIAction(title: "First Replied Status", image: nil, attributes: [], handler: { [weak self] action in
|
|
||||||
guard let self = self else { return }
|
|
||||||
self.moveToFirstRepliedStatus(action)
|
|
||||||
}),
|
|
||||||
UIAction(title: "First Reblog Status", image: nil, attributes: [], handler: { [weak self] action in
|
|
||||||
guard let self = self else { return }
|
|
||||||
self.moveToFirstReblogStatus(action)
|
|
||||||
}),
|
|
||||||
UIAction(title: "First Poll Status", image: nil, attributes: [], handler: { [weak self] action in
|
|
||||||
guard let self = self else { return }
|
|
||||||
self.moveToFirstPollStatus(action)
|
|
||||||
}),
|
|
||||||
UIAction(title: "First Audio Status", image: nil, attributes: [], handler: { [weak self] action in
|
|
||||||
guard let self = self else { return }
|
|
||||||
self.moveToFirstAudioStatus(action)
|
|
||||||
}),
|
|
||||||
UIAction(title: "First Video Status", image: nil, attributes: [], handler: { [weak self] action in
|
|
||||||
guard let self = self else { return }
|
|
||||||
self.moveToFirstVideoStatus(action)
|
|
||||||
}),
|
|
||||||
UIAction(title: "First GIF status", image: nil, attributes: [], handler: { [weak self] action in
|
|
||||||
guard let self = self else { return }
|
|
||||||
self.moveToFirstGIFStatus(action)
|
|
||||||
}),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
var dropMenu: UIMenu {
|
|
||||||
return UIMenu(
|
|
||||||
title: "Drop…",
|
|
||||||
image: UIImage(systemName: "minus.circle"),
|
|
||||||
identifier: nil,
|
|
||||||
options: [],
|
|
||||||
children: [50, 100, 150, 200, 250, 300].map { count in
|
|
||||||
UIAction(title: "Drop Recent \(count) Statuses", image: nil, attributes: [], handler: { [weak self] action in
|
|
||||||
guard let self = self else { return }
|
|
||||||
self.dropRecentStatusAction(action, count: count)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension HomeTimelineViewController {
|
|
||||||
|
|
||||||
@objc private func moveToTopGapAction(_ sender: UIAction) {
|
|
||||||
guard let diffableDataSource = viewModel.diffableDataSource else { return }
|
|
||||||
let snapshotTransitioning = diffableDataSource.snapshot()
|
|
||||||
let item = snapshotTransitioning.itemIdentifiers.first(where: { item in
|
|
||||||
switch item {
|
|
||||||
case .homeMiddleLoader: return true
|
|
||||||
default: return false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
if let targetItem = item, let index = snapshotTransitioning.indexOfItem(targetItem) {
|
|
||||||
tableView.scrollToRow(at: IndexPath(row: index, section: 0), at: .middle, animated: true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc private func moveToFirstReblogStatus(_ sender: UIAction) {
|
|
||||||
guard let diffableDataSource = viewModel.diffableDataSource else { return }
|
|
||||||
let snapshotTransitioning = diffableDataSource.snapshot()
|
|
||||||
let item = snapshotTransitioning.itemIdentifiers.first(where: { item in
|
|
||||||
switch item {
|
|
||||||
case .homeTimelineIndex(let objectID, _):
|
|
||||||
let homeTimelineIndex = viewModel.fetchedResultsController.managedObjectContext.object(with: objectID) as! HomeTimelineIndex
|
|
||||||
return homeTimelineIndex.status.reblog != nil
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
if let targetItem = item, let index = snapshotTransitioning.indexOfItem(targetItem) {
|
|
||||||
tableView.scrollToRow(at: IndexPath(row: index, section: 0), at: .middle, animated: true)
|
|
||||||
tableView.blinkRow(at: IndexPath(row: index, section: 0))
|
|
||||||
} else {
|
|
||||||
print("Not found reblog status")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc private func moveToFirstPollStatus(_ sender: UIAction) {
|
|
||||||
guard let diffableDataSource = viewModel.diffableDataSource else { return }
|
|
||||||
let snapshotTransitioning = diffableDataSource.snapshot()
|
|
||||||
let item = snapshotTransitioning.itemIdentifiers.first(where: { item in
|
|
||||||
switch item {
|
|
||||||
case .homeTimelineIndex(let objectID, _):
|
|
||||||
let homeTimelineIndex = viewModel.fetchedResultsController.managedObjectContext.object(with: objectID) as! HomeTimelineIndex
|
|
||||||
let post = homeTimelineIndex.status.reblog ?? homeTimelineIndex.status
|
|
||||||
return post.poll != nil
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
if let targetItem = item, let index = snapshotTransitioning.indexOfItem(targetItem) {
|
|
||||||
tableView.scrollToRow(at: IndexPath(row: index, section: 0), at: .middle, animated: true)
|
|
||||||
tableView.blinkRow(at: IndexPath(row: index, section: 0))
|
|
||||||
} else {
|
|
||||||
print("Not found poll status")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc private func moveToFirstRepliedStatus(_ sender: UIAction) {
|
|
||||||
guard let diffableDataSource = viewModel.diffableDataSource else { return }
|
|
||||||
let snapshotTransitioning = diffableDataSource.snapshot()
|
|
||||||
let item = snapshotTransitioning.itemIdentifiers.first(where: { item in
|
|
||||||
switch item {
|
|
||||||
case .homeTimelineIndex(let objectID, _):
|
|
||||||
let homeTimelineIndex = viewModel.fetchedResultsController.managedObjectContext.object(with: objectID) as! HomeTimelineIndex
|
|
||||||
guard homeTimelineIndex.status.inReplyToID != nil else {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
if let targetItem = item, let index = snapshotTransitioning.indexOfItem(targetItem) {
|
|
||||||
tableView.scrollToRow(at: IndexPath(row: index, section: 0), at: .middle, animated: true)
|
|
||||||
tableView.blinkRow(at: IndexPath(row: index, section: 0))
|
|
||||||
} else {
|
|
||||||
print("Not found replied status")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc private func moveToFirstAudioStatus(_ sender: UIAction) {
|
|
||||||
guard let diffableDataSource = viewModel.diffableDataSource else { return }
|
|
||||||
let snapshotTransitioning = diffableDataSource.snapshot()
|
|
||||||
let item = snapshotTransitioning.itemIdentifiers.first(where: { item in
|
|
||||||
switch item {
|
|
||||||
case .homeTimelineIndex(let objectID, _):
|
|
||||||
let homeTimelineIndex = viewModel.fetchedResultsController.managedObjectContext.object(with: objectID) as! HomeTimelineIndex
|
|
||||||
let status = homeTimelineIndex.status.reblog ?? homeTimelineIndex.status
|
|
||||||
return status.mediaAttachments?.contains(where: { $0.type == .audio }) ?? false
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
if let targetItem = item, let index = snapshotTransitioning.indexOfItem(targetItem) {
|
|
||||||
tableView.scrollToRow(at: IndexPath(row: index, section: 0), at: .middle, animated: true)
|
|
||||||
tableView.blinkRow(at: IndexPath(row: index, section: 0))
|
|
||||||
} else {
|
|
||||||
print("Not found audio status")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc private func moveToFirstVideoStatus(_ sender: UIAction) {
|
|
||||||
guard let diffableDataSource = viewModel.diffableDataSource else { return }
|
|
||||||
let snapshotTransitioning = diffableDataSource.snapshot()
|
|
||||||
let item = snapshotTransitioning.itemIdentifiers.first(where: { item in
|
|
||||||
switch item {
|
|
||||||
case .homeTimelineIndex(let objectID, _):
|
|
||||||
let homeTimelineIndex = viewModel.fetchedResultsController.managedObjectContext.object(with: objectID) as! HomeTimelineIndex
|
|
||||||
let status = homeTimelineIndex.status.reblog ?? homeTimelineIndex.status
|
|
||||||
return status.mediaAttachments?.contains(where: { $0.type == .video }) ?? false
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
if let targetItem = item, let index = snapshotTransitioning.indexOfItem(targetItem) {
|
|
||||||
tableView.scrollToRow(at: IndexPath(row: index, section: 0), at: .middle, animated: true)
|
|
||||||
tableView.blinkRow(at: IndexPath(row: index, section: 0))
|
|
||||||
} else {
|
|
||||||
print("Not found video status")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc private func moveToFirstGIFStatus(_ sender: UIAction) {
|
|
||||||
guard let diffableDataSource = viewModel.diffableDataSource else { return }
|
|
||||||
let snapshotTransitioning = diffableDataSource.snapshot()
|
|
||||||
let item = snapshotTransitioning.itemIdentifiers.first(where: { item in
|
|
||||||
switch item {
|
|
||||||
case .homeTimelineIndex(let objectID, _):
|
|
||||||
let homeTimelineIndex = viewModel.fetchedResultsController.managedObjectContext.object(with: objectID) as! HomeTimelineIndex
|
|
||||||
let status = homeTimelineIndex.status.reblog ?? homeTimelineIndex.status
|
|
||||||
return status.mediaAttachments?.contains(where: { $0.type == .gifv }) ?? false
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
if let targetItem = item, let index = snapshotTransitioning.indexOfItem(targetItem) {
|
|
||||||
tableView.scrollToRow(at: IndexPath(row: index, section: 0), at: .middle, animated: true)
|
|
||||||
tableView.blinkRow(at: IndexPath(row: index, section: 0))
|
|
||||||
} else {
|
|
||||||
print("Not found GIF status")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc private func dropRecentStatusAction(_ sender: UIAction, count: Int) {
|
|
||||||
guard let diffableDataSource = viewModel.diffableDataSource else { return }
|
|
||||||
let snapshotTransitioning = diffableDataSource.snapshot()
|
|
||||||
|
|
||||||
let droppingObjectIDs = snapshotTransitioning.itemIdentifiers.prefix(count).compactMap { item -> NSManagedObjectID? in
|
|
||||||
switch item {
|
|
||||||
case .homeTimelineIndex(let objectID, _): return objectID
|
|
||||||
default: return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var droppingStatusObjectIDs: [NSManagedObjectID] = []
|
|
||||||
context.apiService.backgroundManagedObjectContext.performChanges { [weak self] in
|
|
||||||
guard let self = self else { return }
|
|
||||||
for objectID in droppingObjectIDs {
|
|
||||||
guard let homeTimelineIndex = try? self.context.apiService.backgroundManagedObjectContext.existingObject(with: objectID) as? HomeTimelineIndex else { continue }
|
|
||||||
droppingStatusObjectIDs.append(homeTimelineIndex.status.objectID)
|
|
||||||
self.context.apiService.backgroundManagedObjectContext.delete(homeTimelineIndex)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.sink { [weak self] result in
|
|
||||||
guard let self = self else { return }
|
|
||||||
switch result {
|
|
||||||
case .success:
|
|
||||||
self.context.apiService.backgroundManagedObjectContext.performChanges { [weak self] in
|
|
||||||
guard let self = self else { return }
|
|
||||||
for objectID in droppingStatusObjectIDs {
|
|
||||||
guard let post = try? self.context.apiService.backgroundManagedObjectContext.existingObject(with: objectID) as? Status else { continue }
|
|
||||||
self.context.apiService.backgroundManagedObjectContext.delete(post)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.sink { _ in
|
|
||||||
// do nothing
|
|
||||||
}
|
|
||||||
.store(in: &self.disposeBag)
|
|
||||||
case .failure(let error):
|
|
||||||
assertionFailure(error.localizedDescription)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.store(in: &disposeBag)
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc private func showWelcomeAction(_ sender: UIAction) {
|
|
||||||
coordinator.present(scene: .welcome, from: self, transition: .modal(animated: true, completion: nil))
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc private func showPublicTimelineAction(_ sender: UIAction) {
|
|
||||||
coordinator.present(scene: .publicTimeline, from: self, transition: .show)
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc private func showProfileAction(_ sender: UIAction) {
|
|
||||||
let alertController = UIAlertController(title: "Enter User ID", message: nil, preferredStyle: .alert)
|
|
||||||
alertController.addTextField()
|
|
||||||
let showAction = UIAlertAction(title: "Show", style: .default) { [weak self, weak alertController] _ in
|
|
||||||
guard let self = self else { return }
|
|
||||||
guard let textField = alertController?.textFields?.first else { return }
|
|
||||||
let profileViewModel = RemoteProfileViewModel(context: self.context, userID: textField.text ?? "")
|
|
||||||
self.coordinator.present(scene: .profile(viewModel: profileViewModel), from: self, transition: .show)
|
|
||||||
}
|
|
||||||
alertController.addAction(showAction)
|
|
||||||
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
|
|
||||||
alertController.addAction(cancelAction)
|
|
||||||
coordinator.present(scene: .alertController(alertController: alertController), from: self, transition: .alertController(animated: true, completion: nil))
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc private func showThreadAction(_ sender: UIAction) {
|
|
||||||
let alertController = UIAlertController(title: "Enter Status ID", message: nil, preferredStyle: .alert)
|
|
||||||
alertController.addTextField()
|
|
||||||
let showAction = UIAlertAction(title: "Show", style: .default) { [weak self, weak alertController] _ in
|
|
||||||
guard let self = self else { return }
|
|
||||||
guard let textField = alertController?.textFields?.first else { return }
|
|
||||||
let threadViewModel = RemoteThreadViewModel(context: self.context, statusID: textField.text ?? "")
|
|
||||||
self.coordinator.present(scene: .thread(viewModel: threadViewModel), from: self, transition: .show)
|
|
||||||
}
|
|
||||||
alertController.addAction(showAction)
|
|
||||||
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
|
|
||||||
alertController.addAction(cancelAction)
|
|
||||||
coordinator.present(scene: .alertController(alertController: alertController), from: self, transition: .alertController(animated: true, completion: nil))
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc private func showSettings(_ sender: UIAction) {
|
|
||||||
<<<<<<< HEAD
|
|
||||||
guard let currentSetting = context.settingService.currentSetting.value else { return }
|
|
||||||
let settingsViewModel = SettingsViewModel(context: context, setting: currentSetting)
|
|
||||||
coordinator.present(scene: .settings(viewModel: settingsViewModel), from: self, transition: .modal(animated: true, completion: nil))
|
|
||||||
=======
|
|
||||||
let viewModel = SettingsViewModel(context: context)
|
|
||||||
coordinator.present(
|
|
||||||
scene: .settings(viewModel: viewModel),
|
|
||||||
from: self,
|
|
||||||
transition: .modal(animated: true, completion: nil)
|
|
||||||
)
|
|
||||||
>>>>>>> 2e8183adc646f2871b530b642717e3aab782721d
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
|
@ -1,215 +0,0 @@
|
||||||
//
|
|
||||||
// SettingsViewModel.swift
|
|
||||||
// Mastodon
|
|
||||||
//
|
|
||||||
// Created by ihugo on 2021/4/7.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Combine
|
|
||||||
import CoreData
|
|
||||||
import CoreDataStack
|
|
||||||
import Foundation
|
|
||||||
import MastodonSDK
|
|
||||||
import UIKit
|
|
||||||
import os.log
|
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
class SettingsViewModel {
|
|
||||||
=======
|
|
||||||
class SettingsViewModel: NSObject {
|
|
||||||
// confirm set only once
|
|
||||||
weak var context: AppContext! { willSet { precondition(context == nil) } }
|
|
||||||
>>>>>>> 2e8183adc646f2871b530b642717e3aab782721d
|
|
||||||
|
|
||||||
var disposeBag = Set<AnyCancellable>()
|
|
||||||
|
|
||||||
let context: AppContext
|
|
||||||
|
|
||||||
// input
|
|
||||||
let setting: CurrentValueSubject<Setting, Never>
|
|
||||||
var updateDisposeBag = Set<AnyCancellable>()
|
|
||||||
var createDisposeBag = Set<AnyCancellable>()
|
|
||||||
|
|
||||||
let viewDidLoad = PassthroughSubject<Void, Never>()
|
|
||||||
|
|
||||||
// output
|
|
||||||
var dataSource: UITableViewDiffableDataSource<SettingsSection, SettingsItem>!
|
|
||||||
/// create a subscription when:
|
|
||||||
/// - does not has one
|
|
||||||
/// - does not find subscription for selected trigger when change trigger
|
|
||||||
let createSubscriptionSubject = PassthroughSubject<(triggerBy: String, values: [Bool?]), Never>()
|
|
||||||
|
|
||||||
/// update a subscription when:
|
|
||||||
/// - change switch for specified alerts
|
|
||||||
let updateSubscriptionSubject = PassthroughSubject<(triggerBy: String, values: [Bool?]), Never>()
|
|
||||||
|
|
||||||
lazy var privacyURL: URL? = {
|
|
||||||
guard let box = AppContext.shared.authenticationService.activeMastodonAuthenticationBox.value else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return Mastodon.API.privacyURL(domain: box.domain)
|
|
||||||
}()
|
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
init(context: AppContext, setting: Setting) {
|
|
||||||
self.context = context
|
|
||||||
self.setting = CurrentValueSubject(setting)
|
|
||||||
=======
|
|
||||||
/// to store who trigger the notification.
|
|
||||||
var triggerBy: String?
|
|
||||||
|
|
||||||
struct Input {
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Output {
|
|
||||||
}
|
|
||||||
|
|
||||||
init(context: AppContext) {
|
|
||||||
self.context = context
|
|
||||||
>>>>>>> 2e8183adc646f2871b530b642717e3aab782721d
|
|
||||||
|
|
||||||
self.setting
|
|
||||||
.sink(receiveValue: { [weak self] setting in
|
|
||||||
guard let self = self else { return }
|
|
||||||
self.processDataSource(setting)
|
|
||||||
})
|
|
||||||
.store(in: &disposeBag)
|
|
||||||
}
|
|
||||||
|
|
||||||
deinit {
|
|
||||||
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s:", ((#file as NSString).lastPathComponent), #line, #function)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
extension SettingsViewModel {
|
|
||||||
|
|
||||||
// MARK: - Private methods
|
|
||||||
private func processDataSource(_ setting: Setting) {
|
|
||||||
guard let dataSource = self.dataSource else { return }
|
|
||||||
var snapshot = NSDiffableDataSourceSnapshot<SettingsSection, SettingsItem>()
|
|
||||||
|
|
||||||
// appearance
|
|
||||||
let appearanceItems = [SettingsItem.apperance(settingObjectID: setting.objectID)]
|
|
||||||
snapshot.appendSections([.apperance])
|
|
||||||
snapshot.appendItems(appearanceItems, toSection: .apperance)
|
|
||||||
|
|
||||||
let notificationItems = SettingsItem.NotificationSwitchMode.allCases.map { mode in
|
|
||||||
SettingsItem.notification(settingObjectID: setting.objectID, switchMode: mode)
|
|
||||||
}
|
|
||||||
snapshot.appendSections([.notifications])
|
|
||||||
snapshot.appendItems(notificationItems, toSection: .notifications)
|
|
||||||
|
|
||||||
// boring zone
|
|
||||||
let boringZoneSettingsItems: [SettingsItem] = {
|
|
||||||
let links: [SettingsItem.Link] = [
|
|
||||||
.termsOfService,
|
|
||||||
.privacyPolicy
|
|
||||||
]
|
|
||||||
let items = links.map { SettingsItem.boringZone(item: $0) }
|
|
||||||
return items
|
|
||||||
}()
|
|
||||||
snapshot.appendSections([.boringZone])
|
|
||||||
snapshot.appendItems(boringZoneSettingsItems, toSection: .boringZone)
|
|
||||||
|
|
||||||
let spicyZoneSettingsItems: [SettingsItem] = {
|
|
||||||
let links: [SettingsItem.Link] = [
|
|
||||||
.clearMediaCache,
|
|
||||||
.signOut
|
|
||||||
]
|
|
||||||
let items = links.map { SettingsItem.spicyZone(item: $0) }
|
|
||||||
return items
|
|
||||||
}()
|
|
||||||
snapshot.appendSections([.spicyZone])
|
|
||||||
snapshot.appendItems(spicyZoneSettingsItems, toSection: .spicyZone)
|
|
||||||
|
|
||||||
dataSource.apply(snapshot, animatingDifferences: false)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
extension SettingsViewModel {
|
|
||||||
func setupDiffableDataSource(
|
|
||||||
for tableView: UITableView,
|
|
||||||
settingsAppearanceTableViewCellDelegate: SettingsAppearanceTableViewCellDelegate,
|
|
||||||
settingsToggleCellDelegate: SettingsToggleCellDelegate
|
|
||||||
) {
|
|
||||||
dataSource = UITableViewDiffableDataSource(tableView: tableView) { [
|
|
||||||
weak self,
|
|
||||||
weak settingsAppearanceTableViewCellDelegate,
|
|
||||||
weak settingsToggleCellDelegate
|
|
||||||
] tableView, indexPath, item -> UITableViewCell? in
|
|
||||||
guard let self = self else { return nil }
|
|
||||||
|
|
||||||
switch item {
|
|
||||||
case .apperance(let objectID):
|
|
||||||
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: SettingsAppearanceTableViewCell.self), for: indexPath) as! SettingsAppearanceTableViewCell
|
|
||||||
self.context.managedObjectContext.performAndWait {
|
|
||||||
let setting = self.context.managedObjectContext.object(with: objectID) as! Setting
|
|
||||||
cell.update(with: setting.appearance)
|
|
||||||
ManagedObjectObserver.observe(object: setting)
|
|
||||||
.sink(receiveCompletion: { _ in
|
|
||||||
// do nothing
|
|
||||||
}, receiveValue: { [weak cell] change in
|
|
||||||
guard let cell = cell else { return }
|
|
||||||
guard case .update(let object) = change.changeType,
|
|
||||||
let setting = object as? Setting else { return }
|
|
||||||
cell.update(with: setting.appearance)
|
|
||||||
})
|
|
||||||
.store(in: &cell.disposeBag)
|
|
||||||
}
|
|
||||||
cell.delegate = settingsAppearanceTableViewCellDelegate
|
|
||||||
return cell
|
|
||||||
case .notification(let objectID, let switchMode):
|
|
||||||
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: SettingsToggleTableViewCell.self), for: indexPath) as! SettingsToggleTableViewCell
|
|
||||||
self.context.managedObjectContext.performAndWait {
|
|
||||||
let setting = self.context.managedObjectContext.object(with: objectID) as! Setting
|
|
||||||
if let subscription = setting.activeSubscription {
|
|
||||||
SettingsViewModel.configureSettingToggle(cell: cell, switchMode: switchMode, subscription: subscription)
|
|
||||||
}
|
|
||||||
ManagedObjectObserver.observe(object: setting)
|
|
||||||
.sink(receiveCompletion: { _ in
|
|
||||||
// do nothing
|
|
||||||
}, receiveValue: { [weak cell] change in
|
|
||||||
guard let cell = cell else { return }
|
|
||||||
guard case .update(let object) = change.changeType,
|
|
||||||
let setting = object as? Setting else { return }
|
|
||||||
guard let subscription = setting.activeSubscription else { return }
|
|
||||||
SettingsViewModel.configureSettingToggle(cell: cell, switchMode: switchMode, subscription: subscription)
|
|
||||||
})
|
|
||||||
.store(in: &cell.disposeBag)
|
|
||||||
}
|
|
||||||
cell.delegate = settingsToggleCellDelegate
|
|
||||||
return cell
|
|
||||||
case .boringZone(let item), .spicyZone(let item):
|
|
||||||
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: SettingsLinkTableViewCell.self), for: indexPath) as! SettingsLinkTableViewCell
|
|
||||||
cell.update(with: item)
|
|
||||||
return cell
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
processDataSource(self.setting.value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension SettingsViewModel {
|
|
||||||
|
|
||||||
static func configureSettingToggle(
|
|
||||||
cell: SettingsToggleTableViewCell,
|
|
||||||
switchMode: SettingsItem.NotificationSwitchMode,
|
|
||||||
subscription: NotificationSubscription
|
|
||||||
) {
|
|
||||||
cell.textLabel?.text = switchMode.title
|
|
||||||
|
|
||||||
let enabled: Bool?
|
|
||||||
switch switchMode {
|
|
||||||
case .favorite: enabled = subscription.alert.favourite
|
|
||||||
case .follow: enabled = subscription.alert.follow
|
|
||||||
case .reblog: enabled = subscription.alert.reblog
|
|
||||||
case .mention: enabled = subscription.alert.mention
|
|
||||||
}
|
|
||||||
cell.update(enabled: enabled)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
Loading…
Reference in New Issue