Refactoring

This commit is contained in:
Justin Mazzocchi 2020-08-26 02:19:38 -07:00
parent 244c190f2a
commit bdd2ee9a9d
No known key found for this signature in database
GPG key ID: E223E6937AAFB01C
14 changed files with 58 additions and 46 deletions

View file

@ -29,7 +29,7 @@ struct ContentDatabase {
} }
extension ContentDatabase { extension ContentDatabase {
func insert(statuses: [Status], collection: StatusCollection? = nil) -> AnyPublisher<Void, Error> { func insert(statuses: [Status], collection: StatusCollection? = nil) -> AnyPublisher<Never, Error> {
databaseQueue.writePublisher { databaseQueue.writePublisher {
try collection?.save($0) try collection?.save($0)
@ -41,6 +41,7 @@ extension ContentDatabase {
try collection?.joinRecord(status: status).save($0) try collection?.joinRecord(status: status).save($0)
} }
} }
.ignoreOutput()
.eraseToAnyPublisher() .eraseToAnyPublisher()
} }

View file

@ -30,7 +30,7 @@ struct IdentityDatabase {
} }
extension IdentityDatabase { extension IdentityDatabase {
func createIdentity(id: UUID, url: URL) -> AnyPublisher<Void, Error> { func createIdentity(id: UUID, url: URL) -> AnyPublisher<Never, Error> {
databaseQueue.writePublisher( databaseQueue.writePublisher(
updates: StoredIdentity( updates: StoredIdentity(
id: id, id: id,
@ -41,25 +41,27 @@ extension IdentityDatabase {
lastRegisteredDeviceToken: nil, lastRegisteredDeviceToken: nil,
pushSubscriptionAlerts: .initial) pushSubscriptionAlerts: .initial)
.save) .save)
.ignoreOutput()
.eraseToAnyPublisher() .eraseToAnyPublisher()
} }
func deleteIdentity(id: UUID) -> AnyPublisher<Void, Error> { func deleteIdentity(id: UUID) -> AnyPublisher<Never, Error> {
return databaseQueue.writePublisher(updates: StoredIdentity.filter(Column("id") == id).deleteAll) return databaseQueue.writePublisher(updates: StoredIdentity.filter(Column("id") == id).deleteAll)
.map { _ in () } .ignoreOutput()
.eraseToAnyPublisher() .eraseToAnyPublisher()
} }
func updateLastUsedAt(identityID: UUID) -> AnyPublisher<Void, Error> { func updateLastUsedAt(identityID: UUID) -> AnyPublisher<Never, Error> {
databaseQueue.writePublisher { databaseQueue.writePublisher {
try StoredIdentity try StoredIdentity
.filter(Column("id") == identityID) .filter(Column("id") == identityID)
.updateAll($0, Column("lastUsedAt").set(to: Date())) .updateAll($0, Column("lastUsedAt").set(to: Date()))
} }
.ignoreOutput()
.eraseToAnyPublisher() .eraseToAnyPublisher()
} }
func updateInstance(_ instance: Instance, forIdentityID identityID: UUID) -> AnyPublisher<Void, Error> { func updateInstance(_ instance: Instance, forIdentityID identityID: UUID) -> AnyPublisher<Never, Error> {
databaseQueue.writePublisher { databaseQueue.writePublisher {
try Identity.Instance( try Identity.Instance(
uri: instance.uri, uri: instance.uri,
@ -71,10 +73,11 @@ extension IdentityDatabase {
.filter(Column("id") == identityID) .filter(Column("id") == identityID)
.updateAll($0, Column("instanceURI").set(to: instance.uri)) .updateAll($0, Column("instanceURI").set(to: instance.uri))
} }
.ignoreOutput()
.eraseToAnyPublisher() .eraseToAnyPublisher()
} }
func updateAccount(_ account: Account, forIdentityID identityID: UUID) -> AnyPublisher<Void, Error> { func updateAccount(_ account: Account, forIdentityID identityID: UUID) -> AnyPublisher<Never, Error> {
databaseQueue.writePublisher( databaseQueue.writePublisher(
updates: Identity.Account( updates: Identity.Account(
id: account.id, id: account.id,
@ -88,11 +91,12 @@ extension IdentityDatabase {
headerStatic: account.headerStatic, headerStatic: account.headerStatic,
emojis: account.emojis) emojis: account.emojis)
.save) .save)
.ignoreOutput()
.eraseToAnyPublisher() .eraseToAnyPublisher()
} }
func updatePreferences(_ preferences: Identity.Preferences, func updatePreferences(_ preferences: Identity.Preferences,
forIdentityID identityID: UUID) -> AnyPublisher<Void, Error> { forIdentityID identityID: UUID) -> AnyPublisher<Never, Error> {
databaseQueue.writePublisher { databaseQueue.writePublisher {
let data = try StoredIdentity.databaseJSONEncoder(for: "preferences").encode(preferences) let data = try StoredIdentity.databaseJSONEncoder(for: "preferences").encode(preferences)
@ -100,12 +104,13 @@ extension IdentityDatabase {
.filter(Column("id") == identityID) .filter(Column("id") == identityID)
.updateAll($0, Column("preferences").set(to: data)) .updateAll($0, Column("preferences").set(to: data))
} }
.ignoreOutput()
.eraseToAnyPublisher() .eraseToAnyPublisher()
} }
func updatePushSubscription(alerts: PushSubscription.Alerts, func updatePushSubscription(alerts: PushSubscription.Alerts,
deviceToken: String? = nil, deviceToken: String? = nil,
forIdentityID identityID: UUID) -> AnyPublisher<Void, Error> { forIdentityID identityID: UUID) -> AnyPublisher<Never, Error> {
databaseQueue.writePublisher { databaseQueue.writePublisher {
let data = try StoredIdentity.databaseJSONEncoder(for: "pushSubscriptionAlerts").encode(alerts) let data = try StoredIdentity.databaseJSONEncoder(for: "pushSubscriptionAlerts").encode(alerts)
@ -119,6 +124,7 @@ extension IdentityDatabase {
.updateAll($0, Column("lastRegisteredDeviceToken").set(to: deviceToken)) .updateAll($0, Column("lastRegisteredDeviceToken").set(to: deviceToken))
} }
} }
.ignoreOutput()
.eraseToAnyPublisher() .eraseToAnyPublisher()
} }

View file

@ -26,11 +26,11 @@ extension IdentitiesService {
environment: environment) environment: environment)
} }
func createIdentity(id: UUID, instanceURL: URL) -> AnyPublisher<Void, Error> { func createIdentity(id: UUID, instanceURL: URL) -> AnyPublisher<Never, Error> {
identityDatabase.createIdentity(id: id, url: instanceURL) identityDatabase.createIdentity(id: id, url: instanceURL)
} }
func authorizeIdentity(id: UUID, instanceURL: URL) -> AnyPublisher<Void, Error> { func authorizeIdentity(id: UUID, instanceURL: URL) -> AnyPublisher<Never, Error> {
let secretsService = SecretsService(identityID: id, keychainService: environment.keychainServiceType) let secretsService = SecretsService(identityID: id, keychainService: environment.keychainServiceType)
let authenticationService = AuthenticationService(environment: environment) let authenticationService = AuthenticationService(environment: environment)
@ -42,22 +42,19 @@ extension IdentitiesService {
return (instanceURL, appAuthorization) return (instanceURL, appAuthorization)
} }
.flatMap(authenticationService.authenticate(instanceURL:appAuthorization:)) .flatMap(authenticationService.authenticate(instanceURL:appAuthorization:))
.tryMap { accessToken -> Void in .tryMap { try secretsService.set($0.accessToken, forItem: .accessToken) }
try secretsService.set(accessToken.accessToken, forItem: .accessToken) .ignoreOutput()
return ()
}
.eraseToAnyPublisher() .eraseToAnyPublisher()
} }
func deleteIdentity(_ identity: Identity) -> AnyPublisher<Void, Error> { func deleteIdentity(_ identity: Identity) -> AnyPublisher<Never, Error> {
let secretsService = SecretsService(identityID: identity.id, keychainService: environment.keychainServiceType) let secretsService = SecretsService(identityID: identity.id, keychainService: environment.keychainServiceType)
let networkClient = MastodonClient(environment: environment) let networkClient = MastodonClient(environment: environment)
networkClient.instanceURL = identity.url networkClient.instanceURL = identity.url
return identityDatabase.deleteIdentity(id: identity.id) return identityDatabase.deleteIdentity(id: identity.id)
.tryMap { .tryMap { _ in
DeletionEndpoint.oauthRevoke( DeletionEndpoint.oauthRevoke(
token: try secretsService.item(.accessToken), token: try secretsService.item(.accessToken),
clientID: try secretsService.item(.clientID), clientID: try secretsService.item(.clientID),
@ -65,12 +62,13 @@ extension IdentitiesService {
} }
.flatMap(networkClient.request) .flatMap(networkClient.request)
.tryMap { _ in try secretsService.deleteAllItems() } .tryMap { _ in try secretsService.deleteAllItems() }
.ignoreOutput()
.eraseToAnyPublisher() .eraseToAnyPublisher()
} }
func updatePushSubscriptions(deviceToken: String) -> AnyPublisher<Void, Error> { func updatePushSubscriptions(deviceToken: String) -> AnyPublisher<Never, Error> {
identityDatabase.identitiesWithOutdatedDeviceTokens(deviceToken: deviceToken) identityDatabase.identitiesWithOutdatedDeviceTokens(deviceToken: deviceToken)
.tryMap { identities -> [AnyPublisher<Void, Never>] in .tryMap { identities -> [AnyPublisher<Never, Never>] in
try identities.map { try identities.map {
try identityService(id: $0.id) try identityService(id: $0.id)
.createPushSubscription(deviceToken: deviceToken, alerts: $0.pushSubscriptionAlerts) .createPushSubscription(deviceToken: deviceToken, alerts: $0.pushSubscriptionAlerts)
@ -79,7 +77,7 @@ extension IdentitiesService {
} }
} }
.map(Publishers.MergeMany.init) .map(Publishers.MergeMany.init)
.map { _ in () } .ignoreOutput()
.eraseToAnyPublisher() .eraseToAnyPublisher()
} }
} }

View file

@ -52,18 +52,18 @@ class IdentityService {
extension IdentityService { extension IdentityService {
var isAuthorized: Bool { networkClient.accessToken != nil } var isAuthorized: Bool { networkClient.accessToken != nil }
func updateLastUse() -> AnyPublisher<Void, Error> { func updateLastUse() -> AnyPublisher<Never, Error> {
identityDatabase.updateLastUsedAt(identityID: identity.id) identityDatabase.updateLastUsedAt(identityID: identity.id)
} }
func verifyCredentials() -> AnyPublisher<Void, Error> { func verifyCredentials() -> AnyPublisher<Never, Error> {
networkClient.request(AccountEndpoint.verifyCredentials) networkClient.request(AccountEndpoint.verifyCredentials)
.zip(Just(identity.id).first().setFailureType(to: Error.self)) .zip(Just(identity.id).first().setFailureType(to: Error.self))
.flatMap(identityDatabase.updateAccount) .flatMap(identityDatabase.updateAccount)
.eraseToAnyPublisher() .eraseToAnyPublisher()
} }
func refreshServerPreferences() -> AnyPublisher<Void, Error> { func refreshServerPreferences() -> AnyPublisher<Never, Error> {
networkClient.request(PreferencesEndpoint.preferences) networkClient.request(PreferencesEndpoint.preferences)
.zip(Just(self).first().setFailureType(to: Error.self)) .zip(Just(self).first().setFailureType(to: Error.self))
.map { ($1.identity.preferences.updated(from: $0), $1.identity.id) } .map { ($1.identity.preferences.updated(from: $0), $1.identity.id) }
@ -71,7 +71,7 @@ extension IdentityService {
.eraseToAnyPublisher() .eraseToAnyPublisher()
} }
func refreshInstance() -> AnyPublisher<Void, Error> { func refreshInstance() -> AnyPublisher<Never, Error> {
networkClient.request(InstanceEndpoint.instance) networkClient.request(InstanceEndpoint.instance)
.zip(Just(identity.id).first().setFailureType(to: Error.self)) .zip(Just(identity.id).first().setFailureType(to: Error.self))
.flatMap(identityDatabase.updateInstance) .flatMap(identityDatabase.updateInstance)
@ -86,7 +86,7 @@ extension IdentityService {
identityDatabase.recentIdentitiesObservation(excluding: identity.id) identityDatabase.recentIdentitiesObservation(excluding: identity.id)
} }
func updatePreferences(_ preferences: Identity.Preferences) -> AnyPublisher<Void, Error> { func updatePreferences(_ preferences: Identity.Preferences) -> AnyPublisher<Never, Error> {
identityDatabase.updatePreferences(preferences, forIdentityID: identity.id) identityDatabase.updatePreferences(preferences, forIdentityID: identity.id)
.zip(Just(self).first().setFailureType(to: Error.self)) .zip(Just(self).first().setFailureType(to: Error.self))
.filter { $1.identity.preferences.useServerPostingReadingPreferences } .filter { $1.identity.preferences.useServerPostingReadingPreferences }
@ -95,7 +95,7 @@ extension IdentityService {
.eraseToAnyPublisher() .eraseToAnyPublisher()
} }
func createPushSubscription(deviceToken: String, alerts: PushSubscription.Alerts) -> AnyPublisher<Void, Error> { func createPushSubscription(deviceToken: String, alerts: PushSubscription.Alerts) -> AnyPublisher<Never, Error> {
let publicKey: String let publicKey: String
let auth: String let auth: String
@ -122,7 +122,7 @@ extension IdentityService {
.eraseToAnyPublisher() .eraseToAnyPublisher()
} }
func updatePushSubscription(alerts: PushSubscription.Alerts) -> AnyPublisher<Void, Error> { func updatePushSubscription(alerts: PushSubscription.Alerts) -> AnyPublisher<Never, Error> {
let identityID = identity.id let identityID = identity.id
return networkClient.request(PushSubscriptionEndpoint.update(alerts: alerts)) return networkClient.request(PushSubscriptionEndpoint.update(alerts: alerts))

View file

@ -60,7 +60,7 @@ extension ContextService: StatusListService {
return status.id != contextParentID && nextStatus.inReplyToId == status.id return status.id != contextParentID && nextStatus.inReplyToId == status.id
} }
func request(maxID: String?, minID: String?) -> AnyPublisher<Void, Error> { func request(maxID: String?, minID: String?) -> AnyPublisher<Never, Error> {
Publishers.Merge( Publishers.Merge(
networkClient.request(StatusEndpoint.status(id: status.id)) networkClient.request(StatusEndpoint.status(id: status.id))
.map { ([$0], collection) } .map { ([$0], collection) }

View file

@ -9,7 +9,7 @@ protocol StatusListService {
func isPinned(status: Status) -> Bool func isPinned(status: Status) -> Bool
func isReplyInContext(status: Status) -> Bool func isReplyInContext(status: Status) -> Bool
func hasReplyFollowing(status: Status) -> Bool func hasReplyFollowing(status: Status) -> Bool
func request(maxID: String?, minID: String?) -> AnyPublisher<Void, Error> func request(maxID: String?, minID: String?) -> AnyPublisher<Never, Error>
func statusService(status: Status) -> StatusService func statusService(status: Status) -> StatusService
func contextService(status: Status) -> ContextService func contextService(status: Status) -> ContextService
} }

View file

@ -21,7 +21,7 @@ struct TimelineService {
} }
extension TimelineService: StatusListService { extension TimelineService: StatusListService {
func request(maxID: String?, minID: String?) -> AnyPublisher<Void, Error> { func request(maxID: String?, minID: String?) -> AnyPublisher<Never, Error> {
return networkClient.request(timeline.endpoint) return networkClient.request(timeline.endpoint)
.map { ($0, timeline) } .map { ($0, timeline) }
.flatMap(contentDatabase.insert(statuses:collection:)) .flatMap(contentDatabase.insert(statuses:collection:))

View file

@ -16,7 +16,7 @@ struct StatusService {
} }
extension StatusService { extension StatusService {
func toggleFavorited() -> AnyPublisher<Void, Error> { func toggleFavorited() -> AnyPublisher<Never, Error> {
networkClient.request(status.favourited networkClient.request(status.favourited
? StatusEndpoint.unfavourite(id: status.id) ? StatusEndpoint.unfavourite(id: status.id)
: StatusEndpoint.favourite(id: status.id)) : StatusEndpoint.favourite(id: status.id))

View file

@ -31,15 +31,19 @@ class AddIdentityViewModel: ObservableObject {
} }
identitiesService.authorizeIdentity(id: identityID, instanceURL: instanceURL) identitiesService.authorizeIdentity(id: identityID, instanceURL: instanceURL)
.map { (identityID, instanceURL) } .collect()
.map { _ in (identityID, instanceURL) }
.flatMap(identitiesService.createIdentity(id:instanceURL:)) .flatMap(identitiesService.createIdentity(id:instanceURL:))
.map { identityID }
.assignErrorsToAlertItem(to: \.alertItem, on: self) .assignErrorsToAlertItem(to: \.alertItem, on: self)
.receive(on: RunLoop.main) .receive(on: RunLoop.main)
.handleEvents( .handleEvents(
receiveSubscription: { [weak self] _ in self?.loading = true }, receiveSubscription: { [weak self] _ in self?.loading = true },
receiveCompletion: { [weak self] _ in self?.loading = false }) receiveCompletion: { [weak self] _ in self?.loading = false })
.sink(receiveValue: addedIdentityIDInput.send) .sink { [weak self] in
guard let self = self, case .finished = $0 else { return }
self.addedIdentityIDInput.send(identityID)
} receiveValue: { _ in }
.store(in: &cancellables) .store(in: &cancellables)
} }
@ -57,9 +61,12 @@ class AddIdentityViewModel: ObservableObject {
// TODO: Ensure instance has not disabled public preview // TODO: Ensure instance has not disabled public preview
identitiesService.createIdentity(id: identityID, instanceURL: instanceURL) identitiesService.createIdentity(id: identityID, instanceURL: instanceURL)
.map { identityID }
.assignErrorsToAlertItem(to: \.alertItem, on: self) .assignErrorsToAlertItem(to: \.alertItem, on: self)
.sink(receiveValue: addedIdentityIDInput.send) .sink { [weak self] in
guard let self = self, case .finished = $0 else { return }
self.addedIdentityIDInput.send(identityID)
} receiveValue: { _ in }
.store(in: &cancellables) .store(in: &cancellables)
} }
} }

View file

@ -38,7 +38,7 @@ private extension NotificationTypesPreferencesViewModel {
self.alertItem = AlertItem(error: error) self.alertItem = AlertItem(error: error)
self.pushSubscriptionAlerts = self.identityService.identity.pushSubscriptionAlerts self.pushSubscriptionAlerts = self.identityService.identity.pushSubscriptionAlerts
} receiveValue: {} } receiveValue: { _ in }
.store(in: &cancellables) .store(in: &cancellables)
} }
} }

View file

@ -24,7 +24,7 @@ class PostingReadingPreferencesViewModel: ObservableObject {
.dropFirst() .dropFirst()
.flatMap(identityService.updatePreferences) .flatMap(identityService.updatePreferences)
.assignErrorsToAlertItem(to: \.alertItem, on: self) .assignErrorsToAlertItem(to: \.alertItem, on: self)
.sink {} .sink { _ in }
.store(in: &cancellables) .store(in: &cancellables)
} }
} }

View file

@ -19,10 +19,10 @@ struct StatusViewModel {
var isReplyInContext = false var isReplyInContext = false
var hasReplyFollowing = false var hasReplyFollowing = false
var sensitiveContentToggled = false var sensitiveContentToggled = false
let events: AnyPublisher<AnyPublisher<Void, Error>, Never> let events: AnyPublisher<AnyPublisher<Never, Error>, Never>
private let statusService: StatusService private let statusService: StatusService
private let eventsInput = PassthroughSubject<AnyPublisher<Void, Error>, Never>() private let eventsInput = PassthroughSubject<AnyPublisher<Never, Error>, Never>()
init(statusService: StatusService) { init(statusService: StatusService) {
self.statusService = statusService self.statusService = statusService

View file

@ -37,7 +37,7 @@ extension StatusesViewModel {
.handleEvents( .handleEvents(
receiveSubscription: { [weak self] _ in self?.loading = true }, receiveSubscription: { [weak self] _ in self?.loading = true },
receiveCompletion: { [weak self] _ in self?.loading = false }) receiveCompletion: { [weak self] _ in self?.loading = false })
.sink {} .sink { _ in }
.store(in: &cancellables) .store(in: &cancellables)
} }
@ -53,7 +53,7 @@ extension StatusesViewModel {
statusViewModelCache[status] = (statusViewModel, statusViewModel.events statusViewModelCache[status] = (statusViewModel, statusViewModel.events
.flatMap { $0 } .flatMap { $0 }
.assignErrorsToAlertItem(to: \.alertItem, on: self) .assignErrorsToAlertItem(to: \.alertItem, on: self)
.sink {}) .sink { _ in })
} }
statusViewModel.isContextParent = status.id == contextParentID statusViewModel.isContextParent = status.id == contextParentID

View file

@ -31,20 +31,20 @@ extension TabNavigationViewModel {
if identityService.isAuthorized { if identityService.isAuthorized {
identityService.verifyCredentials() identityService.verifyCredentials()
.assignErrorsToAlertItem(to: \.alertItem, on: self) .assignErrorsToAlertItem(to: \.alertItem, on: self)
.sink(receiveValue: {}) .sink { _ in }
.store(in: &cancellables) .store(in: &cancellables)
if identity.preferences.useServerPostingReadingPreferences { if identity.preferences.useServerPostingReadingPreferences {
identityService.refreshServerPreferences() identityService.refreshServerPreferences()
.assignErrorsToAlertItem(to: \.alertItem, on: self) .assignErrorsToAlertItem(to: \.alertItem, on: self)
.sink(receiveValue: {}) .sink { _ in }
.store(in: &cancellables) .store(in: &cancellables)
} }
} }
identityService.refreshInstance() identityService.refreshInstance()
.assignErrorsToAlertItem(to: \.alertItem, on: self) .assignErrorsToAlertItem(to: \.alertItem, on: self)
.sink(receiveValue: {}) .sink { _ in }
.store(in: &cancellables) .store(in: &cancellables)
} }