mirror of
https://github.com/metabolist/metatext.git
synced 2024-11-21 15:50:59 +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> {
|
||||
databaseWriter.writePublisher(updates: status.save)
|
||||
.ignoreOutput()
|
||||
.eraseToAnyPublisher()
|
||||
databaseWriter.mutatingPublisher(updates: status.save)
|
||||
}
|
||||
|
||||
// swiftlint:disable:next function_body_length
|
||||
|
@ -61,7 +59,7 @@ public extension ContentDatabase {
|
|||
statuses: [Status],
|
||||
timeline: Timeline,
|
||||
loadMoreAndDirection: (LoadMore, LoadMore.Direction)? = nil) -> AnyPublisher<Never, Error> {
|
||||
databaseWriter.writePublisher {
|
||||
databaseWriter.mutatingPublisher {
|
||||
let timelineRecord = TimelineRecord(timeline: timeline)
|
||||
|
||||
try timelineRecord.save($0)
|
||||
|
@ -122,12 +120,10 @@ public extension ContentDatabase {
|
|||
}
|
||||
}
|
||||
}
|
||||
.ignoreOutput()
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
func insert(context: Context, parentId: Status.Id) -> AnyPublisher<Never, Error> {
|
||||
databaseWriter.writePublisher {
|
||||
databaseWriter.mutatingPublisher {
|
||||
for (index, status) in context.ancestors.enumerated() {
|
||||
try status.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))
|
||||
.deleteAll($0)
|
||||
}
|
||||
.ignoreOutput()
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
func insert(pinnedStatuses: [Status], accountId: Account.Id) -> AnyPublisher<Never, Error> {
|
||||
databaseWriter.writePublisher {
|
||||
databaseWriter.mutatingPublisher {
|
||||
for (index, status) in pinnedStatuses.enumerated() {
|
||||
try status.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))
|
||||
.deleteAll($0)
|
||||
}
|
||||
.ignoreOutput()
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
func toggleShowContent(id: Status.Id) -> AnyPublisher<Never, Error> {
|
||||
databaseWriter.writePublisher {
|
||||
databaseWriter.mutatingPublisher {
|
||||
if let toggle = try StatusShowContentToggle
|
||||
.filter(StatusShowContentToggle.Columns.statusId == id)
|
||||
.fetchOne($0) {
|
||||
|
@ -178,12 +170,10 @@ public extension ContentDatabase {
|
|||
try StatusShowContentToggle(statusId: id).save($0)
|
||||
}
|
||||
}
|
||||
.ignoreOutput()
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
func toggleShowAttachments(id: Status.Id) -> AnyPublisher<Never, Error> {
|
||||
databaseWriter.writePublisher {
|
||||
databaseWriter.mutatingPublisher {
|
||||
if let toggle = try StatusShowAttachmentsToggle
|
||||
.filter(StatusShowAttachmentsToggle.Columns.statusId == id)
|
||||
.fetchOne($0) {
|
||||
|
@ -192,23 +182,19 @@ public extension ContentDatabase {
|
|||
try StatusShowAttachmentsToggle(statusId: id).save($0)
|
||||
}
|
||||
}
|
||||
.ignoreOutput()
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
func expand(ids: Set<Status.Id>) -> AnyPublisher<Never, Error> {
|
||||
databaseWriter.writePublisher {
|
||||
databaseWriter.mutatingPublisher {
|
||||
for id in ids {
|
||||
try StatusShowContentToggle(statusId: id).save($0)
|
||||
try StatusShowAttachmentsToggle(statusId: id).save($0)
|
||||
}
|
||||
}
|
||||
.ignoreOutput()
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
func collapse(ids: Set<Status.Id>) -> AnyPublisher<Never, Error> {
|
||||
databaseWriter.writePublisher {
|
||||
databaseWriter.mutatingPublisher {
|
||||
try StatusShowContentToggle
|
||||
.filter(ids.contains(StatusShowContentToggle.Columns.statusId))
|
||||
.deleteAll($0)
|
||||
|
@ -216,29 +202,23 @@ public extension ContentDatabase {
|
|||
.filter(ids.contains(StatusShowContentToggle.Columns.statusId))
|
||||
.deleteAll($0)
|
||||
}
|
||||
.ignoreOutput()
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
try StatusRecord.filter(StatusRecord.Columns.id == id)
|
||||
.updateAll($0, StatusRecord.Columns.poll.set(to: data))
|
||||
}
|
||||
.ignoreOutput()
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
func delete(id: Status.Id) -> AnyPublisher<Never, Error> {
|
||||
databaseWriter.writePublisher(updates: StatusRecord.filter(StatusRecord.Columns.id == id).deleteAll)
|
||||
.ignoreOutput()
|
||||
.eraseToAnyPublisher()
|
||||
databaseWriter.mutatingPublisher(updates: StatusRecord.filter(StatusRecord.Columns.id == id).deleteAll)
|
||||
}
|
||||
|
||||
func unfollow(id: Account.Id) -> AnyPublisher<Never, Error> {
|
||||
databaseWriter.writePublisher {
|
||||
databaseWriter.mutatingPublisher {
|
||||
let statusIds = try Status.Id.fetchAll(
|
||||
$0,
|
||||
StatusRecord.filter(StatusRecord.Columns.accountId == id).select(StatusRecord.Columns.id))
|
||||
|
@ -248,27 +228,21 @@ public extension ContentDatabase {
|
|||
&& statusIds.contains(TimelineStatusJoin.Columns.statusId))
|
||||
.deleteAll($0)
|
||||
}
|
||||
.ignoreOutput()
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
func mute(id: Account.Id) -> AnyPublisher<Never, Error> {
|
||||
databaseWriter.writePublisher {
|
||||
databaseWriter.mutatingPublisher {
|
||||
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()
|
||||
databaseWriter.mutatingPublisher(updates: AccountRecord.filter(AccountRecord.Columns.id == id).deleteAll)
|
||||
}
|
||||
|
||||
func insert(accounts: [Account], listId: AccountList.Id? = nil) -> AnyPublisher<Never, Error> {
|
||||
databaseWriter.writePublisher {
|
||||
databaseWriter.mutatingPublisher {
|
||||
var order: Int?
|
||||
|
||||
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> {
|
||||
databaseWriter.writePublisher(
|
||||
databaseWriter.mutatingPublisher(
|
||||
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 {
|
||||
databaseWriter.mutatingPublisher {
|
||||
for identityProof in identityProofs {
|
||||
try IdentityProofRecord(
|
||||
accountId: id,
|
||||
|
@ -317,12 +287,10 @@ public extension ContentDatabase {
|
|||
.save($0)
|
||||
}
|
||||
}
|
||||
.ignoreOutput()
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
func insert(featuredTags: [FeaturedTag], id: Account.Id) -> AnyPublisher<Never, Error> {
|
||||
databaseWriter.writePublisher {
|
||||
databaseWriter.mutatingPublisher {
|
||||
for featuredTag in featuredTags {
|
||||
try FeaturedTagRecord(
|
||||
id: featuredTag.id,
|
||||
|
@ -334,22 +302,18 @@ public extension ContentDatabase {
|
|||
.save($0)
|
||||
}
|
||||
}
|
||||
.ignoreOutput()
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
func insert(relationships: [Relationship]) -> AnyPublisher<Never, Error> {
|
||||
databaseWriter.writePublisher {
|
||||
databaseWriter.mutatingPublisher {
|
||||
for relationship in relationships {
|
||||
try relationship.save($0)
|
||||
}
|
||||
}
|
||||
.ignoreOutput()
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
func setLists(_ lists: [List]) -> AnyPublisher<Never, Error> {
|
||||
databaseWriter.writePublisher {
|
||||
databaseWriter.mutatingPublisher {
|
||||
for list in lists {
|
||||
try TimelineRecord(timeline: Timeline.list(list)).save($0)
|
||||
}
|
||||
|
@ -359,86 +323,66 @@ public extension ContentDatabase {
|
|||
&& TimelineRecord.Columns.listTitle != nil)
|
||||
.deleteAll($0)
|
||||
}
|
||||
.ignoreOutput()
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
func createList(_ list: List) -> AnyPublisher<Never, Error> {
|
||||
databaseWriter.writePublisher(updates: TimelineRecord(timeline: Timeline.list(list)).save)
|
||||
.ignoreOutput()
|
||||
.eraseToAnyPublisher()
|
||||
databaseWriter.mutatingPublisher(updates: TimelineRecord(timeline: Timeline.list(list)).save)
|
||||
}
|
||||
|
||||
func deleteList(id: List.Id) -> AnyPublisher<Never, Error> {
|
||||
databaseWriter.writePublisher(updates: TimelineRecord.filter(TimelineRecord.Columns.listId == id).deleteAll)
|
||||
.ignoreOutput()
|
||||
.eraseToAnyPublisher()
|
||||
databaseWriter.mutatingPublisher(updates: TimelineRecord.filter(TimelineRecord.Columns.listId == id).deleteAll)
|
||||
}
|
||||
|
||||
func setFilters(_ filters: [Filter]) -> AnyPublisher<Never, Error> {
|
||||
databaseWriter.writePublisher {
|
||||
databaseWriter.mutatingPublisher {
|
||||
for filter in filters {
|
||||
try filter.save($0)
|
||||
}
|
||||
|
||||
try Filter.filter(!filters.map(\.id).contains(Filter.Columns.id)).deleteAll($0)
|
||||
}
|
||||
.ignoreOutput()
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
func createFilter(_ filter: Filter) -> AnyPublisher<Never, Error> {
|
||||
databaseWriter.writePublisher(updates: filter.save)
|
||||
.ignoreOutput()
|
||||
.eraseToAnyPublisher()
|
||||
databaseWriter.mutatingPublisher(updates: filter.save)
|
||||
}
|
||||
|
||||
func deleteFilter(id: Filter.Id) -> AnyPublisher<Never, Error> {
|
||||
databaseWriter.writePublisher(updates: Filter.filter(Filter.Columns.id == id).deleteAll)
|
||||
.ignoreOutput()
|
||||
.eraseToAnyPublisher()
|
||||
databaseWriter.mutatingPublisher(updates: Filter.filter(Filter.Columns.id == id).deleteAll)
|
||||
}
|
||||
|
||||
func setLastReadId(_ id: String, timelineId: Timeline.Id) -> AnyPublisher<Never, Error> {
|
||||
databaseWriter.writePublisher(updates: LastReadIdRecord(timelineId: timelineId, id: id).save)
|
||||
.ignoreOutput()
|
||||
.eraseToAnyPublisher()
|
||||
databaseWriter.mutatingPublisher(updates: LastReadIdRecord(timelineId: timelineId, id: id).save)
|
||||
}
|
||||
|
||||
func insert(notifications: [MastodonNotification]) -> AnyPublisher<Never, Error> {
|
||||
databaseWriter.writePublisher {
|
||||
databaseWriter.mutatingPublisher {
|
||||
for notification in notifications {
|
||||
try notification.save($0)
|
||||
}
|
||||
}
|
||||
.ignoreOutput()
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
func insert(conversations: [Conversation]) -> AnyPublisher<Never, Error> {
|
||||
databaseWriter.writePublisher {
|
||||
databaseWriter.mutatingPublisher {
|
||||
for conversation in conversations {
|
||||
try conversation.save($0)
|
||||
}
|
||||
}
|
||||
.ignoreOutput()
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
func update(emojis: [Emoji]) -> AnyPublisher<Never, Error> {
|
||||
databaseWriter.writePublisher {
|
||||
databaseWriter.mutatingPublisher {
|
||||
for emoji in emojis {
|
||||
try emoji.save($0)
|
||||
}
|
||||
|
||||
try Emoji.filter(!emojis.map(\.shortcode).contains(Emoji.Columns.shortcode)).deleteAll($0)
|
||||
}
|
||||
.ignoreOutput()
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
func updateUse(emoji: String, system: Bool) -> AnyPublisher<Never, Error> {
|
||||
databaseWriter.writePublisher {
|
||||
databaseWriter.mutatingPublisher {
|
||||
let count = try Int.fetchOne(
|
||||
$0,
|
||||
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)
|
||||
}
|
||||
.ignoreOutput()
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
func update(announcements: [Announcement]) -> AnyPublisher<Never, Error> {
|
||||
databaseWriter.writePublisher {
|
||||
databaseWriter.mutatingPublisher {
|
||||
for announcement in announcements {
|
||||
try announcement.save($0)
|
||||
}
|
||||
|
||||
try Announcement.filter(!announcements.map(\.id).contains(Announcement.Columns.id)).deleteAll($0)
|
||||
}
|
||||
.ignoreOutput()
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
func insert(results: Results) -> AnyPublisher<Never, Error> {
|
||||
databaseWriter.writePublisher {
|
||||
databaseWriter.mutatingPublisher {
|
||||
for account in results.accounts {
|
||||
try account.save($0)
|
||||
}
|
||||
|
@ -472,14 +412,10 @@ public extension ContentDatabase {
|
|||
try status.save($0)
|
||||
}
|
||||
}
|
||||
.ignoreOutput()
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
func insert(instance: Instance) -> AnyPublisher<Never, Error> {
|
||||
databaseWriter.writePublisher(updates: instance.save)
|
||||
.ignoreOutput()
|
||||
.eraseToAnyPublisher()
|
||||
databaseWriter.mutatingPublisher(updates: instance.save)
|
||||
}
|
||||
|
||||
func timelinePublisher(_ timeline: Timeline) -> AnyPublisher<[CollectionSection], Error> {
|
||||
|
@ -697,7 +633,6 @@ public extension ContentDatabase {
|
|||
|
||||
private extension ContentDatabase {
|
||||
static let cleanAfterLastReadIdCount = 40
|
||||
|
||||
static let ephemeralTimelines = NSCountedSet()
|
||||
|
||||
static func fileURL(id: Identity.Id, appGroup: String) throws -> URL {
|
||||
|
|
|
@ -20,6 +20,7 @@ extension DatabasePool {
|
|||
|
||||
configuration.busyMode = .timeout(5)
|
||||
configuration.defaultTransactionKind = .immediate
|
||||
configuration.observesSuspensionNotifications = true
|
||||
configuration.prepareDatabase { db in
|
||||
try db.usePassphrase(passphrase())
|
||||
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 {
|
||||
func createIdentity(id: Identity.Id, url: URL, authenticated: Bool, pending: Bool) -> AnyPublisher<Never, Error> {
|
||||
databaseWriter.writePublisher(
|
||||
databaseWriter.mutatingPublisher(
|
||||
updates: IdentityRecord(
|
||||
id: id,
|
||||
url: url,
|
||||
|
@ -44,28 +44,22 @@ public extension IdentityDatabase {
|
|||
lastRegisteredDeviceToken: nil,
|
||||
pushSubscriptionAlerts: .initial)
|
||||
.save)
|
||||
.ignoreOutput()
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
func deleteIdentity(id: Identity.Id) -> AnyPublisher<Never, Error> {
|
||||
databaseWriter.writePublisher(updates: IdentityRecord.filter(IdentityRecord.Columns.id == id).deleteAll)
|
||||
.ignoreOutput()
|
||||
.eraseToAnyPublisher()
|
||||
databaseWriter.mutatingPublisher(updates: IdentityRecord.filter(IdentityRecord.Columns.id == id).deleteAll)
|
||||
}
|
||||
|
||||
func updateLastUsedAt(id: Identity.Id) -> AnyPublisher<Never, Error> {
|
||||
databaseWriter.writePublisher {
|
||||
databaseWriter.mutatingPublisher {
|
||||
try IdentityRecord
|
||||
.filter(IdentityRecord.Columns.id == id)
|
||||
.updateAll($0, IdentityRecord.Columns.lastUsedAt.set(to: Date()))
|
||||
}
|
||||
.ignoreOutput()
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
func updateInstance(_ instance: Instance, id: Identity.Id) -> AnyPublisher<Never, Error> {
|
||||
databaseWriter.writePublisher {
|
||||
databaseWriter.mutatingPublisher {
|
||||
try Identity.Instance(
|
||||
uri: instance.uri,
|
||||
streamingAPI: instance.urls.streamingApi,
|
||||
|
@ -78,12 +72,10 @@ public extension IdentityDatabase {
|
|||
.filter(IdentityRecord.Columns.id == id)
|
||||
.updateAll($0, IdentityRecord.Columns.instanceURI.set(to: instance.uri))
|
||||
}
|
||||
.ignoreOutput()
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
func updateAccount(_ account: Account, id: Identity.Id) -> AnyPublisher<Never, Error> {
|
||||
databaseWriter.writePublisher(
|
||||
databaseWriter.mutatingPublisher(
|
||||
updates: Identity.Account(
|
||||
id: account.id,
|
||||
identityId: id,
|
||||
|
@ -97,22 +89,18 @@ public extension IdentityDatabase {
|
|||
emojis: account.emojis,
|
||||
followRequestCount: account.source?.followRequestsCount ?? 0)
|
||||
.save)
|
||||
.ignoreOutput()
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
func confirmIdentity(id: Identity.Id) -> AnyPublisher<Never, Error> {
|
||||
databaseWriter.writePublisher {
|
||||
databaseWriter.mutatingPublisher {
|
||||
try IdentityRecord
|
||||
.filter(IdentityRecord.Columns.id == id)
|
||||
.updateAll($0, IdentityRecord.Columns.pending.set(to: false))
|
||||
}
|
||||
.ignoreOutput()
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
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)
|
||||
.fetchOne($0)?
|
||||
.preferences else {
|
||||
|
@ -121,20 +109,16 @@ public extension IdentityDatabase {
|
|||
|
||||
try Self.writePreferences(storedPreferences.updated(from: preferences), id: id)($0)
|
||||
}
|
||||
.ignoreOutput()
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
func updatePreferences(_ preferences: Identity.Preferences, id: Identity.Id) -> AnyPublisher<Never, Error> {
|
||||
databaseWriter.writePublisher(updates: Self.writePreferences(preferences, id: id))
|
||||
.ignoreOutput()
|
||||
.eraseToAnyPublisher()
|
||||
databaseWriter.mutatingPublisher(updates: Self.writePreferences(preferences, id: id))
|
||||
}
|
||||
|
||||
func updatePushSubscription(alerts: PushSubscription.Alerts,
|
||||
deviceToken: Data? = nil,
|
||||
id: Identity.Id) -> AnyPublisher<Never, Error> {
|
||||
databaseWriter.writePublisher {
|
||||
databaseWriter.mutatingPublisher {
|
||||
let data = try IdentityRecord.databaseJSONEncoder(
|
||||
for: IdentityRecord.Columns.pushSubscriptionAlerts.name)
|
||||
.encode(alerts)
|
||||
|
@ -149,8 +133,6 @@ public extension IdentityDatabase {
|
|||
.updateAll($0, IdentityRecord.Columns.lastRegisteredDeviceToken.set(to: deviceToken))
|
||||
}
|
||||
}
|
||||
.ignoreOutput()
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
func identityPublisher(id: Identity.Id, immediate: Bool) -> AnyPublisher<Identity, Error> {
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
// Copyright © 2020 Metabolist. All rights reserved.
|
||||
|
||||
import AVKit
|
||||
import Combine
|
||||
import GRDB
|
||||
import ServiceLayer
|
||||
import SwiftUI
|
||||
import ViewModels
|
||||
|
@ -8,10 +10,36 @@ import ViewModels
|
|||
@main
|
||||
struct MetatextApp: App {
|
||||
@UIApplicationDelegateAdaptor(AppDelegate.self) private var appDelegate
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
init() {
|
||||
try? AVAudioSession.sharedInstance().setCategory(.ambient, mode: .default)
|
||||
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 {
|
||||
|
|
Loading…
Reference in a new issue