forked from zelo72/mastodon-ios
fixed: subscription API call
This commit is contained in:
parent
b2b8b70707
commit
23a06f04ab
|
@ -50,6 +50,7 @@ public extension Subscription {
|
||||||
setting.id = property.id
|
setting.id = property.id
|
||||||
setting.endpoint = property.endpoint
|
setting.endpoint = property.endpoint
|
||||||
setting.serverKey = property.serverKey
|
setting.serverKey = property.serverKey
|
||||||
|
setting.type = property.type
|
||||||
|
|
||||||
return setting
|
return setting
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,8 @@ import ActiveLabel
|
||||||
import CoreData
|
import CoreData
|
||||||
import CoreDataStack
|
import CoreDataStack
|
||||||
|
|
||||||
|
// iTODO: when to ask permission to Use Notifications
|
||||||
|
|
||||||
class SettingsViewController: UIViewController, NeedsDependency {
|
class SettingsViewController: UIViewController, NeedsDependency {
|
||||||
|
|
||||||
weak var context: AppContext! { willSet { precondition(!isViewLoaded) } }
|
weak var context: AppContext! { willSet { precondition(!isViewLoaded) } }
|
||||||
|
@ -26,7 +28,7 @@ class SettingsViewController: UIViewController, NeedsDependency {
|
||||||
let follow = L10n.Scene.Settings.Section.Notifications.Trigger.follow
|
let follow = L10n.Scene.Settings.Section.Notifications.Trigger.follow
|
||||||
let noOne = L10n.Scene.Settings.Section.Notifications.Trigger.noOne
|
let noOne = L10n.Scene.Settings.Section.Notifications.Trigger.noOne
|
||||||
let menu = UIMenu(
|
let menu = UIMenu(
|
||||||
image: UIImage(systemName: "escape"),
|
image: nil,
|
||||||
identifier: nil,
|
identifier: nil,
|
||||||
options: .displayInline,
|
options: .displayInline,
|
||||||
children: [
|
children: [
|
||||||
|
@ -173,7 +175,9 @@ class SettingsViewController: UIViewController, NeedsDependency {
|
||||||
}
|
}
|
||||||
|
|
||||||
private func setupTableView() {
|
private func setupTableView() {
|
||||||
viewModel.dataSource = UITableViewDiffableDataSource(tableView: tableView, cellProvider: { (tableView, indexPath, item) -> UITableViewCell? in
|
viewModel.dataSource = UITableViewDiffableDataSource(tableView: tableView, cellProvider: { [weak self] (tableView, indexPath, item) -> UITableViewCell? in
|
||||||
|
guard let self = self else { return nil }
|
||||||
|
|
||||||
switch item {
|
switch item {
|
||||||
case .apperance(let item):
|
case .apperance(let item):
|
||||||
guard let cell = tableView.dequeueReusableCell(withIdentifier: "SettingsAppearanceTableViewCell") as? SettingsAppearanceTableViewCell else {
|
guard let cell = tableView.dequeueReusableCell(withIdentifier: "SettingsAppearanceTableViewCell") as? SettingsAppearanceTableViewCell else {
|
||||||
|
@ -227,6 +231,10 @@ class SettingsViewController: UIViewController, NeedsDependency {
|
||||||
.store(in: &disposeBag)
|
.store(in: &disposeBag)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s:", ((#file as NSString).lastPathComponent), #line, #function)
|
||||||
|
}
|
||||||
|
|
||||||
// Mark: - Actions
|
// Mark: - Actions
|
||||||
@objc func doneButtonDidClick() {
|
@objc func doneButtonDidClick() {
|
||||||
dismiss(animated: true, completion: nil)
|
dismiss(animated: true, completion: nil)
|
||||||
|
@ -306,38 +314,39 @@ 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 }
|
||||||
|
|
||||||
var values: [Bool?]?
|
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 {
|
})?.alert else {
|
||||||
var items = [Bool?]()
|
return
|
||||||
items.append(alerts.favourite)
|
|
||||||
items.append(alerts.follow)
|
|
||||||
items.append(alerts.reblog)
|
|
||||||
items.append(alerts.mention)
|
|
||||||
values = items
|
|
||||||
}
|
}
|
||||||
guard var alertValues = values else { return }
|
var alertValues = [Bool?]()
|
||||||
guard alertValues.count >= 4 else { return }
|
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 {
|
switch title {
|
||||||
case L10n.Scene.Settings.Section.Notifications.favorites:
|
case L10n.Scene.Settings.Section.Notifications.favorites:
|
||||||
alertValues[0] = isOn
|
alertValues[0] = isOn
|
||||||
|
alerts.favourite = NSNumber(booleanLiteral: isOn)
|
||||||
case L10n.Scene.Settings.Section.Notifications.follows:
|
case L10n.Scene.Settings.Section.Notifications.follows:
|
||||||
alertValues[1] = isOn
|
alertValues[1] = isOn
|
||||||
|
alerts.follow = NSNumber(booleanLiteral: isOn)
|
||||||
case L10n.Scene.Settings.Section.Notifications.boosts:
|
case L10n.Scene.Settings.Section.Notifications.boosts:
|
||||||
alertValues[2] = isOn
|
alertValues[2] = isOn
|
||||||
|
alerts.reblog = NSNumber(booleanLiteral: isOn)
|
||||||
case L10n.Scene.Settings.Section.Notifications.mentions:
|
case L10n.Scene.Settings.Section.Notifications.mentions:
|
||||||
alertValues[3] = isOn
|
alertValues[3] = isOn
|
||||||
|
alerts.mention = NSNumber(booleanLiteral: isOn)
|
||||||
default: break
|
default: break
|
||||||
}
|
}
|
||||||
self.viewModel.alertUpdate.send((triggerBy: triggerBy, values: alertValues))
|
self.viewModel.updateSubscriptionSubject.send((triggerBy: triggerBy, values: alertValues))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension SettingsViewController: SettingsAppearanceTableViewCellDelegate {
|
extension SettingsViewController: SettingsAppearanceTableViewCellDelegate {
|
||||||
func settingsAppearanceCell(_ view: SettingsAppearanceTableViewCell, didSelect: SettingsItem.AppearanceMode) {
|
func settingsAppearanceCell(_ view: SettingsAppearanceTableViewCell, didSelect: SettingsItem.AppearanceMode) {
|
||||||
print("[SettingsViewController]: didSelect \(didSelect)")
|
|
||||||
guard let setting = self.viewModel.setting.value else { return }
|
guard let setting = self.viewModel.setting.value else { return }
|
||||||
|
|
||||||
context.managedObjectContext.performChanges {
|
context.managedObjectContext.performChanges {
|
||||||
|
|
|
@ -20,6 +20,9 @@ class SettingsViewModel: NSObject, NeedsDependency {
|
||||||
|
|
||||||
var dataSource: UITableViewDiffableDataSource<SettingsSection, SettingsItem>!
|
var dataSource: UITableViewDiffableDataSource<SettingsSection, SettingsItem>!
|
||||||
var disposeBag = Set<AnyCancellable>()
|
var disposeBag = Set<AnyCancellable>()
|
||||||
|
var updateDisposeBag = Set<AnyCancellable>()
|
||||||
|
var createDisposeBag = Set<AnyCancellable>()
|
||||||
|
|
||||||
let viewDidLoad = PassthroughSubject<Void, Never>()
|
let viewDidLoad = PassthroughSubject<Void, Never>()
|
||||||
lazy var fetchResultsController: NSFetchedResultsController<Setting> = {
|
lazy var fetchResultsController: NSFetchedResultsController<Setting> = {
|
||||||
let fetchRequest = Setting.sortedFetchRequest
|
let fetchRequest = Setting.sortedFetchRequest
|
||||||
|
@ -42,10 +45,14 @@ class SettingsViewModel: NSObject, NeedsDependency {
|
||||||
}()
|
}()
|
||||||
let setting = CurrentValueSubject<Setting?, Never>(nil)
|
let setting = CurrentValueSubject<Setting?, Never>(nil)
|
||||||
|
|
||||||
/// trigger when
|
/// create a subscription when:
|
||||||
/// - init alerts
|
/// - does not has one
|
||||||
/// - change subscription status everytime
|
/// - does not find subscription for selected trigger when change trigger
|
||||||
let alertUpdate = PassthroughSubject<(triggerBy: String, values: [Bool?]), Never>()
|
let createSubscriptionSubject = PassthroughSubject<(triggerBy: String, values: [Bool?]), Never>()
|
||||||
|
|
||||||
|
/// update a subscription when:
|
||||||
|
/// - change switch for specified alerts
|
||||||
|
let updateSubscriptionSubject = PassthroughSubject<(triggerBy: String, values: [Bool?]), Never>()
|
||||||
|
|
||||||
lazy var notificationDefaultValue: [String: [Bool?]] = {
|
lazy var notificationDefaultValue: [String: [Bool?]] = {
|
||||||
let followerSwitchItems: [Bool?] = [true, nil, true, true]
|
let followerSwitchItems: [Bool?] = [true, nil, true, true]
|
||||||
|
@ -77,7 +84,85 @@ class SettingsViewModel: NSObject, NeedsDependency {
|
||||||
}
|
}
|
||||||
|
|
||||||
func transform(input: Input?) -> Output? {
|
func transform(input: Input?) -> Output? {
|
||||||
//guard let input = input else { return nil }
|
typealias SubscriptionResponse = Mastodon.Response.Content<Mastodon.Entity.Subscription>
|
||||||
|
createSubscriptionSubject
|
||||||
|
.debounce(for: .milliseconds(300), scheduler: DispatchQueue.main)
|
||||||
|
.sink { _ in
|
||||||
|
} receiveValue: { [weak self] (arg) in
|
||||||
|
let (triggerBy, values) = arg
|
||||||
|
guard let self = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
guard let activeMastodonAuthenticationBox =
|
||||||
|
self.context.authenticationService.activeMastodonAuthenticationBox.value else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
guard values.count >= 4 else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
self.createDisposeBag.removeAll()
|
||||||
|
typealias Query = Mastodon.API.Notification.CreateSubscriptionQuery
|
||||||
|
let domain = activeMastodonAuthenticationBox.domain
|
||||||
|
let query = Query(
|
||||||
|
endpoint: "http://www.google.com",
|
||||||
|
p256dh: "BLQELIDm-6b9Bl07YrEuXJ4BL_YBVQ0dvt9NQGGJxIQidJWHPNa9YrouvcQ9d7_MqzvGS9Alz60SZNCG3qfpk=",
|
||||||
|
auth: "4vQK-SvRAN5eo-8ASlrwA==",
|
||||||
|
favourite: values[0],
|
||||||
|
follow: values[1],
|
||||||
|
reblog: values[2],
|
||||||
|
mention: values[3],
|
||||||
|
poll: nil)
|
||||||
|
self.context.apiService.changeSubscription(
|
||||||
|
domain: domain,
|
||||||
|
mastodonAuthenticationBox: activeMastodonAuthenticationBox,
|
||||||
|
query: query,
|
||||||
|
triggerBy: triggerBy
|
||||||
|
)
|
||||||
|
.sink { (_) in
|
||||||
|
} receiveValue: { (_) in
|
||||||
|
}
|
||||||
|
.store(in: &self.createDisposeBag)
|
||||||
|
}
|
||||||
|
.store(in: &disposeBag)
|
||||||
|
|
||||||
|
updateSubscriptionSubject
|
||||||
|
.debounce(for: .milliseconds(300), scheduler: DispatchQueue.main)
|
||||||
|
.sink { _ in
|
||||||
|
} receiveValue: { [weak self] (arg) in
|
||||||
|
let (triggerBy, values) = arg
|
||||||
|
guard let self = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
guard let activeMastodonAuthenticationBox =
|
||||||
|
self.context.authenticationService.activeMastodonAuthenticationBox.value else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
guard values.count >= 4 else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
self.updateDisposeBag.removeAll()
|
||||||
|
typealias Query = Mastodon.API.Notification.UpdateSubscriptionQuery
|
||||||
|
let domain = activeMastodonAuthenticationBox.domain
|
||||||
|
let query = Query(
|
||||||
|
favourite: values[0],
|
||||||
|
follow: values[1],
|
||||||
|
reblog: values[2],
|
||||||
|
mention: values[3],
|
||||||
|
poll: nil)
|
||||||
|
self.context.apiService.updateSubscription(
|
||||||
|
domain: domain,
|
||||||
|
mastodonAuthenticationBox: activeMastodonAuthenticationBox,
|
||||||
|
query: query,
|
||||||
|
triggerBy: triggerBy
|
||||||
|
)
|
||||||
|
.sink { (_) in
|
||||||
|
} receiveValue: { (_) in
|
||||||
|
}
|
||||||
|
.store(in: &self.updateDisposeBag)
|
||||||
|
}
|
||||||
|
.store(in: &disposeBag)
|
||||||
|
|
||||||
// build data for table view
|
// build data for table view
|
||||||
buildDataSource()
|
buildDataSource()
|
||||||
|
@ -85,36 +170,6 @@ class SettingsViewModel: NSObject, NeedsDependency {
|
||||||
// request subsription data for updating or initialization
|
// request subsription data for updating or initialization
|
||||||
requestSubscription()
|
requestSubscription()
|
||||||
|
|
||||||
typealias SubscriptionResponse = Mastodon.Response.Content<Mastodon.Entity.Subscription>
|
|
||||||
alertUpdate
|
|
||||||
.debounce(for: .milliseconds(300), scheduler: DispatchQueue.main)
|
|
||||||
.flatMap { [weak self] (arg) -> AnyPublisher<SubscriptionResponse, Error> in
|
|
||||||
let (triggerBy, values) = arg
|
|
||||||
guard let self = self else {
|
|
||||||
return Empty<SubscriptionResponse, Error>().eraseToAnyPublisher()
|
|
||||||
}
|
|
||||||
guard let activeMastodonAuthenticationBox =
|
|
||||||
self.context.authenticationService.activeMastodonAuthenticationBox.value else {
|
|
||||||
return Empty<SubscriptionResponse, Error>().eraseToAnyPublisher()
|
|
||||||
}
|
|
||||||
guard values.count >= 4 else {
|
|
||||||
return Empty<SubscriptionResponse, Error>().eraseToAnyPublisher()
|
|
||||||
}
|
|
||||||
|
|
||||||
typealias Query = Mastodon.API.Notification.CreateSubscriptionQuery
|
|
||||||
let domain = activeMastodonAuthenticationBox.domain
|
|
||||||
return self.context.apiService.changeSubscription(
|
|
||||||
domain: domain,
|
|
||||||
mastodonAuthenticationBox: activeMastodonAuthenticationBox,
|
|
||||||
query: Query(favourite: values[0], follow: values[1], reblog: values[2], mention: values[3], poll: nil),
|
|
||||||
triggerBy: triggerBy)
|
|
||||||
}
|
|
||||||
.sink { _ in
|
|
||||||
} receiveValue: { (subscription) in
|
|
||||||
}
|
|
||||||
.store(in: &disposeBag)
|
|
||||||
|
|
||||||
|
|
||||||
do {
|
do {
|
||||||
try fetchResultsController.performFetch()
|
try fetchResultsController.performFetch()
|
||||||
setting.value = fetchResultsController.fetchedObjects?.first
|
setting.value = fetchResultsController.fetchedObjects?.first
|
||||||
|
@ -141,15 +196,15 @@ class SettingsViewModel: NSObject, NeedsDependency {
|
||||||
return s.type == settings?.triggerBy
|
return s.type == settings?.triggerBy
|
||||||
})?.alert {
|
})?.alert {
|
||||||
var items = [Bool?]()
|
var items = [Bool?]()
|
||||||
items.append(alerts.favourite)
|
items.append(alerts.favourite?.boolValue)
|
||||||
items.append(alerts.follow)
|
items.append(alerts.follow?.boolValue)
|
||||||
items.append(alerts.reblog)
|
items.append(alerts.reblog?.boolValue)
|
||||||
items.append(alerts.mention)
|
items.append(alerts.mention?.boolValue)
|
||||||
switches = items
|
switches = items
|
||||||
} 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.alertUpdate.send((triggerBy: triggerBy, values: 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
|
||||||
|
@ -178,7 +233,6 @@ class SettingsViewModel: NSObject, NeedsDependency {
|
||||||
L10n.Scene.Settings.Section.BoringZone.privacy]
|
L10n.Scene.Settings.Section.BoringZone.privacy]
|
||||||
var boringLinkItems = [SettingsItem]()
|
var boringLinkItems = [SettingsItem]()
|
||||||
for l in boringLinks {
|
for l in boringLinks {
|
||||||
// FIXME: update color in both light and dark mode
|
|
||||||
let item = SettingsItem.boringZone(item: SettingsItem.Link(title: l, color: .systemBlue))
|
let item = SettingsItem.boringZone(item: SettingsItem.Link(title: l, color: .systemBlue))
|
||||||
boringLinkItems.append(item)
|
boringLinkItems.append(item)
|
||||||
}
|
}
|
||||||
|
@ -191,7 +245,6 @@ class SettingsViewModel: NSObject, NeedsDependency {
|
||||||
L10n.Scene.Settings.Section.SpicyZone.signOut]
|
L10n.Scene.Settings.Section.SpicyZone.signOut]
|
||||||
var spicyLinkItems = [SettingsItem]()
|
var spicyLinkItems = [SettingsItem]()
|
||||||
for l in spicyLinks {
|
for l in spicyLinks {
|
||||||
// FIXME: update color in both light and dark mode
|
|
||||||
let item = SettingsItem.boringZone(item: SettingsItem.Link(title: l, color: .systemRed))
|
let item = SettingsItem.boringZone(item: SettingsItem.Link(title: l, color: .systemRed))
|
||||||
spicyLinkItems.append(item)
|
spicyLinkItems.append(item)
|
||||||
}
|
}
|
||||||
|
@ -203,15 +256,11 @@ class SettingsViewModel: NSObject, NeedsDependency {
|
||||||
}
|
}
|
||||||
|
|
||||||
private func buildDataSource() {
|
private func buildDataSource() {
|
||||||
setting.filter({ $0 != nil }).sink { [weak self] (settings) in
|
setting.sink { [weak self] (settings) in
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
self.processDataSource(settings)
|
self.processDataSource(settings)
|
||||||
}
|
}
|
||||||
.store(in: &disposeBag)
|
.store(in: &disposeBag)
|
||||||
|
|
||||||
// init with no subscription for notification
|
|
||||||
let settings: Setting? = nil
|
|
||||||
self.processDataSource(settings)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func requestSubscription() {
|
private func requestSubscription() {
|
||||||
|
@ -229,11 +278,22 @@ class SettingsViewModel: NSObject, NeedsDependency {
|
||||||
domain: domain,
|
domain: domain,
|
||||||
mastodonAuthenticationBox: activeMastodonAuthenticationBox)
|
mastodonAuthenticationBox: activeMastodonAuthenticationBox)
|
||||||
}
|
}
|
||||||
.sink { _ in
|
.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
|
} receiveValue: { (subscription) in
|
||||||
}
|
}
|
||||||
.store(in: &disposeBag)
|
.store(in: &disposeBag)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s:", ((#file as NSString).lastPathComponent), #line, #function)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - NSFetchedResultsControllerDelegate
|
// MARK: - NSFetchedResultsControllerDelegate
|
||||||
|
|
|
@ -86,12 +86,7 @@ class AppearanceView: UIView {
|
||||||
|
|
||||||
class SettingsAppearanceTableViewCell: UITableViewCell {
|
class SettingsAppearanceTableViewCell: UITableViewCell {
|
||||||
weak var delegate: SettingsAppearanceTableViewCellDelegate?
|
weak var delegate: SettingsAppearanceTableViewCellDelegate?
|
||||||
var appearance: SettingsItem.AppearanceMode = .automatic {
|
var appearance: SettingsItem.AppearanceMode = .automatic
|
||||||
didSet {
|
|
||||||
guard let delegate = self.delegate else { return }
|
|
||||||
delegate.settingsAppearanceCell(self, didSelect: appearance)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
lazy var stackView: UIStackView = {
|
lazy var stackView: UIStackView = {
|
||||||
let view = UIStackView()
|
let view = UIStackView()
|
||||||
|
@ -203,5 +198,8 @@ class SettingsAppearanceTableViewCell: UITableViewCell {
|
||||||
if sender == darkTap {
|
if sender == darkTap {
|
||||||
appearance = .dark
|
appearance = .dark
|
||||||
}
|
}
|
||||||
|
|
||||||
|
guard let delegate = self.delegate else { return }
|
||||||
|
delegate.settingsAppearanceCell(self, didSelect: appearance)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,5 +62,33 @@ extension APIService {
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
}.eraseToAnyPublisher()
|
}.eraseToAnyPublisher()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func updateSubscription(
|
||||||
|
domain: String,
|
||||||
|
mastodonAuthenticationBox: AuthenticationService.MastodonAuthenticationBox,
|
||||||
|
query: Mastodon.API.Notification.UpdateSubscriptionQuery,
|
||||||
|
triggerBy: String
|
||||||
|
) -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Subscription>, Error> {
|
||||||
|
let authorization = mastodonAuthenticationBox.userAuthorization
|
||||||
|
|
||||||
|
return Mastodon.API.Notification.updateSubscription(
|
||||||
|
session: session,
|
||||||
|
domain: domain,
|
||||||
|
authorization: authorization,
|
||||||
|
query: query
|
||||||
|
)
|
||||||
|
.flatMap { response -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Subscription>, Error> in
|
||||||
|
return self.backgroundManagedObjectContext.performChanges {
|
||||||
|
_ = APIService.CoreData.createOrMergeSubscription(
|
||||||
|
into: self.backgroundManagedObjectContext,
|
||||||
|
entity: response.value,
|
||||||
|
domain: domain,
|
||||||
|
triggerBy: triggerBy)
|
||||||
|
}
|
||||||
|
.setFailureType(to: Error.self)
|
||||||
|
.map { _ in return response }
|
||||||
|
.eraseToAnyPublisher()
|
||||||
|
}.eraseToAnyPublisher()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -94,11 +94,12 @@ extension APIService.CoreData {
|
||||||
type: triggerBy ?? setting.triggerBy ?? "")
|
type: triggerBy ?? setting.triggerBy ?? "")
|
||||||
let alertEntity = entity.alerts
|
let alertEntity = entity.alerts
|
||||||
let alert = SubscriptionAlerts.Property(
|
let alert = SubscriptionAlerts.Property(
|
||||||
favourite: alertEntity.favourite,
|
favourite: alertEntity.favouriteNumber,
|
||||||
follow: alertEntity.follow,
|
follow: alertEntity.followNumber,
|
||||||
mention: alertEntity.mention,
|
mention: alertEntity.mentionNumber,
|
||||||
poll: alertEntity.poll,
|
poll: alertEntity.pollNumber,
|
||||||
reblog: alertEntity.reblog)
|
reblog: alertEntity.reblogNumber
|
||||||
|
)
|
||||||
if let oldSubscription = oldSubscription {
|
if let oldSubscription = oldSubscription {
|
||||||
oldSubscription.updateIfNeed(property: property)
|
oldSubscription.updateIfNeed(property: property)
|
||||||
if nil == oldSubscription.alert {
|
if nil == oldSubscription.alert {
|
||||||
|
@ -109,9 +110,10 @@ extension APIService.CoreData {
|
||||||
oldSubscription.alert?.updateIfNeed(property: alert)
|
oldSubscription.alert?.updateIfNeed(property: alert)
|
||||||
}
|
}
|
||||||
|
|
||||||
if oldSubscription.alert?.hasChanges == true {
|
if oldSubscription.alert?.hasChanges == true || oldSubscription.hasChanges {
|
||||||
// don't expand subscription if add existed subscription
|
// don't expand subscription if add existed subscription
|
||||||
setting.mutableSetValue(forKey: #keyPath(Setting.subscription)).add(oldSubscription)
|
//setting.mutableSetValue(forKey: #keyPath(Setting.subscription)).add(oldSubscription)
|
||||||
|
oldSubscription.didUpdate(at: Date())
|
||||||
}
|
}
|
||||||
return (oldSubscription, false)
|
return (oldSubscription, false)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -47,9 +47,9 @@ extension Mastodon.API.Notification {
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Change types of notifications
|
/// Subscribe to push notifications
|
||||||
///
|
///
|
||||||
/// Using this endpoint to change types of notifications
|
/// Add a Web Push API subscription to receive notifications. Each access token can have one push subscription. If you create a new subscription, the old subscription is deleted.
|
||||||
///
|
///
|
||||||
/// - Since: 2.4.0
|
/// - Since: 2.4.0
|
||||||
/// - Version: 3.3.0
|
/// - Version: 3.3.0
|
||||||
|
@ -80,6 +80,40 @@ extension Mastodon.API.Notification {
|
||||||
}
|
}
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Change types of notifications
|
||||||
|
///
|
||||||
|
/// Updates the current push subscription. Only the data part can be updated. To change fundamentals, a new subscription must be created instead.
|
||||||
|
///
|
||||||
|
/// - Since: 2.4.0
|
||||||
|
/// - Version: 3.3.0
|
||||||
|
/// # Last Update
|
||||||
|
/// 2021/4/9
|
||||||
|
/// # Reference
|
||||||
|
/// [Document](https://docs.joinmastodon.org/methods/notifications/push/)
|
||||||
|
/// - Parameters:
|
||||||
|
/// - session: `URLSession`
|
||||||
|
/// - domain: Mastodon instance domain. e.g. "example.com"
|
||||||
|
/// - authorization: User token. Could be nil if status is public
|
||||||
|
/// - Returns: `AnyPublisher` contains `Poll` nested in the response
|
||||||
|
public static func updateSubscription(
|
||||||
|
session: URLSession,
|
||||||
|
domain: String,
|
||||||
|
authorization: Mastodon.API.OAuth.Authorization?,
|
||||||
|
query: UpdateSubscriptionQuery
|
||||||
|
) -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Subscription>, Error> {
|
||||||
|
let request = Mastodon.API.put(
|
||||||
|
url: pushEndpointURL(domain: domain),
|
||||||
|
query: query,
|
||||||
|
authorization: authorization
|
||||||
|
)
|
||||||
|
return session.dataTaskPublisher(for: request)
|
||||||
|
.tryMap { data, response in
|
||||||
|
let value = try Mastodon.API.decode(type: Mastodon.Entity.Subscription.self, from: data, response: response)
|
||||||
|
return Mastodon.Response.Content(value: value, response: response)
|
||||||
|
}
|
||||||
|
.eraseToAnyPublisher()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Mastodon.API.Notification {
|
extension Mastodon.API.Notification {
|
||||||
|
@ -88,27 +122,56 @@ extension Mastodon.API.Notification {
|
||||||
var contentType: String?
|
var contentType: String?
|
||||||
var body: Data?
|
var body: Data?
|
||||||
|
|
||||||
let follow: Bool?
|
public init(
|
||||||
let favourite: Bool?
|
endpoint: String,
|
||||||
let reblog: Bool?
|
p256dh: String,
|
||||||
let mention: Bool?
|
auth: String,
|
||||||
let poll: Bool?
|
favourite: Bool?,
|
||||||
|
follow: Bool?,
|
||||||
// iTODO: missing parameters
|
reblog: Bool?,
|
||||||
// subscription[endpoint]
|
mention: Bool?,
|
||||||
// subscription[keys][p256dh]
|
poll: Bool?
|
||||||
// subscription[keys][auth]
|
) {
|
||||||
public init(favourite: Bool?,
|
queryItems = [URLQueryItem]()
|
||||||
follow: Bool?,
|
|
||||||
reblog: Bool?,
|
|
||||||
mention: Bool?,
|
|
||||||
poll: Bool?) {
|
|
||||||
self.follow = follow
|
|
||||||
self.favourite = favourite
|
|
||||||
self.reblog = reblog
|
|
||||||
self.mention = mention
|
|
||||||
self.poll = poll
|
|
||||||
|
|
||||||
|
queryItems?.append(URLQueryItem(name: "subscription[endpoint]", value: endpoint))
|
||||||
|
queryItems?.append(URLQueryItem(name: "subscription[keys][p256dh]", value: p256dh))
|
||||||
|
queryItems?.append(URLQueryItem(name: "subscription[keys][auth]", value: auth))
|
||||||
|
|
||||||
|
if let followValue = follow?.queryItemValue {
|
||||||
|
let followItem = URLQueryItem(name: "data[alerts][follow]", value: followValue)
|
||||||
|
queryItems?.append(followItem)
|
||||||
|
}
|
||||||
|
|
||||||
|
if let favouriteValue = favourite?.queryItemValue {
|
||||||
|
let favouriteItem = URLQueryItem(name: "data[alerts][favourite]", value: favouriteValue)
|
||||||
|
queryItems?.append(favouriteItem)
|
||||||
|
}
|
||||||
|
|
||||||
|
if let reblogValue = reblog?.queryItemValue {
|
||||||
|
let reblogItem = URLQueryItem(name: "data[alerts][reblog]", value: reblogValue)
|
||||||
|
queryItems?.append(reblogItem)
|
||||||
|
}
|
||||||
|
|
||||||
|
if let mentionValue = mention?.queryItemValue {
|
||||||
|
let mentionItem = URLQueryItem(name: "data[alerts][mention]", value: mentionValue)
|
||||||
|
queryItems?.append(mentionItem)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct UpdateSubscriptionQuery: PutQuery {
|
||||||
|
var queryItems: [URLQueryItem]?
|
||||||
|
var contentType: String?
|
||||||
|
var body: Data?
|
||||||
|
|
||||||
|
public init(
|
||||||
|
favourite: Bool?,
|
||||||
|
follow: Bool?,
|
||||||
|
reblog: Bool?,
|
||||||
|
mention: Bool?,
|
||||||
|
poll: Bool?
|
||||||
|
) {
|
||||||
queryItems = [URLQueryItem]()
|
queryItems = [URLQueryItem]()
|
||||||
|
|
||||||
if let followValue = follow?.queryItemValue {
|
if let followValue = follow?.queryItemValue {
|
||||||
|
|
|
@ -32,11 +32,46 @@ extension Mastodon.Entity {
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct Alerts: Codable {
|
public struct Alerts: Codable {
|
||||||
public let follow: Bool
|
public let follow: Bool?
|
||||||
public let favourite: Bool
|
public let favourite: Bool?
|
||||||
public let reblog: Bool
|
public let reblog: Bool?
|
||||||
public let mention: Bool
|
public let mention: Bool?
|
||||||
public let poll: Bool
|
public let poll: Bool?
|
||||||
|
|
||||||
|
public var followNumber: NSNumber? {
|
||||||
|
guard let value = follow else { return nil }
|
||||||
|
return NSNumber(booleanLiteral: value)
|
||||||
|
}
|
||||||
|
public var favouriteNumber: NSNumber? {
|
||||||
|
guard let value = favourite else { return nil }
|
||||||
|
return NSNumber(booleanLiteral: value)
|
||||||
|
}
|
||||||
|
public var reblogNumber: NSNumber? {
|
||||||
|
guard let value = reblog else { return nil }
|
||||||
|
return NSNumber(booleanLiteral: value)
|
||||||
|
}
|
||||||
|
public var mentionNumber: NSNumber? {
|
||||||
|
guard let value = mention else { return nil }
|
||||||
|
return NSNumber(booleanLiteral: value)
|
||||||
|
}
|
||||||
|
public var pollNumber: NSNumber? {
|
||||||
|
guard let value = poll else { return nil }
|
||||||
|
return NSNumber(booleanLiteral: value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(from decoder: Decoder) throws {
|
||||||
|
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||||
|
|
||||||
|
var id = try? container.decode(String.self, forKey: .id)
|
||||||
|
if nil == id, let numId = try? container.decode(Int.self, forKey: .id) {
|
||||||
|
id = String(numId)
|
||||||
|
}
|
||||||
|
self.id = id ?? ""
|
||||||
|
|
||||||
|
endpoint = try container.decode(String.self, forKey: .endpoint)
|
||||||
|
alerts = try container.decode(Alerts.self, forKey: .alerts)
|
||||||
|
serverKey = try container.decode(String.self, forKey: .serverKey)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,11 +11,11 @@ import CoreData
|
||||||
|
|
||||||
@objc(SubscriptionAlerts)
|
@objc(SubscriptionAlerts)
|
||||||
public final class SubscriptionAlerts: NSManagedObject {
|
public final class SubscriptionAlerts: NSManagedObject {
|
||||||
@NSManaged public var follow: Bool
|
@NSManaged public var follow: NSNumber?
|
||||||
@NSManaged public var favourite: Bool
|
@NSManaged public var favourite: NSNumber?
|
||||||
@NSManaged public var reblog: Bool
|
@NSManaged public var reblog: NSNumber?
|
||||||
@NSManaged public var mention: Bool
|
@NSManaged public var mention: NSNumber?
|
||||||
@NSManaged public var poll: Bool
|
@NSManaged public var poll: NSNumber?
|
||||||
|
|
||||||
@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
|
||||||
|
@ -48,35 +48,35 @@ public extension SubscriptionAlerts {
|
||||||
return alerts
|
return alerts
|
||||||
}
|
}
|
||||||
|
|
||||||
func update(favourite: Bool) {
|
func update(favourite: NSNumber?) {
|
||||||
guard self.favourite != favourite else { return }
|
guard self.favourite != favourite else { return }
|
||||||
self.favourite = favourite
|
self.favourite = favourite
|
||||||
|
|
||||||
didUpdate(at: Date())
|
didUpdate(at: Date())
|
||||||
}
|
}
|
||||||
|
|
||||||
func update(follow: Bool) {
|
func update(follow: NSNumber?) {
|
||||||
guard self.follow != follow else { return }
|
guard self.follow != follow else { return }
|
||||||
self.follow = follow
|
self.follow = follow
|
||||||
|
|
||||||
didUpdate(at: Date())
|
didUpdate(at: Date())
|
||||||
}
|
}
|
||||||
|
|
||||||
func update(mention: Bool) {
|
func update(mention: NSNumber?) {
|
||||||
guard self.mention != mention else { return }
|
guard self.mention != mention else { return }
|
||||||
self.mention = mention
|
self.mention = mention
|
||||||
|
|
||||||
didUpdate(at: Date())
|
didUpdate(at: Date())
|
||||||
}
|
}
|
||||||
|
|
||||||
func update(poll: Bool) {
|
func update(poll: NSNumber?) {
|
||||||
guard self.poll != poll else { return }
|
guard self.poll != poll else { return }
|
||||||
self.poll = poll
|
self.poll = poll
|
||||||
|
|
||||||
didUpdate(at: Date())
|
didUpdate(at: Date())
|
||||||
}
|
}
|
||||||
|
|
||||||
func update(reblog: Bool) {
|
func update(reblog: NSNumber?) {
|
||||||
guard self.reblog != reblog else { return }
|
guard self.reblog != reblog else { return }
|
||||||
self.reblog = reblog
|
self.reblog = reblog
|
||||||
|
|
||||||
|
@ -86,18 +86,18 @@ public extension SubscriptionAlerts {
|
||||||
|
|
||||||
public extension SubscriptionAlerts {
|
public extension SubscriptionAlerts {
|
||||||
struct Property {
|
struct Property {
|
||||||
public let favourite: Bool
|
public let favourite: NSNumber?
|
||||||
public let follow: Bool
|
public let follow: NSNumber?
|
||||||
public let mention: Bool
|
public let mention: NSNumber?
|
||||||
public let poll: Bool
|
public let poll: NSNumber?
|
||||||
public let reblog: Bool
|
public let reblog: NSNumber?
|
||||||
|
|
||||||
public init(favourite: Bool?, follow: Bool?, mention: Bool?, poll: Bool?, reblog: Bool?) {
|
public init(favourite: NSNumber?, follow: NSNumber?, mention: NSNumber?, poll: NSNumber?, reblog: NSNumber?) {
|
||||||
self.favourite = favourite ?? true
|
self.favourite = favourite
|
||||||
self.follow = follow ?? true
|
self.follow = follow
|
||||||
self.mention = mention ?? true
|
self.mention = mention
|
||||||
self.poll = poll ?? true
|
self.poll = poll
|
||||||
self.reblog = reblog ?? true
|
self.reblog = reblog
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue