mirror of
https://github.com/metabolist/metatext.git
synced 2024-11-22 00:01:00 +00:00
Long press to boost / favorite from other accounts
This commit is contained in:
parent
bfcb999b25
commit
f3040eaad5
25 changed files with 236 additions and 60 deletions
|
@ -169,10 +169,12 @@ public extension IdentityDatabase {
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
}
|
}
|
||||||
|
|
||||||
func authenticatedIdentitiesPublisher() -> AnyPublisher<[Identity], Error> {
|
func authenticatedIdentitiesPublisher(excluding: Identity.Id) -> AnyPublisher<[Identity], Error> {
|
||||||
ValueObservation.tracking(
|
ValueObservation.tracking(
|
||||||
IdentityInfo.request(IdentityRecord.order(IdentityRecord.Columns.lastUsedAt.desc))
|
IdentityInfo.request(IdentityRecord.order(IdentityRecord.Columns.lastUsedAt.desc))
|
||||||
.filter(IdentityRecord.Columns.authenticated == true && IdentityRecord.Columns.pending == false)
|
.filter(IdentityRecord.Columns.authenticated == true
|
||||||
|
&& IdentityRecord.Columns.pending == false
|
||||||
|
&& IdentityRecord.Columns.id != excluding)
|
||||||
.fetchAll)
|
.fetchAll)
|
||||||
.removeDuplicates()
|
.removeDuplicates()
|
||||||
.publisher(in: databaseWriter)
|
.publisher(in: databaseWriter)
|
||||||
|
|
|
@ -60,6 +60,7 @@
|
||||||
"account.unnotify" = "Turn off notifications";
|
"account.unnotify" = "Turn off notifications";
|
||||||
"activity.open-in-default-browser" = "Open in default browser";
|
"activity.open-in-default-browser" = "Open in default browser";
|
||||||
"add" = "Add";
|
"add" = "Add";
|
||||||
|
"api-error.unable-to-fetch-remote-status" = "Unable to fetch remote status";
|
||||||
"apns-default-message" = "New notification";
|
"apns-default-message" = "New notification";
|
||||||
"app-icon.brutalist" = "Brutalist";
|
"app-icon.brutalist" = "Brutalist";
|
||||||
"app-icon.rainbow-brutalist" = "Rainbow Brutalist";
|
"app-icon.rainbow-brutalist" = "Rainbow Brutalist";
|
||||||
|
|
|
@ -9,3 +9,8 @@ public struct APIError: Error, Codable {
|
||||||
extension APIError: LocalizedError {
|
extension APIError: LocalizedError {
|
||||||
public var errorDescription: String? { error }
|
public var errorDescription: String? { error }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public extension APIError {
|
||||||
|
static let unableToFetchRemoteStatus =
|
||||||
|
Self(error: NSLocalizedString("api-error.unable-to-fetch-remote-status", comment: ""))
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
// Copyright © 2021 Metabolist. All rights reserved.
|
||||||
|
|
||||||
|
import MastodonAPI
|
||||||
|
import Secrets
|
||||||
|
|
||||||
|
extension MastodonAPIClient {
|
||||||
|
static func forIdentity(id: Identity.Id, environment: AppEnvironment) throws -> Self {
|
||||||
|
let secrets = Secrets(identityId: id, keychain: environment.keychain)
|
||||||
|
|
||||||
|
let client = Self(session: environment.session, instanceURL: try secrets.getInstanceURL())
|
||||||
|
|
||||||
|
client.accessToken = try secrets.getAccessToken()
|
||||||
|
|
||||||
|
return client
|
||||||
|
}
|
||||||
|
}
|
|
@ -22,6 +22,7 @@ public struct AccountListService {
|
||||||
private let accountIdsForRelationshipsSubject = PassthroughSubject<Set<Account.Id>, Never>()
|
private let accountIdsForRelationshipsSubject = PassthroughSubject<Set<Account.Id>, Never>()
|
||||||
|
|
||||||
init(endpoint: AccountsEndpoint,
|
init(endpoint: AccountsEndpoint,
|
||||||
|
environment: AppEnvironment,
|
||||||
mastodonAPIClient: MastodonAPIClient,
|
mastodonAPIClient: MastodonAPIClient,
|
||||||
contentDatabase: ContentDatabase,
|
contentDatabase: ContentDatabase,
|
||||||
titleComponents: [String]? = nil) {
|
titleComponents: [String]? = nil) {
|
||||||
|
@ -32,7 +33,9 @@ public struct AccountListService {
|
||||||
sections = contentDatabase.accountListPublisher(id: listId, configuration: endpoint.configuration)
|
sections = contentDatabase.accountListPublisher(id: listId, configuration: endpoint.configuration)
|
||||||
nextPageMaxId = nextPageMaxIdSubject.eraseToAnyPublisher()
|
nextPageMaxId = nextPageMaxIdSubject.eraseToAnyPublisher()
|
||||||
accountIdsForRelationships = accountIdsForRelationshipsSubject.eraseToAnyPublisher()
|
accountIdsForRelationships = accountIdsForRelationshipsSubject.eraseToAnyPublisher()
|
||||||
navigationService = NavigationService(mastodonAPIClient: mastodonAPIClient, contentDatabase: contentDatabase)
|
navigationService = NavigationService(environment: environment,
|
||||||
|
mastodonAPIClient: mastodonAPIClient,
|
||||||
|
contentDatabase: contentDatabase)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,16 +10,21 @@ public struct AccountService {
|
||||||
public let account: Account
|
public let account: Account
|
||||||
public let navigationService: NavigationService
|
public let navigationService: NavigationService
|
||||||
|
|
||||||
|
private let environment: AppEnvironment
|
||||||
private let mastodonAPIClient: MastodonAPIClient
|
private let mastodonAPIClient: MastodonAPIClient
|
||||||
private let contentDatabase: ContentDatabase
|
private let contentDatabase: ContentDatabase
|
||||||
|
|
||||||
public init(account: Account,
|
public init(account: Account,
|
||||||
identityProofs: [IdentityProof] = [],
|
identityProofs: [IdentityProof] = [],
|
||||||
featuredTags: [FeaturedTag] = [],
|
featuredTags: [FeaturedTag] = [],
|
||||||
|
environment: AppEnvironment,
|
||||||
mastodonAPIClient: MastodonAPIClient,
|
mastodonAPIClient: MastodonAPIClient,
|
||||||
contentDatabase: ContentDatabase) {
|
contentDatabase: ContentDatabase) {
|
||||||
self.account = account
|
self.account = account
|
||||||
navigationService = NavigationService(mastodonAPIClient: mastodonAPIClient, contentDatabase: contentDatabase)
|
navigationService = NavigationService(environment: environment,
|
||||||
|
mastodonAPIClient: mastodonAPIClient,
|
||||||
|
contentDatabase: contentDatabase)
|
||||||
|
self.environment = environment
|
||||||
self.mastodonAPIClient = mastodonAPIClient
|
self.mastodonAPIClient = mastodonAPIClient
|
||||||
self.contentDatabase = contentDatabase
|
self.contentDatabase = contentDatabase
|
||||||
}
|
}
|
||||||
|
@ -136,6 +141,7 @@ public extension AccountService {
|
||||||
func followingService() -> AccountListService {
|
func followingService() -> AccountListService {
|
||||||
AccountListService(
|
AccountListService(
|
||||||
endpoint: .accountsFollowing(id: account.id),
|
endpoint: .accountsFollowing(id: account.id),
|
||||||
|
environment: environment,
|
||||||
mastodonAPIClient: mastodonAPIClient,
|
mastodonAPIClient: mastodonAPIClient,
|
||||||
contentDatabase: contentDatabase,
|
contentDatabase: contentDatabase,
|
||||||
titleComponents: ["account.followed-by-%@", "@".appending(account.acct)])
|
titleComponents: ["account.followed-by-%@", "@".appending(account.acct)])
|
||||||
|
@ -144,6 +150,7 @@ public extension AccountService {
|
||||||
func followersService() -> AccountListService {
|
func followersService() -> AccountListService {
|
||||||
AccountListService(
|
AccountListService(
|
||||||
endpoint: .accountsFollowers(id: account.id),
|
endpoint: .accountsFollowers(id: account.id),
|
||||||
|
environment: environment,
|
||||||
mastodonAPIClient: mastodonAPIClient,
|
mastodonAPIClient: mastodonAPIClient,
|
||||||
contentDatabase: contentDatabase,
|
contentDatabase: contentDatabase,
|
||||||
titleComponents: ["account.%@-followers", "@".appending(account.acct)])
|
titleComponents: ["account.%@-followers", "@".appending(account.acct)])
|
||||||
|
|
|
@ -39,10 +39,6 @@ public extension AllIdentitiesService {
|
||||||
database.immediateMostRecentlyUsedIdentityIdPublisher()
|
database.immediateMostRecentlyUsedIdentityIdPublisher()
|
||||||
}
|
}
|
||||||
|
|
||||||
func authenticatedIdentitiesPublisher() -> AnyPublisher<[Identity], Error> {
|
|
||||||
database.authenticatedIdentitiesPublisher()
|
|
||||||
}
|
|
||||||
|
|
||||||
func mostRecentAuthenticatedIdentity() throws -> Identity? {
|
func mostRecentAuthenticatedIdentity() throws -> Identity? {
|
||||||
try database.mostRecentAuthenticatedIdentity()
|
try database.mostRecentAuthenticatedIdentity()
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,12 +14,17 @@ public struct ContextService {
|
||||||
private let mastodonAPIClient: MastodonAPIClient
|
private let mastodonAPIClient: MastodonAPIClient
|
||||||
private let contentDatabase: ContentDatabase
|
private let contentDatabase: ContentDatabase
|
||||||
|
|
||||||
init(id: Status.Id, mastodonAPIClient: MastodonAPIClient, contentDatabase: ContentDatabase) {
|
init(id: Status.Id,
|
||||||
|
environment: AppEnvironment,
|
||||||
|
mastodonAPIClient: MastodonAPIClient,
|
||||||
|
contentDatabase: ContentDatabase) {
|
||||||
self.id = id
|
self.id = id
|
||||||
self.mastodonAPIClient = mastodonAPIClient
|
self.mastodonAPIClient = mastodonAPIClient
|
||||||
self.contentDatabase = contentDatabase
|
self.contentDatabase = contentDatabase
|
||||||
sections = contentDatabase.contextPublisher(id: id)
|
sections = contentDatabase.contextPublisher(id: id)
|
||||||
navigationService = NavigationService(mastodonAPIClient: mastodonAPIClient, contentDatabase: contentDatabase)
|
navigationService = NavigationService(environment: environment,
|
||||||
|
mastodonAPIClient: mastodonAPIClient,
|
||||||
|
contentDatabase: contentDatabase)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,9 +12,13 @@ public struct ConversationService {
|
||||||
private let mastodonAPIClient: MastodonAPIClient
|
private let mastodonAPIClient: MastodonAPIClient
|
||||||
private let contentDatabase: ContentDatabase
|
private let contentDatabase: ContentDatabase
|
||||||
|
|
||||||
init(conversation: Conversation, mastodonAPIClient: MastodonAPIClient, contentDatabase: ContentDatabase) {
|
init(conversation: Conversation,
|
||||||
|
environment: AppEnvironment,
|
||||||
|
mastodonAPIClient: MastodonAPIClient,
|
||||||
|
contentDatabase: ContentDatabase) {
|
||||||
self.conversation = conversation
|
self.conversation = conversation
|
||||||
self.navigationService = NavigationService(
|
self.navigationService = NavigationService(
|
||||||
|
environment: environment,
|
||||||
mastodonAPIClient: mastodonAPIClient,
|
mastodonAPIClient: mastodonAPIClient,
|
||||||
contentDatabase: contentDatabase)
|
contentDatabase: contentDatabase)
|
||||||
self.mastodonAPIClient = mastodonAPIClient
|
self.mastodonAPIClient = mastodonAPIClient
|
||||||
|
|
|
@ -15,14 +15,17 @@ public struct ConversationsService {
|
||||||
private let contentDatabase: ContentDatabase
|
private let contentDatabase: ContentDatabase
|
||||||
private let nextPageMaxIdSubject = PassthroughSubject<String, Never>()
|
private let nextPageMaxIdSubject = PassthroughSubject<String, Never>()
|
||||||
|
|
||||||
init(mastodonAPIClient: MastodonAPIClient, contentDatabase: ContentDatabase) {
|
init(environment: AppEnvironment, mastodonAPIClient: MastodonAPIClient, contentDatabase: ContentDatabase) {
|
||||||
self.mastodonAPIClient = mastodonAPIClient
|
self.mastodonAPIClient = mastodonAPIClient
|
||||||
self.contentDatabase = contentDatabase
|
self.contentDatabase = contentDatabase
|
||||||
sections = contentDatabase.conversationsPublisher()
|
sections = contentDatabase.conversationsPublisher()
|
||||||
.map { [.init(items: $0.map(CollectionItem.conversation))] }
|
.map { [.init(items: $0.map(CollectionItem.conversation))] }
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
nextPageMaxId = nextPageMaxIdSubject.eraseToAnyPublisher()
|
nextPageMaxId = nextPageMaxIdSubject.eraseToAnyPublisher()
|
||||||
navigationService = NavigationService(mastodonAPIClient: mastodonAPIClient, contentDatabase: contentDatabase)
|
navigationService = NavigationService(
|
||||||
|
environment: environment,
|
||||||
|
mastodonAPIClient: mastodonAPIClient,
|
||||||
|
contentDatabase: contentDatabase)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,10 +12,12 @@ public struct ExploreService {
|
||||||
private let mastodonAPIClient: MastodonAPIClient
|
private let mastodonAPIClient: MastodonAPIClient
|
||||||
private let contentDatabase: ContentDatabase
|
private let contentDatabase: ContentDatabase
|
||||||
|
|
||||||
init(mastodonAPIClient: MastodonAPIClient, contentDatabase: ContentDatabase) {
|
init(environment: AppEnvironment, mastodonAPIClient: MastodonAPIClient, contentDatabase: ContentDatabase) {
|
||||||
self.mastodonAPIClient = mastodonAPIClient
|
self.mastodonAPIClient = mastodonAPIClient
|
||||||
self.contentDatabase = contentDatabase
|
self.contentDatabase = contentDatabase
|
||||||
navigationService = NavigationService(mastodonAPIClient: mastodonAPIClient, contentDatabase: contentDatabase)
|
navigationService = NavigationService(environment: environment,
|
||||||
|
mastodonAPIClient: mastodonAPIClient,
|
||||||
|
contentDatabase: contentDatabase)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -39,6 +39,7 @@ public struct IdentityService {
|
||||||
keychain: environment.keychain)
|
keychain: environment.keychain)
|
||||||
|
|
||||||
navigationService = NavigationService(
|
navigationService = NavigationService(
|
||||||
|
environment: environment,
|
||||||
mastodonAPIClient: mastodonAPIClient,
|
mastodonAPIClient: mastodonAPIClient,
|
||||||
contentDatabase: contentDatabase)
|
contentDatabase: contentDatabase)
|
||||||
}
|
}
|
||||||
|
@ -98,6 +99,10 @@ public extension IdentityService {
|
||||||
identityDatabase.recentIdentitiesPublisher(excluding: id)
|
identityDatabase.recentIdentitiesPublisher(excluding: id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func otherAuthenticatedIdentitiesPublisher() -> AnyPublisher<[Identity], Error> {
|
||||||
|
identityDatabase.authenticatedIdentitiesPublisher(excluding: id)
|
||||||
|
}
|
||||||
|
|
||||||
func refreshLists() -> AnyPublisher<Never, Error> {
|
func refreshLists() -> AnyPublisher<Never, Error> {
|
||||||
mastodonAPIClient.request(ListsEndpoint.lists)
|
mastodonAPIClient.request(ListsEndpoint.lists)
|
||||||
.flatMap(contentDatabase.setLists(_:))
|
.flatMap(contentDatabase.setLists(_:))
|
||||||
|
@ -249,6 +254,7 @@ public extension IdentityService {
|
||||||
.map { _ in
|
.map { _ in
|
||||||
NotificationService(
|
NotificationService(
|
||||||
notification: notification,
|
notification: notification,
|
||||||
|
environment: environment,
|
||||||
mastodonAPIClient: mastodonAPIClient,
|
mastodonAPIClient: mastodonAPIClient,
|
||||||
contentDatabase: contentDatabase)
|
contentDatabase: contentDatabase)
|
||||||
}
|
}
|
||||||
|
@ -259,27 +265,31 @@ public extension IdentityService {
|
||||||
func service(accountList: AccountsEndpoint, titleComponents: [String]? = nil) -> AccountListService {
|
func service(accountList: AccountsEndpoint, titleComponents: [String]? = nil) -> AccountListService {
|
||||||
AccountListService(
|
AccountListService(
|
||||||
endpoint: accountList,
|
endpoint: accountList,
|
||||||
|
environment: environment,
|
||||||
mastodonAPIClient: mastodonAPIClient,
|
mastodonAPIClient: mastodonAPIClient,
|
||||||
contentDatabase: contentDatabase,
|
contentDatabase: contentDatabase,
|
||||||
titleComponents: titleComponents)
|
titleComponents: titleComponents)
|
||||||
}
|
}
|
||||||
|
|
||||||
func exploreService() -> ExploreService {
|
func exploreService() -> ExploreService {
|
||||||
ExploreService(mastodonAPIClient: mastodonAPIClient, contentDatabase: contentDatabase)
|
ExploreService(environment: environment, mastodonAPIClient: mastodonAPIClient, contentDatabase: contentDatabase)
|
||||||
}
|
}
|
||||||
|
|
||||||
func searchService() -> SearchService {
|
func searchService() -> SearchService {
|
||||||
SearchService(mastodonAPIClient: mastodonAPIClient, contentDatabase: contentDatabase)
|
SearchService(environment: environment, mastodonAPIClient: mastodonAPIClient, contentDatabase: contentDatabase)
|
||||||
}
|
}
|
||||||
|
|
||||||
func notificationsService(excludeTypes: Set<MastodonNotification.NotificationType>) -> NotificationsService {
|
func notificationsService(excludeTypes: Set<MastodonNotification.NotificationType>) -> NotificationsService {
|
||||||
NotificationsService(excludeTypes: excludeTypes,
|
NotificationsService(excludeTypes: excludeTypes,
|
||||||
|
environment: environment,
|
||||||
mastodonAPIClient: mastodonAPIClient,
|
mastodonAPIClient: mastodonAPIClient,
|
||||||
contentDatabase: contentDatabase)
|
contentDatabase: contentDatabase)
|
||||||
}
|
}
|
||||||
|
|
||||||
func conversationsService() -> ConversationsService {
|
func conversationsService() -> ConversationsService {
|
||||||
ConversationsService(mastodonAPIClient: mastodonAPIClient, contentDatabase: contentDatabase)
|
ConversationsService(environment: environment,
|
||||||
|
mastodonAPIClient: mastodonAPIClient,
|
||||||
|
contentDatabase: contentDatabase)
|
||||||
}
|
}
|
||||||
|
|
||||||
func domainBlocksService() -> DomainBlocksService {
|
func domainBlocksService() -> DomainBlocksService {
|
||||||
|
|
|
@ -17,11 +17,16 @@ public enum Navigation {
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct NavigationService {
|
public struct NavigationService {
|
||||||
|
private let environment: AppEnvironment
|
||||||
private let mastodonAPIClient: MastodonAPIClient
|
private let mastodonAPIClient: MastodonAPIClient
|
||||||
private let contentDatabase: ContentDatabase
|
private let contentDatabase: ContentDatabase
|
||||||
private let status: Status?
|
private let status: Status?
|
||||||
|
|
||||||
init(mastodonAPIClient: MastodonAPIClient, contentDatabase: ContentDatabase, status: Status? = nil) {
|
init(environment: AppEnvironment,
|
||||||
|
mastodonAPIClient: MastodonAPIClient,
|
||||||
|
contentDatabase: ContentDatabase,
|
||||||
|
status: Status? = nil) {
|
||||||
|
self.environment = environment
|
||||||
self.mastodonAPIClient = mastodonAPIClient
|
self.mastodonAPIClient = mastodonAPIClient
|
||||||
self.contentDatabase = contentDatabase
|
self.contentDatabase = contentDatabase
|
||||||
self.status = status
|
self.status = status
|
||||||
|
@ -35,6 +40,7 @@ public extension NavigationService {
|
||||||
.collection(
|
.collection(
|
||||||
TimelineService(
|
TimelineService(
|
||||||
timeline: .tag(tag),
|
timeline: .tag(tag),
|
||||||
|
environment: environment,
|
||||||
mastodonAPIClient: mastodonAPIClient,
|
mastodonAPIClient: mastodonAPIClient,
|
||||||
contentDatabase: contentDatabase)))
|
contentDatabase: contentDatabase)))
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
|
@ -52,26 +58,38 @@ public extension NavigationService {
|
||||||
}
|
}
|
||||||
|
|
||||||
func contextService(id: Status.Id) -> ContextService {
|
func contextService(id: Status.Id) -> ContextService {
|
||||||
ContextService(id: id, mastodonAPIClient: mastodonAPIClient, contentDatabase: contentDatabase)
|
ContextService(id: id, environment: environment,
|
||||||
|
mastodonAPIClient: mastodonAPIClient,
|
||||||
|
contentDatabase: contentDatabase)
|
||||||
}
|
}
|
||||||
|
|
||||||
func profileService(id: Account.Id) -> ProfileService {
|
func profileService(id: Account.Id) -> ProfileService {
|
||||||
ProfileService(id: id, mastodonAPIClient: mastodonAPIClient, contentDatabase: contentDatabase)
|
ProfileService(id: id,
|
||||||
|
environment: environment,
|
||||||
|
mastodonAPIClient: mastodonAPIClient,
|
||||||
|
contentDatabase: contentDatabase)
|
||||||
}
|
}
|
||||||
|
|
||||||
func profileService(account: Account, relationship: Relationship? = nil) -> ProfileService {
|
func profileService(account: Account, relationship: Relationship? = nil) -> ProfileService {
|
||||||
ProfileService(account: account,
|
ProfileService(account: account,
|
||||||
relationship: relationship,
|
relationship: relationship,
|
||||||
|
environment: environment,
|
||||||
mastodonAPIClient: mastodonAPIClient,
|
mastodonAPIClient: mastodonAPIClient,
|
||||||
contentDatabase: contentDatabase)
|
contentDatabase: contentDatabase)
|
||||||
}
|
}
|
||||||
|
|
||||||
func statusService(status: Status) -> StatusService {
|
func statusService(status: Status) -> StatusService {
|
||||||
StatusService(status: status, mastodonAPIClient: mastodonAPIClient, contentDatabase: contentDatabase)
|
StatusService(environment: environment,
|
||||||
|
status: status,
|
||||||
|
mastodonAPIClient: mastodonAPIClient,
|
||||||
|
contentDatabase: contentDatabase)
|
||||||
}
|
}
|
||||||
|
|
||||||
func accountService(account: Account) -> AccountService {
|
func accountService(account: Account) -> AccountService {
|
||||||
AccountService(account: account, mastodonAPIClient: mastodonAPIClient, contentDatabase: contentDatabase)
|
AccountService(account: account,
|
||||||
|
environment: environment,
|
||||||
|
mastodonAPIClient: mastodonAPIClient,
|
||||||
|
contentDatabase: contentDatabase)
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadMoreService(loadMore: LoadMore) -> LoadMoreService {
|
func loadMoreService(loadMore: LoadMore) -> LoadMoreService {
|
||||||
|
@ -81,6 +99,7 @@ public extension NavigationService {
|
||||||
func notificationService(notification: MastodonNotification) -> NotificationService {
|
func notificationService(notification: MastodonNotification) -> NotificationService {
|
||||||
NotificationService(
|
NotificationService(
|
||||||
notification: notification,
|
notification: notification,
|
||||||
|
environment: environment,
|
||||||
mastodonAPIClient: mastodonAPIClient,
|
mastodonAPIClient: mastodonAPIClient,
|
||||||
contentDatabase: contentDatabase)
|
contentDatabase: contentDatabase)
|
||||||
}
|
}
|
||||||
|
@ -88,12 +107,16 @@ public extension NavigationService {
|
||||||
func conversationService(conversation: Conversation) -> ConversationService {
|
func conversationService(conversation: Conversation) -> ConversationService {
|
||||||
ConversationService(
|
ConversationService(
|
||||||
conversation: conversation,
|
conversation: conversation,
|
||||||
|
environment: environment,
|
||||||
mastodonAPIClient: mastodonAPIClient,
|
mastodonAPIClient: mastodonAPIClient,
|
||||||
contentDatabase: contentDatabase)
|
contentDatabase: contentDatabase)
|
||||||
}
|
}
|
||||||
|
|
||||||
func timelineService(timeline: Timeline) -> TimelineService {
|
func timelineService(timeline: Timeline) -> TimelineService {
|
||||||
TimelineService(timeline: timeline, mastodonAPIClient: mastodonAPIClient, contentDatabase: contentDatabase)
|
TimelineService(timeline: timeline,
|
||||||
|
environment: environment,
|
||||||
|
mastodonAPIClient: mastodonAPIClient,
|
||||||
|
contentDatabase: contentDatabase)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,6 +155,7 @@ private extension NavigationService {
|
||||||
return .collection(
|
return .collection(
|
||||||
TimelineService(
|
TimelineService(
|
||||||
timeline: .tag(tag.name),
|
timeline: .tag(tag.name),
|
||||||
|
environment: environment,
|
||||||
mastodonAPIClient: mastodonAPIClient,
|
mastodonAPIClient: mastodonAPIClient,
|
||||||
contentDatabase: contentDatabase))
|
contentDatabase: contentDatabase))
|
||||||
} else if let account = results.accounts.first {
|
} else if let account = results.accounts.first {
|
||||||
|
|
|
@ -12,9 +12,13 @@ public struct NotificationService {
|
||||||
private let mastodonAPIClient: MastodonAPIClient
|
private let mastodonAPIClient: MastodonAPIClient
|
||||||
private let contentDatabase: ContentDatabase
|
private let contentDatabase: ContentDatabase
|
||||||
|
|
||||||
init(notification: MastodonNotification, mastodonAPIClient: MastodonAPIClient, contentDatabase: ContentDatabase) {
|
init(notification: MastodonNotification,
|
||||||
|
environment: AppEnvironment,
|
||||||
|
mastodonAPIClient: MastodonAPIClient,
|
||||||
|
contentDatabase: ContentDatabase) {
|
||||||
self.notification = notification
|
self.notification = notification
|
||||||
self.navigationService = NavigationService(
|
self.navigationService = NavigationService(
|
||||||
|
environment: environment,
|
||||||
mastodonAPIClient: mastodonAPIClient,
|
mastodonAPIClient: mastodonAPIClient,
|
||||||
contentDatabase: contentDatabase,
|
contentDatabase: contentDatabase,
|
||||||
status: nil)
|
status: nil)
|
||||||
|
|
|
@ -18,6 +18,7 @@ public struct NotificationsService {
|
||||||
private let nextPageMaxIdSubject: CurrentValueSubject<String, Never>
|
private let nextPageMaxIdSubject: CurrentValueSubject<String, Never>
|
||||||
|
|
||||||
init(excludeTypes: Set<MastodonNotification.NotificationType>,
|
init(excludeTypes: Set<MastodonNotification.NotificationType>,
|
||||||
|
environment: AppEnvironment,
|
||||||
mastodonAPIClient: MastodonAPIClient,
|
mastodonAPIClient: MastodonAPIClient,
|
||||||
contentDatabase: ContentDatabase) {
|
contentDatabase: ContentDatabase) {
|
||||||
self.excludeTypes = excludeTypes
|
self.excludeTypes = excludeTypes
|
||||||
|
@ -37,7 +38,9 @@ public struct NotificationsService {
|
||||||
})
|
})
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
nextPageMaxId = nextPageMaxIdSubject.eraseToAnyPublisher()
|
nextPageMaxId = nextPageMaxIdSubject.eraseToAnyPublisher()
|
||||||
navigationService = NavigationService(mastodonAPIClient: mastodonAPIClient, contentDatabase: contentDatabase)
|
navigationService = NavigationService(environment: environment,
|
||||||
|
mastodonAPIClient: mastodonAPIClient,
|
||||||
|
contentDatabase: contentDatabase)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,25 +10,32 @@ public struct ProfileService {
|
||||||
public let profilePublisher: AnyPublisher<Profile, Error>
|
public let profilePublisher: AnyPublisher<Profile, Error>
|
||||||
|
|
||||||
private let id: Account.Id
|
private let id: Account.Id
|
||||||
|
private let environment: AppEnvironment
|
||||||
private let mastodonAPIClient: MastodonAPIClient
|
private let mastodonAPIClient: MastodonAPIClient
|
||||||
private let contentDatabase: ContentDatabase
|
private let contentDatabase: ContentDatabase
|
||||||
|
|
||||||
init(account: Account,
|
init(account: Account,
|
||||||
relationship: Relationship?,
|
relationship: Relationship?,
|
||||||
|
environment: AppEnvironment,
|
||||||
mastodonAPIClient: MastodonAPIClient,
|
mastodonAPIClient: MastodonAPIClient,
|
||||||
contentDatabase: ContentDatabase) {
|
contentDatabase: ContentDatabase) {
|
||||||
self.init(
|
self.init(
|
||||||
id: account.id,
|
id: account.id,
|
||||||
account: account,
|
account: account,
|
||||||
relationship: relationship,
|
relationship: relationship,
|
||||||
|
environment: environment,
|
||||||
mastodonAPIClient: mastodonAPIClient,
|
mastodonAPIClient: mastodonAPIClient,
|
||||||
contentDatabase: contentDatabase)
|
contentDatabase: contentDatabase)
|
||||||
}
|
}
|
||||||
|
|
||||||
init(id: Account.Id, mastodonAPIClient: MastodonAPIClient, contentDatabase: ContentDatabase) {
|
init(id: Account.Id,
|
||||||
|
environment: AppEnvironment,
|
||||||
|
mastodonAPIClient: MastodonAPIClient,
|
||||||
|
contentDatabase: ContentDatabase) {
|
||||||
self.init(id: id,
|
self.init(id: id,
|
||||||
account: nil,
|
account: nil,
|
||||||
relationship: nil,
|
relationship: nil,
|
||||||
|
environment: environment,
|
||||||
mastodonAPIClient: mastodonAPIClient,
|
mastodonAPIClient: mastodonAPIClient,
|
||||||
contentDatabase: contentDatabase)
|
contentDatabase: contentDatabase)
|
||||||
}
|
}
|
||||||
|
@ -37,9 +44,11 @@ public struct ProfileService {
|
||||||
id: Account.Id,
|
id: Account.Id,
|
||||||
account: Account?,
|
account: Account?,
|
||||||
relationship: Relationship?,
|
relationship: Relationship?,
|
||||||
|
environment: AppEnvironment,
|
||||||
mastodonAPIClient: MastodonAPIClient,
|
mastodonAPIClient: MastodonAPIClient,
|
||||||
contentDatabase: ContentDatabase) {
|
contentDatabase: ContentDatabase) {
|
||||||
self.id = id
|
self.id = id
|
||||||
|
self.environment = environment
|
||||||
self.mastodonAPIClient = mastodonAPIClient
|
self.mastodonAPIClient = mastodonAPIClient
|
||||||
self.contentDatabase = contentDatabase
|
self.contentDatabase = contentDatabase
|
||||||
|
|
||||||
|
@ -60,6 +69,7 @@ public extension ProfileService {
|
||||||
func timelineService(profileCollection: ProfileCollection) -> TimelineService {
|
func timelineService(profileCollection: ProfileCollection) -> TimelineService {
|
||||||
TimelineService(
|
TimelineService(
|
||||||
timeline: .profile(accountId: id, profileCollection: profileCollection),
|
timeline: .profile(accountId: id, profileCollection: profileCollection),
|
||||||
|
environment: environment,
|
||||||
mastodonAPIClient: mastodonAPIClient,
|
mastodonAPIClient: mastodonAPIClient,
|
||||||
contentDatabase: contentDatabase)
|
contentDatabase: contentDatabase)
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,11 +16,13 @@ public struct SearchService {
|
||||||
private let nextPageMaxIdSubject = PassthroughSubject<String, Never>()
|
private let nextPageMaxIdSubject = PassthroughSubject<String, Never>()
|
||||||
private let resultsSubject = PassthroughSubject<(Results, Search), Error>()
|
private let resultsSubject = PassthroughSubject<(Results, Search), Error>()
|
||||||
|
|
||||||
init(mastodonAPIClient: MastodonAPIClient, contentDatabase: ContentDatabase) {
|
init(environment: AppEnvironment, mastodonAPIClient: MastodonAPIClient, contentDatabase: ContentDatabase) {
|
||||||
self.mastodonAPIClient = mastodonAPIClient
|
self.mastodonAPIClient = mastodonAPIClient
|
||||||
self.contentDatabase = contentDatabase
|
self.contentDatabase = contentDatabase
|
||||||
nextPageMaxId = nextPageMaxIdSubject.eraseToAnyPublisher()
|
nextPageMaxId = nextPageMaxIdSubject.eraseToAnyPublisher()
|
||||||
navigationService = NavigationService(mastodonAPIClient: mastodonAPIClient, contentDatabase: contentDatabase)
|
navigationService = NavigationService(environment: environment,
|
||||||
|
mastodonAPIClient: mastodonAPIClient,
|
||||||
|
contentDatabase: contentDatabase)
|
||||||
sections = resultsSubject.scan((.empty, nil)) {
|
sections = resultsSubject.scan((.empty, nil)) {
|
||||||
let (results, search) = $1
|
let (results, search) = $1
|
||||||
|
|
||||||
|
|
|
@ -9,15 +9,21 @@ import MastodonAPI
|
||||||
public struct StatusService {
|
public struct StatusService {
|
||||||
public let status: Status
|
public let status: Status
|
||||||
public let navigationService: NavigationService
|
public let navigationService: NavigationService
|
||||||
|
private let environment: AppEnvironment
|
||||||
private let mastodonAPIClient: MastodonAPIClient
|
private let mastodonAPIClient: MastodonAPIClient
|
||||||
private let contentDatabase: ContentDatabase
|
private let contentDatabase: ContentDatabase
|
||||||
|
|
||||||
init(status: Status, mastodonAPIClient: MastodonAPIClient, contentDatabase: ContentDatabase) {
|
init(environment: AppEnvironment,
|
||||||
|
status: Status,
|
||||||
|
mastodonAPIClient: MastodonAPIClient,
|
||||||
|
contentDatabase: ContentDatabase) {
|
||||||
self.status = status
|
self.status = status
|
||||||
self.navigationService = NavigationService(
|
self.navigationService = NavigationService(
|
||||||
|
environment: environment,
|
||||||
mastodonAPIClient: mastodonAPIClient,
|
mastodonAPIClient: mastodonAPIClient,
|
||||||
contentDatabase: contentDatabase,
|
contentDatabase: contentDatabase,
|
||||||
status: status.displayStatus)
|
status: status.displayStatus)
|
||||||
|
self.environment = environment
|
||||||
self.mastodonAPIClient = mastodonAPIClient
|
self.mastodonAPIClient = mastodonAPIClient
|
||||||
self.contentDatabase = contentDatabase
|
self.contentDatabase = contentDatabase
|
||||||
}
|
}
|
||||||
|
@ -32,20 +38,28 @@ public extension StatusService {
|
||||||
contentDatabase.toggleShowAttachments(id: status.displayStatus.id)
|
contentDatabase.toggleShowAttachments(id: status.displayStatus.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
func toggleReblogged() -> AnyPublisher<Never, Error> {
|
func toggleReblogged(identityId: Identity.Id?) -> AnyPublisher<Never, Error> {
|
||||||
mastodonAPIClient.request(status.displayStatus.reblogged
|
if let identityId = identityId {
|
||||||
? StatusEndpoint.unreblog(id: status.displayStatus.id)
|
return request(identityId: identityId, endpointClosure: StatusEndpoint.reblog(id:))
|
||||||
: StatusEndpoint.reblog(id: status.displayStatus.id))
|
} else {
|
||||||
.flatMap(contentDatabase.insert(status:))
|
return mastodonAPIClient.request(status.displayStatus.reblogged
|
||||||
.eraseToAnyPublisher()
|
? StatusEndpoint.unreblog(id: status.displayStatus.id)
|
||||||
|
: StatusEndpoint.reblog(id: status.displayStatus.id))
|
||||||
|
.flatMap(contentDatabase.insert(status:))
|
||||||
|
.eraseToAnyPublisher()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func toggleFavorited() -> AnyPublisher<Never, Error> {
|
func toggleFavorited(identityId: Identity.Id?) -> AnyPublisher<Never, Error> {
|
||||||
mastodonAPIClient.request(status.displayStatus.favourited
|
if let identityId = identityId {
|
||||||
? StatusEndpoint.unfavourite(id: status.displayStatus.id)
|
return request(identityId: identityId, endpointClosure: StatusEndpoint.favourite(id:))
|
||||||
: StatusEndpoint.favourite(id: status.displayStatus.id))
|
} else {
|
||||||
.flatMap(contentDatabase.insert(status:))
|
return mastodonAPIClient.request(status.displayStatus.favourited
|
||||||
.eraseToAnyPublisher()
|
? StatusEndpoint.unfavourite(id: status.displayStatus.id)
|
||||||
|
: StatusEndpoint.favourite(id: status.displayStatus.id))
|
||||||
|
.flatMap(contentDatabase.insert(status:))
|
||||||
|
.eraseToAnyPublisher()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func toggleBookmarked() -> AnyPublisher<Never, Error> {
|
func toggleBookmarked() -> AnyPublisher<Never, Error> {
|
||||||
|
@ -84,7 +98,8 @@ public extension StatusService {
|
||||||
if let inReplyToId = status.displayStatus.inReplyToId {
|
if let inReplyToId = status.displayStatus.inReplyToId {
|
||||||
inReplyToPublisher = mastodonAPIClient.request(StatusEndpoint.status(id: inReplyToId))
|
inReplyToPublisher = mastodonAPIClient.request(StatusEndpoint.status(id: inReplyToId))
|
||||||
.map {
|
.map {
|
||||||
Self(status: $0,
|
Self(environment: environment,
|
||||||
|
status: $0,
|
||||||
mastodonAPIClient: mastodonAPIClient,
|
mastodonAPIClient: mastodonAPIClient,
|
||||||
contentDatabase: contentDatabase) as Self?
|
contentDatabase: contentDatabase) as Self?
|
||||||
}
|
}
|
||||||
|
@ -103,6 +118,7 @@ public extension StatusService {
|
||||||
func rebloggedByService() -> AccountListService {
|
func rebloggedByService() -> AccountListService {
|
||||||
AccountListService(
|
AccountListService(
|
||||||
endpoint: .rebloggedBy(id: status.id),
|
endpoint: .rebloggedBy(id: status.id),
|
||||||
|
environment: environment,
|
||||||
mastodonAPIClient: mastodonAPIClient,
|
mastodonAPIClient: mastodonAPIClient,
|
||||||
contentDatabase: contentDatabase)
|
contentDatabase: contentDatabase)
|
||||||
}
|
}
|
||||||
|
@ -110,6 +126,7 @@ public extension StatusService {
|
||||||
func favoritedByService() -> AccountListService {
|
func favoritedByService() -> AccountListService {
|
||||||
AccountListService(
|
AccountListService(
|
||||||
endpoint: .favouritedBy(id: status.id),
|
endpoint: .favouritedBy(id: status.id),
|
||||||
|
environment: environment,
|
||||||
mastodonAPIClient: mastodonAPIClient,
|
mastodonAPIClient: mastodonAPIClient,
|
||||||
contentDatabase: contentDatabase)
|
contentDatabase: contentDatabase)
|
||||||
}
|
}
|
||||||
|
@ -130,3 +147,28 @@ public extension StatusService {
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private extension StatusService {
|
||||||
|
func request(identityId: Identity.Id,
|
||||||
|
endpointClosure: @escaping (Status.Id) -> StatusEndpoint) -> AnyPublisher<Never, Error> {
|
||||||
|
let client: MastodonAPIClient
|
||||||
|
|
||||||
|
do {
|
||||||
|
client = try MastodonAPIClient.forIdentity(id: identityId, environment: environment)
|
||||||
|
} catch {
|
||||||
|
return Fail(error: error).eraseToAnyPublisher()
|
||||||
|
}
|
||||||
|
|
||||||
|
return client
|
||||||
|
.request(ResultsEndpoint.search(.init(query: status.displayStatus.uri, resolve: true, limit: 1)))
|
||||||
|
.tryMap {
|
||||||
|
guard let id = $0.statuses.first?.id else { throw APIError.unableToFetchRemoteStatus }
|
||||||
|
|
||||||
|
return id
|
||||||
|
}
|
||||||
|
.flatMap { client.request(endpointClosure($0)) }
|
||||||
|
.flatMap { _ in mastodonAPIClient.request(StatusEndpoint.status(id: status.displayStatus.id)) }
|
||||||
|
.flatMap(contentDatabase.insert(status:))
|
||||||
|
.eraseToAnyPublisher()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -21,12 +21,17 @@ public struct TimelineService {
|
||||||
private let nextPageMaxIdSubject = PassthroughSubject<String, Never>()
|
private let nextPageMaxIdSubject = PassthroughSubject<String, Never>()
|
||||||
private let accountIdsForRelationshipsSubject = PassthroughSubject<Set<Account.Id>, Never>()
|
private let accountIdsForRelationshipsSubject = PassthroughSubject<Set<Account.Id>, Never>()
|
||||||
|
|
||||||
init(timeline: Timeline, mastodonAPIClient: MastodonAPIClient, contentDatabase: ContentDatabase) {
|
init(timeline: Timeline,
|
||||||
|
environment: AppEnvironment,
|
||||||
|
mastodonAPIClient: MastodonAPIClient,
|
||||||
|
contentDatabase: ContentDatabase) {
|
||||||
self.timeline = timeline
|
self.timeline = timeline
|
||||||
self.mastodonAPIClient = mastodonAPIClient
|
self.mastodonAPIClient = mastodonAPIClient
|
||||||
self.contentDatabase = contentDatabase
|
self.contentDatabase = contentDatabase
|
||||||
sections = contentDatabase.timelinePublisher(timeline)
|
sections = contentDatabase.timelinePublisher(timeline)
|
||||||
navigationService = NavigationService(mastodonAPIClient: mastodonAPIClient, contentDatabase: contentDatabase)
|
navigationService = NavigationService(environment: environment,
|
||||||
|
mastodonAPIClient: mastodonAPIClient,
|
||||||
|
contentDatabase: contentDatabase)
|
||||||
nextPageMaxId = nextPageMaxIdSubject.eraseToAnyPublisher()
|
nextPageMaxId = nextPageMaxIdSubject.eraseToAnyPublisher()
|
||||||
accountIdsForRelationships = accountIdsForRelationshipsSubject.eraseToAnyPublisher()
|
accountIdsForRelationships = accountIdsForRelationshipsSubject.eraseToAnyPublisher()
|
||||||
|
|
||||||
|
|
|
@ -81,6 +81,7 @@ public extension ReportViewModel {
|
||||||
static let preview = ReportViewModel(
|
static let preview = ReportViewModel(
|
||||||
accountService: AccountService(
|
accountService: AccountService(
|
||||||
account: .preview,
|
account: .preview,
|
||||||
|
environment: environment,
|
||||||
mastodonAPIClient: .preview,
|
mastodonAPIClient: .preview,
|
||||||
contentDatabase: .preview),
|
contentDatabase: .preview),
|
||||||
identityContext: .preview)
|
identityContext: .preview)
|
||||||
|
@ -90,6 +91,7 @@ public extension MuteViewModel {
|
||||||
static let preview = MuteViewModel(
|
static let preview = MuteViewModel(
|
||||||
accountService: AccountService(
|
accountService: AccountService(
|
||||||
account: .preview,
|
account: .preview,
|
||||||
|
environment: environment,
|
||||||
mastodonAPIClient: .preview,
|
mastodonAPIClient: .preview,
|
||||||
contentDatabase: .preview),
|
contentDatabase: .preview),
|
||||||
identityContext: .preview)
|
identityContext: .preview)
|
||||||
|
|
|
@ -6,6 +6,7 @@ import ServiceLayer
|
||||||
|
|
||||||
public final class IdentityContext: ObservableObject {
|
public final class IdentityContext: ObservableObject {
|
||||||
@Published private(set) public var identity: Identity
|
@Published private(set) public var identity: Identity
|
||||||
|
@Published private(set) public var authenticatedOtherIdentities = [Identity]()
|
||||||
@Published public var appPreferences: AppPreferences
|
@Published public var appPreferences: AppPreferences
|
||||||
let service: IdentityService
|
let service: IdentityService
|
||||||
|
|
||||||
|
@ -19,6 +20,9 @@ public final class IdentityContext: ObservableObject {
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
publisher.dropFirst().assign(to: &self.$identity)
|
publisher.dropFirst().assign(to: &self.$identity)
|
||||||
|
service.otherAuthenticatedIdentitiesPublisher()
|
||||||
|
.replaceError(with: [])
|
||||||
|
.assign(to: &self.$authenticatedOtherIdentities)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,6 @@ public final class NewStatusViewModel: ObservableObject {
|
||||||
@Published public var visibility: Status.Visibility
|
@Published public var visibility: Status.Visibility
|
||||||
@Published public private(set) var compositionViewModels = [CompositionViewModel]()
|
@Published public private(set) var compositionViewModels = [CompositionViewModel]()
|
||||||
@Published public private(set) var identityContext: IdentityContext
|
@Published public private(set) var identityContext: IdentityContext
|
||||||
@Published public private(set) var authenticatedIdentities = [Identity]()
|
|
||||||
@Published public var canPost = false
|
@Published public var canPost = false
|
||||||
@Published public var alertItem: AlertItem?
|
@Published public var alertItem: AlertItem?
|
||||||
@Published public private(set) var postingState = PostingState.composing
|
@Published public private(set) var postingState = PostingState.composing
|
||||||
|
@ -87,14 +86,6 @@ public final class NewStatusViewModel: ObservableObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
compositionViewModels = [compositionViewModel]
|
compositionViewModels = [compositionViewModel]
|
||||||
|
|
||||||
allIdentitiesService.authenticatedIdentitiesPublisher()
|
|
||||||
.assignErrorsToAlertItem(to: \.alertItem, on: self)
|
|
||||||
.combineLatest($identityContext)
|
|
||||||
.map { authenticatedIdentities, currentIdentity in
|
|
||||||
authenticatedIdentities.filter { $0.id != currentIdentity.identity.id }
|
|
||||||
}
|
|
||||||
.assign(to: &$authenticatedIdentities)
|
|
||||||
$compositionViewModels.flatMap { Publishers.MergeMany($0.map(\.$isPostable)) }
|
$compositionViewModels.flatMap { Publishers.MergeMany($0.map(\.$isPostable)) }
|
||||||
.receive(on: DispatchQueue.main) // hack to punt to next run loop, consider refactoring
|
.receive(on: DispatchQueue.main) // hack to punt to next run loop, consider refactoring
|
||||||
.compactMap { [weak self] _ in self?.compositionViewModels.allSatisfy(\.isPostable) }
|
.compactMap { [weak self] _ in self?.compositionViewModels.allSatisfy(\.isPostable) }
|
||||||
|
|
|
@ -255,16 +255,16 @@ public extension StatusViewModel {
|
||||||
.eraseToAnyPublisher())
|
.eraseToAnyPublisher())
|
||||||
}
|
}
|
||||||
|
|
||||||
func toggleReblogged() {
|
func toggleReblogged(identityId: Identity.Id? = nil) {
|
||||||
eventsSubject.send(
|
eventsSubject.send(
|
||||||
statusService.toggleReblogged()
|
statusService.toggleReblogged(identityId: identityId)
|
||||||
.map { _ in .ignorableOutput }
|
.map { _ in .ignorableOutput }
|
||||||
.eraseToAnyPublisher())
|
.eraseToAnyPublisher())
|
||||||
}
|
}
|
||||||
|
|
||||||
func toggleFavorited() {
|
func toggleFavorited(identityId: Identity.Id? = nil) {
|
||||||
eventsSubject.send(
|
eventsSubject.send(
|
||||||
statusService.toggleFavorited()
|
statusService.toggleFavorited(identityId: identityId)
|
||||||
.map { _ in .ignorableOutput }
|
.map { _ in .ignorableOutput }
|
||||||
.eraseToAnyPublisher())
|
.eraseToAnyPublisher())
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,7 +74,8 @@ private extension CompositionView {
|
||||||
avatarImageView.isUserInteractionEnabled = true
|
avatarImageView.isUserInteractionEnabled = true
|
||||||
changeIdentityButton.setBackgroundImage(.highlightedButtonBackground, for: .highlighted)
|
changeIdentityButton.setBackgroundImage(.highlightedButtonBackground, for: .highlighted)
|
||||||
changeIdentityButton.showsMenuAsPrimaryAction = true
|
changeIdentityButton.showsMenuAsPrimaryAction = true
|
||||||
changeIdentityButton.menu = changeIdentityMenu(identities: parentViewModel.authenticatedIdentities)
|
changeIdentityButton.menu =
|
||||||
|
changeIdentityMenu(identities: parentViewModel.identityContext.authenticatedOtherIdentities)
|
||||||
|
|
||||||
let stackView = UIStackView()
|
let stackView = UIStackView()
|
||||||
|
|
||||||
|
@ -205,7 +206,7 @@ private extension CompositionView {
|
||||||
}
|
}
|
||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
|
|
||||||
parentViewModel.$authenticatedIdentities
|
parentViewModel.identityContext.$authenticatedOtherIdentities
|
||||||
.sink { [weak self] in self?.changeIdentityButton.menu = self?.changeIdentityMenu(identities: $0) }
|
.sink { [weak self] in self?.changeIdentityButton.menu = self?.changeIdentityMenu(identities: $0) }
|
||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
|
|
||||||
|
|
|
@ -616,9 +616,11 @@ private extension StatusView {
|
||||||
|
|
||||||
setReblogButtonColor(reblogged: viewModel.reblogged)
|
setReblogButtonColor(reblogged: viewModel.reblogged)
|
||||||
reblogButton.isEnabled = viewModel.canBeReblogged && isAuthenticated
|
reblogButton.isEnabled = viewModel.canBeReblogged && isAuthenticated
|
||||||
|
reblogButton.menu = authenticatedIdentitiesMenu { viewModel.toggleReblogged(identityId: $0.id) }
|
||||||
|
|
||||||
setFavoriteButtonColor(favorited: viewModel.favorited)
|
setFavoriteButtonColor(favorited: viewModel.favorited)
|
||||||
favoriteButton.isEnabled = isAuthenticated
|
favoriteButton.isEnabled = isAuthenticated
|
||||||
|
favoriteButton.menu = authenticatedIdentitiesMenu { viewModel.toggleFavorited(identityId: $0.id) }
|
||||||
|
|
||||||
shareButton.tag = viewModel.sharingURL?.hashValue ?? 0
|
shareButton.tag = viewModel.sharingURL?.hashValue ?? 0
|
||||||
|
|
||||||
|
@ -1127,6 +1129,38 @@ private extension StatusView {
|
||||||
|
|
||||||
return actions
|
return actions
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func authenticatedIdentitiesMenu(action: @escaping (Identity) -> Void) -> UIMenu {
|
||||||
|
let imageTransformer = SDImageRoundCornerTransformer(
|
||||||
|
radius: .greatestFiniteMagnitude,
|
||||||
|
corners: .allCorners,
|
||||||
|
borderWidth: 0,
|
||||||
|
borderColor: nil)
|
||||||
|
|
||||||
|
return UIMenu(children: statusConfiguration.viewModel
|
||||||
|
.identityContext
|
||||||
|
.authenticatedOtherIdentities.map { identity in
|
||||||
|
UIDeferredMenuElement { completion in
|
||||||
|
let menuItemAction = UIAction(title: identity.handle) { _ in
|
||||||
|
action(identity)
|
||||||
|
}
|
||||||
|
|
||||||
|
if let image = identity.image {
|
||||||
|
SDWebImageManager.shared.loadImage(
|
||||||
|
with: image,
|
||||||
|
options: [.transformAnimatedImage],
|
||||||
|
context: [.imageTransformer: imageTransformer],
|
||||||
|
progress: nil) { (image, _, _, _, _, _) in
|
||||||
|
menuItemAction.image = image
|
||||||
|
|
||||||
|
completion([menuItemAction])
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
completion([menuItemAction])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension UIButton {
|
private extension UIButton {
|
||||||
|
|
Loading…
Reference in a new issue