wip
This commit is contained in:
parent
b32a85aebc
commit
f1e3f1a7fa
|
@ -0,0 +1,20 @@
|
|||
// Copyright © 2021 Metabolist. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
import GRDB
|
||||
import Mastodon
|
||||
|
||||
struct AccountListItemsInfo: Codable, Hashable, FetchableRecord {
|
||||
let accountList: AccountList
|
||||
let accountInfos: [AccountInfo]
|
||||
}
|
||||
|
||||
extension AccountListItemsInfo {
|
||||
static func addingIncludes<T: DerivableRequest>(_ request: T) -> T where T.RowDecoder == AccountList {
|
||||
request.including(all: AccountInfo.addingIncludes(AccountList.accounts).forKey(CodingKeys.accountInfos))
|
||||
}
|
||||
|
||||
static func request(_ request: QueryInterfaceRequest<AccountList>) -> QueryInterfaceRequest<Self> {
|
||||
addingIncludes(request).asRequest(of: self)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
// Copyright © 2021 Metabolist. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
import GRDB
|
||||
import Mastodon
|
||||
|
||||
struct AccountListJoin: ContentDatabaseRecord {
|
||||
let accountListId: AccountList.Id
|
||||
let accountId: Account.Id
|
||||
let order: Int
|
||||
|
||||
static let account = belongsTo(AccountRecord.self)
|
||||
}
|
||||
|
||||
extension AccountListJoin {
|
||||
enum Columns {
|
||||
static let accountListId = Column(CodingKeys.accountListId)
|
||||
static let accountId = Column(CodingKeys.accountId)
|
||||
static let order = Column(CodingKeys.order)
|
||||
}
|
||||
}
|
|
@ -252,6 +252,22 @@ extension ContentDatabase {
|
|||
}
|
||||
}
|
||||
|
||||
migrator.registerMigration("1.0.0") { db in
|
||||
try db.create(table: "accountList") { t in
|
||||
t.column("id", .text).primaryKey(onConflict: .replace)
|
||||
}
|
||||
|
||||
try db.create(table: "accountListJoin") { t in
|
||||
t.column("accountListId", .text).indexed().notNull()
|
||||
.references("accountList", onDelete: .cascade)
|
||||
t.column("accountId", .text).indexed().notNull()
|
||||
.references("accountRecord", onDelete: .cascade)
|
||||
t.column("order", .integer).notNull()
|
||||
|
||||
t.primaryKey(["accountListId", "accountId", "order"], onConflict: .replace)
|
||||
}
|
||||
}
|
||||
|
||||
return migrator
|
||||
}
|
||||
}
|
||||
|
|
|
@ -269,16 +269,43 @@ public extension ContentDatabase {
|
|||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
func insert(accounts: [Account]) -> AnyPublisher<Never, Error> {
|
||||
func insert(accounts: [Account], listId: AccountList.Id? = nil) -> AnyPublisher<Never, Error> {
|
||||
databaseWriter.writePublisher {
|
||||
var order: Int?
|
||||
|
||||
if let listId = listId {
|
||||
try AccountList(id: listId).save($0)
|
||||
order = try Int.fetchOne(
|
||||
$0,
|
||||
AccountListJoin.filter(AccountListJoin.Columns.accountListId == listId)
|
||||
.select(max(AccountListJoin.Columns.order)))
|
||||
?? 0
|
||||
}
|
||||
|
||||
for account in accounts {
|
||||
try account.save($0)
|
||||
|
||||
if let listId = listId, let presentOrder = order {
|
||||
try AccountListJoin(accountListId: listId, accountId: account.id, order: presentOrder).save($0)
|
||||
|
||||
order = presentOrder + 1
|
||||
}
|
||||
}
|
||||
}
|
||||
.ignoreOutput()
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
func remove(id: Account.Id, from listId: AccountList.Id) -> AnyPublisher<Never, Error> {
|
||||
databaseWriter.writePublisher(
|
||||
updates: AccountListJoin.filter(
|
||||
AccountListJoin.Columns.accountId == id
|
||||
&& AccountListJoin.Columns.accountListId == listId)
|
||||
.deleteAll)
|
||||
.ignoreOutput()
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
func insert(identityProofs: [IdentityProof], id: Account.Id) -> AnyPublisher<Never, Error> {
|
||||
databaseWriter.writePublisher {
|
||||
for identityProof in identityProofs {
|
||||
|
@ -494,6 +521,19 @@ public extension ContentDatabase {
|
|||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
func accountListPublisher(
|
||||
id: AccountList.Id,
|
||||
configuration: CollectionItem.AccountConfiguration) -> AnyPublisher<[CollectionSection], Error> {
|
||||
ValueObservation.tracking(
|
||||
AccountListItemsInfo.request(AccountList.filter(AccountList.Columns.id == id)).fetchOne)
|
||||
.removeDuplicates()
|
||||
.publisher(in: databaseWriter)
|
||||
.map { $0?.accountInfos.map { CollectionItem.account(.init(info: $0), configuration, nil) } }
|
||||
.replaceNil(with: [])
|
||||
.map { [CollectionSection(items: $0)] }
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
func listsPublisher() -> AnyPublisher<[Timeline], Error> {
|
||||
ValueObservation.tracking(TimelineRecord.filter(TimelineRecord.Columns.listId != nil)
|
||||
.order(TimelineRecord.Columns.listTitle.asc)
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
// Copyright © 2021 Metabolist. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
import GRDB
|
||||
|
||||
public struct AccountList: ContentDatabaseRecord, Hashable {
|
||||
let id: Id
|
||||
}
|
||||
|
||||
public extension AccountList {
|
||||
typealias Id = String
|
||||
}
|
||||
|
||||
extension AccountList {
|
||||
enum Columns {
|
||||
static let id = Column(CodingKeys.id)
|
||||
}
|
||||
|
||||
static let accountListJoins = hasMany(AccountListJoin.self)
|
||||
static let accounts = hasMany(
|
||||
AccountRecord.self,
|
||||
through: accountListJoins.order(AccountListJoin.Columns.order),
|
||||
using: AccountListJoin.account)
|
||||
}
|
|
@ -13,7 +13,7 @@ public struct AccountListService {
|
|||
public let navigationService: NavigationService
|
||||
public let canRefresh = false
|
||||
|
||||
private let accountsSubject = CurrentValueSubject<[Account], Error>([])
|
||||
private let listId = UUID().uuidString
|
||||
private let endpoint: AccountsEndpoint
|
||||
private let mastodonAPIClient: MastodonAPIClient
|
||||
private let contentDatabase: ContentDatabase
|
||||
|
@ -29,10 +29,7 @@ public struct AccountListService {
|
|||
self.mastodonAPIClient = mastodonAPIClient
|
||||
self.contentDatabase = contentDatabase
|
||||
self.titleComponents = titleComponents
|
||||
sections = accountsSubject
|
||||
.map { [.init(items: $0.map { CollectionItem.account($0, endpoint.configuration, nil) })] } // TODO: revisit
|
||||
.removeDuplicates()
|
||||
.eraseToAnyPublisher()
|
||||
sections = contentDatabase.accountListPublisher(id: listId, configuration: endpoint.configuration)
|
||||
nextPageMaxId = nextPageMaxIdSubject.eraseToAnyPublisher()
|
||||
accountIdsForRelationships = accountIdsForRelationshipsSubject.eraseToAnyPublisher()
|
||||
navigationService = NavigationService(mastodonAPIClient: mastodonAPIClient, contentDatabase: contentDatabase)
|
||||
|
@ -40,8 +37,8 @@ public struct AccountListService {
|
|||
}
|
||||
|
||||
public extension AccountListService {
|
||||
func remove(id: Account.Id) {
|
||||
accountsSubject.value.removeAll { $0.id == id }
|
||||
func remove(id: Account.Id) -> AnyPublisher<Never, Error> {
|
||||
contentDatabase.remove(id: id, from: listId)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -49,15 +46,13 @@ extension AccountListService: CollectionService {
|
|||
public func request(maxId: String?, minId: String?, search: Search?) -> AnyPublisher<Never, Error> {
|
||||
mastodonAPIClient.pagedRequest(endpoint, maxId: maxId, minId: minId)
|
||||
.handleEvents(receiveOutput: {
|
||||
let presentIds = Set(accountsSubject.value.map(\.id))
|
||||
accountsSubject.value.append(contentsOf: $0.result.filter { !presentIds.contains($0.id) })
|
||||
accountIdsForRelationshipsSubject.send(Set($0.result.map(\.id)))
|
||||
|
||||
guard let maxId = $0.info.maxId else { return }
|
||||
|
||||
nextPageMaxIdSubject.send(maxId)
|
||||
accountIdsForRelationshipsSubject.send(Set($0.result.map(\.id)))
|
||||
})
|
||||
.flatMap { contentDatabase.insert(accounts: $0.result) }
|
||||
.flatMap { contentDatabase.insert(accounts: $0.result, listId: listId) }
|
||||
.ignoreOutput()
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
|
|
@ -336,6 +336,8 @@ extension CollectionItemsViewModel: CollectionViewModel {
|
|||
|
||||
public func applyAccountListEdit(viewModel: AccountViewModel, edit: CollectionItemEvent.AccountListEdit) {
|
||||
(collectionService as? AccountListService)?.remove(id: viewModel.id)
|
||||
.sink { _ in } receiveValue: { _ in }
|
||||
.store(in: &cancellables)
|
||||
|
||||
switch edit {
|
||||
case .acceptFollowRequest, .rejectFollowRequest:
|
||||
|
|
Loading…
Reference in New Issue