mirror of
https://github.com/metabolist/metatext.git
synced 2024-11-22 00:01:00 +00:00
0xDEAD10CC mitigation
This commit is contained in:
parent
ac2d1fb805
commit
58333b558b
5 changed files with 99 additions and 124 deletions
|
@ -51,9 +51,7 @@ public extension ContentDatabase {
|
||||||
}
|
}
|
||||||
|
|
||||||
func insert(status: Status) -> AnyPublisher<Never, Error> {
|
func insert(status: Status) -> AnyPublisher<Never, Error> {
|
||||||
databaseWriter.writePublisher(updates: status.save)
|
databaseWriter.mutatingPublisher(updates: status.save)
|
||||||
.ignoreOutput()
|
|
||||||
.eraseToAnyPublisher()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// swiftlint:disable:next function_body_length
|
// swiftlint:disable:next function_body_length
|
||||||
|
@ -61,7 +59,7 @@ public extension ContentDatabase {
|
||||||
statuses: [Status],
|
statuses: [Status],
|
||||||
timeline: Timeline,
|
timeline: Timeline,
|
||||||
loadMoreAndDirection: (LoadMore, LoadMore.Direction)? = nil) -> AnyPublisher<Never, Error> {
|
loadMoreAndDirection: (LoadMore, LoadMore.Direction)? = nil) -> AnyPublisher<Never, Error> {
|
||||||
databaseWriter.writePublisher {
|
databaseWriter.mutatingPublisher {
|
||||||
let timelineRecord = TimelineRecord(timeline: timeline)
|
let timelineRecord = TimelineRecord(timeline: timeline)
|
||||||
|
|
||||||
try timelineRecord.save($0)
|
try timelineRecord.save($0)
|
||||||
|
@ -122,12 +120,10 @@ public extension ContentDatabase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.ignoreOutput()
|
|
||||||
.eraseToAnyPublisher()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func insert(context: Context, parentId: Status.Id) -> AnyPublisher<Never, Error> {
|
func insert(context: Context, parentId: Status.Id) -> AnyPublisher<Never, Error> {
|
||||||
databaseWriter.writePublisher {
|
databaseWriter.mutatingPublisher {
|
||||||
for (index, status) in context.ancestors.enumerated() {
|
for (index, status) in context.ancestors.enumerated() {
|
||||||
try status.save($0)
|
try status.save($0)
|
||||||
try StatusAncestorJoin(parentId: parentId, statusId: status.id, order: index).save($0)
|
try StatusAncestorJoin(parentId: parentId, statusId: status.id, order: index).save($0)
|
||||||
|
@ -148,12 +144,10 @@ public extension ContentDatabase {
|
||||||
&& !context.descendants.map(\.id).contains(StatusDescendantJoin.Columns.statusId))
|
&& !context.descendants.map(\.id).contains(StatusDescendantJoin.Columns.statusId))
|
||||||
.deleteAll($0)
|
.deleteAll($0)
|
||||||
}
|
}
|
||||||
.ignoreOutput()
|
|
||||||
.eraseToAnyPublisher()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func insert(pinnedStatuses: [Status], accountId: Account.Id) -> AnyPublisher<Never, Error> {
|
func insert(pinnedStatuses: [Status], accountId: Account.Id) -> AnyPublisher<Never, Error> {
|
||||||
databaseWriter.writePublisher {
|
databaseWriter.mutatingPublisher {
|
||||||
for (index, status) in pinnedStatuses.enumerated() {
|
for (index, status) in pinnedStatuses.enumerated() {
|
||||||
try status.save($0)
|
try status.save($0)
|
||||||
try AccountPinnedStatusJoin(accountId: accountId, statusId: status.id, order: index).save($0)
|
try AccountPinnedStatusJoin(accountId: accountId, statusId: status.id, order: index).save($0)
|
||||||
|
@ -164,12 +158,10 @@ public extension ContentDatabase {
|
||||||
&& !pinnedStatuses.map(\.id).contains(AccountPinnedStatusJoin.Columns.statusId))
|
&& !pinnedStatuses.map(\.id).contains(AccountPinnedStatusJoin.Columns.statusId))
|
||||||
.deleteAll($0)
|
.deleteAll($0)
|
||||||
}
|
}
|
||||||
.ignoreOutput()
|
|
||||||
.eraseToAnyPublisher()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func toggleShowContent(id: Status.Id) -> AnyPublisher<Never, Error> {
|
func toggleShowContent(id: Status.Id) -> AnyPublisher<Never, Error> {
|
||||||
databaseWriter.writePublisher {
|
databaseWriter.mutatingPublisher {
|
||||||
if let toggle = try StatusShowContentToggle
|
if let toggle = try StatusShowContentToggle
|
||||||
.filter(StatusShowContentToggle.Columns.statusId == id)
|
.filter(StatusShowContentToggle.Columns.statusId == id)
|
||||||
.fetchOne($0) {
|
.fetchOne($0) {
|
||||||
|
@ -178,12 +170,10 @@ public extension ContentDatabase {
|
||||||
try StatusShowContentToggle(statusId: id).save($0)
|
try StatusShowContentToggle(statusId: id).save($0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.ignoreOutput()
|
|
||||||
.eraseToAnyPublisher()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func toggleShowAttachments(id: Status.Id) -> AnyPublisher<Never, Error> {
|
func toggleShowAttachments(id: Status.Id) -> AnyPublisher<Never, Error> {
|
||||||
databaseWriter.writePublisher {
|
databaseWriter.mutatingPublisher {
|
||||||
if let toggle = try StatusShowAttachmentsToggle
|
if let toggle = try StatusShowAttachmentsToggle
|
||||||
.filter(StatusShowAttachmentsToggle.Columns.statusId == id)
|
.filter(StatusShowAttachmentsToggle.Columns.statusId == id)
|
||||||
.fetchOne($0) {
|
.fetchOne($0) {
|
||||||
|
@ -192,23 +182,19 @@ public extension ContentDatabase {
|
||||||
try StatusShowAttachmentsToggle(statusId: id).save($0)
|
try StatusShowAttachmentsToggle(statusId: id).save($0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.ignoreOutput()
|
|
||||||
.eraseToAnyPublisher()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func expand(ids: Set<Status.Id>) -> AnyPublisher<Never, Error> {
|
func expand(ids: Set<Status.Id>) -> AnyPublisher<Never, Error> {
|
||||||
databaseWriter.writePublisher {
|
databaseWriter.mutatingPublisher {
|
||||||
for id in ids {
|
for id in ids {
|
||||||
try StatusShowContentToggle(statusId: id).save($0)
|
try StatusShowContentToggle(statusId: id).save($0)
|
||||||
try StatusShowAttachmentsToggle(statusId: id).save($0)
|
try StatusShowAttachmentsToggle(statusId: id).save($0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.ignoreOutput()
|
|
||||||
.eraseToAnyPublisher()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func collapse(ids: Set<Status.Id>) -> AnyPublisher<Never, Error> {
|
func collapse(ids: Set<Status.Id>) -> AnyPublisher<Never, Error> {
|
||||||
databaseWriter.writePublisher {
|
databaseWriter.mutatingPublisher {
|
||||||
try StatusShowContentToggle
|
try StatusShowContentToggle
|
||||||
.filter(ids.contains(StatusShowContentToggle.Columns.statusId))
|
.filter(ids.contains(StatusShowContentToggle.Columns.statusId))
|
||||||
.deleteAll($0)
|
.deleteAll($0)
|
||||||
|
@ -216,29 +202,23 @@ public extension ContentDatabase {
|
||||||
.filter(ids.contains(StatusShowContentToggle.Columns.statusId))
|
.filter(ids.contains(StatusShowContentToggle.Columns.statusId))
|
||||||
.deleteAll($0)
|
.deleteAll($0)
|
||||||
}
|
}
|
||||||
.ignoreOutput()
|
|
||||||
.eraseToAnyPublisher()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func update(id: Status.Id, poll: Poll) -> AnyPublisher<Never, Error> {
|
func update(id: Status.Id, poll: Poll) -> AnyPublisher<Never, Error> {
|
||||||
databaseWriter.writePublisher {
|
databaseWriter.mutatingPublisher {
|
||||||
let data = try StatusRecord.databaseJSONEncoder(for: StatusRecord.Columns.poll.name).encode(poll)
|
let data = try StatusRecord.databaseJSONEncoder(for: StatusRecord.Columns.poll.name).encode(poll)
|
||||||
|
|
||||||
try StatusRecord.filter(StatusRecord.Columns.id == id)
|
try StatusRecord.filter(StatusRecord.Columns.id == id)
|
||||||
.updateAll($0, StatusRecord.Columns.poll.set(to: data))
|
.updateAll($0, StatusRecord.Columns.poll.set(to: data))
|
||||||
}
|
}
|
||||||
.ignoreOutput()
|
|
||||||
.eraseToAnyPublisher()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func delete(id: Status.Id) -> AnyPublisher<Never, Error> {
|
func delete(id: Status.Id) -> AnyPublisher<Never, Error> {
|
||||||
databaseWriter.writePublisher(updates: StatusRecord.filter(StatusRecord.Columns.id == id).deleteAll)
|
databaseWriter.mutatingPublisher(updates: StatusRecord.filter(StatusRecord.Columns.id == id).deleteAll)
|
||||||
.ignoreOutput()
|
|
||||||
.eraseToAnyPublisher()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func unfollow(id: Account.Id) -> AnyPublisher<Never, Error> {
|
func unfollow(id: Account.Id) -> AnyPublisher<Never, Error> {
|
||||||
databaseWriter.writePublisher {
|
databaseWriter.mutatingPublisher {
|
||||||
let statusIds = try Status.Id.fetchAll(
|
let statusIds = try Status.Id.fetchAll(
|
||||||
$0,
|
$0,
|
||||||
StatusRecord.filter(StatusRecord.Columns.accountId == id).select(StatusRecord.Columns.id))
|
StatusRecord.filter(StatusRecord.Columns.accountId == id).select(StatusRecord.Columns.id))
|
||||||
|
@ -248,27 +228,21 @@ public extension ContentDatabase {
|
||||||
&& statusIds.contains(TimelineStatusJoin.Columns.statusId))
|
&& statusIds.contains(TimelineStatusJoin.Columns.statusId))
|
||||||
.deleteAll($0)
|
.deleteAll($0)
|
||||||
}
|
}
|
||||||
.ignoreOutput()
|
|
||||||
.eraseToAnyPublisher()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func mute(id: Account.Id) -> AnyPublisher<Never, Error> {
|
func mute(id: Account.Id) -> AnyPublisher<Never, Error> {
|
||||||
databaseWriter.writePublisher {
|
databaseWriter.mutatingPublisher {
|
||||||
try StatusRecord.filter(StatusRecord.Columns.accountId == id).deleteAll($0)
|
try StatusRecord.filter(StatusRecord.Columns.accountId == id).deleteAll($0)
|
||||||
try NotificationRecord.filter(NotificationRecord.Columns.accountId == id).deleteAll($0)
|
try NotificationRecord.filter(NotificationRecord.Columns.accountId == id).deleteAll($0)
|
||||||
}
|
}
|
||||||
.ignoreOutput()
|
|
||||||
.eraseToAnyPublisher()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func block(id: Account.Id) -> AnyPublisher<Never, Error> {
|
func block(id: Account.Id) -> AnyPublisher<Never, Error> {
|
||||||
databaseWriter.writePublisher(updates: AccountRecord.filter(AccountRecord.Columns.id == id).deleteAll)
|
databaseWriter.mutatingPublisher(updates: AccountRecord.filter(AccountRecord.Columns.id == id).deleteAll)
|
||||||
.ignoreOutput()
|
|
||||||
.eraseToAnyPublisher()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func insert(accounts: [Account], listId: AccountList.Id? = nil) -> AnyPublisher<Never, Error> {
|
func insert(accounts: [Account], listId: AccountList.Id? = nil) -> AnyPublisher<Never, Error> {
|
||||||
databaseWriter.writePublisher {
|
databaseWriter.mutatingPublisher {
|
||||||
var order: Int?
|
var order: Int?
|
||||||
|
|
||||||
if let listId = listId {
|
if let listId = listId {
|
||||||
|
@ -290,22 +264,18 @@ public extension ContentDatabase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.ignoreOutput()
|
|
||||||
.eraseToAnyPublisher()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func remove(id: Account.Id, from listId: AccountList.Id) -> AnyPublisher<Never, Error> {
|
func remove(id: Account.Id, from listId: AccountList.Id) -> AnyPublisher<Never, Error> {
|
||||||
databaseWriter.writePublisher(
|
databaseWriter.mutatingPublisher(
|
||||||
updates: AccountListJoin.filter(
|
updates: AccountListJoin.filter(
|
||||||
AccountListJoin.Columns.accountId == id
|
AccountListJoin.Columns.accountId == id
|
||||||
&& AccountListJoin.Columns.accountListId == listId)
|
&& AccountListJoin.Columns.accountListId == listId)
|
||||||
.deleteAll)
|
.deleteAll)
|
||||||
.ignoreOutput()
|
|
||||||
.eraseToAnyPublisher()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func insert(identityProofs: [IdentityProof], id: Account.Id) -> AnyPublisher<Never, Error> {
|
func insert(identityProofs: [IdentityProof], id: Account.Id) -> AnyPublisher<Never, Error> {
|
||||||
databaseWriter.writePublisher {
|
databaseWriter.mutatingPublisher {
|
||||||
for identityProof in identityProofs {
|
for identityProof in identityProofs {
|
||||||
try IdentityProofRecord(
|
try IdentityProofRecord(
|
||||||
accountId: id,
|
accountId: id,
|
||||||
|
@ -317,12 +287,10 @@ public extension ContentDatabase {
|
||||||
.save($0)
|
.save($0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.ignoreOutput()
|
|
||||||
.eraseToAnyPublisher()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func insert(featuredTags: [FeaturedTag], id: Account.Id) -> AnyPublisher<Never, Error> {
|
func insert(featuredTags: [FeaturedTag], id: Account.Id) -> AnyPublisher<Never, Error> {
|
||||||
databaseWriter.writePublisher {
|
databaseWriter.mutatingPublisher {
|
||||||
for featuredTag in featuredTags {
|
for featuredTag in featuredTags {
|
||||||
try FeaturedTagRecord(
|
try FeaturedTagRecord(
|
||||||
id: featuredTag.id,
|
id: featuredTag.id,
|
||||||
|
@ -334,22 +302,18 @@ public extension ContentDatabase {
|
||||||
.save($0)
|
.save($0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.ignoreOutput()
|
|
||||||
.eraseToAnyPublisher()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func insert(relationships: [Relationship]) -> AnyPublisher<Never, Error> {
|
func insert(relationships: [Relationship]) -> AnyPublisher<Never, Error> {
|
||||||
databaseWriter.writePublisher {
|
databaseWriter.mutatingPublisher {
|
||||||
for relationship in relationships {
|
for relationship in relationships {
|
||||||
try relationship.save($0)
|
try relationship.save($0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.ignoreOutput()
|
|
||||||
.eraseToAnyPublisher()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func setLists(_ lists: [List]) -> AnyPublisher<Never, Error> {
|
func setLists(_ lists: [List]) -> AnyPublisher<Never, Error> {
|
||||||
databaseWriter.writePublisher {
|
databaseWriter.mutatingPublisher {
|
||||||
for list in lists {
|
for list in lists {
|
||||||
try TimelineRecord(timeline: Timeline.list(list)).save($0)
|
try TimelineRecord(timeline: Timeline.list(list)).save($0)
|
||||||
}
|
}
|
||||||
|
@ -359,86 +323,66 @@ public extension ContentDatabase {
|
||||||
&& TimelineRecord.Columns.listTitle != nil)
|
&& TimelineRecord.Columns.listTitle != nil)
|
||||||
.deleteAll($0)
|
.deleteAll($0)
|
||||||
}
|
}
|
||||||
.ignoreOutput()
|
|
||||||
.eraseToAnyPublisher()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func createList(_ list: List) -> AnyPublisher<Never, Error> {
|
func createList(_ list: List) -> AnyPublisher<Never, Error> {
|
||||||
databaseWriter.writePublisher(updates: TimelineRecord(timeline: Timeline.list(list)).save)
|
databaseWriter.mutatingPublisher(updates: TimelineRecord(timeline: Timeline.list(list)).save)
|
||||||
.ignoreOutput()
|
|
||||||
.eraseToAnyPublisher()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func deleteList(id: List.Id) -> AnyPublisher<Never, Error> {
|
func deleteList(id: List.Id) -> AnyPublisher<Never, Error> {
|
||||||
databaseWriter.writePublisher(updates: TimelineRecord.filter(TimelineRecord.Columns.listId == id).deleteAll)
|
databaseWriter.mutatingPublisher(updates: TimelineRecord.filter(TimelineRecord.Columns.listId == id).deleteAll)
|
||||||
.ignoreOutput()
|
|
||||||
.eraseToAnyPublisher()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func setFilters(_ filters: [Filter]) -> AnyPublisher<Never, Error> {
|
func setFilters(_ filters: [Filter]) -> AnyPublisher<Never, Error> {
|
||||||
databaseWriter.writePublisher {
|
databaseWriter.mutatingPublisher {
|
||||||
for filter in filters {
|
for filter in filters {
|
||||||
try filter.save($0)
|
try filter.save($0)
|
||||||
}
|
}
|
||||||
|
|
||||||
try Filter.filter(!filters.map(\.id).contains(Filter.Columns.id)).deleteAll($0)
|
try Filter.filter(!filters.map(\.id).contains(Filter.Columns.id)).deleteAll($0)
|
||||||
}
|
}
|
||||||
.ignoreOutput()
|
|
||||||
.eraseToAnyPublisher()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func createFilter(_ filter: Filter) -> AnyPublisher<Never, Error> {
|
func createFilter(_ filter: Filter) -> AnyPublisher<Never, Error> {
|
||||||
databaseWriter.writePublisher(updates: filter.save)
|
databaseWriter.mutatingPublisher(updates: filter.save)
|
||||||
.ignoreOutput()
|
|
||||||
.eraseToAnyPublisher()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func deleteFilter(id: Filter.Id) -> AnyPublisher<Never, Error> {
|
func deleteFilter(id: Filter.Id) -> AnyPublisher<Never, Error> {
|
||||||
databaseWriter.writePublisher(updates: Filter.filter(Filter.Columns.id == id).deleteAll)
|
databaseWriter.mutatingPublisher(updates: Filter.filter(Filter.Columns.id == id).deleteAll)
|
||||||
.ignoreOutput()
|
|
||||||
.eraseToAnyPublisher()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func setLastReadId(_ id: String, timelineId: Timeline.Id) -> AnyPublisher<Never, Error> {
|
func setLastReadId(_ id: String, timelineId: Timeline.Id) -> AnyPublisher<Never, Error> {
|
||||||
databaseWriter.writePublisher(updates: LastReadIdRecord(timelineId: timelineId, id: id).save)
|
databaseWriter.mutatingPublisher(updates: LastReadIdRecord(timelineId: timelineId, id: id).save)
|
||||||
.ignoreOutput()
|
|
||||||
.eraseToAnyPublisher()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func insert(notifications: [MastodonNotification]) -> AnyPublisher<Never, Error> {
|
func insert(notifications: [MastodonNotification]) -> AnyPublisher<Never, Error> {
|
||||||
databaseWriter.writePublisher {
|
databaseWriter.mutatingPublisher {
|
||||||
for notification in notifications {
|
for notification in notifications {
|
||||||
try notification.save($0)
|
try notification.save($0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.ignoreOutput()
|
|
||||||
.eraseToAnyPublisher()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func insert(conversations: [Conversation]) -> AnyPublisher<Never, Error> {
|
func insert(conversations: [Conversation]) -> AnyPublisher<Never, Error> {
|
||||||
databaseWriter.writePublisher {
|
databaseWriter.mutatingPublisher {
|
||||||
for conversation in conversations {
|
for conversation in conversations {
|
||||||
try conversation.save($0)
|
try conversation.save($0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.ignoreOutput()
|
|
||||||
.eraseToAnyPublisher()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func update(emojis: [Emoji]) -> AnyPublisher<Never, Error> {
|
func update(emojis: [Emoji]) -> AnyPublisher<Never, Error> {
|
||||||
databaseWriter.writePublisher {
|
databaseWriter.mutatingPublisher {
|
||||||
for emoji in emojis {
|
for emoji in emojis {
|
||||||
try emoji.save($0)
|
try emoji.save($0)
|
||||||
}
|
}
|
||||||
|
|
||||||
try Emoji.filter(!emojis.map(\.shortcode).contains(Emoji.Columns.shortcode)).deleteAll($0)
|
try Emoji.filter(!emojis.map(\.shortcode).contains(Emoji.Columns.shortcode)).deleteAll($0)
|
||||||
}
|
}
|
||||||
.ignoreOutput()
|
|
||||||
.eraseToAnyPublisher()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateUse(emoji: String, system: Bool) -> AnyPublisher<Never, Error> {
|
func updateUse(emoji: String, system: Bool) -> AnyPublisher<Never, Error> {
|
||||||
databaseWriter.writePublisher {
|
databaseWriter.mutatingPublisher {
|
||||||
let count = try Int.fetchOne(
|
let count = try Int.fetchOne(
|
||||||
$0,
|
$0,
|
||||||
EmojiUse.filter(EmojiUse.Columns.system == system && EmojiUse.Columns.emoji == emoji)
|
EmojiUse.filter(EmojiUse.Columns.system == system && EmojiUse.Columns.emoji == emoji)
|
||||||
|
@ -446,24 +390,20 @@ public extension ContentDatabase {
|
||||||
|
|
||||||
try EmojiUse(emoji: emoji, system: system, lastUse: Date(), count: (count ?? 0) + 1).save($0)
|
try EmojiUse(emoji: emoji, system: system, lastUse: Date(), count: (count ?? 0) + 1).save($0)
|
||||||
}
|
}
|
||||||
.ignoreOutput()
|
|
||||||
.eraseToAnyPublisher()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func update(announcements: [Announcement]) -> AnyPublisher<Never, Error> {
|
func update(announcements: [Announcement]) -> AnyPublisher<Never, Error> {
|
||||||
databaseWriter.writePublisher {
|
databaseWriter.mutatingPublisher {
|
||||||
for announcement in announcements {
|
for announcement in announcements {
|
||||||
try announcement.save($0)
|
try announcement.save($0)
|
||||||
}
|
}
|
||||||
|
|
||||||
try Announcement.filter(!announcements.map(\.id).contains(Announcement.Columns.id)).deleteAll($0)
|
try Announcement.filter(!announcements.map(\.id).contains(Announcement.Columns.id)).deleteAll($0)
|
||||||
}
|
}
|
||||||
.ignoreOutput()
|
|
||||||
.eraseToAnyPublisher()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func insert(results: Results) -> AnyPublisher<Never, Error> {
|
func insert(results: Results) -> AnyPublisher<Never, Error> {
|
||||||
databaseWriter.writePublisher {
|
databaseWriter.mutatingPublisher {
|
||||||
for account in results.accounts {
|
for account in results.accounts {
|
||||||
try account.save($0)
|
try account.save($0)
|
||||||
}
|
}
|
||||||
|
@ -472,14 +412,10 @@ public extension ContentDatabase {
|
||||||
try status.save($0)
|
try status.save($0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.ignoreOutput()
|
|
||||||
.eraseToAnyPublisher()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func insert(instance: Instance) -> AnyPublisher<Never, Error> {
|
func insert(instance: Instance) -> AnyPublisher<Never, Error> {
|
||||||
databaseWriter.writePublisher(updates: instance.save)
|
databaseWriter.mutatingPublisher(updates: instance.save)
|
||||||
.ignoreOutput()
|
|
||||||
.eraseToAnyPublisher()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func timelinePublisher(_ timeline: Timeline) -> AnyPublisher<[CollectionSection], Error> {
|
func timelinePublisher(_ timeline: Timeline) -> AnyPublisher<[CollectionSection], Error> {
|
||||||
|
@ -697,7 +633,6 @@ public extension ContentDatabase {
|
||||||
|
|
||||||
private extension ContentDatabase {
|
private extension ContentDatabase {
|
||||||
static let cleanAfterLastReadIdCount = 40
|
static let cleanAfterLastReadIdCount = 40
|
||||||
|
|
||||||
static let ephemeralTimelines = NSCountedSet()
|
static let ephemeralTimelines = NSCountedSet()
|
||||||
|
|
||||||
static func fileURL(id: Identity.Id, appGroup: String) throws -> URL {
|
static func fileURL(id: Identity.Id, appGroup: String) throws -> URL {
|
||||||
|
|
|
@ -20,6 +20,7 @@ extension DatabasePool {
|
||||||
|
|
||||||
configuration.busyMode = .timeout(5)
|
configuration.busyMode = .timeout(5)
|
||||||
configuration.defaultTransactionKind = .immediate
|
configuration.defaultTransactionKind = .immediate
|
||||||
|
configuration.observesSuspensionNotifications = true
|
||||||
configuration.prepareDatabase { db in
|
configuration.prepareDatabase { db in
|
||||||
try db.usePassphrase(passphrase())
|
try db.usePassphrase(passphrase())
|
||||||
try db.execute(sql: "PRAGMA cipher_plaintext_header_size = 32")
|
try db.execute(sql: "PRAGMA cipher_plaintext_header_size = 32")
|
||||||
|
|
29
DB/Sources/DB/Extensions/DatabaseWriter+Extensions.swift
Normal file
29
DB/Sources/DB/Extensions/DatabaseWriter+Extensions.swift
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
// Copyright © 2021 Metabolist. All rights reserved.
|
||||||
|
|
||||||
|
import Combine
|
||||||
|
import Foundation
|
||||||
|
import GRDB
|
||||||
|
|
||||||
|
// swiftlint:disable:next line_length
|
||||||
|
// https://github.com/groue/GRDB.swift/blob/master/Documentation/SharingADatabase.md#how-to-limit-the-0xdead10cc-exception
|
||||||
|
|
||||||
|
extension DatabaseWriter {
|
||||||
|
func mutatingPublisher<Output>(updates: @escaping (Database) throws -> Output) -> AnyPublisher<Never, Error> {
|
||||||
|
let publisher = writePublisher(updates: updates)
|
||||||
|
|
||||||
|
return publisher
|
||||||
|
.tryCatch { error -> AnyPublisher<Output, Error> in
|
||||||
|
if let databaseError = error as? DatabaseError, databaseError.isInterruptionError {
|
||||||
|
return NotificationCenter.default.publisher(for: Database.resumeNotification)
|
||||||
|
.timeout(.seconds(1), scheduler: DispatchQueue.global())
|
||||||
|
.flatMap { _ in publisher }
|
||||||
|
.eraseToAnyPublisher()
|
||||||
|
} else {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.retry(1)
|
||||||
|
.ignoreOutput()
|
||||||
|
.eraseToAnyPublisher()
|
||||||
|
}
|
||||||
|
}
|
|
@ -32,7 +32,7 @@ public struct IdentityDatabase {
|
||||||
|
|
||||||
public extension IdentityDatabase {
|
public extension IdentityDatabase {
|
||||||
func createIdentity(id: Identity.Id, url: URL, authenticated: Bool, pending: Bool) -> AnyPublisher<Never, Error> {
|
func createIdentity(id: Identity.Id, url: URL, authenticated: Bool, pending: Bool) -> AnyPublisher<Never, Error> {
|
||||||
databaseWriter.writePublisher(
|
databaseWriter.mutatingPublisher(
|
||||||
updates: IdentityRecord(
|
updates: IdentityRecord(
|
||||||
id: id,
|
id: id,
|
||||||
url: url,
|
url: url,
|
||||||
|
@ -44,28 +44,22 @@ public extension IdentityDatabase {
|
||||||
lastRegisteredDeviceToken: nil,
|
lastRegisteredDeviceToken: nil,
|
||||||
pushSubscriptionAlerts: .initial)
|
pushSubscriptionAlerts: .initial)
|
||||||
.save)
|
.save)
|
||||||
.ignoreOutput()
|
|
||||||
.eraseToAnyPublisher()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func deleteIdentity(id: Identity.Id) -> AnyPublisher<Never, Error> {
|
func deleteIdentity(id: Identity.Id) -> AnyPublisher<Never, Error> {
|
||||||
databaseWriter.writePublisher(updates: IdentityRecord.filter(IdentityRecord.Columns.id == id).deleteAll)
|
databaseWriter.mutatingPublisher(updates: IdentityRecord.filter(IdentityRecord.Columns.id == id).deleteAll)
|
||||||
.ignoreOutput()
|
|
||||||
.eraseToAnyPublisher()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateLastUsedAt(id: Identity.Id) -> AnyPublisher<Never, Error> {
|
func updateLastUsedAt(id: Identity.Id) -> AnyPublisher<Never, Error> {
|
||||||
databaseWriter.writePublisher {
|
databaseWriter.mutatingPublisher {
|
||||||
try IdentityRecord
|
try IdentityRecord
|
||||||
.filter(IdentityRecord.Columns.id == id)
|
.filter(IdentityRecord.Columns.id == id)
|
||||||
.updateAll($0, IdentityRecord.Columns.lastUsedAt.set(to: Date()))
|
.updateAll($0, IdentityRecord.Columns.lastUsedAt.set(to: Date()))
|
||||||
}
|
}
|
||||||
.ignoreOutput()
|
|
||||||
.eraseToAnyPublisher()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateInstance(_ instance: Instance, id: Identity.Id) -> AnyPublisher<Never, Error> {
|
func updateInstance(_ instance: Instance, id: Identity.Id) -> AnyPublisher<Never, Error> {
|
||||||
databaseWriter.writePublisher {
|
databaseWriter.mutatingPublisher {
|
||||||
try Identity.Instance(
|
try Identity.Instance(
|
||||||
uri: instance.uri,
|
uri: instance.uri,
|
||||||
streamingAPI: instance.urls.streamingApi,
|
streamingAPI: instance.urls.streamingApi,
|
||||||
|
@ -78,12 +72,10 @@ public extension IdentityDatabase {
|
||||||
.filter(IdentityRecord.Columns.id == id)
|
.filter(IdentityRecord.Columns.id == id)
|
||||||
.updateAll($0, IdentityRecord.Columns.instanceURI.set(to: instance.uri))
|
.updateAll($0, IdentityRecord.Columns.instanceURI.set(to: instance.uri))
|
||||||
}
|
}
|
||||||
.ignoreOutput()
|
|
||||||
.eraseToAnyPublisher()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateAccount(_ account: Account, id: Identity.Id) -> AnyPublisher<Never, Error> {
|
func updateAccount(_ account: Account, id: Identity.Id) -> AnyPublisher<Never, Error> {
|
||||||
databaseWriter.writePublisher(
|
databaseWriter.mutatingPublisher(
|
||||||
updates: Identity.Account(
|
updates: Identity.Account(
|
||||||
id: account.id,
|
id: account.id,
|
||||||
identityId: id,
|
identityId: id,
|
||||||
|
@ -97,22 +89,18 @@ public extension IdentityDatabase {
|
||||||
emojis: account.emojis,
|
emojis: account.emojis,
|
||||||
followRequestCount: account.source?.followRequestsCount ?? 0)
|
followRequestCount: account.source?.followRequestsCount ?? 0)
|
||||||
.save)
|
.save)
|
||||||
.ignoreOutput()
|
|
||||||
.eraseToAnyPublisher()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func confirmIdentity(id: Identity.Id) -> AnyPublisher<Never, Error> {
|
func confirmIdentity(id: Identity.Id) -> AnyPublisher<Never, Error> {
|
||||||
databaseWriter.writePublisher {
|
databaseWriter.mutatingPublisher {
|
||||||
try IdentityRecord
|
try IdentityRecord
|
||||||
.filter(IdentityRecord.Columns.id == id)
|
.filter(IdentityRecord.Columns.id == id)
|
||||||
.updateAll($0, IdentityRecord.Columns.pending.set(to: false))
|
.updateAll($0, IdentityRecord.Columns.pending.set(to: false))
|
||||||
}
|
}
|
||||||
.ignoreOutput()
|
|
||||||
.eraseToAnyPublisher()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func updatePreferences(_ preferences: Mastodon.Preferences, id: Identity.Id) -> AnyPublisher<Never, Error> {
|
func updatePreferences(_ preferences: Mastodon.Preferences, id: Identity.Id) -> AnyPublisher<Never, Error> {
|
||||||
databaseWriter.writePublisher {
|
databaseWriter.mutatingPublisher {
|
||||||
guard let storedPreferences = try IdentityRecord.filter(IdentityRecord.Columns.id == id)
|
guard let storedPreferences = try IdentityRecord.filter(IdentityRecord.Columns.id == id)
|
||||||
.fetchOne($0)?
|
.fetchOne($0)?
|
||||||
.preferences else {
|
.preferences else {
|
||||||
|
@ -121,20 +109,16 @@ public extension IdentityDatabase {
|
||||||
|
|
||||||
try Self.writePreferences(storedPreferences.updated(from: preferences), id: id)($0)
|
try Self.writePreferences(storedPreferences.updated(from: preferences), id: id)($0)
|
||||||
}
|
}
|
||||||
.ignoreOutput()
|
|
||||||
.eraseToAnyPublisher()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func updatePreferences(_ preferences: Identity.Preferences, id: Identity.Id) -> AnyPublisher<Never, Error> {
|
func updatePreferences(_ preferences: Identity.Preferences, id: Identity.Id) -> AnyPublisher<Never, Error> {
|
||||||
databaseWriter.writePublisher(updates: Self.writePreferences(preferences, id: id))
|
databaseWriter.mutatingPublisher(updates: Self.writePreferences(preferences, id: id))
|
||||||
.ignoreOutput()
|
|
||||||
.eraseToAnyPublisher()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func updatePushSubscription(alerts: PushSubscription.Alerts,
|
func updatePushSubscription(alerts: PushSubscription.Alerts,
|
||||||
deviceToken: Data? = nil,
|
deviceToken: Data? = nil,
|
||||||
id: Identity.Id) -> AnyPublisher<Never, Error> {
|
id: Identity.Id) -> AnyPublisher<Never, Error> {
|
||||||
databaseWriter.writePublisher {
|
databaseWriter.mutatingPublisher {
|
||||||
let data = try IdentityRecord.databaseJSONEncoder(
|
let data = try IdentityRecord.databaseJSONEncoder(
|
||||||
for: IdentityRecord.Columns.pushSubscriptionAlerts.name)
|
for: IdentityRecord.Columns.pushSubscriptionAlerts.name)
|
||||||
.encode(alerts)
|
.encode(alerts)
|
||||||
|
@ -149,8 +133,6 @@ public extension IdentityDatabase {
|
||||||
.updateAll($0, IdentityRecord.Columns.lastRegisteredDeviceToken.set(to: deviceToken))
|
.updateAll($0, IdentityRecord.Columns.lastRegisteredDeviceToken.set(to: deviceToken))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.ignoreOutput()
|
|
||||||
.eraseToAnyPublisher()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func identityPublisher(id: Identity.Id, immediate: Bool) -> AnyPublisher<Identity, Error> {
|
func identityPublisher(id: Identity.Id, immediate: Bool) -> AnyPublisher<Identity, Error> {
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
// Copyright © 2020 Metabolist. All rights reserved.
|
// Copyright © 2020 Metabolist. All rights reserved.
|
||||||
|
|
||||||
import AVKit
|
import AVKit
|
||||||
|
import Combine
|
||||||
|
import GRDB
|
||||||
import ServiceLayer
|
import ServiceLayer
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
import ViewModels
|
import ViewModels
|
||||||
|
@ -8,10 +10,36 @@ import ViewModels
|
||||||
@main
|
@main
|
||||||
struct MetatextApp: App {
|
struct MetatextApp: App {
|
||||||
@UIApplicationDelegateAdaptor(AppDelegate.self) private var appDelegate
|
@UIApplicationDelegateAdaptor(AppDelegate.self) private var appDelegate
|
||||||
|
private var cancellables = Set<AnyCancellable>()
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
try? AVAudioSession.sharedInstance().setCategory(.ambient, mode: .default)
|
try? AVAudioSession.sharedInstance().setCategory(.ambient, mode: .default)
|
||||||
try? ImageCacheConfiguration(environment: Self.environment).configure()
|
try? ImageCacheConfiguration(environment: Self.environment).configure()
|
||||||
|
|
||||||
|
// swiftlint:disable:next line_length
|
||||||
|
// https://github.com/groue/GRDB.swift/blob/master/Documentation/SharingADatabase.md#how-to-limit-the-0xdead10cc-exception
|
||||||
|
// This would ideally be accomplished with `@Environment(\.scenePhase) private var scenePhase`
|
||||||
|
// and `.onChange(of: scenePhase)` on the `WindowGroup`, but that does not give an accurate
|
||||||
|
// aggregate scene activation state for iPad multitasking as of iOS 14.4.1
|
||||||
|
Publishers.MergeMany([UIScene.willConnectNotification,
|
||||||
|
UIScene.didDisconnectNotification,
|
||||||
|
UIScene.didActivateNotification,
|
||||||
|
UIScene.willDeactivateNotification,
|
||||||
|
UIScene.willEnterForegroundNotification,
|
||||||
|
UIScene.didEnterBackgroundNotification]
|
||||||
|
.map { NotificationCenter.default.publisher(for: $0) })
|
||||||
|
.map { _ in
|
||||||
|
UIApplication.shared.openSessions
|
||||||
|
.compactMap(\.scene)
|
||||||
|
.allSatisfy { $0.activationState == .background }
|
||||||
|
}
|
||||||
|
.removeDuplicates()
|
||||||
|
.sink {
|
||||||
|
NotificationCenter.default.post(
|
||||||
|
name: $0 ? Database.suspendNotification : Database.resumeNotification,
|
||||||
|
object: nil)
|
||||||
|
}
|
||||||
|
.store(in: &cancellables)
|
||||||
}
|
}
|
||||||
|
|
||||||
var body: some Scene {
|
var body: some Scene {
|
||||||
|
|
Loading…
Reference in a new issue