forked from zelo72/mastodon-ios
fix: server-side data is inconsistent with local
This commit is contained in:
parent
e42af11bf7
commit
8c7149af89
|
@ -155,13 +155,14 @@
|
||||||
<relationship name="account" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="MastodonUser"/>
|
<relationship name="account" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="MastodonUser"/>
|
||||||
<relationship name="hashtag" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Tag"/>
|
<relationship name="hashtag" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Tag"/>
|
||||||
</entity>
|
</entity>
|
||||||
<entity name="Setting" representedClassName="Setting" syncable="YES">
|
<entity name="Setting" representedClassName=".Setting" syncable="YES">
|
||||||
<attribute name="appearance" optional="YES" attributeType="String"/>
|
<attribute name="appearance" optional="YES" attributeType="String"/>
|
||||||
<attribute name="createdAt" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
<attribute name="createdAt" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||||
<attribute name="domain" optional="YES" attributeType="String"/>
|
<attribute name="domain" optional="YES" attributeType="String"/>
|
||||||
<attribute name="id" optional="YES" attributeType="UUID" usesScalarValueType="NO"/>
|
<attribute name="id" optional="YES" attributeType="UUID" usesScalarValueType="NO"/>
|
||||||
<attribute name="triggerBy" optional="YES" attributeType="String"/>
|
<attribute name="triggerBy" optional="YES" attributeType="String"/>
|
||||||
<attribute name="updatedAt" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
<attribute name="updatedAt" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||||
|
<attribute name="userID" optional="YES" attributeType="String"/>
|
||||||
<relationship name="subscription" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="Subscription" inverseName="setting" inverseEntity="Subscription"/>
|
<relationship name="subscription" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="Subscription" inverseName="setting" inverseEntity="Subscription"/>
|
||||||
</entity>
|
</entity>
|
||||||
<entity name="Status" representedClassName=".Status" syncable="YES">
|
<entity name="Status" representedClassName=".Status" syncable="YES">
|
||||||
|
@ -202,7 +203,7 @@
|
||||||
<relationship name="replyTo" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Status" inverseName="replyFrom" inverseEntity="Status"/>
|
<relationship name="replyTo" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Status" inverseName="replyFrom" inverseEntity="Status"/>
|
||||||
<relationship name="tags" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="Tag" inverseName="statuses" inverseEntity="Tag"/>
|
<relationship name="tags" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="Tag" inverseName="statuses" inverseEntity="Tag"/>
|
||||||
</entity>
|
</entity>
|
||||||
<entity name="Subscription" representedClassName="Subscription" syncable="YES">
|
<entity name="Subscription" representedClassName=".Subscription" syncable="YES">
|
||||||
<attribute name="createdAt" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
<attribute name="createdAt" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||||
<attribute name="endpoint" optional="YES" attributeType="String"/>
|
<attribute name="endpoint" optional="YES" attributeType="String"/>
|
||||||
<attribute name="id" optional="YES" attributeType="String"/>
|
<attribute name="id" optional="YES" attributeType="String"/>
|
||||||
|
@ -212,7 +213,7 @@
|
||||||
<relationship name="alert" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="SubscriptionAlerts" inverseName="subscription" inverseEntity="SubscriptionAlerts"/>
|
<relationship name="alert" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="SubscriptionAlerts" inverseName="subscription" inverseEntity="SubscriptionAlerts"/>
|
||||||
<relationship name="setting" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Setting" inverseName="subscription" inverseEntity="Setting"/>
|
<relationship name="setting" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Setting" inverseName="subscription" inverseEntity="Setting"/>
|
||||||
</entity>
|
</entity>
|
||||||
<entity name="SubscriptionAlerts" representedClassName="SubscriptionAlerts" syncable="YES">
|
<entity name="SubscriptionAlerts" representedClassName=".SubscriptionAlerts" syncable="YES">
|
||||||
<attribute name="createdAt" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
<attribute name="createdAt" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||||
<attribute name="favourite" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
<attribute name="favourite" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||||
<attribute name="follow" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
<attribute name="follow" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||||
|
@ -244,10 +245,10 @@
|
||||||
<element name="PollOption" positionX="0" positionY="0" width="128" height="134"/>
|
<element name="PollOption" positionX="0" positionY="0" width="128" height="134"/>
|
||||||
<element name="PrivateNote" positionX="0" positionY="0" width="128" height="89"/>
|
<element name="PrivateNote" positionX="0" positionY="0" width="128" height="89"/>
|
||||||
<element name="SearchHistory" positionX="0" positionY="0" width="128" height="104"/>
|
<element name="SearchHistory" positionX="0" positionY="0" width="128" height="104"/>
|
||||||
|
<element name="Setting" positionX="72" positionY="162" width="128" height="149"/>
|
||||||
<element name="Status" positionX="0" positionY="0" width="128" height="569"/>
|
<element name="Status" positionX="0" positionY="0" width="128" height="569"/>
|
||||||
<element name="Tag" positionX="0" positionY="0" width="128" height="134"/>
|
|
||||||
<element name="Setting" positionX="72" positionY="162" width="128" height="134"/>
|
|
||||||
<element name="Subscription" positionX="81" positionY="171" width="128" height="149"/>
|
<element name="Subscription" positionX="81" positionY="171" width="128" height="149"/>
|
||||||
<element name="SubscriptionAlerts" positionX="72" positionY="162" width="128" height="149"/>
|
<element name="SubscriptionAlerts" positionX="72" positionY="162" width="128" height="149"/>
|
||||||
|
<element name="Tag" positionX="0" positionY="0" width="128" height="134"/>
|
||||||
</elements>
|
</elements>
|
||||||
</model>
|
</model>
|
|
@ -8,11 +8,11 @@
|
||||||
import CoreData
|
import CoreData
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
@objc(Setting)
|
|
||||||
public final class Setting: NSManagedObject {
|
public final class Setting: NSManagedObject {
|
||||||
@NSManaged public var appearance: String?
|
@NSManaged public var appearance: String?
|
||||||
@NSManaged public var triggerBy: String?
|
@NSManaged public var triggerBy: String?
|
||||||
@NSManaged public var domain: String?
|
@NSManaged public var domain: String?
|
||||||
|
@NSManaged public var userID: String?
|
||||||
|
|
||||||
@NSManaged public private(set) var createdAt: Date
|
@NSManaged public private(set) var createdAt: Date
|
||||||
@NSManaged public private(set) var updatedAt: Date
|
@NSManaged public private(set) var updatedAt: Date
|
||||||
|
@ -40,6 +40,7 @@ public extension Setting {
|
||||||
setting.appearance = property.appearance
|
setting.appearance = property.appearance
|
||||||
setting.triggerBy = property.triggerBy
|
setting.triggerBy = property.triggerBy
|
||||||
setting.domain = property.domain
|
setting.domain = property.domain
|
||||||
|
setting.userID = property.userID
|
||||||
return setting
|
return setting
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,11 +62,13 @@ public extension Setting {
|
||||||
public let appearance: String
|
public let appearance: String
|
||||||
public let triggerBy: String
|
public let triggerBy: String
|
||||||
public let domain: String
|
public let domain: String
|
||||||
|
public let userID: String
|
||||||
|
|
||||||
public init(appearance: String, triggerBy: String, domain: String) {
|
public init(appearance: String, triggerBy: String, domain: String, userID: String) {
|
||||||
self.appearance = appearance
|
self.appearance = appearance
|
||||||
self.triggerBy = triggerBy
|
self.triggerBy = triggerBy
|
||||||
self.domain = domain
|
self.domain = domain
|
||||||
|
self.userID = userID
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -77,8 +80,11 @@ extension Setting: Managed {
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Setting {
|
extension Setting {
|
||||||
public static func predicate(domain: String) -> NSPredicate {
|
public static func predicate(domain: String, userID: String) -> NSPredicate {
|
||||||
return NSPredicate(format: "%K == %@", #keyPath(Setting.domain), domain)
|
return NSPredicate(format: "%K == %@ AND %K == %@",
|
||||||
|
#keyPath(Setting.domain), domain,
|
||||||
|
#keyPath(Setting.userID), userID
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,6 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
import CoreData
|
import CoreData
|
||||||
|
|
||||||
@objc(Subscription)
|
|
||||||
public final class Subscription: NSManagedObject {
|
public final class Subscription: NSManagedObject {
|
||||||
@NSManaged public var id: String
|
@NSManaged public var id: String
|
||||||
@NSManaged public var endpoint: String
|
@NSManaged public var endpoint: String
|
||||||
|
@ -95,8 +94,8 @@ extension Subscription: Managed {
|
||||||
|
|
||||||
extension Subscription {
|
extension Subscription {
|
||||||
|
|
||||||
public static func predicate(id: String) -> NSPredicate {
|
public static func predicate(type: String) -> NSPredicate {
|
||||||
return NSPredicate(format: "%K == %@", #keyPath(Subscription.id), id)
|
return NSPredicate(format: "%K == %@", #keyPath(Subscription.type), type)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@ public final class SubscriptionAlerts: NSManagedObject {
|
||||||
@NSManaged public private(set) var updatedAt: Date
|
@NSManaged public private(set) var updatedAt: Date
|
||||||
|
|
||||||
// MARK: - relationships
|
// MARK: - relationships
|
||||||
@NSManaged public var pushSubscription: Subscription?
|
@NSManaged public var subscription: Subscription?
|
||||||
}
|
}
|
||||||
|
|
||||||
public extension SubscriptionAlerts {
|
public extension SubscriptionAlerts {
|
||||||
|
|
|
@ -46,7 +46,7 @@ class SettingsViewController: UIViewController, NeedsDependency {
|
||||||
UIAction(title: noOne, image: UIImage(systemName: "nosign"), attributes: []) { [weak self] action in
|
UIAction(title: noOne, image: UIImage(systemName: "nosign"), attributes: []) { [weak self] action in
|
||||||
self?.updateTrigger(by: noOne)
|
self?.updateTrigger(by: noOne)
|
||||||
},
|
},
|
||||||
].reversed()
|
]
|
||||||
)
|
)
|
||||||
return menu
|
return menu
|
||||||
}
|
}
|
||||||
|
@ -344,10 +344,15 @@ extension SettingsViewController: UITableViewDelegate {
|
||||||
// Update setting into core data
|
// Update setting into core data
|
||||||
extension SettingsViewController {
|
extension SettingsViewController {
|
||||||
func updateTrigger(by who: String) {
|
func updateTrigger(by who: String) {
|
||||||
|
guard self.viewModel.triggerBy != who else { return }
|
||||||
guard let setting = self.viewModel.setting.value else { return }
|
guard let setting = self.viewModel.setting.value else { return }
|
||||||
|
|
||||||
_ = context.managedObjectContext.performChanges {
|
setting.update(triggerBy: who)
|
||||||
setting.update(triggerBy: who)
|
// trigger to call `subscription` API with POST method
|
||||||
|
// confirm the local data is correct even if request failed
|
||||||
|
// The asynchronous execution is to solve the problem of dropped frames for animations.
|
||||||
|
DispatchQueue.main.async { [weak self] in
|
||||||
|
self?.viewModel.setting.value = setting
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -356,34 +361,35 @@ extension SettingsViewController {
|
||||||
guard let settings = self.viewModel.setting.value else { return }
|
guard let settings = self.viewModel.setting.value else { return }
|
||||||
guard let triggerBy = settings.triggerBy else { return }
|
guard let triggerBy = settings.triggerBy else { return }
|
||||||
|
|
||||||
guard let alerts = settings.subscription?.first(where: { (s) -> Bool in
|
if let alerts = settings.subscription?.first(where: { (s) -> Bool in
|
||||||
return s.type == settings.triggerBy
|
return s.type == settings.triggerBy
|
||||||
})?.alert else {
|
})?.alert {
|
||||||
return
|
var alertValues = [Bool?]()
|
||||||
|
alertValues.append(alerts.favourite?.boolValue)
|
||||||
|
alertValues.append(alerts.follow?.boolValue)
|
||||||
|
alertValues.append(alerts.reblog?.boolValue)
|
||||||
|
alertValues.append(alerts.mention?.boolValue)
|
||||||
|
|
||||||
|
// need to update `alerts` to make update API with correct parameter
|
||||||
|
switch title {
|
||||||
|
case L10n.Scene.Settings.Section.Notifications.favorites:
|
||||||
|
alertValues[0] = isOn
|
||||||
|
alerts.favourite = NSNumber(booleanLiteral: isOn)
|
||||||
|
case L10n.Scene.Settings.Section.Notifications.follows:
|
||||||
|
alertValues[1] = isOn
|
||||||
|
alerts.follow = NSNumber(booleanLiteral: isOn)
|
||||||
|
case L10n.Scene.Settings.Section.Notifications.boosts:
|
||||||
|
alertValues[2] = isOn
|
||||||
|
alerts.reblog = NSNumber(booleanLiteral: isOn)
|
||||||
|
case L10n.Scene.Settings.Section.Notifications.mentions:
|
||||||
|
alertValues[3] = isOn
|
||||||
|
alerts.mention = NSNumber(booleanLiteral: isOn)
|
||||||
|
default: break
|
||||||
|
}
|
||||||
|
self.viewModel.updateSubscriptionSubject.send((triggerBy: triggerBy, values: alertValues))
|
||||||
|
} else if let alertValues = self.viewModel.notificationDefaultValue[triggerBy] {
|
||||||
|
self.viewModel.updateSubscriptionSubject.send((triggerBy: triggerBy, values: alertValues))
|
||||||
}
|
}
|
||||||
var alertValues = [Bool?]()
|
|
||||||
alertValues.append(alerts.favourite?.boolValue)
|
|
||||||
alertValues.append(alerts.follow?.boolValue)
|
|
||||||
alertValues.append(alerts.reblog?.boolValue)
|
|
||||||
alertValues.append(alerts.mention?.boolValue)
|
|
||||||
|
|
||||||
// need to update `alerts` to make update API with correct parameter
|
|
||||||
switch title {
|
|
||||||
case L10n.Scene.Settings.Section.Notifications.favorites:
|
|
||||||
alertValues[0] = isOn
|
|
||||||
alerts.favourite = NSNumber(booleanLiteral: isOn)
|
|
||||||
case L10n.Scene.Settings.Section.Notifications.follows:
|
|
||||||
alertValues[1] = isOn
|
|
||||||
alerts.follow = NSNumber(booleanLiteral: isOn)
|
|
||||||
case L10n.Scene.Settings.Section.Notifications.boosts:
|
|
||||||
alertValues[2] = isOn
|
|
||||||
alerts.reblog = NSNumber(booleanLiteral: isOn)
|
|
||||||
case L10n.Scene.Settings.Section.Notifications.mentions:
|
|
||||||
alertValues[3] = isOn
|
|
||||||
alerts.mention = NSNumber(booleanLiteral: isOn)
|
|
||||||
default: break
|
|
||||||
}
|
|
||||||
self.viewModel.updateSubscriptionSubject.send((triggerBy: triggerBy, values: alertValues))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -435,7 +441,7 @@ extension SettingsViewController {
|
||||||
guard let setting: Setting? = {
|
guard let setting: Setting? = {
|
||||||
let domain = box.domain
|
let domain = box.domain
|
||||||
let request = Setting.sortedFetchRequest
|
let request = Setting.sortedFetchRequest
|
||||||
request.predicate = Setting.predicate(domain: domain)
|
request.predicate = Setting.predicate(domain: domain, userID: box.userID)
|
||||||
request.fetchLimit = 1
|
request.fetchLimit = 1
|
||||||
request.returnsObjectsAsFaults = false
|
request.returnsObjectsAsFaults = false
|
||||||
do {
|
do {
|
||||||
|
|
|
@ -29,7 +29,7 @@ class SettingsViewModel: NSObject, NeedsDependency {
|
||||||
if let box =
|
if let box =
|
||||||
self.context.authenticationService.activeMastodonAuthenticationBox.value {
|
self.context.authenticationService.activeMastodonAuthenticationBox.value {
|
||||||
let domain = box.domain
|
let domain = box.domain
|
||||||
fetchRequest.predicate = Setting.predicate(domain: domain)
|
fetchRequest.predicate = Setting.predicate(domain: domain, userID: box.userID)
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchRequest.fetchLimit = 1
|
fetchRequest.fetchLimit = 1
|
||||||
|
@ -78,6 +78,9 @@ class SettingsViewModel: NSObject, NeedsDependency {
|
||||||
return Mastodon.API.privacyURL(domain: box.domain)
|
return Mastodon.API.privacyURL(domain: box.domain)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
/// to store who trigger the notification.
|
||||||
|
var triggerBy: String?
|
||||||
|
|
||||||
struct Input {
|
struct Input {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,12 +124,14 @@ class SettingsViewModel: NSObject, NeedsDependency {
|
||||||
follow: values[1],
|
follow: values[1],
|
||||||
reblog: values[2],
|
reblog: values[2],
|
||||||
mention: values[3],
|
mention: values[3],
|
||||||
poll: nil)
|
poll: nil
|
||||||
|
)
|
||||||
self.context.apiService.changeSubscription(
|
self.context.apiService.changeSubscription(
|
||||||
domain: domain,
|
domain: domain,
|
||||||
mastodonAuthenticationBox: activeMastodonAuthenticationBox,
|
mastodonAuthenticationBox: activeMastodonAuthenticationBox,
|
||||||
query: query,
|
query: query,
|
||||||
triggerBy: triggerBy
|
triggerBy: triggerBy,
|
||||||
|
userID: activeMastodonAuthenticationBox.userID
|
||||||
)
|
)
|
||||||
.sink { (_) in
|
.sink { (_) in
|
||||||
} receiveValue: { (_) in
|
} receiveValue: { (_) in
|
||||||
|
@ -164,7 +169,8 @@ class SettingsViewModel: NSObject, NeedsDependency {
|
||||||
domain: domain,
|
domain: domain,
|
||||||
mastodonAuthenticationBox: activeMastodonAuthenticationBox,
|
mastodonAuthenticationBox: activeMastodonAuthenticationBox,
|
||||||
query: query,
|
query: query,
|
||||||
triggerBy: triggerBy
|
triggerBy: triggerBy,
|
||||||
|
userID: activeMastodonAuthenticationBox.userID
|
||||||
)
|
)
|
||||||
.sink { (_) in
|
.sink { (_) in
|
||||||
} receiveValue: { (_) in
|
} receiveValue: { (_) in
|
||||||
|
@ -178,13 +184,6 @@ class SettingsViewModel: NSObject, NeedsDependency {
|
||||||
|
|
||||||
// request subsription data for updating or initialization
|
// request subsription data for updating or initialization
|
||||||
requestSubscription()
|
requestSubscription()
|
||||||
|
|
||||||
do {
|
|
||||||
try fetchResultsController.performFetch()
|
|
||||||
setting.value = fetchResultsController.fetchedObjects?.first
|
|
||||||
} catch {
|
|
||||||
assertionFailure(error.localizedDescription)
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -213,12 +212,12 @@ class SettingsViewModel: NSObject, NeedsDependency {
|
||||||
} else if let triggerBy = settings?.triggerBy,
|
} else if let triggerBy = settings?.triggerBy,
|
||||||
let values = self.notificationDefaultValue[triggerBy] {
|
let values = self.notificationDefaultValue[triggerBy] {
|
||||||
switches = values
|
switches = values
|
||||||
self.createSubscriptionSubject.send((triggerBy: triggerBy, values: values))
|
|
||||||
} else {
|
} else {
|
||||||
// fallback a default value
|
// fallback a default value
|
||||||
let anyone = L10n.Scene.Settings.Section.Notifications.Trigger.anyone
|
let anyone = L10n.Scene.Settings.Section.Notifications.Trigger.anyone
|
||||||
switches = self.notificationDefaultValue[anyone]
|
switches = self.notificationDefaultValue[anyone]
|
||||||
}
|
}
|
||||||
|
|
||||||
let notifications = [L10n.Scene.Settings.Section.Notifications.favorites,
|
let notifications = [L10n.Scene.Settings.Section.Notifications.favorites,
|
||||||
L10n.Scene.Settings.Section.Notifications.follows,
|
L10n.Scene.Settings.Section.Notifications.follows,
|
||||||
L10n.Scene.Settings.Section.Notifications.boosts,
|
L10n.Scene.Settings.Section.Notifications.boosts,
|
||||||
|
@ -273,31 +272,61 @@ class SettingsViewModel: NSObject, NeedsDependency {
|
||||||
}
|
}
|
||||||
|
|
||||||
private func requestSubscription() {
|
private func requestSubscription() {
|
||||||
// request subscription of notifications
|
setting.sink { [weak self] (settings) in
|
||||||
typealias SubscriptionResponse = Mastodon.Response.Content<Mastodon.Entity.Subscription>
|
guard let self = self else { return }
|
||||||
viewDidLoad.flatMap { [weak self] (_) -> AnyPublisher<SubscriptionResponse, Error> in
|
guard settings != nil else { return }
|
||||||
guard let self = self,
|
guard self.triggerBy != settings?.triggerBy else { return }
|
||||||
let activeMastodonAuthenticationBox =
|
self.triggerBy = settings?.triggerBy
|
||||||
self.context.authenticationService.activeMastodonAuthenticationBox.value else {
|
|
||||||
return Empty<SubscriptionResponse, Error>().eraseToAnyPublisher()
|
var switches: [Bool?]?
|
||||||
|
var who: String?
|
||||||
|
if let alerts = settings?.subscription?.first(where: { (s) -> Bool in
|
||||||
|
return s.type == settings?.triggerBy
|
||||||
|
})?.alert {
|
||||||
|
var items = [Bool?]()
|
||||||
|
items.append(alerts.favourite?.boolValue)
|
||||||
|
items.append(alerts.follow?.boolValue)
|
||||||
|
items.append(alerts.reblog?.boolValue)
|
||||||
|
items.append(alerts.mention?.boolValue)
|
||||||
|
switches = items
|
||||||
|
who = settings?.triggerBy
|
||||||
|
} else if let triggerBy = settings?.triggerBy,
|
||||||
|
let values = self.notificationDefaultValue[triggerBy] {
|
||||||
|
switches = values
|
||||||
|
who = triggerBy
|
||||||
|
} else {
|
||||||
|
// fallback a default value
|
||||||
|
let anyone = L10n.Scene.Settings.Section.Notifications.Trigger.anyone
|
||||||
|
switches = self.notificationDefaultValue[anyone]
|
||||||
|
who = anyone
|
||||||
}
|
}
|
||||||
|
|
||||||
let domain = activeMastodonAuthenticationBox.domain
|
// should create a subscription whenever change trigger
|
||||||
return self.context.apiService.subscription(
|
if let values = switches, let triggerBy = who {
|
||||||
domain: domain,
|
self.createSubscriptionSubject.send((triggerBy: triggerBy, values: values))
|
||||||
mastodonAuthenticationBox: activeMastodonAuthenticationBox)
|
|
||||||
}
|
|
||||||
.sink { [weak self] competion in
|
|
||||||
if case .failure(_) = competion {
|
|
||||||
// create a subscription when doesn't has one
|
|
||||||
let anyone = L10n.Scene.Settings.Section.Notifications.Trigger.anyone
|
|
||||||
if let values = self?.notificationDefaultValue[anyone] {
|
|
||||||
self?.createSubscriptionSubject.send((triggerBy: anyone, values: values))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} receiveValue: { (subscription) in
|
|
||||||
}
|
}
|
||||||
.store(in: &disposeBag)
|
.store(in: &disposeBag)
|
||||||
|
|
||||||
|
guard let activeMastodonAuthenticationBox = self.context.authenticationService.activeMastodonAuthenticationBox.value else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let domain = activeMastodonAuthenticationBox.domain
|
||||||
|
let userId = activeMastodonAuthenticationBox.userID
|
||||||
|
|
||||||
|
do {
|
||||||
|
try fetchResultsController.performFetch()
|
||||||
|
if nil == fetchResultsController.fetchedObjects?.first {
|
||||||
|
let anyone = L10n.Scene.Settings.Section.Notifications.Trigger.anyone
|
||||||
|
setting.value = self.context.apiService.createSettingIfNeed(domain: domain,
|
||||||
|
userId: userId,
|
||||||
|
triggerBy: anyone)
|
||||||
|
} else {
|
||||||
|
setting.value = fetchResultsController.fetchedObjects?.first
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
assertionFailure(error.localizedDescription)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
|
|
|
@ -5,44 +5,71 @@
|
||||||
// Created by ihugo on 2021/4/9.
|
// Created by ihugo on 2021/4/9.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
import Combine
|
||||||
|
import CoreData
|
||||||
|
import CoreDataStack
|
||||||
import Foundation
|
import Foundation
|
||||||
import MastodonSDK
|
import MastodonSDK
|
||||||
import Combine
|
|
||||||
|
|
||||||
extension APIService {
|
extension APIService {
|
||||||
|
|
||||||
func subscription(
|
func subscription(
|
||||||
domain: String,
|
domain: String,
|
||||||
|
userID: String,
|
||||||
mastodonAuthenticationBox: AuthenticationService.MastodonAuthenticationBox
|
mastodonAuthenticationBox: AuthenticationService.MastodonAuthenticationBox
|
||||||
) -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Subscription>, Error> {
|
) -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Subscription>, Error> {
|
||||||
let authorization = mastodonAuthenticationBox.userAuthorization
|
let authorization = mastodonAuthenticationBox.userAuthorization
|
||||||
|
|
||||||
|
let findSettings: Setting? = {
|
||||||
|
let request = Setting.sortedFetchRequest
|
||||||
|
request.predicate = Setting.predicate(domain: domain, userID: userID)
|
||||||
|
request.fetchLimit = 1
|
||||||
|
request.returnsObjectsAsFaults = false
|
||||||
|
do {
|
||||||
|
return try self.backgroundManagedObjectContext.fetch(request).first
|
||||||
|
} catch {
|
||||||
|
assertionFailure(error.localizedDescription)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
let triggerBy = findSettings?.triggerBy ?? "anyone"
|
||||||
|
let setting = self.createSettingIfNeed(
|
||||||
|
domain: domain,
|
||||||
|
userId: userID,
|
||||||
|
triggerBy: triggerBy
|
||||||
|
)
|
||||||
return Mastodon.API.Subscriptions.subscription(
|
return Mastodon.API.Subscriptions.subscription(
|
||||||
session: session,
|
session: session,
|
||||||
domain: domain,
|
domain: domain,
|
||||||
authorization: authorization)
|
authorization: authorization
|
||||||
.flatMap { response -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Subscription>, Error> in
|
)
|
||||||
return self.backgroundManagedObjectContext.performChanges {
|
.flatMap { response -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Subscription>, Error> in
|
||||||
_ = APIService.CoreData.createOrMergeSubscription(
|
return self.backgroundManagedObjectContext.performChanges {
|
||||||
into: self.backgroundManagedObjectContext,
|
_ = APIService.CoreData.createOrMergeSubscription(
|
||||||
entity: response.value,
|
into: self.backgroundManagedObjectContext,
|
||||||
domain: domain)
|
entity: response.value,
|
||||||
}
|
domain: domain,
|
||||||
.setFailureType(to: Error.self)
|
triggerBy: triggerBy,
|
||||||
.map { _ in return response }
|
setting: setting)
|
||||||
.eraseToAnyPublisher()
|
}
|
||||||
}.eraseToAnyPublisher()
|
.setFailureType(to: Error.self)
|
||||||
|
.map { _ in return response }
|
||||||
|
.eraseToAnyPublisher()
|
||||||
|
}.eraseToAnyPublisher()
|
||||||
}
|
}
|
||||||
|
|
||||||
func changeSubscription(
|
func changeSubscription(
|
||||||
domain: String,
|
domain: String,
|
||||||
mastodonAuthenticationBox: AuthenticationService.MastodonAuthenticationBox,
|
mastodonAuthenticationBox: AuthenticationService.MastodonAuthenticationBox,
|
||||||
query: Mastodon.API.Subscriptions.CreateSubscriptionQuery,
|
query: Mastodon.API.Subscriptions.CreateSubscriptionQuery,
|
||||||
triggerBy: String
|
triggerBy: String,
|
||||||
|
userID: String
|
||||||
) -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Subscription>, Error> {
|
) -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Subscription>, Error> {
|
||||||
let authorization = mastodonAuthenticationBox.userAuthorization
|
let authorization = mastodonAuthenticationBox.userAuthorization
|
||||||
|
|
||||||
|
let setting = self.createSettingIfNeed(domain: domain,
|
||||||
|
userId: userID,
|
||||||
|
triggerBy: triggerBy)
|
||||||
return Mastodon.API.Subscriptions.createSubscription(
|
return Mastodon.API.Subscriptions.createSubscription(
|
||||||
session: session,
|
session: session,
|
||||||
domain: domain,
|
domain: domain,
|
||||||
|
@ -55,7 +82,9 @@ extension APIService {
|
||||||
into: self.backgroundManagedObjectContext,
|
into: self.backgroundManagedObjectContext,
|
||||||
entity: response.value,
|
entity: response.value,
|
||||||
domain: domain,
|
domain: domain,
|
||||||
triggerBy: triggerBy)
|
triggerBy: triggerBy,
|
||||||
|
setting: setting
|
||||||
|
)
|
||||||
}
|
}
|
||||||
.setFailureType(to: Error.self)
|
.setFailureType(to: Error.self)
|
||||||
.map { _ in return response }
|
.map { _ in return response }
|
||||||
|
@ -67,10 +96,15 @@ extension APIService {
|
||||||
domain: String,
|
domain: String,
|
||||||
mastodonAuthenticationBox: AuthenticationService.MastodonAuthenticationBox,
|
mastodonAuthenticationBox: AuthenticationService.MastodonAuthenticationBox,
|
||||||
query: Mastodon.API.Subscriptions.UpdateSubscriptionQuery,
|
query: Mastodon.API.Subscriptions.UpdateSubscriptionQuery,
|
||||||
triggerBy: String
|
triggerBy: String,
|
||||||
|
userID: String
|
||||||
) -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Subscription>, Error> {
|
) -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Subscription>, Error> {
|
||||||
let authorization = mastodonAuthenticationBox.userAuthorization
|
let authorization = mastodonAuthenticationBox.userAuthorization
|
||||||
|
|
||||||
|
let setting = self.createSettingIfNeed(domain: domain,
|
||||||
|
userId: userID,
|
||||||
|
triggerBy: triggerBy)
|
||||||
|
|
||||||
return Mastodon.API.Subscriptions.updateSubscription(
|
return Mastodon.API.Subscriptions.updateSubscription(
|
||||||
session: session,
|
session: session,
|
||||||
domain: domain,
|
domain: domain,
|
||||||
|
@ -83,12 +117,47 @@ extension APIService {
|
||||||
into: self.backgroundManagedObjectContext,
|
into: self.backgroundManagedObjectContext,
|
||||||
entity: response.value,
|
entity: response.value,
|
||||||
domain: domain,
|
domain: domain,
|
||||||
triggerBy: triggerBy)
|
triggerBy: triggerBy,
|
||||||
|
setting: setting
|
||||||
|
)
|
||||||
}
|
}
|
||||||
.setFailureType(to: Error.self)
|
.setFailureType(to: Error.self)
|
||||||
.map { _ in return response }
|
.map { _ in return response }
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
}.eraseToAnyPublisher()
|
}.eraseToAnyPublisher()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func createSettingIfNeed(domain: String, userId: String, triggerBy: String) -> Setting {
|
||||||
|
// create setting entity if possible
|
||||||
|
let oldSetting: Setting? = {
|
||||||
|
let request = Setting.sortedFetchRequest
|
||||||
|
request.predicate = Setting.predicate(domain: domain, userID: userId)
|
||||||
|
request.fetchLimit = 1
|
||||||
|
request.returnsObjectsAsFaults = false
|
||||||
|
do {
|
||||||
|
return try backgroundManagedObjectContext.fetch(request).first
|
||||||
|
} catch {
|
||||||
|
assertionFailure(error.localizedDescription)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
var setting: Setting!
|
||||||
|
if let oldSetting = oldSetting {
|
||||||
|
setting = oldSetting
|
||||||
|
} else {
|
||||||
|
let property = Setting.Property(
|
||||||
|
appearance: "automatic",
|
||||||
|
triggerBy: triggerBy,
|
||||||
|
domain: domain,
|
||||||
|
userID: userId)
|
||||||
|
(setting, _) = APIService.CoreData.createOrMergeSetting(
|
||||||
|
into: backgroundManagedObjectContext,
|
||||||
|
domain: domain,
|
||||||
|
userID: userId,
|
||||||
|
property: property
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return setting
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,11 +16,12 @@ extension APIService.CoreData {
|
||||||
static func createOrMergeSetting(
|
static func createOrMergeSetting(
|
||||||
into managedObjectContext: NSManagedObjectContext,
|
into managedObjectContext: NSManagedObjectContext,
|
||||||
domain: String,
|
domain: String,
|
||||||
|
userID: String,
|
||||||
property: Setting.Property
|
property: Setting.Property
|
||||||
) -> (Subscription: Setting, isCreated: Bool) {
|
) -> (Subscription: Setting, isCreated: Bool) {
|
||||||
let oldSetting: Setting? = {
|
let oldSetting: Setting? = {
|
||||||
let request = Setting.sortedFetchRequest
|
let request = Setting.sortedFetchRequest
|
||||||
request.predicate = Setting.predicate(domain: property.domain)
|
request.predicate = Setting.predicate(domain: property.domain, userID: userID)
|
||||||
request.fetchLimit = 1
|
request.fetchLimit = 1
|
||||||
request.returnsObjectsAsFaults = false
|
request.returnsObjectsAsFaults = false
|
||||||
do {
|
do {
|
||||||
|
@ -45,38 +46,12 @@ extension APIService.CoreData {
|
||||||
into managedObjectContext: NSManagedObjectContext,
|
into managedObjectContext: NSManagedObjectContext,
|
||||||
entity: Mastodon.Entity.Subscription,
|
entity: Mastodon.Entity.Subscription,
|
||||||
domain: String,
|
domain: String,
|
||||||
triggerBy: String? = nil
|
triggerBy: String,
|
||||||
|
setting: Setting
|
||||||
) -> (Subscription: Subscription, isCreated: Bool) {
|
) -> (Subscription: Subscription, isCreated: Bool) {
|
||||||
// create setting entity if possible
|
|
||||||
let oldSetting: Setting? = {
|
|
||||||
let request = Setting.sortedFetchRequest
|
|
||||||
request.predicate = Setting.predicate(domain: domain)
|
|
||||||
request.fetchLimit = 1
|
|
||||||
request.returnsObjectsAsFaults = false
|
|
||||||
do {
|
|
||||||
return try managedObjectContext.fetch(request).first
|
|
||||||
} catch {
|
|
||||||
assertionFailure(error.localizedDescription)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
var setting: Setting!
|
|
||||||
if let oldSetting = oldSetting {
|
|
||||||
setting = oldSetting
|
|
||||||
} else {
|
|
||||||
let property = Setting.Property(
|
|
||||||
appearance: "automatic",
|
|
||||||
triggerBy: "anyone",
|
|
||||||
domain: domain)
|
|
||||||
(setting, _) = createOrMergeSetting(
|
|
||||||
into: managedObjectContext,
|
|
||||||
domain: domain,
|
|
||||||
property: property)
|
|
||||||
}
|
|
||||||
|
|
||||||
let oldSubscription: Subscription? = {
|
let oldSubscription: Subscription? = {
|
||||||
let request = Subscription.sortedFetchRequest
|
let request = Subscription.sortedFetchRequest
|
||||||
request.predicate = Subscription.predicate(id: entity.id)
|
request.predicate = Subscription.predicate(type: triggerBy)
|
||||||
request.fetchLimit = 1
|
request.fetchLimit = 1
|
||||||
request.returnsObjectsAsFaults = false
|
request.returnsObjectsAsFaults = false
|
||||||
do {
|
do {
|
||||||
|
@ -91,7 +66,8 @@ extension APIService.CoreData {
|
||||||
endpoint: entity.endpoint,
|
endpoint: entity.endpoint,
|
||||||
id: entity.id,
|
id: entity.id,
|
||||||
serverKey: entity.serverKey,
|
serverKey: entity.serverKey,
|
||||||
type: triggerBy ?? setting.triggerBy ?? "")
|
type: triggerBy
|
||||||
|
)
|
||||||
let alertEntity = entity.alerts
|
let alertEntity = entity.alerts
|
||||||
let alert = SubscriptionAlerts.Property(
|
let alert = SubscriptionAlerts.Property(
|
||||||
favourite: alertEntity.favouriteNumber,
|
favourite: alertEntity.favouriteNumber,
|
||||||
|
@ -105,7 +81,8 @@ extension APIService.CoreData {
|
||||||
if nil == oldSubscription.alert {
|
if nil == oldSubscription.alert {
|
||||||
oldSubscription.alert = SubscriptionAlerts.insert(
|
oldSubscription.alert = SubscriptionAlerts.insert(
|
||||||
into: managedObjectContext,
|
into: managedObjectContext,
|
||||||
property: alert)
|
property: alert
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
oldSubscription.alert?.updateIfNeed(property: alert)
|
oldSubscription.alert?.updateIfNeed(property: alert)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue