2020-08-18 05:13:37 +00:00
|
|
|
// Copyright © 2020 Metabolist. All rights reserved.
|
|
|
|
|
|
|
|
import Combine
|
2020-09-05 02:31:43 +00:00
|
|
|
import Foundation
|
2020-08-18 05:13:37 +00:00
|
|
|
import GRDB
|
2020-09-04 06:12:06 +00:00
|
|
|
import Keychain
|
2020-08-30 23:33:11 +00:00
|
|
|
import Mastodon
|
2020-09-04 06:12:06 +00:00
|
|
|
import Secrets
|
2020-08-18 05:13:37 +00:00
|
|
|
|
2020-10-27 03:01:12 +00:00
|
|
|
// swiftlint:disable file_length
|
2020-09-03 03:28:34 +00:00
|
|
|
public struct ContentDatabase {
|
2020-10-03 20:50:46 +00:00
|
|
|
public let activeFiltersPublisher: AnyPublisher<[Filter], Error>
|
|
|
|
|
2020-12-02 19:39:42 +00:00
|
|
|
private let id: Identity.Id
|
2020-09-28 03:11:49 +00:00
|
|
|
private let databaseWriter: DatabaseWriter
|
2020-08-18 05:13:37 +00:00
|
|
|
|
2020-10-27 03:01:12 +00:00
|
|
|
public init(id: Identity.Id,
|
|
|
|
useHomeTimelineLastReadId: Bool,
|
|
|
|
useNotificationsLastReadId: Bool,
|
|
|
|
inMemory: Bool,
|
2020-11-09 03:07:23 +00:00
|
|
|
appGroup: String,
|
2020-10-27 03:01:12 +00:00
|
|
|
keychain: Keychain.Type) throws {
|
2020-12-02 19:39:42 +00:00
|
|
|
self.id = id
|
|
|
|
|
2020-09-03 03:28:34 +00:00
|
|
|
if inMemory {
|
2020-09-28 03:11:49 +00:00
|
|
|
databaseWriter = DatabaseQueue()
|
2020-11-09 03:07:23 +00:00
|
|
|
try Self.migrator.migrate(databaseWriter)
|
2020-08-18 05:13:37 +00:00
|
|
|
} else {
|
2020-11-09 03:07:23 +00:00
|
|
|
databaseWriter = try DatabasePool.withFileCoordinator(
|
|
|
|
url: Self.fileURL(id: id, appGroup: appGroup),
|
|
|
|
migrator: Self.migrator) {
|
|
|
|
try Secrets.databaseKey(identityId: id, keychain: keychain)
|
2020-09-04 06:12:06 +00:00
|
|
|
}
|
2020-08-18 05:13:37 +00:00
|
|
|
}
|
|
|
|
|
2020-10-27 03:01:12 +00:00
|
|
|
try Self.clean(
|
|
|
|
databaseWriter,
|
|
|
|
useHomeTimelineLastReadId: useHomeTimelineLastReadId,
|
|
|
|
useNotificationsLastReadId: useNotificationsLastReadId)
|
2020-10-03 20:50:46 +00:00
|
|
|
|
|
|
|
activeFiltersPublisher = ValueObservation.tracking {
|
|
|
|
try Filter.filter(Filter.Columns.expiresAt == nil || Filter.Columns.expiresAt > Date()).fetchAll($0)
|
|
|
|
}
|
|
|
|
.removeDuplicates()
|
|
|
|
.publisher(in: databaseWriter)
|
|
|
|
.eraseToAnyPublisher()
|
2020-08-18 05:13:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-03 03:28:34 +00:00
|
|
|
public extension ContentDatabase {
|
2020-11-09 03:07:23 +00:00
|
|
|
static func delete(id: Identity.Id, appGroup: String) throws {
|
|
|
|
try FileManager.default.removeItem(at: fileURL(id: id, appGroup: appGroup))
|
2020-09-03 01:14:33 +00:00
|
|
|
}
|
|
|
|
|
2020-09-02 09:39:44 +00:00
|
|
|
func insert(status: Status) -> AnyPublisher<Never, Error> {
|
2020-09-28 03:11:49 +00:00
|
|
|
databaseWriter.writePublisher(updates: status.save)
|
2020-09-02 09:39:44 +00:00
|
|
|
.ignoreOutput()
|
|
|
|
.eraseToAnyPublisher()
|
|
|
|
}
|
|
|
|
|
2021-01-31 15:42:44 +00:00
|
|
|
// swiftlint:disable:next function_body_length
|
2020-10-04 08:39:54 +00:00
|
|
|
func insert(
|
|
|
|
statuses: [Status],
|
|
|
|
timeline: Timeline,
|
|
|
|
loadMoreAndDirection: (LoadMore, LoadMore.Direction)? = nil) -> AnyPublisher<Never, Error> {
|
2020-09-28 03:11:49 +00:00
|
|
|
databaseWriter.writePublisher {
|
2020-10-02 07:41:30 +00:00
|
|
|
let timelineRecord = TimelineRecord(timeline: timeline)
|
|
|
|
|
|
|
|
try timelineRecord.save($0)
|
|
|
|
|
2020-10-05 22:50:05 +00:00
|
|
|
let maxIdPresent = try String.fetchOne($0, timelineRecord.statuses.select(max(StatusRecord.Columns.id)))
|
2020-08-18 05:13:37 +00:00
|
|
|
|
2021-01-31 15:42:44 +00:00
|
|
|
var order = timeline.ordered
|
|
|
|
? try Int.fetchOne(
|
|
|
|
$0,
|
|
|
|
TimelineStatusJoin.filter(TimelineStatusJoin.Columns.timelineId == timeline.id)
|
|
|
|
.select(max(TimelineStatusJoin.Columns.order)))
|
|
|
|
: nil
|
|
|
|
|
2020-08-18 05:13:37 +00:00
|
|
|
for status in statuses {
|
2020-09-02 09:39:44 +00:00
|
|
|
try status.save($0)
|
2020-08-18 05:13:37 +00:00
|
|
|
|
2021-01-31 15:42:44 +00:00
|
|
|
if let order = order {
|
|
|
|
print("saving with order: \(order)")
|
|
|
|
}
|
|
|
|
try TimelineStatusJoin(timelineId: timeline.id, statusId: status.id, order: order).save($0)
|
|
|
|
|
|
|
|
if let presentOrder = order {
|
|
|
|
order = presentOrder + 1
|
|
|
|
}
|
2020-09-02 02:39:06 +00:00
|
|
|
}
|
2020-10-02 07:41:30 +00:00
|
|
|
|
2020-10-05 22:50:05 +00:00
|
|
|
if let maxIdPresent = maxIdPresent,
|
|
|
|
let minIdInserted = statuses.map(\.id).min(),
|
|
|
|
minIdInserted > maxIdPresent {
|
2020-10-04 08:39:54 +00:00
|
|
|
try LoadMoreRecord(
|
|
|
|
timelineId: timeline.id,
|
2020-10-05 22:50:05 +00:00
|
|
|
afterStatusId: minIdInserted,
|
|
|
|
beforeStatusId: maxIdPresent)
|
2020-10-04 08:39:54 +00:00
|
|
|
.save($0)
|
|
|
|
}
|
|
|
|
|
|
|
|
guard let (loadMore, direction) = loadMoreAndDirection else { return }
|
|
|
|
|
|
|
|
try LoadMoreRecord(
|
|
|
|
timelineId: loadMore.timeline.id,
|
|
|
|
afterStatusId: loadMore.afterStatusId,
|
|
|
|
beforeStatusId: loadMore.beforeStatusId)
|
|
|
|
.delete($0)
|
|
|
|
|
|
|
|
switch direction {
|
|
|
|
case .up:
|
2020-10-05 22:50:05 +00:00
|
|
|
if let maxIdInserted = statuses.map(\.id).max(), maxIdInserted < loadMore.afterStatusId {
|
2020-10-04 08:39:54 +00:00
|
|
|
try LoadMoreRecord(
|
|
|
|
timelineId: loadMore.timeline.id,
|
|
|
|
afterStatusId: loadMore.afterStatusId,
|
2020-10-05 22:50:05 +00:00
|
|
|
beforeStatusId: maxIdInserted)
|
2020-10-04 08:39:54 +00:00
|
|
|
.save($0)
|
|
|
|
}
|
|
|
|
case .down:
|
2020-10-05 22:50:05 +00:00
|
|
|
if let minIdInserted = statuses.map(\.id).min(), minIdInserted > loadMore.beforeStatusId {
|
2020-10-04 08:39:54 +00:00
|
|
|
try LoadMoreRecord(
|
|
|
|
timelineId: loadMore.timeline.id,
|
2020-10-05 22:50:05 +00:00
|
|
|
afterStatusId: minIdInserted,
|
2020-10-04 08:39:54 +00:00
|
|
|
beforeStatusId: loadMore.beforeStatusId)
|
|
|
|
.save($0)
|
|
|
|
}
|
2020-10-02 07:41:30 +00:00
|
|
|
}
|
2020-09-02 02:39:06 +00:00
|
|
|
}
|
|
|
|
.ignoreOutput()
|
|
|
|
.eraseToAnyPublisher()
|
|
|
|
}
|
|
|
|
|
2020-10-05 22:50:05 +00:00
|
|
|
func insert(context: Context, parentId: Status.Id) -> AnyPublisher<Never, Error> {
|
2020-09-28 03:11:49 +00:00
|
|
|
databaseWriter.writePublisher {
|
2020-10-03 09:19:05 +00:00
|
|
|
for (index, status) in context.ancestors.enumerated() {
|
2020-09-02 09:39:44 +00:00
|
|
|
try status.save($0)
|
2021-01-31 15:49:09 +00:00
|
|
|
try StatusAncestorJoin(parentId: parentId, statusId: status.id, order: index).save($0)
|
2020-09-02 02:39:06 +00:00
|
|
|
}
|
|
|
|
|
2020-10-03 09:19:05 +00:00
|
|
|
for (index, status) in context.descendants.enumerated() {
|
|
|
|
try status.save($0)
|
2021-01-31 15:49:09 +00:00
|
|
|
try StatusDescendantJoin(parentId: parentId, statusId: status.id, order: index).save($0)
|
2020-08-18 05:13:37 +00:00
|
|
|
}
|
2020-10-03 09:19:05 +00:00
|
|
|
|
|
|
|
try StatusAncestorJoin.filter(
|
2020-10-05 22:50:05 +00:00
|
|
|
StatusAncestorJoin.Columns.parentId == parentId
|
2020-10-03 09:19:05 +00:00
|
|
|
&& !context.ancestors.map(\.id).contains(StatusAncestorJoin.Columns.statusId))
|
|
|
|
.deleteAll($0)
|
|
|
|
|
|
|
|
try StatusDescendantJoin.filter(
|
2020-10-05 22:50:05 +00:00
|
|
|
StatusDescendantJoin.Columns.parentId == parentId
|
2020-10-03 09:19:05 +00:00
|
|
|
&& !context.descendants.map(\.id).contains(StatusDescendantJoin.Columns.statusId))
|
|
|
|
.deleteAll($0)
|
2020-08-18 05:13:37 +00:00
|
|
|
}
|
2020-08-26 09:19:38 +00:00
|
|
|
.ignoreOutput()
|
2020-08-18 05:13:37 +00:00
|
|
|
.eraseToAnyPublisher()
|
|
|
|
}
|
|
|
|
|
2020-10-05 22:50:05 +00:00
|
|
|
func insert(pinnedStatuses: [Status], accountId: Account.Id) -> AnyPublisher<Never, Error> {
|
2020-09-28 03:11:49 +00:00
|
|
|
databaseWriter.writePublisher {
|
2020-09-18 00:16:41 +00:00
|
|
|
for (index, status) in pinnedStatuses.enumerated() {
|
|
|
|
try status.save($0)
|
2021-01-31 15:49:09 +00:00
|
|
|
try AccountPinnedStatusJoin(accountId: accountId, statusId: status.id, order: index).save($0)
|
2020-09-18 00:16:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
try AccountPinnedStatusJoin.filter(
|
2020-10-05 22:50:05 +00:00
|
|
|
AccountPinnedStatusJoin.Columns.accountId == accountId
|
2020-09-29 06:06:25 +00:00
|
|
|
&& !pinnedStatuses.map(\.id).contains(AccountPinnedStatusJoin.Columns.statusId))
|
2020-09-18 00:16:41 +00:00
|
|
|
.deleteAll($0)
|
|
|
|
}
|
|
|
|
.ignoreOutput()
|
|
|
|
.eraseToAnyPublisher()
|
|
|
|
}
|
|
|
|
|
2020-10-14 00:03:01 +00:00
|
|
|
func toggleShowContent(id: Status.Id) -> AnyPublisher<Never, Error> {
|
2020-10-07 21:06:26 +00:00
|
|
|
databaseWriter.writePublisher {
|
2020-10-14 00:03:01 +00:00
|
|
|
if let toggle = try StatusShowContentToggle
|
|
|
|
.filter(StatusShowContentToggle.Columns.statusId == id)
|
2020-10-07 21:06:26 +00:00
|
|
|
.fetchOne($0) {
|
|
|
|
try toggle.delete($0)
|
|
|
|
} else {
|
2020-10-14 00:03:01 +00:00
|
|
|
try StatusShowContentToggle(statusId: id).save($0)
|
2020-10-07 21:06:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
.ignoreOutput()
|
|
|
|
.eraseToAnyPublisher()
|
|
|
|
}
|
|
|
|
|
2020-10-14 00:03:01 +00:00
|
|
|
func toggleShowAttachments(id: Status.Id) -> AnyPublisher<Never, Error> {
|
|
|
|
databaseWriter.writePublisher {
|
|
|
|
if let toggle = try StatusShowAttachmentsToggle
|
|
|
|
.filter(StatusShowAttachmentsToggle.Columns.statusId == id)
|
|
|
|
.fetchOne($0) {
|
|
|
|
try toggle.delete($0)
|
|
|
|
} else {
|
|
|
|
try StatusShowAttachmentsToggle(statusId: id).save($0)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
.ignoreOutput()
|
|
|
|
.eraseToAnyPublisher()
|
|
|
|
}
|
|
|
|
|
|
|
|
func expand(ids: Set<Status.Id>) -> AnyPublisher<Never, Error> {
|
2020-10-07 21:06:26 +00:00
|
|
|
databaseWriter.writePublisher {
|
|
|
|
for id in ids {
|
2020-10-14 00:03:01 +00:00
|
|
|
try StatusShowContentToggle(statusId: id).save($0)
|
|
|
|
try StatusShowAttachmentsToggle(statusId: id).save($0)
|
2020-10-07 21:06:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
.ignoreOutput()
|
|
|
|
.eraseToAnyPublisher()
|
|
|
|
}
|
|
|
|
|
2020-10-14 00:03:01 +00:00
|
|
|
func collapse(ids: Set<Status.Id>) -> AnyPublisher<Never, Error> {
|
|
|
|
databaseWriter.writePublisher {
|
|
|
|
try StatusShowContentToggle
|
|
|
|
.filter(ids.contains(StatusShowContentToggle.Columns.statusId))
|
|
|
|
.deleteAll($0)
|
|
|
|
try StatusShowAttachmentsToggle
|
|
|
|
.filter(ids.contains(StatusShowContentToggle.Columns.statusId))
|
|
|
|
.deleteAll($0)
|
|
|
|
}
|
|
|
|
.ignoreOutput()
|
|
|
|
.eraseToAnyPublisher()
|
2020-10-07 21:06:26 +00:00
|
|
|
}
|
|
|
|
|
2020-10-25 02:31:44 +00:00
|
|
|
func update(id: Status.Id, poll: Poll) -> AnyPublisher<Never, Error> {
|
|
|
|
databaseWriter.writePublisher {
|
|
|
|
let data = try StatusRecord.databaseJSONEncoder(for: StatusRecord.Columns.poll.name).encode(poll)
|
|
|
|
|
|
|
|
try StatusRecord.filter(StatusRecord.Columns.id == id)
|
|
|
|
.updateAll($0, StatusRecord.Columns.poll.set(to: data))
|
|
|
|
}
|
|
|
|
.ignoreOutput()
|
|
|
|
.eraseToAnyPublisher()
|
2021-01-11 22:45:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func delete(id: Status.Id) -> AnyPublisher<Never, Error> {
|
|
|
|
databaseWriter.writePublisher(updates: StatusRecord.filter(StatusRecord.Columns.id == id).deleteAll)
|
|
|
|
.ignoreOutput()
|
|
|
|
.eraseToAnyPublisher()
|
2020-10-25 02:31:44 +00:00
|
|
|
}
|
|
|
|
|
2020-11-12 08:32:18 +00:00
|
|
|
func unfollow(id: Account.Id) -> AnyPublisher<Never, Error> {
|
|
|
|
databaseWriter.writePublisher {
|
|
|
|
let statusIds = try Status.Id.fetchAll(
|
|
|
|
$0,
|
|
|
|
StatusRecord.filter(StatusRecord.Columns.accountId == id).select(StatusRecord.Columns.id))
|
|
|
|
|
|
|
|
try TimelineStatusJoin.filter(
|
|
|
|
TimelineStatusJoin.Columns.timelineId == Timeline.home.id
|
|
|
|
&& statusIds.contains(TimelineStatusJoin.Columns.statusId))
|
|
|
|
.deleteAll($0)
|
|
|
|
}
|
|
|
|
.ignoreOutput()
|
|
|
|
.eraseToAnyPublisher()
|
|
|
|
}
|
|
|
|
|
2020-11-17 06:46:48 +00:00
|
|
|
func mute(id: Account.Id) -> AnyPublisher<Never, Error> {
|
|
|
|
databaseWriter.writePublisher {
|
|
|
|
try StatusRecord.filter(StatusRecord.Columns.accountId == id).deleteAll($0)
|
|
|
|
try NotificationRecord.filter(NotificationRecord.Columns.accountId == id).deleteAll($0)
|
|
|
|
}
|
|
|
|
.ignoreOutput()
|
|
|
|
.eraseToAnyPublisher()
|
|
|
|
}
|
|
|
|
|
|
|
|
func block(id: Account.Id) -> AnyPublisher<Never, Error> {
|
|
|
|
databaseWriter.writePublisher(updates: AccountRecord.filter(AccountRecord.Columns.id == id).deleteAll)
|
|
|
|
.ignoreOutput()
|
|
|
|
.eraseToAnyPublisher()
|
|
|
|
}
|
|
|
|
|
2021-01-16 10:04:04 +00:00
|
|
|
func insert(accounts: [Account]) -> AnyPublisher<Never, Error> {
|
2020-09-28 03:11:49 +00:00
|
|
|
databaseWriter.writePublisher {
|
2021-01-16 10:04:04 +00:00
|
|
|
for account in accounts {
|
2020-09-22 06:53:11 +00:00
|
|
|
try account.save($0)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
.ignoreOutput()
|
|
|
|
.eraseToAnyPublisher()
|
|
|
|
}
|
|
|
|
|
2020-11-11 07:31:56 +00:00
|
|
|
func insert(identityProofs: [IdentityProof], id: Account.Id) -> AnyPublisher<Never, Error> {
|
|
|
|
databaseWriter.writePublisher {
|
|
|
|
for identityProof in identityProofs {
|
|
|
|
try IdentityProofRecord(
|
|
|
|
accountId: id,
|
|
|
|
provider: identityProof.provider,
|
|
|
|
providerUsername: identityProof.providerUsername,
|
|
|
|
profileUrl: identityProof.profileUrl,
|
|
|
|
proofUrl: identityProof.proofUrl,
|
|
|
|
updatedAt: identityProof.updatedAt)
|
|
|
|
.save($0)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
.ignoreOutput()
|
|
|
|
.eraseToAnyPublisher()
|
|
|
|
}
|
|
|
|
|
2021-01-18 07:17:45 +00:00
|
|
|
func insert(featuredTags: [FeaturedTag], id: Account.Id) -> AnyPublisher<Never, Error> {
|
|
|
|
databaseWriter.writePublisher {
|
|
|
|
for featuredTag in featuredTags {
|
|
|
|
try FeaturedTagRecord(
|
|
|
|
id: featuredTag.id,
|
|
|
|
name: featuredTag.name,
|
|
|
|
url: featuredTag.url,
|
|
|
|
statusesCount: featuredTag.statusesCount,
|
|
|
|
lastStatusAt: featuredTag.lastStatusAt,
|
|
|
|
accountId: id)
|
|
|
|
.save($0)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
.ignoreOutput()
|
|
|
|
.eraseToAnyPublisher()
|
|
|
|
}
|
|
|
|
|
2020-11-11 07:31:56 +00:00
|
|
|
func insert(relationships: [Relationship]) -> AnyPublisher<Never, Error> {
|
|
|
|
databaseWriter.writePublisher {
|
|
|
|
for relationship in relationships {
|
|
|
|
try relationship.save($0)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
.ignoreOutput()
|
|
|
|
.eraseToAnyPublisher()
|
|
|
|
}
|
|
|
|
|
2020-09-26 03:28:50 +00:00
|
|
|
func setLists(_ lists: [List]) -> AnyPublisher<Never, Error> {
|
2020-09-28 03:11:49 +00:00
|
|
|
databaseWriter.writePublisher {
|
2020-08-29 03:50:58 +00:00
|
|
|
for list in lists {
|
2020-10-02 07:41:30 +00:00
|
|
|
try TimelineRecord(timeline: Timeline.list(list)).save($0)
|
2020-08-29 03:50:58 +00:00
|
|
|
}
|
|
|
|
|
2020-10-01 02:35:06 +00:00
|
|
|
try TimelineRecord
|
|
|
|
.filter(!lists.map(\.id).contains(TimelineRecord.Columns.listId)
|
|
|
|
&& TimelineRecord.Columns.listTitle != nil)
|
2020-09-09 05:40:49 +00:00
|
|
|
.deleteAll($0)
|
2020-08-29 03:50:58 +00:00
|
|
|
}
|
|
|
|
.ignoreOutput()
|
|
|
|
.eraseToAnyPublisher()
|
|
|
|
}
|
|
|
|
|
2020-09-26 03:28:50 +00:00
|
|
|
func createList(_ list: List) -> AnyPublisher<Never, Error> {
|
2020-10-02 07:41:30 +00:00
|
|
|
databaseWriter.writePublisher(updates: TimelineRecord(timeline: Timeline.list(list)).save)
|
2020-08-29 03:50:58 +00:00
|
|
|
.ignoreOutput()
|
|
|
|
.eraseToAnyPublisher()
|
|
|
|
}
|
|
|
|
|
2020-10-05 22:50:05 +00:00
|
|
|
func deleteList(id: List.Id) -> AnyPublisher<Never, Error> {
|
2020-10-01 02:35:06 +00:00
|
|
|
databaseWriter.writePublisher(updates: TimelineRecord.filter(TimelineRecord.Columns.listId == id).deleteAll)
|
2020-08-29 10:26:26 +00:00
|
|
|
.ignoreOutput()
|
|
|
|
.eraseToAnyPublisher()
|
|
|
|
}
|
|
|
|
|
|
|
|
func setFilters(_ filters: [Filter]) -> AnyPublisher<Never, Error> {
|
2020-09-28 03:11:49 +00:00
|
|
|
databaseWriter.writePublisher {
|
2020-08-29 10:26:26 +00:00
|
|
|
for filter in filters {
|
|
|
|
try filter.save($0)
|
|
|
|
}
|
|
|
|
|
2020-09-29 06:06:25 +00:00
|
|
|
try Filter.filter(!filters.map(\.id).contains(Filter.Columns.id)).deleteAll($0)
|
2020-08-29 10:26:26 +00:00
|
|
|
}
|
2020-08-29 03:50:58 +00:00
|
|
|
.ignoreOutput()
|
|
|
|
.eraseToAnyPublisher()
|
|
|
|
}
|
|
|
|
|
2020-08-29 10:26:26 +00:00
|
|
|
func createFilter(_ filter: Filter) -> AnyPublisher<Never, Error> {
|
2020-09-28 03:11:49 +00:00
|
|
|
databaseWriter.writePublisher(updates: filter.save)
|
2020-08-29 10:26:26 +00:00
|
|
|
.ignoreOutput()
|
|
|
|
.eraseToAnyPublisher()
|
|
|
|
}
|
|
|
|
|
2020-10-05 22:50:05 +00:00
|
|
|
func deleteFilter(id: Filter.Id) -> AnyPublisher<Never, Error> {
|
2020-09-29 06:06:25 +00:00
|
|
|
databaseWriter.writePublisher(updates: Filter.filter(Filter.Columns.id == id).deleteAll)
|
2020-08-29 10:26:26 +00:00
|
|
|
.ignoreOutput()
|
|
|
|
.eraseToAnyPublisher()
|
|
|
|
}
|
|
|
|
|
2020-10-27 03:01:12 +00:00
|
|
|
func setLastReadId(_ id: String, markerTimeline: Marker.Timeline) -> AnyPublisher<Never, Error> {
|
|
|
|
databaseWriter.writePublisher(updates: LastReadIdRecord(markerTimeline: markerTimeline, id: id).save)
|
|
|
|
.ignoreOutput()
|
|
|
|
.eraseToAnyPublisher()
|
|
|
|
}
|
|
|
|
|
2020-10-30 07:11:24 +00:00
|
|
|
func insert(notifications: [MastodonNotification]) -> AnyPublisher<Never, Error> {
|
|
|
|
databaseWriter.writePublisher {
|
|
|
|
for notification in notifications {
|
|
|
|
try notification.save($0)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
.ignoreOutput()
|
|
|
|
.eraseToAnyPublisher()
|
|
|
|
}
|
|
|
|
|
2020-10-29 06:03:45 +00:00
|
|
|
func insert(conversations: [Conversation]) -> AnyPublisher<Never, Error> {
|
|
|
|
databaseWriter.writePublisher {
|
|
|
|
for conversation in conversations {
|
|
|
|
try conversation.save($0)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
.ignoreOutput()
|
|
|
|
.eraseToAnyPublisher()
|
|
|
|
}
|
|
|
|
|
2021-01-14 17:49:53 +00:00
|
|
|
func update(emojis: [Emoji]) -> AnyPublisher<Never, Error> {
|
|
|
|
databaseWriter.writePublisher {
|
|
|
|
for emoji in emojis {
|
|
|
|
try emoji.save($0)
|
|
|
|
}
|
|
|
|
|
|
|
|
try Emoji.filter(!emojis.map(\.shortcode).contains(Emoji.Columns.shortcode)).deleteAll($0)
|
|
|
|
}
|
|
|
|
.ignoreOutput()
|
|
|
|
.eraseToAnyPublisher()
|
|
|
|
}
|
|
|
|
|
2021-01-16 00:58:10 +00:00
|
|
|
func updateUse(emoji: String, system: Bool) -> AnyPublisher<Never, Error> {
|
|
|
|
databaseWriter.writePublisher {
|
|
|
|
let count = try Int.fetchOne(
|
|
|
|
$0,
|
|
|
|
EmojiUse.filter(EmojiUse.Columns.system == system && EmojiUse.Columns.emoji == emoji)
|
|
|
|
.select(EmojiUse.Columns.count))
|
|
|
|
|
|
|
|
try EmojiUse(emoji: emoji, system: system, lastUse: Date(), count: (count ?? 0) + 1).save($0)
|
|
|
|
}
|
|
|
|
.ignoreOutput()
|
|
|
|
.eraseToAnyPublisher()
|
|
|
|
}
|
|
|
|
|
2021-01-16 05:31:06 +00:00
|
|
|
func update(announcements: [Announcement]) -> AnyPublisher<Never, Error> {
|
|
|
|
databaseWriter.writePublisher {
|
|
|
|
for announcement in announcements {
|
|
|
|
try announcement.save($0)
|
|
|
|
}
|
|
|
|
|
|
|
|
try Announcement.filter(!announcements.map(\.id).contains(Announcement.Columns.id)).deleteAll($0)
|
|
|
|
}
|
|
|
|
.ignoreOutput()
|
|
|
|
.eraseToAnyPublisher()
|
|
|
|
}
|
|
|
|
|
2021-01-24 07:21:35 +00:00
|
|
|
func insert(results: Results) -> AnyPublisher<Never, Error> {
|
|
|
|
databaseWriter.writePublisher {
|
2021-01-23 03:48:33 +00:00
|
|
|
for account in results.accounts {
|
2021-01-24 07:21:35 +00:00
|
|
|
try account.save($0)
|
2021-01-23 03:48:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for status in results.statuses {
|
2021-01-24 07:21:35 +00:00
|
|
|
try status.save($0)
|
2021-01-23 03:48:33 +00:00
|
|
|
}
|
|
|
|
}
|
2021-01-24 07:21:35 +00:00
|
|
|
.ignoreOutput()
|
2021-01-23 03:48:33 +00:00
|
|
|
.eraseToAnyPublisher()
|
|
|
|
}
|
|
|
|
|
2021-01-30 22:27:49 +00:00
|
|
|
func insert(instance: Instance) -> AnyPublisher<Never, Error> {
|
|
|
|
databaseWriter.writePublisher(updates: instance.save)
|
|
|
|
.ignoreOutput()
|
|
|
|
.eraseToAnyPublisher()
|
|
|
|
}
|
|
|
|
|
2021-01-23 06:15:52 +00:00
|
|
|
func timelinePublisher(_ timeline: Timeline) -> AnyPublisher<[CollectionSection], Error> {
|
2020-10-03 20:50:46 +00:00
|
|
|
ValueObservation.tracking(
|
2021-01-31 15:42:44 +00:00
|
|
|
TimelineItemsInfo.request(TimelineRecord.filter(TimelineRecord.Columns.id == timeline.id),
|
|
|
|
ordered: timeline.ordered).fetchOne)
|
2020-10-03 20:50:46 +00:00
|
|
|
.removeDuplicates()
|
|
|
|
.publisher(in: databaseWriter)
|
2020-12-02 19:39:42 +00:00
|
|
|
.handleEvents(
|
|
|
|
receiveSubscription: { _ in
|
|
|
|
if let ephemeralityId = timeline.ephemeralityId(id: id) {
|
|
|
|
Self.ephemeralTimelines.add(ephemeralityId)
|
|
|
|
}
|
|
|
|
},
|
|
|
|
receiveCancel: {
|
|
|
|
guard let ephemeralityId = timeline.ephemeralityId(id: id) else { return }
|
|
|
|
|
|
|
|
Self.ephemeralTimelines.remove(ephemeralityId)
|
|
|
|
|
|
|
|
if Self.ephemeralTimelines.count(for: ephemeralityId) == 0 {
|
|
|
|
databaseWriter.asyncWrite(TimelineRecord(timeline: timeline).delete) { _, _ in }
|
|
|
|
}
|
|
|
|
})
|
2020-10-03 20:50:46 +00:00
|
|
|
.combineLatest(activeFiltersPublisher)
|
|
|
|
.compactMap { $0?.items(filters: $1) }
|
|
|
|
.eraseToAnyPublisher()
|
2020-08-19 22:16:03 +00:00
|
|
|
}
|
2020-08-29 03:50:58 +00:00
|
|
|
|
2021-01-23 06:15:52 +00:00
|
|
|
func contextPublisher(id: Status.Id) -> AnyPublisher<[CollectionSection], Error> {
|
2020-10-03 20:50:46 +00:00
|
|
|
ValueObservation.tracking(
|
2020-10-05 22:50:05 +00:00
|
|
|
ContextItemsInfo.request(StatusRecord.filter(StatusRecord.Columns.id == id)).fetchOne)
|
2020-10-03 20:50:46 +00:00
|
|
|
.removeDuplicates()
|
|
|
|
.publisher(in: databaseWriter)
|
|
|
|
.combineLatest(activeFiltersPublisher)
|
2021-01-28 06:41:06 +00:00
|
|
|
.map { $0?.items(filters: $1) }
|
|
|
|
.replaceNil(with: [])
|
2020-10-03 20:50:46 +00:00
|
|
|
.eraseToAnyPublisher()
|
2020-09-18 00:16:41 +00:00
|
|
|
}
|
|
|
|
|
2020-10-06 20:44:22 +00:00
|
|
|
func listsPublisher() -> AnyPublisher<[Timeline], Error> {
|
2020-10-01 02:35:06 +00:00
|
|
|
ValueObservation.tracking(TimelineRecord.filter(TimelineRecord.Columns.listId != nil)
|
|
|
|
.order(TimelineRecord.Columns.listTitle.asc)
|
2020-08-29 03:50:58 +00:00
|
|
|
.fetchAll)
|
2020-08-29 10:26:26 +00:00
|
|
|
.removeDuplicates()
|
2020-09-28 03:11:49 +00:00
|
|
|
.publisher(in: databaseWriter)
|
2020-10-03 09:19:05 +00:00
|
|
|
.tryMap { $0.map(Timeline.init(record:)).compactMap { $0 } }
|
2020-08-29 10:26:26 +00:00
|
|
|
.eraseToAnyPublisher()
|
|
|
|
}
|
|
|
|
|
2020-10-06 20:44:22 +00:00
|
|
|
func expiredFiltersPublisher() -> AnyPublisher<[Filter], Error> {
|
2020-10-03 20:50:46 +00:00
|
|
|
ValueObservation.tracking { try Filter.filter(Filter.Columns.expiresAt < Date()).fetchAll($0) }
|
2020-08-30 00:32:34 +00:00
|
|
|
.removeDuplicates()
|
2020-09-28 03:11:49 +00:00
|
|
|
.publisher(in: databaseWriter)
|
2020-08-30 00:32:34 +00:00
|
|
|
.eraseToAnyPublisher()
|
|
|
|
}
|
2020-09-22 06:53:11 +00:00
|
|
|
|
2020-11-11 07:31:56 +00:00
|
|
|
func profilePublisher(id: Account.Id) -> AnyPublisher<Profile, Error> {
|
|
|
|
ValueObservation.tracking(ProfileInfo.request(AccountRecord.filter(AccountRecord.Columns.id == id)).fetchOne)
|
|
|
|
.removeDuplicates()
|
|
|
|
.publisher(in: databaseWriter)
|
|
|
|
.compactMap { $0 }
|
|
|
|
.map(Profile.init(info:))
|
|
|
|
.eraseToAnyPublisher()
|
|
|
|
}
|
|
|
|
|
|
|
|
func relationshipPublisher(id: Account.Id) -> AnyPublisher<Relationship, Error> {
|
|
|
|
ValueObservation.tracking(Relationship.filter(Relationship.Columns.id == id).fetchOne)
|
2020-09-22 06:53:11 +00:00
|
|
|
.removeDuplicates()
|
2020-10-01 02:35:06 +00:00
|
|
|
.publisher(in: databaseWriter)
|
2020-10-03 20:55:07 +00:00
|
|
|
.compactMap { $0 }
|
2020-09-22 06:53:11 +00:00
|
|
|
.eraseToAnyPublisher()
|
|
|
|
}
|
2020-09-28 22:40:03 +00:00
|
|
|
|
2021-01-25 07:42:39 +00:00
|
|
|
// swiftlint:disable:next function_body_length
|
|
|
|
func publisher(results: Results, limit: Int?) -> AnyPublisher<[CollectionSection], Error> {
|
2021-01-24 07:21:35 +00:00
|
|
|
let accountIds = results.accounts.map(\.id)
|
|
|
|
let statusIds = results.statuses.map(\.id)
|
|
|
|
|
|
|
|
let accountsPublisher = ValueObservation.tracking(
|
|
|
|
AccountInfo.request(
|
|
|
|
AccountRecord.filter(accountIds.contains(AccountRecord.Columns.id)))
|
|
|
|
.fetchAll)
|
|
|
|
.removeDuplicates()
|
|
|
|
.publisher(in: databaseWriter)
|
2021-01-25 07:42:39 +00:00
|
|
|
.map { infos -> [CollectionItem] in
|
|
|
|
var accounts = infos.sorted {
|
2021-01-24 07:21:35 +00:00
|
|
|
accountIds.firstIndex(of: $0.record.id) ?? 0
|
|
|
|
< accountIds.firstIndex(of: $1.record.id) ?? 0
|
|
|
|
}
|
2021-01-26 06:57:44 +00:00
|
|
|
.map { CollectionItem.account(.init(info: $0), .withoutNote) }
|
2021-01-25 07:42:39 +00:00
|
|
|
|
|
|
|
if let limit = limit, accounts.count >= limit {
|
|
|
|
accounts.append(.moreResults(.init(scope: .accounts)))
|
|
|
|
}
|
|
|
|
|
|
|
|
return accounts
|
2021-01-24 07:21:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
let statusesPublisher = ValueObservation.tracking(
|
|
|
|
StatusInfo.request(
|
|
|
|
StatusRecord.filter(statusIds.contains(StatusRecord.Columns.id)))
|
|
|
|
.fetchAll)
|
|
|
|
.removeDuplicates()
|
|
|
|
.publisher(in: databaseWriter)
|
2021-01-25 07:42:39 +00:00
|
|
|
.map { infos -> [CollectionItem] in
|
|
|
|
var statuses = infos.sorted {
|
2021-01-24 07:21:35 +00:00
|
|
|
statusIds.firstIndex(of: $0.record.id) ?? 0
|
|
|
|
< statusIds.firstIndex(of: $1.record.id) ?? 0
|
|
|
|
}
|
|
|
|
.map {
|
|
|
|
CollectionItem.status(
|
|
|
|
.init(info: $0),
|
|
|
|
.init(showContentToggled: $0.showContentToggled,
|
|
|
|
showAttachmentsToggled: $0.showAttachmentsToggled))
|
|
|
|
}
|
2021-01-25 07:42:39 +00:00
|
|
|
|
|
|
|
if let limit = limit, statuses.count >= limit {
|
|
|
|
statuses.append(.moreResults(.init(scope: .statuses)))
|
|
|
|
}
|
|
|
|
|
|
|
|
return statuses
|
2021-01-24 07:21:35 +00:00
|
|
|
}
|
|
|
|
|
2021-01-25 07:42:39 +00:00
|
|
|
var hashtags = results.hashtags.map(CollectionItem.tag)
|
|
|
|
|
|
|
|
if let limit = limit, hashtags.count >= limit {
|
|
|
|
hashtags.append(.moreResults(.init(scope: .tags)))
|
|
|
|
}
|
|
|
|
|
2021-01-24 07:21:35 +00:00
|
|
|
return accountsPublisher.combineLatest(statusesPublisher)
|
|
|
|
.map { accounts, statuses in
|
2021-01-31 01:43:48 +00:00
|
|
|
[.init(items: accounts, searchScope: .accounts),
|
|
|
|
.init(items: statuses, searchScope: .statuses),
|
|
|
|
.init(items: hashtags, searchScope: .tags)]
|
2021-01-25 02:10:41 +00:00
|
|
|
.filter { !$0.items.isEmpty }
|
2021-01-24 07:21:35 +00:00
|
|
|
}
|
2021-01-25 02:10:41 +00:00
|
|
|
.removeDuplicates()
|
2021-01-24 07:21:35 +00:00
|
|
|
.eraseToAnyPublisher()
|
|
|
|
}
|
|
|
|
|
2021-01-26 02:10:24 +00:00
|
|
|
func notificationsPublisher(
|
|
|
|
excludeTypes: Set<MastodonNotification.NotificationType>) -> AnyPublisher<[CollectionSection], Error> {
|
2020-10-30 07:11:24 +00:00
|
|
|
ValueObservation.tracking(
|
|
|
|
NotificationInfo.request(
|
2021-01-26 02:10:24 +00:00
|
|
|
NotificationRecord.order(NotificationRecord.Columns.id.desc)
|
|
|
|
.filter(!excludeTypes.map(\.rawValue).contains(NotificationRecord.Columns.type))).fetchAll)
|
2020-10-30 07:11:24 +00:00
|
|
|
.removeDuplicates()
|
|
|
|
.publisher(in: databaseWriter)
|
2021-01-23 06:15:52 +00:00
|
|
|
.map { [.init(items: $0.map {
|
2020-10-30 07:11:24 +00:00
|
|
|
let configuration: CollectionItem.StatusConfiguration?
|
|
|
|
|
|
|
|
if $0.record.type == .mention, let statusInfo = $0.statusInfo {
|
|
|
|
configuration = CollectionItem.StatusConfiguration(
|
|
|
|
showContentToggled: statusInfo.showContentToggled,
|
|
|
|
showAttachmentsToggled: statusInfo.showAttachmentsToggled)
|
|
|
|
} else {
|
|
|
|
configuration = nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return .notification(MastodonNotification(info: $0), configuration)
|
2021-01-23 06:15:52 +00:00
|
|
|
})] }
|
2020-10-30 07:11:24 +00:00
|
|
|
.eraseToAnyPublisher()
|
|
|
|
}
|
|
|
|
|
2020-10-29 06:03:45 +00:00
|
|
|
func conversationsPublisher() -> AnyPublisher<[Conversation], Error> {
|
|
|
|
ValueObservation.tracking(ConversationInfo.request(ConversationRecord.all()).fetchAll)
|
|
|
|
.removeDuplicates()
|
|
|
|
.publisher(in: databaseWriter)
|
|
|
|
.map {
|
|
|
|
$0.sorted { $0.lastStatusInfo.record.createdAt > $1.lastStatusInfo.record.createdAt }
|
|
|
|
.map(Conversation.init(info:))
|
|
|
|
}
|
|
|
|
.eraseToAnyPublisher()
|
|
|
|
}
|
|
|
|
|
2021-01-30 22:27:49 +00:00
|
|
|
func instancePublisher(uri: String) -> AnyPublisher<Instance, Error> {
|
|
|
|
ValueObservation.tracking(
|
|
|
|
InstanceInfo.request(InstanceRecord.filter(InstanceRecord.Columns.uri == uri)).fetchOne)
|
|
|
|
.removeDuplicates()
|
|
|
|
.publisher(in: databaseWriter)
|
|
|
|
.compactMap { $0 }
|
|
|
|
.map(Instance.init(info:))
|
|
|
|
.eraseToAnyPublisher()
|
|
|
|
}
|
|
|
|
|
2021-01-14 17:49:53 +00:00
|
|
|
func pickerEmojisPublisher() -> AnyPublisher<[Emoji], Error> {
|
2021-01-15 10:13:10 +00:00
|
|
|
ValueObservation.tracking(
|
|
|
|
Emoji.filter(Emoji.Columns.visibleInPicker == true)
|
|
|
|
.order(Emoji.Columns.shortcode.asc)
|
|
|
|
.fetchAll)
|
2021-01-14 17:49:53 +00:00
|
|
|
.removeDuplicates()
|
|
|
|
.publisher(in: databaseWriter)
|
|
|
|
.eraseToAnyPublisher()
|
|
|
|
}
|
|
|
|
|
2021-01-16 00:58:10 +00:00
|
|
|
func emojiUses(limit: Int) -> AnyPublisher<[EmojiUse], Error> {
|
|
|
|
databaseWriter.readPublisher(value: EmojiUse.all().order(EmojiUse.Columns.count.desc).limit(limit).fetchAll)
|
|
|
|
.eraseToAnyPublisher()
|
|
|
|
}
|
|
|
|
|
2020-10-27 03:01:12 +00:00
|
|
|
func lastReadId(_ markerTimeline: Marker.Timeline) -> String? {
|
|
|
|
try? databaseWriter.read {
|
|
|
|
try String.fetchOne(
|
|
|
|
$0,
|
|
|
|
LastReadIdRecord.filter(LastReadIdRecord.Columns.markerTimeline == markerTimeline.rawValue)
|
|
|
|
.select(LastReadIdRecord.Columns.id))
|
|
|
|
}
|
|
|
|
}
|
2020-08-18 05:13:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private extension ContentDatabase {
|
2020-10-27 03:01:12 +00:00
|
|
|
static let cleanAfterLastReadIdCount = 40
|
2020-12-02 19:39:42 +00:00
|
|
|
|
|
|
|
static let ephemeralTimelines = NSCountedSet()
|
|
|
|
|
2020-11-09 03:07:23 +00:00
|
|
|
static func fileURL(id: Identity.Id, appGroup: String) throws -> URL {
|
|
|
|
try FileManager.default.databaseDirectoryURL(name: id.uuidString, appGroup: appGroup)
|
2020-09-03 01:14:33 +00:00
|
|
|
}
|
|
|
|
|
2020-11-03 04:24:56 +00:00
|
|
|
// swiftlint:disable:next function_body_length
|
2020-10-27 03:01:12 +00:00
|
|
|
static func clean(_ databaseWriter: DatabaseWriter,
|
|
|
|
useHomeTimelineLastReadId: Bool,
|
|
|
|
useNotificationsLastReadId: Bool) throws {
|
2020-09-29 06:35:11 +00:00
|
|
|
try databaseWriter.write {
|
2020-11-03 04:24:56 +00:00
|
|
|
let notificationAccountIds: [Account.Id]
|
|
|
|
let notificationStatusIds: [Status.Id]
|
|
|
|
|
2020-10-29 06:03:45 +00:00
|
|
|
try ConversationRecord.deleteAll($0)
|
2021-01-16 08:41:52 +00:00
|
|
|
try StatusAncestorJoin.deleteAll($0)
|
|
|
|
try StatusDescendantJoin.deleteAll($0)
|
2020-10-29 06:03:45 +00:00
|
|
|
|
2020-11-03 04:24:56 +00:00
|
|
|
if useNotificationsLastReadId {
|
|
|
|
var notificationIds = try MastodonNotification.Id.fetchAll(
|
|
|
|
$0,
|
|
|
|
NotificationRecord.select(NotificationRecord.Columns.id)
|
|
|
|
.order(NotificationRecord.Columns.id.desc))
|
|
|
|
|
|
|
|
if let lastReadId = try MastodonNotification.Id.fetchOne(
|
|
|
|
$0,
|
|
|
|
LastReadIdRecord.filter(
|
|
|
|
LastReadIdRecord.Columns.markerTimeline == Marker.Timeline.notifications.rawValue)
|
|
|
|
.select(LastReadIdRecord.Columns.id))
|
|
|
|
?? notificationIds.first,
|
|
|
|
let index = notificationIds.firstIndex(of: lastReadId) {
|
|
|
|
notificationIds = Array(notificationIds.prefix(index + Self.cleanAfterLastReadIdCount))
|
|
|
|
}
|
|
|
|
|
|
|
|
try NotificationRecord.filter(!notificationIds.contains(NotificationRecord.Columns.id)).deleteAll($0)
|
|
|
|
notificationAccountIds = try Account.Id.fetchAll(
|
|
|
|
$0,
|
|
|
|
NotificationRecord.select(NotificationRecord.Columns.accountId))
|
|
|
|
notificationStatusIds = try Status.Id.fetchAll(
|
|
|
|
$0,
|
|
|
|
NotificationRecord.filter(
|
|
|
|
NotificationRecord.Columns.statusId != nil)
|
|
|
|
.select(NotificationRecord.Columns.statusId))
|
|
|
|
} else {
|
|
|
|
try NotificationRecord.deleteAll($0)
|
|
|
|
notificationAccountIds = []
|
|
|
|
notificationStatusIds = []
|
|
|
|
}
|
2020-10-30 07:11:24 +00:00
|
|
|
|
2020-10-27 03:01:12 +00:00
|
|
|
if useHomeTimelineLastReadId {
|
|
|
|
try TimelineRecord.filter(TimelineRecord.Columns.id != Timeline.home.id).deleteAll($0)
|
|
|
|
var statusIds = try Status.Id.fetchAll(
|
|
|
|
$0,
|
|
|
|
TimelineStatusJoin.select(TimelineStatusJoin.Columns.statusId)
|
|
|
|
.order(TimelineStatusJoin.Columns.statusId.desc))
|
|
|
|
|
|
|
|
if let lastReadId = try Status.Id.fetchOne(
|
|
|
|
$0,
|
|
|
|
LastReadIdRecord.filter(LastReadIdRecord.Columns.markerTimeline == Marker.Timeline.home.rawValue)
|
|
|
|
.select(LastReadIdRecord.Columns.id))
|
|
|
|
?? statusIds.first,
|
|
|
|
let index = statusIds.firstIndex(of: lastReadId) {
|
|
|
|
statusIds = Array(statusIds.prefix(index + Self.cleanAfterLastReadIdCount))
|
|
|
|
}
|
|
|
|
|
2020-11-03 04:24:56 +00:00
|
|
|
statusIds += notificationStatusIds
|
2020-10-27 03:01:12 +00:00
|
|
|
statusIds += try Status.Id.fetchAll(
|
|
|
|
$0,
|
|
|
|
StatusRecord.filter(statusIds.contains(StatusRecord.Columns.id)
|
|
|
|
&& StatusRecord.Columns.reblogId != nil)
|
|
|
|
.select(StatusRecord.Columns.reblogId))
|
2020-11-03 04:24:56 +00:00
|
|
|
try StatusRecord.filter(!statusIds.contains(StatusRecord.Columns.id)).deleteAll($0)
|
2020-10-27 03:01:12 +00:00
|
|
|
var accountIds = try Account.Id.fetchAll($0, StatusRecord.select(StatusRecord.Columns.accountId))
|
2020-11-03 04:24:56 +00:00
|
|
|
accountIds += notificationAccountIds
|
2020-10-27 03:01:12 +00:00
|
|
|
accountIds += try Account.Id.fetchAll(
|
|
|
|
$0,
|
|
|
|
AccountRecord.filter(accountIds.contains(AccountRecord.Columns.id)
|
|
|
|
&& AccountRecord.Columns.movedId != nil)
|
|
|
|
.select(AccountRecord.Columns.movedId))
|
|
|
|
try AccountRecord.filter(!accountIds.contains(AccountRecord.Columns.id)).deleteAll($0)
|
|
|
|
} else {
|
|
|
|
try TimelineRecord.deleteAll($0)
|
2020-11-03 04:24:56 +00:00
|
|
|
try StatusRecord.filter(!notificationStatusIds.contains(StatusRecord.Columns.id)).deleteAll($0)
|
|
|
|
try AccountRecord.filter(!notificationAccountIds.contains(AccountRecord.Columns.id)).deleteAll($0)
|
2020-10-27 03:01:12 +00:00
|
|
|
}
|
2020-09-29 06:35:11 +00:00
|
|
|
}
|
2020-09-17 03:16:32 +00:00
|
|
|
}
|
2020-08-18 05:13:37 +00:00
|
|
|
}
|
2020-10-27 03:01:12 +00:00
|
|
|
// swiftlint:enable file_length
|