This commit is contained in:
Justin Mazzocchi 2021-01-25 16:06:35 -08:00
parent d71cda0b92
commit fe093d7e33
No known key found for this signature in database
GPG key ID: E223E6937AAFB01C
56 changed files with 286 additions and 280 deletions

View file

@ -32,12 +32,12 @@ extension CollectionItem {
}
}
func estimatedHeight(width: CGFloat, identification: Identification) -> CGFloat {
func estimatedHeight(width: CGFloat, identityContext: IdentityContext) -> CGFloat {
switch self {
case let .status(status, configuration):
return StatusView.estimatedHeight(
width: width,
identification: identification,
identityContext: identityContext,
status: status,
configuration: configuration)
case let .account(account):
@ -47,13 +47,13 @@ extension CollectionItem {
case let .notification(notification, configuration):
return NotificationView.estimatedHeight(
width: width,
identification: identification,
identityContext: identityContext,
notification: notification,
configuration: configuration)
case let .conversation(conversation):
return ConversationView.estimatedHeight(
width: width,
identification: identification,
identityContext: identityContext,
conversation: conversation)
case let .tag(tag):
return TagView.estimatedHeight(width: width, tag: tag)

View file

@ -75,14 +75,14 @@ final class EmojiPickerViewController: UIViewController {
private lazy var defaultSkinToneSelectionMenu: UIMenu = {
let clearSkinToneAction = UIAction(title: SystemEmoji.SkinTone.noneExample) { [weak self] _ in
self?.skinToneButton.setTitle(SystemEmoji.SkinTone.noneExample, for: .normal)
self?.viewModel.identification.appPreferences.defaultEmojiSkinTone = nil
self?.viewModel.identityContext.appPreferences.defaultEmojiSkinTone = nil
self?.reloadVisibleItems()
}
let setSkinToneActions = SystemEmoji.SkinTone.allCases.map { [weak self] skinTone in
UIAction(title: skinTone.example) { _ in
self?.skinToneButton.setTitle(skinTone.example, for: .normal)
self?.viewModel.identification.appPreferences.defaultEmojiSkinTone = skinTone
self?.viewModel.identityContext.appPreferences.defaultEmojiSkinTone = skinTone
self?.reloadVisibleItems()
}
}
@ -126,7 +126,7 @@ final class EmojiPickerViewController: UIViewController {
skinToneButton.translatesAutoresizingMaskIntoConstraints = false
skinToneButton.titleLabel?.adjustsFontSizeToFitWidth = true
skinToneButton.setTitle(
viewModel.identification.appPreferences.defaultEmojiSkinTone?.example ?? SystemEmoji.SkinTone.noneExample,
viewModel.identityContext.appPreferences.defaultEmojiSkinTone?.example ?? SystemEmoji.SkinTone.noneExample,
for: .normal)
skinToneButton.showsMenuAsPrimaryAction = true
skinToneButton.menu = defaultSkinToneSelectionMenu
@ -241,7 +241,7 @@ private extension EmojiPickerViewController {
func applyingDefaultSkinTone(emoji: PickerEmoji) -> PickerEmoji {
if case let .system(systemEmoji, inFrequentlyUsed) = emoji,
let defaultEmojiSkinTone = viewModel.identification.appPreferences.defaultEmojiSkinTone {
let defaultEmojiSkinTone = viewModel.identityContext.appPreferences.defaultEmojiSkinTone {
return .system(systemEmoji.applying(skinTone: defaultEmojiSkinTone), inFrequentlyUsed: inFrequentlyUsed)
} else {
return emoji

View file

@ -7,13 +7,13 @@ import ViewModels
final class ExploreViewController: UICollectionViewController {
private let viewModel: ExploreViewModel
private let rootViewModel: RootViewModel
private let identification: Identification
private let identityContext: IdentityContext
private var cancellables = Set<AnyCancellable>()
init(viewModel: ExploreViewModel, rootViewModel: RootViewModel, identification: Identification) {
init(viewModel: ExploreViewModel, rootViewModel: RootViewModel, identityContext: IdentityContext) {
self.viewModel = viewModel
self.rootViewModel = rootViewModel
self.identification = identification
self.identityContext = identityContext
super.init(collectionViewLayout: UICollectionViewFlowLayout())
@ -36,7 +36,7 @@ final class ExploreViewController: UICollectionViewController {
let searchResultsController = TableViewController(
viewModel: viewModel.searchViewModel,
rootViewModel: rootViewModel,
identification: identification,
identityContext: identityContext,
insetBottom: false,
parentNavigationController: navigationController)

View file

@ -27,7 +27,7 @@ final class MainNavigationViewController: UITabBarController {
setupViewControllers()
if viewModel.identification.identity.authenticated {
if viewModel.identityContext.identity.authenticated {
setupNewStatusButton()
}
@ -61,14 +61,14 @@ private extension MainNavigationViewController {
ExploreViewController(
viewModel: viewModel.exploreViewModel,
rootViewModel: rootViewModel,
identification: viewModel.identification)
identityContext: viewModel.identityContext)
]
if let notificationsViewModel = viewModel.notificationsViewModel {
let notificationsViewController = TableViewController(
viewModel: notificationsViewModel,
rootViewModel: rootViewModel,
identification: viewModel.identification)
identityContext: viewModel.identityContext)
notificationsViewController.tabBarItem = NavigationViewModel.Tab.notifications.tabBarItem
@ -79,7 +79,7 @@ private extension MainNavigationViewController {
let conversationsViewController = TableViewController(
viewModel: conversationsViewModel,
rootViewModel: rootViewModel,
identification: viewModel.identification)
identityContext: viewModel.identityContext)
conversationsViewController.tabBarItem = NavigationViewModel.Tab.messages.tabBarItem
conversationsViewController.navigationItem.title = NavigationViewModel.Tab.messages.title
@ -100,7 +100,7 @@ private extension MainNavigationViewController {
let newStatusButtonView = NewStatusButtonView(primaryAction: UIAction { [weak self] _ in
guard let self = self else { return }
let newStatusViewModel = self.rootViewModel.newStatusViewModel(
identification: self.viewModel.identification)
identityContext: self.viewModel.identityContext)
let newStatusViewController = NewStatusViewController(viewModel: newStatusViewModel)
let newStatusNavigationController = UINavigationController(rootViewController: newStatusViewController)

View file

@ -66,7 +66,7 @@ final class NewStatusViewController: UIViewController {
activityIndicatorView.centerYAnchor.constraint(equalTo: scrollView.centerYAnchor)
])
setupBarButtonItems(identification: viewModel.identification)
setupBarButtonItems(identityContext: viewModel.identityContext)
postButton.primaryAction = UIAction(title: NSLocalizedString("post", comment: "")) { [weak self] _ in
self?.viewModel.post()
@ -234,8 +234,8 @@ private extension NewStatusViewController {
viewModel.$compositionViewModels
.sink { [weak self] in self?.set(compositionViewModels: $0) }
.store(in: &cancellables)
viewModel.$identification
.sink { [weak self] in self?.setupBarButtonItems(identification: $0) }
viewModel.$identityContext
.sink { [weak self] in self?.setupBarButtonItems(identityContext: $0) }
.store(in: &cancellables)
viewModel.$postingState
.sink { [weak self] in self?.apply(postingState: $0) }
@ -250,14 +250,14 @@ private extension NewStatusViewController {
.store(in: &cancellables)
}
func setupBarButtonItems(identification: Identification) {
func setupBarButtonItems(identityContext: IdentityContext) {
let cancelButton = UIBarButtonItem(
systemItem: .cancel,
primaryAction: UIAction { [weak self] _ in self?.dismiss() })
navigationItem.leftBarButtonItem = cancelButton
navigationItem.titleView = viewModel.canChangeIdentity
? changeIdentityButton(identification: identification)
? changeIdentityButton(identityContext: identityContext)
: nil
navigationItem.rightBarButtonItem = postButton
}
@ -371,7 +371,7 @@ private extension NewStatusViewController {
guard let fromView = view.viewWithTag(tag) else { return }
let emojiPickerViewModel = EmojiPickerViewModel(identification: viewModel.identification)
let emojiPickerViewModel = EmojiPickerViewModel(identityContext: viewModel.identityContext)
emojiPickerViewModel.$alertItem.assign(to: \.alertItem, on: viewModel).store(in: &cancellables)
@ -422,14 +422,14 @@ private extension NewStatusViewController {
}
}
func changeIdentityButton(identification: Identification) -> UIButton {
func changeIdentityButton(identityContext: IdentityContext) -> UIButton {
let changeIdentityButton = UIButton()
let downsampled = KingfisherOptionsInfo.downsampled(
dimension: .barButtonItemDimension,
scaleFactor: UIScreen.main.scale)
let menuItems = viewModel.authenticatedIdentities
.filter { $0.id != identification.identity.id }
.filter { $0.id != identityContext.identity.id }
.map { identity in
UIDeferredMenuElement { completion in
let action = UIAction(title: identity.handle) { [weak self] _ in
@ -451,7 +451,7 @@ private extension NewStatusViewController {
}
changeIdentityButton.kf.setImage(
with: identification.identity.image,
with: identityContext.identity.image,
for: .normal,
options: downsampled)
changeIdentityButton.showsMenuAsPrimaryAction = true

View file

@ -12,14 +12,14 @@ final class ProfileViewController: TableViewController {
required init(
viewModel: ProfileViewModel,
rootViewModel: RootViewModel,
identification: Identification,
identityContext: IdentityContext,
parentNavigationController: UINavigationController?) {
self.viewModel = viewModel
super.init(
viewModel: viewModel,
rootViewModel: rootViewModel,
identification: identification,
identityContext: identityContext,
parentNavigationController: parentNavigationController)
}

View file

@ -13,7 +13,7 @@ class TableViewController: UITableViewController {
private let viewModel: CollectionViewModel
private let rootViewModel: RootViewModel
private let identification: Identification
private let identityContext: IdentityContext
private let loadingTableFooterView = LoadingTableFooterView()
private let webfingerIndicatorView = WebfingerIndicatorView()
@Published private var loading = false
@ -30,12 +30,12 @@ class TableViewController: UITableViewController {
init(viewModel: CollectionViewModel,
rootViewModel: RootViewModel,
identification: Identification,
identityContext: IdentityContext,
insetBottom: Bool = true,
parentNavigationController: UINavigationController? = nil) {
self.viewModel = viewModel
self.rootViewModel = rootViewModel
self.identification = identification
self.identityContext = identityContext
self.insetBottom = insetBottom
self.parentNavigationController = parentNavigationController
@ -130,7 +130,7 @@ class TableViewController: UITableViewController {
return cellHeightCaches[tableView.frame.width]?[item]
?? item.estimatedHeight(width: tableView.readableContentGuide.layoutFrame.width,
identification: identification)
identityContext: identityContext)
}
override func tableView(_ tableView: UITableView, shouldHighlightRowAt indexPath: IndexPath) -> Bool {
@ -346,9 +346,9 @@ private extension TableViewController {
let vc = TableViewController(
viewModel: CollectionItemsViewModel(
collectionService: collectionService,
identification: identification),
identityContext: identityContext),
rootViewModel: rootViewModel,
identification: identification,
identityContext: identityContext,
parentNavigationController: parentNavigationController)
if let parentNavigationController = parentNavigationController {
@ -360,9 +360,9 @@ private extension TableViewController {
let vc = ProfileViewController(
viewModel: ProfileViewModel(
profileService: profileService,
identification: identification),
identityContext: identityContext),
rootViewModel: rootViewModel,
identification: identification,
identityContext: identityContext,
parentNavigationController: parentNavigationController)
if let parentNavigationController = parentNavigationController {
@ -420,7 +420,7 @@ private extension TableViewController {
func compose(inReplyToViewModel: StatusViewModel?, redraft: Status?) {
let newStatusViewModel = rootViewModel.newStatusViewModel(
identification: identification,
identityContext: identityContext,
inReplyTo: inReplyToViewModel,
redraft: redraft)
let newStatusViewController = NewStatusViewController(viewModel: newStatusViewModel)

View file

@ -22,7 +22,7 @@ final class TimelinesViewController: UIPageViewController {
TableViewController(
viewModel: viewModel.viewModel(timeline: timeline),
rootViewModel: rootViewModel,
identification: viewModel.identification))
identityContext: viewModel.identityContext))
segmentedControl.insertSegment(withTitle: timeline.title, at: index, animated: false)
}
@ -77,7 +77,7 @@ final class TimelinesViewController: UIPageViewController {
let vc = TableViewController(
viewModel: self.viewModel.viewModel(timeline: $0),
rootViewModel: self.rootViewModel,
identification: self.viewModel.identification)
identityContext: self.viewModel.identityContext)
vc.navigationItem.title = $0.title

View file

@ -73,8 +73,8 @@ public extension RootViewModel {
static let preview = try! RootViewModel(environment: environment) { Empty().eraseToAnyPublisher() }
}
public extension Identification {
static let preview = RootViewModel.preview.navigationViewModel!.identification
public extension IdentityContext {
static let preview = RootViewModel.preview.navigationViewModel!.identityContext
}
public extension ReportViewModel {
@ -83,7 +83,7 @@ public extension ReportViewModel {
account: .preview,
mastodonAPIClient: .preview,
contentDatabase: .preview),
identification: .preview)
identityContext: .preview)
}
public extension DomainBlocksViewModel {

View file

@ -4,7 +4,7 @@ import Combine
import Foundation
import ServiceLayer
public final class Identification: ObservableObject {
public final class IdentityContext: ObservableObject {
@Published private(set) public var identity: Identity
@Published public var appPreferences: AppPreferences
let service: IdentityService

View file

@ -9,19 +9,19 @@ public struct AccountViewModel: CollectionItemViewModel {
public let events: AnyPublisher<AnyPublisher<CollectionItemEvent, Error>, Never>
private let accountService: AccountService
private let identification: Identification
private let identityContext: IdentityContext
private let eventsSubject = PassthroughSubject<AnyPublisher<CollectionItemEvent, Error>, Never>()
init(accountService: AccountService, identification: Identification) {
init(accountService: AccountService, identityContext: IdentityContext) {
self.accountService = accountService
self.identification = identification
self.identityContext = identityContext
events = eventsSubject.eraseToAnyPublisher()
}
}
public extension AccountViewModel {
var headerURL: URL {
if !identification.appPreferences.shouldReduceMotion, identification.appPreferences.animateHeaders {
if !identityContext.appPreferences.shouldReduceMotion, identityContext.appPreferences.animateHeaders {
return accountService.account.header
} else {
return accountService.account.headerStatic
@ -56,12 +56,12 @@ public extension AccountViewModel {
var followersCount: Int { accountService.account.followersCount }
var isSelf: Bool { accountService.account.id == identification.identity.account?.id }
var isSelf: Bool { accountService.account.id == identityContext.identity.account?.id }
func avatarURL(profile: Bool = false) -> URL {
if !identification.appPreferences.shouldReduceMotion,
(identification.appPreferences.animateAvatars == .everywhere
|| identification.appPreferences.animateAvatars == .profiles && profile) {
if !identityContext.appPreferences.shouldReduceMotion,
(identityContext.appPreferences.animateAvatars == .everywhere
|| identityContext.appPreferences.animateAvatars == .profiles && profile) {
return accountService.account.avatar
} else {
return accountService.account.avatarStatic
@ -91,7 +91,7 @@ public extension AccountViewModel {
}
func reportViewModel() -> ReportViewModel {
ReportViewModel(accountService: accountService, identification: identification)
ReportViewModel(accountService: accountService, identityContext: identityContext)
}
func follow() {

View file

@ -11,12 +11,12 @@ public final class AttachmentViewModel: ObservableObject {
@Published public var editingFocus: Attachment.Meta.Focus
@Published public private(set) var descriptionRemainingCharacters = AttachmentViewModel.descriptionMaxCharacters
private let identification: Identification
private let identityContext: IdentityContext
private let status: Status?
init(attachment: Attachment, identification: Identification, status: Status? = nil) {
init(attachment: Attachment, identityContext: IdentityContext, status: Status? = nil) {
self.attachment = attachment
self.identification = identification
self.identityContext = identityContext
self.status = status
editingDescription = attachment.description ?? ""
editingFocus = attachment.meta?.focus ?? .default
@ -34,12 +34,12 @@ public extension AttachmentViewModel {
var shouldAutoplay: Bool {
switch attachment.type {
case .video:
return identification.appPreferences.autoplayVideos == .always
|| (identification.appPreferences.autoplayVideos == .wifi
return identityContext.appPreferences.autoplayVideos == .always
|| (identityContext.appPreferences.autoplayVideos == .wifi
&& Self.wifiMonitor.currentPath.status == .satisfied)
case .gifv:
return identification.appPreferences.autoplayGIFs == .always
|| (identification.appPreferences.autoplayGIFs == .wifi
return identityContext.appPreferences.autoplayGIFs == .always
|| (identityContext.appPreferences.autoplayGIFs == .wifi
&& Self.wifiMonitor.currentPath.status == .satisfied)
default: return false
}
@ -48,11 +48,13 @@ public extension AttachmentViewModel {
extension AttachmentViewModel {
func updated() -> AnyPublisher<AttachmentViewModel, Error> {
identification.service.updateAttachment(id: attachment.id, description: editingDescription, focus: editingFocus)
identityContext.service.updateAttachment(id: attachment.id,
description: editingDescription,
focus: editingFocus)
.compactMap { [weak self] in
guard let self = self else { return nil }
return AttachmentViewModel(attachment: $0, identification: self.identification, status: self.status)
return AttachmentViewModel(attachment: $0, identityContext: self.identityContext, status: self.status)
}
.eraseToAnyPublisher()
}

View file

@ -15,7 +15,7 @@ public class CollectionItemsViewModel: ObservableObject {
maintainScrollPositionItemId: nil,
shouldAdjustContentInset: false)
private let collectionService: CollectionService
private let identification: Identification
private let identityContext: IdentityContext
private var viewModelCache = [CollectionItem: (viewModel: CollectionItemViewModel, events: AnyCancellable)]()
private let eventsSubject = PassthroughSubject<CollectionItemEvent, Never>()
private let loadingSubject = PassthroughSubject<Bool, Never>()
@ -27,11 +27,11 @@ public class CollectionItemsViewModel: ObservableObject {
private var shouldRestorePositionOfLocalLastReadId = false
private var cancellables = Set<AnyCancellable>()
public init(collectionService: CollectionService, identification: Identification) {
public init(collectionService: CollectionService, identityContext: IdentityContext) {
self.collectionService = collectionService
self.identification = identification
self.identityContext = identityContext
expandAllSubject = CurrentValueSubject(
collectionService is ContextService && !identification.identity.preferences.readingExpandSpoilers
collectionService is ContextService && !identityContext.identity.preferences.readingExpandSpoilers
? .expand : .hidden)
collectionService.sections
@ -47,11 +47,11 @@ public class CollectionItemsViewModel: ObservableObject {
if let markerTimeline = collectionService.markerTimeline {
shouldRestorePositionOfLocalLastReadId =
identification.appPreferences.positionBehavior(markerTimeline: markerTimeline) == .rememberPosition
identityContext.appPreferences.positionBehavior(markerTimeline: markerTimeline) == .rememberPosition
lastReadId.compactMap { $0 }
.removeDuplicates()
.debounce(for: .seconds(Self.lastReadIdDebounceInterval), scheduler: DispatchQueue.global())
.flatMap { identification.service.setLastReadId($0, forMarker: markerTimeline) }
.flatMap { identityContext.service.setLastReadId($0, forMarker: markerTimeline) }
.sink { _ in } receiveValue: { _ in }
.store(in: &cancellables)
}
@ -94,9 +94,9 @@ extension CollectionItemsViewModel: CollectionViewModel {
let publisher: AnyPublisher<Never, Error>
if let markerTimeline = collectionService.markerTimeline,
identification.appPreferences.positionBehavior(markerTimeline: markerTimeline) == .syncPosition,
identityContext.appPreferences.positionBehavior(markerTimeline: markerTimeline) == .syncPosition,
!hasRequestedUsingMarker {
publisher = identification.service.getMarker(markerTimeline)
publisher = identityContext.service.getMarker(markerTimeline)
.flatMap { [weak self] in
self?.collectionService.request(maxId: $0.lastReadId, minId: nil, search: nil)
?? Empty().eraseToAnyPublisher()
@ -207,7 +207,7 @@ extension CollectionItemsViewModel: CollectionViewModel {
} else {
viewModel = .init(
statusService: collectionService.navigationService.statusService(status: status),
identification: identification)
identityContext: identityContext)
cache(viewModel: viewModel, forItem: item)
}
@ -232,7 +232,7 @@ extension CollectionItemsViewModel: CollectionViewModel {
let viewModel = AccountViewModel(
accountService: collectionService.navigationService.accountService(account: account),
identification: identification)
identityContext: identityContext)
cache(viewModel: viewModel, forItem: item)
@ -245,7 +245,7 @@ extension CollectionItemsViewModel: CollectionViewModel {
} else if let status = notification.status, let statusConfiguration = statusConfiguration {
let statusViewModel = StatusViewModel(
statusService: collectionService.navigationService.statusService(status: status),
identification: identification)
identityContext: identityContext)
statusViewModel.configuration = statusConfiguration
viewModel = statusViewModel
cache(viewModel: viewModel, forItem: item)
@ -253,7 +253,7 @@ extension CollectionItemsViewModel: CollectionViewModel {
viewModel = NotificationViewModel(
notificationService: collectionService.navigationService.notificationService(
notification: notification),
identification: identification)
identityContext: identityContext)
cache(viewModel: viewModel, forItem: item)
}
@ -266,7 +266,7 @@ extension CollectionItemsViewModel: CollectionViewModel {
let viewModel = ConversationViewModel(
conversationService: collectionService.navigationService.conversationService(
conversation: conversation),
identification: identification)
identityContext: identityContext)
cache(viewModel: viewModel, forItem: item)
@ -354,7 +354,7 @@ private extension CollectionItemsViewModel {
guard let maxId = maxId else { return nil }
guard let markerTimeline = collectionService.markerTimeline,
identification.appPreferences.positionBehavior(markerTimeline: markerTimeline) == .rememberPosition,
identityContext.appPreferences.positionBehavior(markerTimeline: markerTimeline) == .rememberPosition,
let lastItemId = lastUpdate.sections.last?.items.last?.itemId
else { return maxId }
@ -367,7 +367,7 @@ private extension CollectionItemsViewModel {
if shouldRestorePositionOfLocalLastReadId,
let markerTimeline = collectionService.markerTimeline,
let localLastReadId = identification.service.getLocalLastReadId(markerTimeline),
let localLastReadId = identityContext.service.getLocalLastReadId(markerTimeline),
newItems.contains(where: { $0.itemId == localLastReadId }) {
shouldRestorePositionOfLocalLastReadId = false

View file

@ -96,7 +96,9 @@ public extension CompositionViewModel {
typealias Id = UUID
convenience init(eventsSubject: PassthroughSubject<Event, Never>, redraft: Status, identification: Identification) {
convenience init(eventsSubject: PassthroughSubject<Event, Never>,
redraft: Status,
identityContext: IdentityContext) {
self.init(eventsSubject: eventsSubject)
if let text = redraft.text {
@ -108,7 +110,7 @@ public extension CompositionViewModel {
sensitive = redraft.sensitive
displayPoll = redraft.poll != nil
attachmentViewModels = redraft.mediaAttachments.map {
AttachmentViewModel(attachment: $0, identification: identification)
AttachmentViewModel(attachment: $0, identityContext: identityContext)
}
if let poll = redraft.poll {
@ -209,7 +211,7 @@ extension CompositionViewModel {
self.attachmentUpload = AttachmentUpload(progress: progress, data: data, mimeType: mimeType)
}
return parentViewModel.identification.service.uploadAttachment(
return parentViewModel.identityContext.service.uploadAttachment(
data: data,
mimeType: mimeType,
progress: progress)
@ -223,7 +225,7 @@ extension CompositionViewModel {
self?.attachmentViewModels.append(
AttachmentViewModel(
attachment: $0,
identification: parentViewModel.identification))
identityContext: parentViewModel.identityContext))
}
}
}

View file

@ -11,26 +11,26 @@ public final class ConversationViewModel: CollectionItemViewModel, ObservableObj
public let events: AnyPublisher<AnyPublisher<CollectionItemEvent, Error>, Never>
private let conversationService: ConversationService
private let identification: Identification
private let identityContext: IdentityContext
private let eventsSubject = PassthroughSubject<AnyPublisher<CollectionItemEvent, Error>, Never>()
init(conversationService: ConversationService, identification: Identification) {
init(conversationService: ConversationService, identityContext: IdentityContext) {
accountViewModels = conversationService.conversation.accounts.map {
AccountViewModel(
accountService: conversationService.navigationService.accountService(account: $0),
identification: identification)
identityContext: identityContext)
}
if let status = conversationService.conversation.lastStatus {
statusViewModel = StatusViewModel(
statusService: conversationService.navigationService.statusService(status: status),
identification: identification)
identityContext: identityContext)
} else {
statusViewModel = nil
}
self.conversationService = conversationService
self.identification = identification
self.identityContext = identityContext
self.events = eventsSubject.eraseToAnyPublisher()
}
}

View file

@ -15,13 +15,13 @@ public final class EditFilterViewModel: ObservableObject {
didSet { filter.expiresAt = date }
}
private let identification: Identification
private let identityContext: IdentityContext
private let saveCompletedSubject = PassthroughSubject<Void, Never>()
private var cancellables = Set<AnyCancellable>()
public init(filter: Filter, identification: Identification) {
public init(filter: Filter, identityContext: IdentityContext) {
self.filter = filter
self.identification = identification
self.identityContext = identityContext
date = filter.expiresAt ?? Date()
saveCompleted = saveCompletedSubject.eraseToAnyPublisher()
}
@ -41,7 +41,7 @@ public extension EditFilterViewModel {
}
func save() {
(isNew ? identification.service.createFilter(filter) : identification.service.updateFilter(filter))
(isNew ? identityContext.service.createFilter(filter) : identityContext.service.updateFilter(filter))
.assignErrorsToAlertItem(to: \.alertItem, on: self)
.handleEvents(
receiveSubscription: { [weak self] _ in self?.saving = true },

View file

@ -10,7 +10,7 @@ final public class EmojiPickerViewModel: ObservableObject {
@Published public var query = ""
@Published public var locale = Locale.current
@Published public private(set) var emoji = [PickerEmoji.Category: [PickerEmoji]]()
public let identification: Identification
public let identityContext: IdentityContext
private let emojiPickerService: EmojiPickerService
@Published private var customEmoji = [PickerEmoji.Category: [PickerEmoji]]()
@ -20,9 +20,9 @@ final public class EmojiPickerViewModel: ObservableObject {
private var cancellables = Set<AnyCancellable>()
// swiftlint:disable:next function_body_length
public init(identification: Identification) {
self.identification = identification
emojiPickerService = identification.service.emojiPickerService()
public init(identityContext: IdentityContext) {
self.identityContext = identityContext
emojiPickerService = identityContext.service.emojiPickerService()
emojiPickerService.customEmojiPublisher()
.receive(on: DispatchQueue.main)

View file

@ -7,13 +7,13 @@ public final class ExploreViewModel: ObservableObject {
public let searchViewModel: SearchViewModel
private let exploreService: ExploreService
private let identification: Identification
private let identityContext: IdentityContext
init(service: ExploreService, identification: Identification) {
init(service: ExploreService, identityContext: IdentityContext) {
exploreService = service
self.identification = identification
self.identityContext = identityContext
searchViewModel = SearchViewModel(
searchService: exploreService.searchService(),
identification: identification)
identityContext: identityContext)
}
}

View file

@ -10,17 +10,17 @@ public final class FiltersViewModel: ObservableObject {
@Published public var expiredFilters = [Filter]()
@Published public var alertItem: AlertItem?
private let identification: Identification
private let identityContext: IdentityContext
private var cancellables = Set<AnyCancellable>()
public init(identification: Identification) {
self.identification = identification
public init(identityContext: IdentityContext) {
self.identityContext = identityContext
identification.service.activeFiltersPublisher()
identityContext.service.activeFiltersPublisher()
.assignErrorsToAlertItem(to: \.alertItem, on: self)
.assign(to: &$activeFilters)
identification.service.expiredFiltersPublisher()
identityContext.service.expiredFiltersPublisher()
.assignErrorsToAlertItem(to: \.alertItem, on: self)
.assign(to: &$expiredFilters)
}
@ -28,14 +28,14 @@ public final class FiltersViewModel: ObservableObject {
public extension FiltersViewModel {
func refreshFilters() {
identification.service.refreshFilters()
identityContext.service.refreshFilters()
.assignErrorsToAlertItem(to: \.alertItem, on: self)
.sink { _ in }
.store(in: &cancellables)
}
func delete(filter: Filter) {
identification.service.deleteFilter(id: filter.id)
identityContext.service.deleteFilter(id: filter.id)
.assignErrorsToAlertItem(to: \.alertItem, on: self)
.sink { _ in }
.store(in: &cancellables)

View file

@ -11,14 +11,14 @@ public final class IdentitiesViewModel: ObservableObject {
@Published public var pending = [Identity]()
@Published public var alertItem: AlertItem?
private let identification: Identification
private let identityContext: IdentityContext
private var cancellables = Set<AnyCancellable>()
public init(identification: Identification) {
self.identification = identification
currentIdentityId = identification.identity.id
public init(identityContext: IdentityContext) {
self.identityContext = identityContext
currentIdentityId = identityContext.identity.id
let identitiesPublisher = identification.service.identitiesPublisher()
let identitiesPublisher = identityContext.service.identitiesPublisher()
.assignErrorsToAlertItem(to: \.alertItem, on: self)
.share()

View file

@ -10,13 +10,13 @@ public final class ListsViewModel: ObservableObject {
@Published public private(set) var creatingList = false
@Published public var alertItem: AlertItem?
private let identification: Identification
private let identityContext: IdentityContext
private var cancellables = Set<AnyCancellable>()
public init(identification: Identification) {
self.identification = identification
public init(identityContext: IdentityContext) {
self.identityContext = identityContext
identification.service.listsPublisher()
identityContext.service.listsPublisher()
.map {
$0.compactMap {
guard case let .list(list) = $0 else { return nil }
@ -31,14 +31,14 @@ public final class ListsViewModel: ObservableObject {
public extension ListsViewModel {
func refreshLists() {
identification.service.refreshLists()
identityContext.service.refreshLists()
.assignErrorsToAlertItem(to: \.alertItem, on: self)
.sink { _ in }
.store(in: &cancellables)
}
func createList(title: String) {
identification.service.createList(title: title)
identityContext.service.createList(title: title)
.assignErrorsToAlertItem(to: \.alertItem, on: self)
.handleEvents(
receiveSubscription: { [weak self] _ in self?.creatingList = true },
@ -48,7 +48,7 @@ public extension ListsViewModel {
}
func delete(list: List) {
identification.service.deleteList(id: list.id)
identityContext.service.deleteList(id: list.id)
.assignErrorsToAlertItem(to: \.alertItem, on: self)
.sink { _ in }
.store(in: &cancellables)

View file

@ -5,9 +5,9 @@ import Foundation
import ServiceLayer
public final class MediaPreferencesViewModel: ObservableObject {
private let identification: Identification
private let identityContext: IdentityContext
public init(identification: Identification) {
self.identification = identification
public init(identityContext: IdentityContext) {
self.identityContext = identityContext
}
}

View file

@ -6,7 +6,7 @@ import Mastodon
import ServiceLayer
public final class NavigationViewModel: ObservableObject {
public let identification: Identification
public let identityContext: IdentityContext
public let timelineNavigations: AnyPublisher<Timeline, Never>
@Published public private(set) var recentIdentities = [Identity]()
@ -15,8 +15,8 @@ public final class NavigationViewModel: ObservableObject {
public lazy var exploreViewModel: ExploreViewModel = {
let exploreViewModel = ExploreViewModel(
service: identification.service.exploreService(),
identification: identification)
service: identityContext.service.exploreService(),
identityContext: identityContext)
// TODO: initial request
@ -24,10 +24,10 @@ public final class NavigationViewModel: ObservableObject {
}()
public lazy var notificationsViewModel: CollectionViewModel? = {
if identification.identity.authenticated {
if identityContext.identity.authenticated {
let notificationsViewModel = CollectionItemsViewModel(
collectionService: identification.service.notificationsService(),
identification: identification)
collectionService: identityContext.service.notificationsService(),
identityContext: identityContext)
notificationsViewModel.request(maxId: nil, minId: nil, search: nil)
@ -38,10 +38,10 @@ public final class NavigationViewModel: ObservableObject {
}()
public lazy var conversationsViewModel: CollectionViewModel? = {
if identification.identity.authenticated {
if identityContext.identity.authenticated {
let conversationsViewModel = CollectionItemsViewModel(
collectionService: identification.service.conversationsService(),
identification: identification)
collectionService: identityContext.service.conversationsService(),
identityContext: identityContext)
conversationsViewModel.request(maxId: nil, minId: nil, search: nil)
@ -54,15 +54,15 @@ public final class NavigationViewModel: ObservableObject {
private let timelineNavigationsSubject = PassthroughSubject<Timeline, Never>()
private var cancellables = Set<AnyCancellable>()
public init(identification: Identification) {
self.identification = identification
public init(identityContext: IdentityContext) {
self.identityContext = identityContext
timelineNavigations = timelineNavigationsSubject.eraseToAnyPublisher()
identification.$identity
identityContext.$identity
.sink { [weak self] _ in self?.objectWillChange.send() }
.store(in: &cancellables)
identification.service.recentIdentitiesPublisher()
identityContext.service.recentIdentitiesPublisher()
.assignErrorsToAlertItem(to: \.alertItem, on: self)
.assign(to: &$recentIdentities)
}
@ -77,7 +77,7 @@ public extension NavigationViewModel {
}
var tabs: [Tab] {
if identification.identity.authenticated {
if identityContext.identity.authenticated {
return Tab.allCases
} else {
return [.timelines, .explore]
@ -85,7 +85,7 @@ public extension NavigationViewModel {
}
var timelines: [Timeline] {
if identification.identity.authenticated {
if identityContext.identity.authenticated {
return Timeline.authenticatedDefaults
} else {
return Timeline.unauthenticatedDefaults
@ -93,39 +93,39 @@ public extension NavigationViewModel {
}
func refreshIdentity() {
if identification.identity.pending {
identification.service.verifyCredentials()
if identityContext.identity.pending {
identityContext.service.verifyCredentials()
.collect()
.map { _ in () }
.flatMap(identification.service.confirmIdentity)
.flatMap(identityContext.service.confirmIdentity)
.sink { _ in } receiveValue: { _ in }
.store(in: &cancellables)
} else if identification.identity.authenticated {
identification.service.verifyCredentials()
} else if identityContext.identity.authenticated {
identityContext.service.verifyCredentials()
.assignErrorsToAlertItem(to: \.alertItem, on: self)
.sink { _ in }
.store(in: &cancellables)
identification.service.refreshLists()
identityContext.service.refreshLists()
.sink { _ in } receiveValue: { _ in }
.store(in: &cancellables)
identification.service.refreshFilters()
identityContext.service.refreshFilters()
.sink { _ in } receiveValue: { _ in }
.store(in: &cancellables)
identification.service.refreshEmojis()
identityContext.service.refreshEmojis()
.sink { _ in } receiveValue: { _ in }
.store(in: &cancellables)
identification.service.refreshAnnouncements()
identityContext.service.refreshAnnouncements()
.sink { _ in } receiveValue: { _ in }
.store(in: &cancellables)
if identification.identity.preferences.useServerPostingReadingPreferences {
identification.service.refreshServerPreferences()
if identityContext.identity.preferences.useServerPostingReadingPreferences {
identityContext.service.refreshServerPreferences()
.sink { _ in } receiveValue: { _ in }
.store(in: &cancellables)
}
}
identification.service.refreshInstance()
identityContext.service.refreshInstance()
.sink { _ in } receiveValue: { _ in }
.store(in: &cancellables)
}
@ -137,7 +137,7 @@ public extension NavigationViewModel {
func viewModel(timeline: Timeline) -> CollectionItemsViewModel {
CollectionItemsViewModel(
collectionService: identification.service.service(timeline: timeline),
identification: identification)
collectionService: identityContext.service.service(timeline: timeline),
identityContext: identityContext)
}
}

View file

@ -8,7 +8,7 @@ import ServiceLayer
public final class NewStatusViewModel: ObservableObject {
@Published public var visibility: Status.Visibility
@Published public private(set) var compositionViewModels = [CompositionViewModel]()
@Published public private(set) var identification: Identification
@Published public private(set) var identityContext: IdentityContext
@Published public private(set) var authenticatedIdentities = [Identity]()
@Published public var canPost = false
@Published public var canChangeIdentity = true
@ -24,17 +24,17 @@ public final class NewStatusViewModel: ObservableObject {
private var cancellables = Set<AnyCancellable>()
public init(allIdentitiesService: AllIdentitiesService,
identification: Identification,
identityContext: IdentityContext,
environment: AppEnvironment,
inReplyTo: StatusViewModel?,
redraft: Status?,
extensionContext: NSExtensionContext?) {
self.allIdentitiesService = allIdentitiesService
self.identification = identification
self.identityContext = identityContext
self.environment = environment
inReplyToViewModel = inReplyTo
events = eventsSubject.eraseToAnyPublisher()
visibility = identification.identity.preferences.postingDefaultVisibility
visibility = identityContext.identity.preferences.postingDefaultVisibility
let compositionViewModel: CompositionViewModel
@ -42,7 +42,7 @@ public final class NewStatusViewModel: ObservableObject {
compositionViewModel = CompositionViewModel(
eventsSubject: compositionEventsSubject,
redraft: redraft,
identification: identification)
identityContext: identityContext)
} else if let extensionContext = extensionContext {
compositionViewModel = CompositionViewModel(
eventsSubject: compositionEventsSubject,
@ -95,7 +95,7 @@ public extension NewStatusViewModel {
return
}
identification = Identification(
identityContext = IdentityContext(
identity: identity,
publisher: identityService.identityPublisher(immediate: false)
.assignErrorsToAlertItem(to: \.alertItem, on: self),
@ -161,7 +161,7 @@ private extension NewStatusViewModel {
}
func post(viewModel: CompositionViewModel, inReplyToId: Status.Id?) {
postingState = .posting
identification.service.post(statusComponents: viewModel.components(
identityContext.service.post(statusComponents: viewModel.components(
inReplyToId: inReplyToId,
visibility: visibility))
.receive(on: DispatchQueue.main)

View file

@ -9,14 +9,14 @@ public final class NotificationTypesPreferencesViewModel: ObservableObject {
@Published public var pushSubscriptionAlerts: PushSubscription.Alerts
@Published public var alertItem: AlertItem?
private let identification: Identification
private let identityContext: IdentityContext
private var cancellables = Set<AnyCancellable>()
public init(identification: Identification) {
self.identification = identification
pushSubscriptionAlerts = identification.identity.pushSubscriptionAlerts
public init(identityContext: IdentityContext) {
self.identityContext = identityContext
pushSubscriptionAlerts = identityContext.identity.pushSubscriptionAlerts
identification.$identity
identityContext.$identity
.map(\.pushSubscriptionAlerts)
.dropFirst()
.removeDuplicates()
@ -32,14 +32,14 @@ public final class NotificationTypesPreferencesViewModel: ObservableObject {
private extension NotificationTypesPreferencesViewModel {
func update(alerts: PushSubscription.Alerts) {
guard alerts != identification.identity.pushSubscriptionAlerts else { return }
guard alerts != identityContext.identity.pushSubscriptionAlerts else { return }
identification.service.updatePushSubscription(alerts: alerts)
identityContext.service.updatePushSubscription(alerts: alerts)
.sink { [weak self] in
guard let self = self, case let .failure(error) = $0 else { return }
self.alertItem = AlertItem(error: error)
self.pushSubscriptionAlerts = self.identification.identity.pushSubscriptionAlerts
self.pushSubscriptionAlerts = self.identityContext.identity.pushSubscriptionAlerts
} receiveValue: { _ in }
.store(in: &cancellables)
}

View file

@ -11,21 +11,21 @@ public final class NotificationViewModel: CollectionItemViewModel, ObservableObj
public let events: AnyPublisher<AnyPublisher<CollectionItemEvent, Error>, Never>
private let notificationService: NotificationService
private let identification: Identification
private let identityContext: IdentityContext
private let eventsSubject = PassthroughSubject<AnyPublisher<CollectionItemEvent, Error>, Never>()
init(notificationService: NotificationService, identification: Identification) {
init(notificationService: NotificationService, identityContext: IdentityContext) {
self.notificationService = notificationService
self.identification = identification
self.identityContext = identityContext
self.accountViewModel = AccountViewModel(
accountService: notificationService.navigationService.accountService(
account: notificationService.notification.account),
identification: identification)
identityContext: identityContext)
if let status = notificationService.notification.status {
statusViewModel = StatusViewModel(
statusService: notificationService.navigationService.statusService(status: status),
identification: identification)
identityContext: identityContext)
} else {
statusViewModel = nil
}

View file

@ -8,14 +8,14 @@ public final class PostingReadingPreferencesViewModel: ObservableObject {
@Published public var preferences: Identity.Preferences
@Published public var alertItem: AlertItem?
private let identification: Identification
private let identityContext: IdentityContext
private var cancellables = Set<AnyCancellable>()
public init(identification: Identification) {
self.identification = identification
preferences = identification.identity.preferences
public init(identityContext: IdentityContext) {
self.identityContext = identityContext
preferences = identityContext.identity.preferences
identification.$identity
identityContext.$identity
.map(\.preferences)
.dropFirst()
.removeDuplicates()
@ -23,7 +23,7 @@ public final class PostingReadingPreferencesViewModel: ObservableObject {
$preferences
.dropFirst()
.flatMap(identification.service.updatePreferences)
.flatMap(identityContext.service.updatePreferences)
.assignErrorsToAlertItem(to: \.alertItem, on: self)
.sink { _ in }
.store(in: &cancellables)

View file

@ -7,30 +7,30 @@ public final class PreferencesViewModel: ObservableObject {
public let handle: String
public let shouldShowNotificationTypePreferences: Bool
private let identification: Identification
private let identityContext: IdentityContext
public init(identification: Identification) {
self.identification = identification
handle = identification.identity.handle
public init(identityContext: IdentityContext) {
self.identityContext = identityContext
handle = identityContext.identity.handle
shouldShowNotificationTypePreferences = identification.identity.lastRegisteredDeviceToken != nil
shouldShowNotificationTypePreferences = identityContext.identity.lastRegisteredDeviceToken != nil
}
}
public extension PreferencesViewModel {
func mutedUsersViewModel() -> CollectionViewModel {
CollectionItemsViewModel(
collectionService: identification.service.service(accountList: .mutes),
identification: identification)
collectionService: identityContext.service.service(accountList: .mutes),
identityContext: identityContext)
}
func blockedUsersViewModel() -> CollectionViewModel {
CollectionItemsViewModel(
collectionService: identification.service.service(accountList: .blocks),
identification: identification)
collectionService: identityContext.service.service(accountList: .blocks),
identityContext: identityContext)
}
func domainBlocksViewModel() -> DomainBlocksViewModel {
DomainBlocksViewModel(service: identification.service.domainBlocksService())
DomainBlocksViewModel(service: identityContext.service.domainBlocksService())
}
}

View file

@ -16,23 +16,23 @@ final public class ProfileViewModel {
private let imagePresentationsSubject = PassthroughSubject<URL, Never>()
private var cancellables = Set<AnyCancellable>()
public init(profileService: ProfileService, identification: Identification) {
public init(profileService: ProfileService, identityContext: IdentityContext) {
self.profileService = profileService
imagePresentations = imagePresentationsSubject.eraseToAnyPublisher()
collectionViewModel = CurrentValueSubject(
CollectionItemsViewModel(
collectionService: profileService.timelineService(profileCollection: .statuses),
identification: identification))
identityContext: identityContext))
profileService.accountServicePublisher
.map { AccountViewModel(accountService: $0, identification: identification) }
.map { AccountViewModel(accountService: $0, identityContext: identityContext) }
.assignErrorsToAlertItem(to: \.alertItem, on: self)
.assign(to: &$accountViewModel)
$collection.dropFirst()
.map(profileService.timelineService(profileCollection:))
.map { CollectionItemsViewModel(collectionService: $0, identification: identification) }
.map { CollectionItemsViewModel(collectionService: $0, identityContext: identityContext) }
.sink { [weak self] in
guard let self = self else { return }

View file

@ -16,13 +16,13 @@ public final class ReportViewModel: ObservableObject {
private let eventsSubject = PassthroughSubject<Event, Never>()
private var cancellables = Set<AnyCancellable>()
public init(accountService: AccountService, statusService: StatusService? = nil, identification: Identification) {
public init(accountService: AccountService, statusService: StatusService? = nil, identityContext: IdentityContext) {
self.accountService = accountService
elements = ReportElements(accountId: accountService.account.id)
events = eventsSubject.eraseToAnyPublisher()
if let statusService = statusService {
statusViewModel = StatusViewModel(statusService: statusService, identification: identification)
statusViewModel = StatusViewModel(statusService: statusService, identityContext: identityContext)
elements.statusIds.insert(statusService.status.displayStatus.id)
} else {
statusViewModel = nil

View file

@ -60,12 +60,12 @@ public extension RootViewModel {
}
func newStatusViewModel(
identification: Identification,
identityContext: IdentityContext,
inReplyTo: StatusViewModel? = nil,
redraft: Status? = nil) -> NewStatusViewModel {
NewStatusViewModel(
allIdentitiesService: allIdentitiesService,
identification: identification,
identityContext: identityContext,
environment: environment,
inReplyTo: inReplyTo,
redraft: redraft,
@ -96,30 +96,30 @@ private extension RootViewModel {
.share()
identityPublisher
.filter { [weak self] in $0.id != self?.navigationViewModel?.identification.identity.id }
.filter { [weak self] in $0.id != self?.navigationViewModel?.identityContext.identity.id }
.map { [weak self] in
guard let self = self else { return nil }
let identification = Identification(
let identityContext = IdentityContext(
identity: $0,
publisher: identityPublisher.eraseToAnyPublisher(),
service: identityService,
environment: self.environment)
identification.service.updateLastUse()
identityContext.service.updateLastUse()
.sink { _ in } receiveValue: { _ in }
.store(in: &self.cancellables)
self.userNotificationService.isAuthorized()
.filter { $0 }
.zip(self.registerForRemoteNotifications())
.filter { identification.identity.lastRegisteredDeviceToken != $1 }
.map { ($1, identification.identity.pushSubscriptionAlerts) }
.flatMap(identification.service.createPushSubscription(deviceToken:alerts:))
.filter { identityContext.identity.lastRegisteredDeviceToken != $1 }
.map { ($1, identityContext.identity.pushSubscriptionAlerts) }
.flatMap(identityContext.service.createPushSubscription(deviceToken:alerts:))
.sink { _ in } receiveValue: { _ in }
.store(in: &self.cancellables)
return NavigationViewModel(identification: identification)
return NavigationViewModel(identityContext: identityContext)
}
.assign(to: &$navigationViewModel)
}

View file

@ -11,10 +11,10 @@ public final class SearchViewModel: CollectionItemsViewModel {
private let searchService: SearchService
private var cancellables = Set<AnyCancellable>()
public init(searchService: SearchService, identification: Identification) {
public init(searchService: SearchService, identityContext: IdentityContext) {
self.searchService = searchService
super.init(collectionService: searchService, identification: identification)
super.init(collectionService: searchService, identityContext: identityContext)
$query.removeDuplicates()
.throttle(for: .seconds(Self.throttleInterval), scheduler: DispatchQueue.global(), latest: true)

View file

@ -26,7 +26,7 @@ public extension ShareExtensionNavigationViewModel {
else { throw ShareExtensionError.noAccountFound }
let identityService = try allIdentitiesService.identityService(id: identity.id)
let identification = Identification(
let identityContext = IdentityContext(
identity: identity,
publisher: identityService.identityPublisher(immediate: false)
.assignErrorsToAlertItem(to: \.alertItem, on: self),
@ -35,7 +35,7 @@ public extension ShareExtensionNavigationViewModel {
return NewStatusViewModel(
allIdentitiesService: allIdentitiesService,
identification: identification,
identityContext: identityContext,
environment: environment,
inReplyTo: nil,
redraft: nil,

View file

@ -21,12 +21,12 @@ public final class StatusViewModel: CollectionItemViewModel, AttachmentsRenderin
public let events: AnyPublisher<AnyPublisher<CollectionItemEvent, Error>, Never>
private let statusService: StatusService
private let identification: Identification
private let identityContext: IdentityContext
private let eventsSubject = PassthroughSubject<AnyPublisher<CollectionItemEvent, Error>, Never>()
init(statusService: StatusService, identification: Identification) {
init(statusService: StatusService, identityContext: IdentityContext) {
self.statusService = statusService
self.identification = identification
self.identityContext = identityContext
content = statusService.status.displayStatus.content.attributed
contentEmojis = statusService.status.displayStatus.emojis
displayName = statusService.status.displayStatus.account.displayName.isEmpty
@ -40,19 +40,19 @@ public final class StatusViewModel: CollectionItemViewModel, AttachmentsRenderin
: statusService.status.account.displayName
rebloggedByDisplayNameEmojis = statusService.status.account.emojis
attachmentViewModels = statusService.status.displayStatus.mediaAttachments
.map { AttachmentViewModel(attachment: $0, identification: identification, status: statusService.status) }
.map { AttachmentViewModel(attachment: $0, identityContext: identityContext, status: statusService.status) }
pollEmojis = statusService.status.displayStatus.poll?.emojis ?? []
events = eventsSubject.eraseToAnyPublisher()
}
}
public extension StatusViewModel {
var isMine: Bool { statusService.status.displayStatus.account.id == identification.identity.account?.id }
var isMine: Bool { statusService.status.displayStatus.account.id == identityContext.identity.account?.id }
var shouldShowContent: Bool {
guard spoilerText != "" else { return true }
if identification.identity.preferences.readingExpandSpoilers {
if identityContext.identity.preferences.readingExpandSpoilers {
return !configuration.showContentToggled
} else {
return configuration.showContentToggled
@ -60,7 +60,7 @@ public extension StatusViewModel {
}
var shouldShowAttachments: Bool {
switch identification.identity.preferences.readingExpandMedia {
switch identityContext.identity.preferences.readingExpandMedia {
case .default, .unknown:
return !sensitive || configuration.showAttachmentsToggled
case .showAll:
@ -71,7 +71,7 @@ public extension StatusViewModel {
}
var shouldShowHideAttachmentsButton: Bool {
sensitive || identification.identity.preferences.readingExpandMedia == .hideAll
sensitive || identityContext.identity.preferences.readingExpandMedia == .hideAll
}
var id: Status.Id { statusService.status.displayStatus.id }
@ -79,8 +79,8 @@ public extension StatusViewModel {
var accountName: String { "@".appending(statusService.status.displayStatus.account.acct) }
var avatarURL: URL {
if !identification.appPreferences.shouldReduceMotion,
identification.appPreferences.animateAvatars == .everywhere {
if !identityContext.appPreferences.shouldReduceMotion,
identityContext.appPreferences.animateAvatars == .everywhere {
return statusService.status.displayStatus.account.avatar
} else {
return statusService.status.displayStatus.account.avatarStatic
@ -209,7 +209,7 @@ public extension StatusViewModel {
}
func reply() {
let replyViewModel = Self(statusService: statusService, identification: identification)
let replyViewModel = Self(statusService: statusService, identityContext: identityContext)
replyViewModel.configuration = configuration.reply()
@ -269,7 +269,7 @@ public extension StatusViewModel {
}
func deleteAndRedraft() {
let identification = self.identification
let identityContext = self.identityContext
eventsSubject.send(
statusService.deleteAndRedraft()
@ -279,7 +279,7 @@ public extension StatusViewModel {
if let inReplyToStatusService = inReplyToStatusService {
inReplyToViewModel = Self(
statusService: inReplyToStatusService,
identification: identification)
identityContext: identityContext)
inReplyToViewModel?.configuration = CollectionItem.StatusConfiguration.default.reply()
} else {
inReplyToViewModel = nil
@ -306,7 +306,7 @@ public extension StatusViewModel {
accountService: statusService.navigationService.accountService(
account: statusService.status.displayStatus.account),
statusService: statusService,
identification: identification)))
identityContext: identityContext)))
.setFailureType(to: Error.self)
.eraseToAnyPublisher())
}

View file

@ -82,7 +82,7 @@ final class AttachmentsView: UIView {
extension AttachmentsView {
static func estimatedHeight(width: CGFloat,
identification: Identification,
identityContext: IdentityContext,
status: Status,
configuration: CollectionItem.StatusConfiguration) -> CGFloat {
let height: CGFloat

View file

@ -175,7 +175,7 @@ private extension CompositionView {
}
.store(in: &cancellables)
parentViewModel.$identification.map(\.identity.image)
parentViewModel.$identityContext.map(\.identity.image)
.sink { [weak self] in self?.avatarImageView.kf.setImage(with: $0) }
.store(in: &cancellables)

View file

@ -28,7 +28,7 @@ final class ConversationView: UIView {
extension ConversationView {
static func estimatedHeight(width: CGFloat,
identification: Identification,
identityContext: IdentityContext,
conversation: Conversation) -> CGFloat {
guard let status = conversation.lastStatus else { return UITableView.automaticDimension }
@ -38,7 +38,7 @@ extension ConversationView {
+ UIFont.preferredFont(forTextStyle: .headline).lineHeight
+ StatusBodyView.estimatedHeight(
width: bodyWidth,
identification: identification,
identityContext: identityContext,
status: status,
configuration: .default)
}

View file

@ -101,7 +101,7 @@ import PreviewViewModels
struct EditFilterView_Previews: PreviewProvider {
static var previews: some View {
EditFilterView(viewModel: .init(filter: .new, identification: .preview))
EditFilterView(viewModel: .init(filter: .new, identityContext: .preview))
}
}
#endif

View file

@ -6,13 +6,13 @@ import ViewModels
struct FiltersView: View {
@StateObject var viewModel: FiltersViewModel
@EnvironmentObject var identification: Identification
@EnvironmentObject var identityContext: IdentityContext
var body: some View {
Form {
Section {
NavigationLink(destination: EditFilterView(
viewModel: .init(filter: .new, identification: identification))) {
viewModel: .init(filter: .new, identityContext: identityContext))) {
Label("add", systemImage: "plus.circle")
}
}
@ -37,7 +37,7 @@ private extension FiltersView {
Section(header: Text(title)) {
ForEach(filters) { filter in
NavigationLink(destination: EditFilterView(
viewModel: .init(filter: filter, identification: identification))) {
viewModel: .init(filter: filter, identityContext: identityContext))) {
HStack {
Text(filter.phrase)
Spacer()
@ -61,7 +61,7 @@ import PreviewViewModels
struct FiltersView_Previews: PreviewProvider {
static var previews: some View {
FiltersView(viewModel: .init(identification: .preview))
FiltersView(viewModel: .init(identityContext: .preview))
}
}
#endif

View file

@ -105,7 +105,7 @@ import PreviewViewModels
struct IdentitiesView_Previews: PreviewProvider {
static var previews: some View {
IdentitiesView(viewModel: .init(identification: .preview))
IdentitiesView(viewModel: .init(identityContext: .preview))
.environmentObject(RootViewModel.preview)
}
}

View file

@ -63,8 +63,8 @@ import PreviewViewModels
struct ListsView_Previews: PreviewProvider {
static var previews: some View {
ListsView(viewModel: .init(identification: .preview))
.environmentObject(NavigationViewModel(identification: .preview))
ListsView(viewModel: .init(identityContext: .preview))
.environmentObject(NavigationViewModel(identityContext: .preview))
}
}
#endif

View file

@ -6,7 +6,7 @@ import ViewModels
struct MainNavigationView: UIViewControllerRepresentable {
let viewModelClosure: () -> NavigationViewModel
@EnvironmentObject var rootViewModel: RootViewModel
@EnvironmentObject var identification: Identification
@EnvironmentObject var identityContext: IdentityContext
func makeUIViewController(context: Context) -> MainNavigationViewController {
MainNavigationViewController(
@ -24,8 +24,8 @@ import PreviewViewModels
struct MainNavigationView_Previews: PreviewProvider {
static var previews: some View {
MainNavigationView { NavigationViewModel(identification: .preview) }
.environmentObject(Identification.preview)
MainNavigationView { NavigationViewModel(identityContext: .preview) }
.environmentObject(IdentityContext.preview)
.environmentObject(RootViewModel.preview)
}
}

View file

@ -5,7 +5,7 @@ import ViewModels
struct MediaPreferencesView: View {
@StateObject var viewModel: MediaPreferencesViewModel
@EnvironmentObject var identification: Identification
@EnvironmentObject var identityContext: IdentityContext
@Environment(\.accessibilityReduceMotion) var accessibilityReduceMotion
var body: some View {
@ -13,18 +13,18 @@ struct MediaPreferencesView: View {
if accessibilityReduceMotion {
Section {
Toggle("preferences.media.use-system-reduce-motion",
isOn: $identification.appPreferences.useSystemReduceMotionForMedia)
isOn: $identityContext.appPreferences.useSystemReduceMotionForMedia)
}
}
Section(header: Text("preferences.media.autoplay")) {
Picker("preferences.media.autoplay.gifs",
selection: reduceMotion ? .constant(.never) : $identification.appPreferences.autoplayGIFs) {
selection: reduceMotion ? .constant(.never) : $identityContext.appPreferences.autoplayGIFs) {
ForEach(AppPreferences.Autoplay.allCases) { option in
Text(option.localizedStringKey).tag(option)
}
}
Picker("preferences.media.autoplay.videos",
selection: reduceMotion ? .constant(.never) : $identification.appPreferences.autoplayVideos) {
selection: reduceMotion ? .constant(.never) : $identityContext.appPreferences.autoplayVideos) {
ForEach(AppPreferences.Autoplay.allCases) { option in
Text(option.localizedStringKey).tag(option)
}
@ -33,7 +33,7 @@ struct MediaPreferencesView: View {
.disabled(reduceMotion)
Section(header: Text("preferences.media.avatars")) {
Picker("preferences.media.avatars.animate",
selection: reduceMotion ? .constant(.never) : $identification.appPreferences.animateAvatars) {
selection: reduceMotion ? .constant(.never) : $identityContext.appPreferences.animateAvatars) {
ForEach(AppPreferences.AnimateAvatars.allCases) { option in
Text(option.localizedStringKey).tag(option)
}
@ -42,7 +42,7 @@ struct MediaPreferencesView: View {
}
Section(header: Text("preferences.media.headers")) {
Toggle("preferences.media.headers.animate",
isOn: reduceMotion ? .constant(false) : $identification.appPreferences.animateHeaders)
isOn: reduceMotion ? .constant(false) : $identityContext.appPreferences.animateHeaders)
.disabled(reduceMotion)
}
}
@ -52,7 +52,7 @@ struct MediaPreferencesView: View {
private extension MediaPreferencesView {
var reduceMotion: Bool {
identification.appPreferences.shouldReduceMotion
identityContext.appPreferences.shouldReduceMotion
}
}
@ -85,7 +85,7 @@ extension AppPreferences.Autoplay {
#if DEBUG
struct MediaPreferencesView_Previews: PreviewProvider {
static var previews: some View {
MediaPreferencesView(viewModel: .init(identification: .preview))
MediaPreferencesView(viewModel: .init(identityContext: .preview))
}
}
#endif

View file

@ -29,7 +29,7 @@ import PreviewViewModels
struct NotificationTypesPreferencesView_Previews: PreviewProvider {
static var previews: some View {
NotificationTypesPreferencesView(viewModel: .init(identification: .preview))
NotificationTypesPreferencesView(viewModel: .init(identityContext: .preview))
}
}
#endif

View file

@ -32,7 +32,7 @@ final class NotificationView: UIView {
extension NotificationView {
static func estimatedHeight(width: CGFloat,
identification: Identification,
identityContext: IdentityContext,
notification: MastodonNotification,
configuration: CollectionItem.StatusConfiguration?) -> CGFloat {
let bodyWidth = width - .defaultSpacing - .avatarDimension
@ -44,7 +44,7 @@ extension NotificationView {
if let status = notification.status {
height += StatusBodyView.estimatedHeight(
width: bodyWidth,
identification: identification,
identityContext: identityContext,
status: status,
configuration: configuration ?? .default)
} else {

View file

@ -114,7 +114,7 @@ final class PollView: UIView {
extension PollView {
static func estimatedHeight(width: CGFloat,
identification: Identification,
identityContext: IdentityContext,
status: Status,
configuration: CollectionItem.StatusConfiguration) -> CGFloat {
if let poll = status.displayStatus.poll {

View file

@ -54,7 +54,7 @@ import PreviewViewModels
struct PostingReadingPreferencesViewView_Previews: PreviewProvider {
static var previews: some View {
PostingReadingPreferencesView(viewModel: .init(identification: .preview))
PostingReadingPreferencesView(viewModel: .init(identityContext: .preview))
}
}
#endif

View file

@ -5,21 +5,21 @@ import ViewModels
struct PreferencesView: View {
@StateObject var viewModel: PreferencesViewModel
@EnvironmentObject var identification: Identification
@EnvironmentObject var identityContext: IdentityContext
var body: some View {
Form {
Section(header: Text(viewModel.handle)) {
NavigationLink("preferences.posting-reading",
destination: PostingReadingPreferencesView(
viewModel: .init(identification: identification)))
viewModel: .init(identityContext: identityContext)))
NavigationLink("preferences.filters",
destination: FiltersView(
viewModel: .init(identification: identification)))
viewModel: .init(identityContext: identityContext)))
if viewModel.shouldShowNotificationTypePreferences {
NavigationLink("preferences.notification-types",
destination: NotificationTypesPreferencesView(
viewModel: .init(identification: identification)))
viewModel: .init(identityContext: identityContext)))
}
NavigationLink("preferences.muted-users",
destination: TableView(viewModelClosure: viewModel.mutedUsersViewModel)
@ -33,7 +33,7 @@ struct PreferencesView: View {
Section(header: Text("preferences.app")) {
NavigationLink("preferences.media",
destination: MediaPreferencesView(
viewModel: .init(identification: identification)))
viewModel: .init(identityContext: identityContext)))
NavigationLink("preferences.startup-and-syncing",
destination: StartupAndSyncingPreferencesView())
}
@ -47,7 +47,7 @@ import PreviewViewModels
struct PreferencesView_Previews: PreviewProvider {
static var previews: some View {
PreferencesView(viewModel: .init(identification: .preview))
PreferencesView(viewModel: .init(identityContext: .preview))
}
}
#endif

View file

@ -9,7 +9,7 @@ struct RootView: View {
var body: some View {
if let navigationViewModel = viewModel.navigationViewModel {
MainNavigationView { navigationViewModel }
.id(navigationViewModel.identification.identity.id)
.id(navigationViewModel.identityContext.identity.id)
.environmentObject(viewModel)
.transition(.opacity)
.edgesIgnoringSafeArea(.all)

View file

@ -29,7 +29,7 @@ final class SecondaryNavigationButton: UIBarButtonItem {
button.heightAnchor.constraint(equalToConstant: .barButtonItemDimension)
])
viewModel.identification.$identity.sink {
viewModel.identityContext.$identity.sink {
button.kf.setImage(
with: $0.image,
for: .normal,

View file

@ -13,30 +13,30 @@ struct SecondaryNavigationView: View {
Form {
Section {
NavigationLink(
destination: IdentitiesView(viewModel: .init(identification: viewModel.identification))
destination: IdentitiesView(viewModel: .init(identityContext: viewModel.identityContext))
.environmentObject(rootViewModel)
.environmentObject(viewModel.identification),
.environmentObject(viewModel.identityContext),
label: {
HStack {
KFImage(viewModel.identification.identity.image)
KFImage(viewModel.identityContext.identity.image)
.downsampled(dimension: .avatarDimension, scaleFactor: displayScale)
VStack(alignment: .leading) {
if viewModel.identification.identity.authenticated {
if let account = viewModel.identification.identity.account {
if viewModel.identityContext.identity.authenticated {
if let account = viewModel.identityContext.identity.account {
CustomEmojiText(
text: account.displayName,
emojis: account.emojis,
textStyle: .headline)
}
Text(viewModel.identification.identity.handle)
Text(viewModel.identityContext.identity.handle)
.font(.subheadline)
.foregroundColor(.secondary)
.lineLimit(1)
.minimumScaleFactor(0.5)
} else {
Text(viewModel.identification.identity.handle)
Text(viewModel.identityContext.identity.handle)
.font(.headline)
if let instance = viewModel.identification.identity.instance {
if let instance = viewModel.identityContext.identity.instance {
Text(instance.uri)
.font(.subheadline)
.foregroundColor(.secondary)
@ -54,9 +54,9 @@ struct SecondaryNavigationView: View {
})
}
Section {
NavigationLink(destination: ListsView(viewModel: .init(identification: viewModel.identification))
NavigationLink(destination: ListsView(viewModel: .init(identityContext: viewModel.identityContext))
.environmentObject(rootViewModel)
.environmentObject(viewModel.identification)) {
.environmentObject(viewModel.identityContext)) {
Label("secondary-navigation.lists", systemImage: "scroll")
}
ForEach([Timeline.favorites, Timeline.bookmarks]) { timeline in
@ -73,9 +73,9 @@ struct SecondaryNavigationView: View {
}
Section {
NavigationLink(
destination: PreferencesView(viewModel: .init(identification: viewModel.identification))
destination: PreferencesView(viewModel: .init(identityContext: viewModel.identityContext))
.environmentObject(rootViewModel)
.environmentObject(viewModel.identification)) {
.environmentObject(viewModel.identityContext)) {
Label("secondary-navigation.preferences", systemImage: "gear")
}
}
@ -88,7 +88,7 @@ import PreviewViewModels
struct SecondaryNavigationView_Previews: PreviewProvider {
static var previews: some View {
SecondaryNavigationView(viewModel: NavigationViewModel(identification: .preview))
SecondaryNavigationView(viewModel: NavigationViewModel(identityContext: .preview))
.environmentObject(RootViewModel.preview)
}
}

View file

@ -4,13 +4,13 @@ import SwiftUI
import ViewModels
struct StartupAndSyncingPreferencesView: View {
@EnvironmentObject var identification: Identification
@EnvironmentObject var identityContext: IdentityContext
var body: some View {
Form {
Section(header: Text("preferences.startup-and-syncing.home-timeline")) {
Picker("preferences.startup-and-syncing.position-on-startup",
selection: $identification.appPreferences.homeTimelineBehavior) {
selection: $identityContext.appPreferences.homeTimelineBehavior) {
ForEach(AppPreferences.PositionBehavior.allCases) { option in
Text(option.localizedStringKey).tag(option)
}
@ -18,7 +18,7 @@ struct StartupAndSyncingPreferencesView: View {
}
Section(header: Text("preferences.startup-and-syncing.notifications-tab")) {
Picker("preferences.startup-and-syncing.position-on-startup",
selection: $identification.appPreferences.notificationsTabBehavior) {
selection: $identityContext.appPreferences.notificationsTabBehavior) {
ForEach(AppPreferences.PositionBehavior.allCases) { option in
Text(option.localizedStringKey).tag(option)
}
@ -47,7 +47,7 @@ import PreviewViewModels
struct StartupAndSyncingPreferencesView_Previews: PreviewProvider {
static var previews: some View {
StartupAndSyncingPreferencesView()
.environmentObject(Identification.preview)
.environmentObject(IdentityContext.preview)
}
}
#endif

View file

@ -47,7 +47,7 @@ final class CardView: UIView {
extension CardView {
static func estimatedHeight(width: CGFloat,
identification: Identification,
identityContext: IdentityContext,
status: Status,
configuration: CollectionItem.StatusConfiguration) -> CGFloat {
if status.displayStatus.card != nil {

View file

@ -72,7 +72,7 @@ final class StatusBodyView: UIView {
extension StatusBodyView {
static func estimatedHeight(width: CGFloat,
identification: Identification,
identityContext: IdentityContext,
status: Status,
configuration: CollectionItem.StatusConfiguration) -> CGFloat {
let contentFont = UIFont.preferredFont(forTextStyle: configuration.isContextParent ? .title3 : .callout)
@ -86,7 +86,7 @@ extension StatusBodyView {
contentHeight += .compactSpacing
contentHeight += CardView.estimatedHeight(
width: width,
identification: identification,
identityContext: identityContext,
status: status,
configuration: configuration)
}
@ -95,7 +95,7 @@ extension StatusBodyView {
contentHeight += .defaultSpacing
contentHeight += PollView.estimatedHeight(
width: width,
identification: identification,
identityContext: identityContext,
status: status,
configuration: configuration)
}
@ -109,7 +109,7 @@ extension StatusBodyView {
width: width,
font: .preferredFont(forTextStyle: .headline))
if configuration.showContentToggled && !identification.identity.preferences.readingExpandSpoilers {
if configuration.showContentToggled && !identityContext.identity.preferences.readingExpandSpoilers {
height += .compactSpacing
height += contentHeight
}
@ -119,7 +119,7 @@ extension StatusBodyView {
height += .compactSpacing
height += AttachmentsView.estimatedHeight(
width: width,
identification: identification,
identityContext: identityContext,
status: status,
configuration: configuration)
}

View file

@ -59,7 +59,7 @@ final class StatusView: UIView {
extension StatusView {
static func estimatedHeight(width: CGFloat,
identification: Identification,
identityContext: IdentityContext,
status: Status,
configuration: CollectionItem.StatusConfiguration) -> CGFloat {
var height = CGFloat.defaultSpacing * 2
@ -78,7 +78,7 @@ extension StatusView {
height += StatusBodyView.estimatedHeight(
width: bodyWidth,
identification: identification,
identityContext: identityContext,
status: status,
configuration: configuration)
+ .compactSpacing

View file

@ -4,12 +4,14 @@ import SwiftUI
import ViewModels
struct TableView: UIViewControllerRepresentable {
@EnvironmentObject var identification: Identification
@EnvironmentObject var identityContext: IdentityContext
@EnvironmentObject var rootViewModel: RootViewModel
let viewModelClosure: () -> CollectionViewModel
func makeUIViewController(context: Context) -> TableViewController {
TableViewController(viewModel: viewModelClosure(), rootViewModel: rootViewModel, identification: identification)
TableViewController(viewModel: viewModelClosure(),
rootViewModel: rootViewModel,
identityContext: identityContext)
}
func updateUIViewController(_ uiViewController: TableViewController, context: Context) {