mirror of
https://github.com/metabolist/metatext.git
synced 2024-11-25 17:50:59 +00:00
Refactoring
This commit is contained in:
parent
f1ef5def5b
commit
9659085ca6
7 changed files with 118 additions and 68 deletions
|
@ -15,6 +15,8 @@
|
||||||
D0091B6F24DD68090040E8D2 /* PreferencesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0091B6D24DD68090040E8D2 /* PreferencesView.swift */; };
|
D0091B6F24DD68090040E8D2 /* PreferencesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0091B6D24DD68090040E8D2 /* PreferencesView.swift */; };
|
||||||
D0091B7124DD68220040E8D2 /* PreferencesViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0091B7024DD68220040E8D2 /* PreferencesViewModel.swift */; };
|
D0091B7124DD68220040E8D2 /* PreferencesViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0091B7024DD68220040E8D2 /* PreferencesViewModel.swift */; };
|
||||||
D0091B7224DD68220040E8D2 /* PreferencesViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0091B7024DD68220040E8D2 /* PreferencesViewModel.swift */; };
|
D0091B7224DD68220040E8D2 /* PreferencesViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0091B7024DD68220040E8D2 /* PreferencesViewModel.swift */; };
|
||||||
|
D0091B7424DDF4860040E8D2 /* IdentifiedEnvironment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0091B7324DDF4860040E8D2 /* IdentifiedEnvironment.swift */; };
|
||||||
|
D0091B7524DDF4860040E8D2 /* IdentifiedEnvironment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0091B7324DDF4860040E8D2 /* IdentifiedEnvironment.swift */; };
|
||||||
D047FAAE24C3E21200AF17C5 /* MetatextApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = D047FA8524C3E21000AF17C5 /* MetatextApp.swift */; };
|
D047FAAE24C3E21200AF17C5 /* MetatextApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = D047FA8524C3E21000AF17C5 /* MetatextApp.swift */; };
|
||||||
D047FAAF24C3E21200AF17C5 /* MetatextApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = D047FA8524C3E21000AF17C5 /* MetatextApp.swift */; };
|
D047FAAF24C3E21200AF17C5 /* MetatextApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = D047FA8524C3E21000AF17C5 /* MetatextApp.swift */; };
|
||||||
D047FAB224C3E21200AF17C5 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D047FA8724C3E21200AF17C5 /* Assets.xcassets */; };
|
D047FAB224C3E21200AF17C5 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D047FA8724C3E21200AF17C5 /* Assets.xcassets */; };
|
||||||
|
@ -171,6 +173,7 @@
|
||||||
D0091B6A24DC10CE0040E8D2 /* PostingReadingPreferencesViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostingReadingPreferencesViewModel.swift; sourceTree = "<group>"; };
|
D0091B6A24DC10CE0040E8D2 /* PostingReadingPreferencesViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostingReadingPreferencesViewModel.swift; sourceTree = "<group>"; };
|
||||||
D0091B6D24DD68090040E8D2 /* PreferencesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesView.swift; sourceTree = "<group>"; };
|
D0091B6D24DD68090040E8D2 /* PreferencesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesView.swift; sourceTree = "<group>"; };
|
||||||
D0091B7024DD68220040E8D2 /* PreferencesViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesViewModel.swift; sourceTree = "<group>"; };
|
D0091B7024DD68220040E8D2 /* PreferencesViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesViewModel.swift; sourceTree = "<group>"; };
|
||||||
|
D0091B7324DDF4860040E8D2 /* IdentifiedEnvironment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IdentifiedEnvironment.swift; sourceTree = "<group>"; };
|
||||||
D047FA8524C3E21000AF17C5 /* MetatextApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MetatextApp.swift; sourceTree = "<group>"; };
|
D047FA8524C3E21000AF17C5 /* MetatextApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MetatextApp.swift; sourceTree = "<group>"; };
|
||||||
D047FA8724C3E21200AF17C5 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
D047FA8724C3E21200AF17C5 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||||
D047FA8C24C3E21200AF17C5 /* Metatext.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Metatext.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
D047FA8C24C3E21200AF17C5 /* Metatext.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Metatext.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
@ -358,6 +361,7 @@
|
||||||
D0666A4424C6BC0A00F3F04B /* DatabaseError.swift */,
|
D0666A4424C6BC0A00F3F04B /* DatabaseError.swift */,
|
||||||
D052BBCE24D750C000A80A7A /* Defaults.swift */,
|
D052BBCE24D750C000A80A7A /* Defaults.swift */,
|
||||||
D0666A5324C6C3E500F3F04B /* Emoji.swift */,
|
D0666A5324C6C3E500F3F04B /* Emoji.swift */,
|
||||||
|
D0091B7324DDF4860040E8D2 /* IdentifiedEnvironment.swift */,
|
||||||
D0666A4A24C6C37700F3F04B /* Identity.swift */,
|
D0666A4A24C6C37700F3F04B /* Identity.swift */,
|
||||||
D0666A4124C6BB7B00F3F04B /* IdentityDatabase.swift */,
|
D0666A4124C6BB7B00F3F04B /* IdentityDatabase.swift */,
|
||||||
D0666A4D24C6C39600F3F04B /* Instance.swift */,
|
D0666A4D24C6C39600F3F04B /* Instance.swift */,
|
||||||
|
@ -690,6 +694,7 @@
|
||||||
D0BEC94724CA22C400E864C4 /* TimelineViewModel.swift in Sources */,
|
D0BEC94724CA22C400E864C4 /* TimelineViewModel.swift in Sources */,
|
||||||
D0666A4E24C6C39600F3F04B /* Instance.swift in Sources */,
|
D0666A4E24C6C39600F3F04B /* Instance.swift in Sources */,
|
||||||
D0ED1BDA24CF963E00B4899C /* AppAuthorizationEndpoint.swift in Sources */,
|
D0ED1BDA24CF963E00B4899C /* AppAuthorizationEndpoint.swift in Sources */,
|
||||||
|
D0091B7424DDF4860040E8D2 /* IdentifiedEnvironment.swift in Sources */,
|
||||||
D052BBCF24D750C000A80A7A /* Defaults.swift in Sources */,
|
D052BBCF24D750C000A80A7A /* Defaults.swift in Sources */,
|
||||||
D0ED1BE324CFA84400B4899C /* MastodonError.swift in Sources */,
|
D0ED1BE324CFA84400B4899C /* MastodonError.swift in Sources */,
|
||||||
D0666A6324C6DC6C00F3F04B /* AppAuthorization.swift in Sources */,
|
D0666A6324C6DC6C00F3F04B /* AppAuthorization.swift in Sources */,
|
||||||
|
@ -764,6 +769,7 @@
|
||||||
D0BEC94824CA22C400E864C4 /* TimelineViewModel.swift in Sources */,
|
D0BEC94824CA22C400E864C4 /* TimelineViewModel.swift in Sources */,
|
||||||
D0666A4F24C6C39600F3F04B /* Instance.swift in Sources */,
|
D0666A4F24C6C39600F3F04B /* Instance.swift in Sources */,
|
||||||
D0ED1BDB24CF963E00B4899C /* AppAuthorizationEndpoint.swift in Sources */,
|
D0ED1BDB24CF963E00B4899C /* AppAuthorizationEndpoint.swift in Sources */,
|
||||||
|
D0091B7524DDF4860040E8D2 /* IdentifiedEnvironment.swift in Sources */,
|
||||||
D052BBD024D750C000A80A7A /* Defaults.swift in Sources */,
|
D052BBD024D750C000A80A7A /* Defaults.swift in Sources */,
|
||||||
D0ED1BE424CFA84400B4899C /* MastodonError.swift in Sources */,
|
D0ED1BE424CFA84400B4899C /* MastodonError.swift in Sources */,
|
||||||
D0666A6424C6DC6C00F3F04B /* AppAuthorization.swift in Sources */,
|
D0666A6424C6DC6C00F3F04B /* AppAuthorization.swift in Sources */,
|
||||||
|
|
|
@ -16,4 +16,24 @@ extension Publisher {
|
||||||
}
|
}
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func continuingIfWeakReferenceIsStillAlive<T: AnyObject>(to object: T) -> AnyPublisher<(Output, T), Error> {
|
||||||
|
tryMap { [weak object] in
|
||||||
|
guard let object = object else { throw WeakReferenceError.deallocated }
|
||||||
|
|
||||||
|
return ($0, object)
|
||||||
|
}
|
||||||
|
.tryCatch { error -> Empty<(Output, T), Never> in
|
||||||
|
if case WeakReferenceError.deallocated = error {
|
||||||
|
return Empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
.eraseToAnyPublisher()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum WeakReferenceError: Error {
|
||||||
|
case deallocated
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
// Copyright © 2020 Metabolist. All rights reserved.
|
// Copyright © 2020 Metabolist. All rights reserved.
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import Combine
|
|
||||||
|
|
||||||
struct AppEnvironment {
|
struct AppEnvironment {
|
||||||
let URLSessionConfiguration: URLSessionConfiguration
|
let URLSessionConfiguration: URLSessionConfiguration
|
||||||
|
@ -10,41 +9,3 @@ struct AppEnvironment {
|
||||||
let secrets: Secrets
|
let secrets: Secrets
|
||||||
let webAuthSessionType: WebAuthSession.Type
|
let webAuthSessionType: WebAuthSession.Type
|
||||||
}
|
}
|
||||||
|
|
||||||
class IdentifiedEnvironment {
|
|
||||||
@Published var identity: Identity
|
|
||||||
let observationErrors: AnyPublisher<Error, Never>
|
|
||||||
let networkClient: MastodonClient
|
|
||||||
let appEnvironment: AppEnvironment
|
|
||||||
|
|
||||||
private var cancellables = Set<AnyCancellable>()
|
|
||||||
private let observationErrorsInput = PassthroughSubject<Error, Never>()
|
|
||||||
|
|
||||||
init(identityID: String, appEnvironment: AppEnvironment) throws {
|
|
||||||
self.appEnvironment = appEnvironment
|
|
||||||
observationErrors = observationErrorsInput.eraseToAnyPublisher()
|
|
||||||
networkClient = MastodonClient(configuration: appEnvironment.URLSessionConfiguration)
|
|
||||||
networkClient.accessToken = try appEnvironment.secrets.item(.accessToken, forIdentityID: identityID)
|
|
||||||
|
|
||||||
let observation = appEnvironment.identityDatabase.identityObservation(id: identityID).share()
|
|
||||||
|
|
||||||
var initialIdentity: Identity?
|
|
||||||
|
|
||||||
observation.first().sink(
|
|
||||||
receiveCompletion: { _ in },
|
|
||||||
receiveValue: { initialIdentity = $0 })
|
|
||||||
.store(in: &cancellables)
|
|
||||||
|
|
||||||
guard let identity = initialIdentity else { throw IdentityDatabaseError.identityNotFound }
|
|
||||||
|
|
||||||
self.identity = identity
|
|
||||||
networkClient.instanceURL = identity.url
|
|
||||||
|
|
||||||
observation.catch { [weak self] error -> Empty<Identity, Never> in
|
|
||||||
self?.observationErrorsInput.send(error)
|
|
||||||
|
|
||||||
return Empty()
|
|
||||||
}
|
|
||||||
.assign(to: &$identity)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
82
Shared/Model/IdentifiedEnvironment.swift
Normal file
82
Shared/Model/IdentifiedEnvironment.swift
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
// Copyright © 2020 Metabolist. All rights reserved.
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import Combine
|
||||||
|
|
||||||
|
class IdentifiedEnvironment {
|
||||||
|
@Published var identity: Identity
|
||||||
|
let observationErrors: AnyPublisher<Error, Never>
|
||||||
|
|
||||||
|
private let networkClient: MastodonClient
|
||||||
|
private let appEnvironment: AppEnvironment
|
||||||
|
private let observationErrorsInput = PassthroughSubject<Error, Never>()
|
||||||
|
private var cancellables = Set<AnyCancellable>()
|
||||||
|
|
||||||
|
init(identityID: String, appEnvironment: AppEnvironment) throws {
|
||||||
|
self.appEnvironment = appEnvironment
|
||||||
|
observationErrors = observationErrorsInput.eraseToAnyPublisher()
|
||||||
|
networkClient = MastodonClient(configuration: appEnvironment.URLSessionConfiguration)
|
||||||
|
networkClient.accessToken = try appEnvironment.secrets.item(.accessToken, forIdentityID: identityID)
|
||||||
|
|
||||||
|
let observation = appEnvironment.identityDatabase.identityObservation(id: identityID).share()
|
||||||
|
|
||||||
|
var initialIdentity: Identity?
|
||||||
|
|
||||||
|
observation.first().sink(
|
||||||
|
receiveCompletion: { _ in },
|
||||||
|
receiveValue: { initialIdentity = $0 })
|
||||||
|
.store(in: &cancellables)
|
||||||
|
|
||||||
|
guard let identity = initialIdentity else { throw IdentityDatabaseError.identityNotFound }
|
||||||
|
|
||||||
|
self.identity = identity
|
||||||
|
networkClient.instanceURL = identity.url
|
||||||
|
|
||||||
|
observation.catch { [weak self] error -> Empty<Identity, Never> in
|
||||||
|
self?.observationErrorsInput.send(error)
|
||||||
|
|
||||||
|
return Empty()
|
||||||
|
}
|
||||||
|
.assign(to: &$identity)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension IdentifiedEnvironment {
|
||||||
|
var isAuthorized: Bool { networkClient.accessToken != nil }
|
||||||
|
|
||||||
|
func verifyCredentials() -> AnyPublisher<Void, Error> {
|
||||||
|
networkClient.request(AccountEndpoint.verifyCredentials)
|
||||||
|
.continuingIfWeakReferenceIsStillAlive(to: self)
|
||||||
|
.map { ($0, $1.identity.id) }
|
||||||
|
.flatMap(appEnvironment.identityDatabase.updateAccount)
|
||||||
|
.eraseToAnyPublisher()
|
||||||
|
}
|
||||||
|
|
||||||
|
func refreshServerPreferences() -> AnyPublisher<Void, Error> {
|
||||||
|
networkClient.request(PreferencesEndpoint.preferences)
|
||||||
|
.continuingIfWeakReferenceIsStillAlive(to: self)
|
||||||
|
.map { ($1.identity.preferences.updated(from: $0), $1.identity.id) }
|
||||||
|
.flatMap(appEnvironment.identityDatabase.updatePreferences)
|
||||||
|
.eraseToAnyPublisher()
|
||||||
|
}
|
||||||
|
|
||||||
|
func refreshInstance() -> AnyPublisher<Void, Error> {
|
||||||
|
networkClient.request(InstanceEndpoint.instance)
|
||||||
|
.continuingIfWeakReferenceIsStillAlive(to: self)
|
||||||
|
.map { ($0, $1.identity.id) }
|
||||||
|
.flatMap(appEnvironment.identityDatabase.updateInstance)
|
||||||
|
.eraseToAnyPublisher()
|
||||||
|
}
|
||||||
|
|
||||||
|
func identitiesObservation() -> AnyPublisher<[Identity], Error> {
|
||||||
|
appEnvironment.identityDatabase.identitiesObservation()
|
||||||
|
}
|
||||||
|
|
||||||
|
func recentIdentitiesObservation() -> AnyPublisher<[Identity], Error> {
|
||||||
|
appEnvironment.identityDatabase.recentIdentitiesObservation(excluding: identity.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func updatePreferences(_ preferences: Identity.Preferences) -> AnyPublisher<Void, Error> {
|
||||||
|
appEnvironment.identityDatabase.updatePreferences(preferences, forIdentityID: identity.id)
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,7 +15,7 @@ class IdentitiesViewModel: ObservableObject {
|
||||||
self.environment = environment
|
self.environment = environment
|
||||||
identity = environment.identity
|
identity = environment.identity
|
||||||
|
|
||||||
environment.appEnvironment.identityDatabase.identitiesObservation()
|
environment.identitiesObservation()
|
||||||
.assignErrorsToAlertItem(to: \.alertItem, on: self)
|
.assignErrorsToAlertItem(to: \.alertItem, on: self)
|
||||||
.assign(to: &$identities)
|
.assign(to: &$identities)
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,8 +18,7 @@ class MainNavigationViewModel: ObservableObject {
|
||||||
identity = environment.identity
|
identity = environment.identity
|
||||||
environment.$identity.dropFirst().assign(to: &$identity)
|
environment.$identity.dropFirst().assign(to: &$identity)
|
||||||
|
|
||||||
environment.appEnvironment.identityDatabase
|
environment.recentIdentitiesObservation()
|
||||||
.recentIdentitiesObservation(excluding: environment.identity.id)
|
|
||||||
.assignErrorsToAlertItem(to: \.alertItem, on: self)
|
.assignErrorsToAlertItem(to: \.alertItem, on: self)
|
||||||
.assign(to: &$recentIdentities)
|
.assign(to: &$recentIdentities)
|
||||||
}
|
}
|
||||||
|
@ -27,31 +26,21 @@ class MainNavigationViewModel: ObservableObject {
|
||||||
|
|
||||||
extension MainNavigationViewModel {
|
extension MainNavigationViewModel {
|
||||||
func refreshIdentity() {
|
func refreshIdentity() {
|
||||||
let id = identity.id
|
if environment.isAuthorized {
|
||||||
|
environment.verifyCredentials()
|
||||||
if environment.networkClient.accessToken != nil {
|
|
||||||
environment.networkClient.request(AccountEndpoint.verifyCredentials)
|
|
||||||
.map { ($0, id) }
|
|
||||||
.flatMap(environment.appEnvironment.identityDatabase.updateAccount)
|
|
||||||
.assignErrorsToAlertItem(to: \.alertItem, on: self)
|
.assignErrorsToAlertItem(to: \.alertItem, on: self)
|
||||||
.sink(receiveValue: {})
|
.sink(receiveValue: {})
|
||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
|
|
||||||
if identity.preferences.useServerPostingReadingPreferences {
|
if identity.preferences.useServerPostingReadingPreferences {
|
||||||
let capturedPreferences = identity.preferences
|
environment.refreshServerPreferences()
|
||||||
|
|
||||||
environment.networkClient.request(PreferencesEndpoint.preferences)
|
|
||||||
.map { (capturedPreferences.updated(from: $0), id) }
|
|
||||||
.flatMap(environment.appEnvironment.identityDatabase.updatePreferences)
|
|
||||||
.assignErrorsToAlertItem(to: \.alertItem, on: self)
|
.assignErrorsToAlertItem(to: \.alertItem, on: self)
|
||||||
.sink(receiveValue: {})
|
.sink(receiveValue: {})
|
||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
environment.networkClient.request(InstanceEndpoint.instance)
|
environment.refreshInstance()
|
||||||
.map { ($0, id) }
|
|
||||||
.flatMap(environment.appEnvironment.identityDatabase.updateInstance)
|
|
||||||
.assignErrorsToAlertItem(to: \.alertItem, on: self)
|
.assignErrorsToAlertItem(to: \.alertItem, on: self)
|
||||||
.sink(receiveValue: {})
|
.sink(receiveValue: {})
|
||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
|
|
|
@ -21,16 +21,13 @@ class PostingReadingPreferencesViewModel: ObservableObject {
|
||||||
.removeDuplicates()
|
.removeDuplicates()
|
||||||
.handleEvents(receiveOutput: { [weak self] in
|
.handleEvents(receiveOutput: { [weak self] in
|
||||||
if $0.useServerPostingReadingPreferences {
|
if $0.useServerPostingReadingPreferences {
|
||||||
self?.refreshPreferences()
|
self?.refreshServerPreferences()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.assign(to: &$preferences)
|
.assign(to: &$preferences)
|
||||||
|
|
||||||
let id = environment.identity.id
|
|
||||||
|
|
||||||
$preferences.dropFirst()
|
$preferences.dropFirst()
|
||||||
.map { ($0, id) }
|
.flatMap(environment.updatePreferences)
|
||||||
.flatMap(environment.appEnvironment.identityDatabase.updatePreferences)
|
|
||||||
.assignErrorsToAlertItem(to: \.alertItem, on: self)
|
.assignErrorsToAlertItem(to: \.alertItem, on: self)
|
||||||
.sink(receiveValue: {})
|
.sink(receiveValue: {})
|
||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
|
@ -38,13 +35,8 @@ class PostingReadingPreferencesViewModel: ObservableObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
extension PostingReadingPreferencesViewModel {
|
extension PostingReadingPreferencesViewModel {
|
||||||
func refreshPreferences() {
|
private func refreshServerPreferences() {
|
||||||
let id = environment.identity.id
|
environment.refreshServerPreferences()
|
||||||
let capturedPreferences = preferences
|
|
||||||
|
|
||||||
environment.networkClient.request(PreferencesEndpoint.preferences)
|
|
||||||
.map { (capturedPreferences.updated(from: $0), id) }
|
|
||||||
.flatMap(environment.appEnvironment.identityDatabase.updatePreferences)
|
|
||||||
.assignErrorsToAlertItem(to: \.alertItem, on: self)
|
.assignErrorsToAlertItem(to: \.alertItem, on: self)
|
||||||
.sink(receiveValue: {})
|
.sink(receiveValue: {})
|
||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
|
|
Loading…
Reference in a new issue