mirror of
https://github.com/metabolist/metatext.git
synced 2024-11-28 19:11:30 +00:00
Account list db sync and pagination
This commit is contained in:
parent
8b6a521db1
commit
a9e5bb7ef3
4 changed files with 75 additions and 11 deletions
27
DB/Sources/DB/Content/AccountList.swift
Normal file
27
DB/Sources/DB/Content/AccountList.swift
Normal file
|
@ -0,0 +1,27 @@
|
|||
// Copyright © 2020 Metabolist. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
import GRDB
|
||||
|
||||
public struct AccountList: Codable, FetchableRecord, PersistableRecord {
|
||||
let id: UUID
|
||||
|
||||
public init() {
|
||||
id = UUID()
|
||||
}
|
||||
}
|
||||
|
||||
extension AccountList {
|
||||
static let joins = hasMany(
|
||||
AccountListJoin.self,
|
||||
using: ForeignKey([Column("listId")]))
|
||||
.order(Column("index"))
|
||||
static let accounts = hasMany(
|
||||
AccountRecord.self,
|
||||
through: joins,
|
||||
using: AccountListJoin.account)
|
||||
|
||||
var accounts: QueryInterfaceRequest<AccountResult> {
|
||||
request(for: Self.accounts).accountResultRequest
|
||||
}
|
||||
}
|
12
DB/Sources/DB/Content/AccountListJoin.swift
Normal file
12
DB/Sources/DB/Content/AccountListJoin.swift
Normal file
|
@ -0,0 +1,12 @@
|
|||
// Copyright © 2020 Metabolist. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
import GRDB
|
||||
|
||||
struct AccountListJoin: Codable, FetchableRecord, PersistableRecord {
|
||||
let accountId: String
|
||||
let listId: UUID
|
||||
let index: Int
|
||||
|
||||
static let account = belongsTo(AccountRecord.self, using: ForeignKey([Column("accountId")]))
|
||||
}
|
|
@ -115,10 +115,15 @@ public extension ContentDatabase {
|
|||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
func insert(accounts: [Account]) -> AnyPublisher<Never, Error> {
|
||||
func append(accounts: [Account], toList list: AccountList) -> AnyPublisher<Never, Error> {
|
||||
databaseWriter.writePublisher {
|
||||
for account in accounts {
|
||||
try list.save($0)
|
||||
|
||||
let count = try list.accounts.fetchCount($0)
|
||||
|
||||
for (index, account) in accounts.enumerated() {
|
||||
try account.save($0)
|
||||
try AccountListJoin(accountId: account.id, listId: list.id, index: count + index).save($0)
|
||||
}
|
||||
}
|
||||
.ignoreOutput()
|
||||
|
@ -271,6 +276,14 @@ public extension ContentDatabase {
|
|||
}
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
func accountListObservation(_ list: AccountList) -> AnyPublisher<[Account], Error> {
|
||||
ValueObservation.tracking(list.accounts.fetchAll)
|
||||
.removeDuplicates()
|
||||
.publisher(in: databaseWriter)
|
||||
.map { $0.map(Account.init(result:)) }
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
}
|
||||
|
||||
private extension ContentDatabase {
|
||||
|
@ -390,6 +403,20 @@ private extension ContentDatabase {
|
|||
|
||||
t.primaryKey(["accountId", "statusId", "collection"], onConflict: .replace)
|
||||
}
|
||||
|
||||
try db.create(table: "accountList") { t in
|
||||
t.column("id", .text).primaryKey(onConflict: .replace)
|
||||
}
|
||||
|
||||
try db.create(table: "accountListJoin") { t in
|
||||
t.column("accountId", .text).indexed().notNull()
|
||||
.references("accountRecord", column: "id", onDelete: .cascade, onUpdate: .cascade)
|
||||
t.column("listId", .text).indexed().notNull()
|
||||
.references("accountList", column: "id", onDelete: .cascade, onUpdate: .cascade)
|
||||
t.column("index", .integer).notNull()
|
||||
|
||||
t.primaryKey(["accountId", "listId"], onConflict: .replace)
|
||||
}
|
||||
}
|
||||
|
||||
return migrator
|
||||
|
@ -401,6 +428,7 @@ private extension ContentDatabase {
|
|||
try StatusContextJoin.deleteAll($0)
|
||||
try AccountPinnedStatusJoin.deleteAll($0)
|
||||
try AccountStatusJoin.deleteAll($0)
|
||||
try AccountList.deleteAll($0)
|
||||
} completion: { _, _ in }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ public struct AccountListService {
|
|||
public let nextPageMaxIDs: AnyPublisher<String?, Never>
|
||||
public let navigationService: NavigationService
|
||||
|
||||
private let list: AccountList
|
||||
private let mastodonAPIClient: MastodonAPIClient
|
||||
private let contentDatabase: ContentDatabase
|
||||
private let requestClosure: (_ maxID: String?, _ minID: String?) -> AnyPublisher<Never, Error>
|
||||
|
@ -18,27 +19,23 @@ public struct AccountListService {
|
|||
|
||||
extension AccountListService {
|
||||
init(favoritedByStatusID statusID: String, mastodonAPIClient: MastodonAPIClient, contentDatabase: ContentDatabase) {
|
||||
let accountSectionsSubject = PassthroughSubject<[[Account]], Error>()
|
||||
let list = AccountList()
|
||||
let nextPageMaxIDsSubject = PassthroughSubject<String?, Never>()
|
||||
|
||||
self.init(
|
||||
accountSections: accountSectionsSubject.eraseToAnyPublisher(),
|
||||
accountSections: contentDatabase.accountListObservation(list).map { [$0] }.eraseToAnyPublisher(),
|
||||
nextPageMaxIDs: nextPageMaxIDsSubject.eraseToAnyPublisher(),
|
||||
navigationService: NavigationService(
|
||||
status: nil,
|
||||
mastodonAPIClient: mastodonAPIClient,
|
||||
contentDatabase: contentDatabase),
|
||||
list: list,
|
||||
mastodonAPIClient: mastodonAPIClient,
|
||||
contentDatabase: contentDatabase) { maxID, minID -> AnyPublisher<Never, Error> in
|
||||
mastodonAPIClient.pagedRequest(
|
||||
AccountsEndpoint.statusFavouritedBy(id: statusID), maxID: maxID, minID: minID)
|
||||
.handleEvents(
|
||||
receiveOutput: {
|
||||
nextPageMaxIDsSubject.send($0.info.maxID)
|
||||
accountSectionsSubject.send([$0.result])
|
||||
},
|
||||
receiveCompletion: accountSectionsSubject.send)
|
||||
.flatMap { contentDatabase.insert(accounts: $0.result) }
|
||||
.handleEvents(receiveOutput: { nextPageMaxIDsSubject.send($0.info.maxID) })
|
||||
.flatMap { contentDatabase.append(accounts: $0.result, toList: list) }
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue