mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
416 lines
16 KiB
Swift
416 lines
16 KiB
Swift
import Foundation
|
|
import UIKit
|
|
import Display
|
|
import AsyncDisplayKit
|
|
import SwiftSignalKit
|
|
import TelegramCore
|
|
import TelegramPresentationData
|
|
import TelegramUIPreferences
|
|
import DeviceAccess
|
|
import ItemListUI
|
|
import PresentationDataUtils
|
|
import AccountContext
|
|
import AlertUI
|
|
import PresentationDataUtils
|
|
import TelegramNotices
|
|
import NotificationSoundSelectionUI
|
|
import TelegramStringFormatting
|
|
|
|
private final class ReactionNotificationSettingsControllerArguments {
|
|
let context: AccountContext
|
|
let soundSelectionDisposable: MetaDisposable
|
|
|
|
let openMessages: () -> Void
|
|
let openStories: () -> Void
|
|
let toggleMessages: (Bool) -> Void
|
|
let toggleStories: (Bool) -> Void
|
|
let updatePreviews: (Bool) -> Void
|
|
let openSound: (PeerMessageSound) -> Void
|
|
|
|
init(
|
|
context: AccountContext,
|
|
soundSelectionDisposable: MetaDisposable,
|
|
openMessages: @escaping () -> Void,
|
|
openStories: @escaping () -> Void,
|
|
toggleMessages: @escaping (Bool) -> Void,
|
|
toggleStories: @escaping (Bool) -> Void,
|
|
updatePreviews: @escaping (Bool) -> Void,
|
|
openSound: @escaping (PeerMessageSound) -> Void
|
|
) {
|
|
self.context = context
|
|
self.soundSelectionDisposable = soundSelectionDisposable
|
|
self.openMessages = openMessages
|
|
self.openStories = openStories
|
|
self.toggleMessages = toggleMessages
|
|
self.toggleStories = toggleStories
|
|
self.updatePreviews = updatePreviews
|
|
self.openSound = openSound
|
|
}
|
|
}
|
|
|
|
private enum ReactionNotificationSettingsSection: Int32 {
|
|
case categories
|
|
case options
|
|
}
|
|
|
|
private enum ReactionNotificationSettingsEntry: ItemListNodeEntry {
|
|
enum StableId: Hashable {
|
|
case categoriesHeader
|
|
case messages
|
|
case stories
|
|
case optionsHeader
|
|
case previews
|
|
case sound
|
|
}
|
|
|
|
case categoriesHeader(String)
|
|
case messages(title: String, text: String?, value: Bool)
|
|
case stories(title: String, text: String?, value: Bool)
|
|
|
|
case optionsHeader(String)
|
|
case previews(String, Bool)
|
|
case sound(String, String, PeerMessageSound)
|
|
|
|
var section: ItemListSectionId {
|
|
switch self {
|
|
case .categoriesHeader, .messages, .stories:
|
|
return ReactionNotificationSettingsSection.categories.rawValue
|
|
case .optionsHeader, .previews, .sound:
|
|
return ReactionNotificationSettingsSection.options.rawValue
|
|
}
|
|
}
|
|
|
|
var stableId: StableId {
|
|
switch self {
|
|
case .categoriesHeader:
|
|
return .categoriesHeader
|
|
case .messages:
|
|
return .messages
|
|
case .stories:
|
|
return .stories
|
|
case .optionsHeader:
|
|
return .optionsHeader
|
|
case .previews:
|
|
return .previews
|
|
case .sound:
|
|
return .sound
|
|
}
|
|
}
|
|
|
|
var sortIndex: Int32 {
|
|
switch self {
|
|
case .categoriesHeader:
|
|
return 0
|
|
case .messages:
|
|
return 1
|
|
case .stories:
|
|
return 2
|
|
case .optionsHeader:
|
|
return 3
|
|
case .previews:
|
|
return 4
|
|
case .sound:
|
|
return 5
|
|
}
|
|
}
|
|
|
|
static func ==(lhs: ReactionNotificationSettingsEntry, rhs: ReactionNotificationSettingsEntry) -> Bool {
|
|
switch lhs {
|
|
case let .categoriesHeader(lhsText):
|
|
if case let .categoriesHeader(rhsText) = rhs, lhsText == rhsText {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
case let .messages(title, text, value):
|
|
if case .messages(title, text, value) = rhs {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
case let .stories(title, text, value):
|
|
if case .stories(title, text, value) = rhs {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
case let .optionsHeader(lhsText):
|
|
if case let .optionsHeader(rhsText) = rhs, lhsText == rhsText {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
case let .previews(lhsText, lhsValue):
|
|
if case let .previews(rhsText, rhsValue) = rhs, lhsText == rhsText, lhsValue == rhsValue {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
case let .sound(lhsText, lhsValue, lhsSound):
|
|
if case let .sound(rhsText, rhsValue, rhsSound) = rhs, lhsText == rhsText, lhsValue == rhsValue, lhsSound == rhsSound {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
|
|
static func <(lhs: ReactionNotificationSettingsEntry, rhs: ReactionNotificationSettingsEntry) -> Bool {
|
|
return lhs.sortIndex < rhs.sortIndex
|
|
}
|
|
|
|
func item(presentationData: ItemListPresentationData, arguments: Any) -> ListViewItem {
|
|
let arguments = arguments as! ReactionNotificationSettingsControllerArguments
|
|
switch self {
|
|
case let .categoriesHeader(text):
|
|
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
|
|
case let .messages(title, text, value):
|
|
return ItemListSwitchItem(presentationData: presentationData, title: title, text: text, textColor: .accent, value: value, sectionId: self.section, style: .blocks, updated: { value in
|
|
arguments.toggleMessages(value)
|
|
}, action: {
|
|
arguments.openMessages()
|
|
})
|
|
case let .stories(title, text, value):
|
|
return ItemListSwitchItem(presentationData: presentationData, title: title, text: text, textColor: .accent, value: value, sectionId: self.section, style: .blocks, updated: { value in
|
|
arguments.toggleStories(value)
|
|
}, action: {
|
|
arguments.openStories()
|
|
})
|
|
case let .optionsHeader(text):
|
|
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
|
|
case let .previews(text, value):
|
|
return ItemListSwitchItem(presentationData: presentationData, title: text, value: value, sectionId: self.section, style: .blocks, updated: { value in
|
|
arguments.updatePreviews(value)
|
|
})
|
|
case let .sound(text, value, sound):
|
|
return ItemListDisclosureItem(presentationData: presentationData, title: text, label: value, labelStyle: .text, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: {
|
|
arguments.openSound(sound)
|
|
}, tag: self.tag)
|
|
}
|
|
}
|
|
}
|
|
|
|
private func filteredGlobalSound(_ sound: PeerMessageSound) -> PeerMessageSound {
|
|
if case .default = sound {
|
|
return defaultCloudPeerNotificationSound
|
|
} else {
|
|
return sound
|
|
}
|
|
}
|
|
|
|
private func reactionNotificationSettingsEntries(
|
|
globalSettings: GlobalNotificationSettingsSet,
|
|
state: ReactionNotificationSettingsState,
|
|
presentationData: PresentationData,
|
|
notificationSoundList: NotificationSoundList?
|
|
) -> [ReactionNotificationSettingsEntry] {
|
|
var entries: [ReactionNotificationSettingsEntry] = []
|
|
|
|
entries.append(.categoriesHeader(presentationData.strings.Notifications_Reactions_SettingsHeader))
|
|
|
|
let messagesText: String?
|
|
let messagesValue: Bool
|
|
switch globalSettings.reactionSettings.messages {
|
|
case .nobody:
|
|
messagesText = nil
|
|
messagesValue = false
|
|
case .contacts:
|
|
messagesText = presentationData.strings.Notifications_Reactions_SubtitleContacts
|
|
messagesValue = true
|
|
case .everyone:
|
|
messagesText = presentationData.strings.Notifications_Reactions_SubtitleEveryone
|
|
messagesValue = true
|
|
}
|
|
|
|
let storiesText: String?
|
|
let storiesValue: Bool
|
|
switch globalSettings.reactionSettings.stories {
|
|
case .nobody:
|
|
storiesText = nil
|
|
storiesValue = false
|
|
case .contacts:
|
|
storiesText = presentationData.strings.Notifications_Reactions_SubtitleContacts
|
|
storiesValue = true
|
|
case .everyone:
|
|
storiesText = presentationData.strings.Notifications_Reactions_SubtitleEveryone
|
|
storiesValue = true
|
|
}
|
|
|
|
entries.append(.messages(title: presentationData.strings.Notifications_Reactions_ItemMessages, text: messagesText, value: messagesValue))
|
|
entries.append(.stories(title: presentationData.strings.Notifications_Reactions_ItemStories, text: storiesText, value: storiesValue))
|
|
|
|
if messagesValue || storiesValue {
|
|
entries.append(.optionsHeader(presentationData.strings.Notifications_Options.uppercased()))
|
|
|
|
entries.append(.previews(presentationData.strings.Notifications_Stories_DisplayName, globalSettings.reactionSettings.hideSender != .hide))
|
|
entries.append(.sound(presentationData.strings.Notifications_MessageNotificationsSound, localizedPeerNotificationSoundString(strings: presentationData.strings, notificationSoundList: notificationSoundList, sound: filteredGlobalSound(globalSettings.reactionSettings.sound)), filteredGlobalSound(globalSettings.reactionSettings.sound)))
|
|
}
|
|
|
|
return entries
|
|
}
|
|
|
|
private struct ReactionNotificationSettingsState: Equatable {
|
|
init() {
|
|
}
|
|
}
|
|
|
|
public func reactionNotificationSettingsController(
|
|
context: AccountContext
|
|
) -> ViewController {
|
|
var presentControllerImpl: ((ViewController, Any?) -> Void)?
|
|
var pushControllerImpl: ((ViewController) -> Void)?
|
|
|
|
let stateValue = Atomic<ReactionNotificationSettingsState>(value: ReactionNotificationSettingsState())
|
|
let statePromise: ValuePromise<ReactionNotificationSettingsState> = ValuePromise(ignoreRepeated: true)
|
|
|
|
statePromise.set(stateValue.with { $0 })
|
|
|
|
let updateState: ((ReactionNotificationSettingsState) -> ReactionNotificationSettingsState) -> Void = { f in
|
|
let result = stateValue.modify { f($0) }
|
|
statePromise.set(result)
|
|
}
|
|
let _ = updateState
|
|
|
|
let openCategory: (Bool) -> Void = { isMessages in
|
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
|
|
|
let text: String
|
|
if isMessages {
|
|
text = presentationData.strings.Notifications_Reactions_SheetTitleMessages
|
|
} else {
|
|
text = presentationData.strings.Notifications_Reactions_SheetTitleStories
|
|
}
|
|
|
|
let actionSheet = ActionSheetController(presentationData: presentationData)
|
|
actionSheet.setItemGroups([ActionSheetItemGroup(items: [
|
|
ActionSheetTextItem(title: text),
|
|
ActionSheetButtonItem(title: presentationData.strings.Notifications_Reactions_SheetValueEveryone, color: .accent, action: { [weak actionSheet] in
|
|
actionSheet?.dismissAnimated()
|
|
|
|
let _ = updateGlobalNotificationSettingsInteractively(postbox: context.account.postbox, { settings in
|
|
var settings = settings
|
|
if isMessages {
|
|
settings.reactionSettings.messages = .everyone
|
|
} else {
|
|
settings.reactionSettings.stories = .everyone
|
|
}
|
|
return settings
|
|
}).start()
|
|
}),
|
|
ActionSheetButtonItem(title: presentationData.strings.Notifications_Reactions_SheetValueContacts, color: .accent, action: { [weak actionSheet] in
|
|
actionSheet?.dismissAnimated()
|
|
|
|
let _ = updateGlobalNotificationSettingsInteractively(postbox: context.account.postbox, { settings in
|
|
var settings = settings
|
|
if isMessages {
|
|
settings.reactionSettings.messages = .contacts
|
|
} else {
|
|
settings.reactionSettings.stories = .contacts
|
|
}
|
|
return settings
|
|
}).start()
|
|
})
|
|
]), ActionSheetItemGroup(items: [
|
|
ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak actionSheet] in
|
|
actionSheet?.dismissAnimated()
|
|
})
|
|
])])
|
|
presentControllerImpl?(actionSheet, nil)
|
|
}
|
|
|
|
let arguments = ReactionNotificationSettingsControllerArguments(
|
|
context: context,
|
|
soundSelectionDisposable: MetaDisposable(),
|
|
openMessages: {
|
|
openCategory(true)
|
|
},
|
|
openStories: {
|
|
openCategory(false)
|
|
},
|
|
toggleMessages: { value in
|
|
let _ = updateGlobalNotificationSettingsInteractively(postbox: context.account.postbox, { settings in
|
|
var settings = settings
|
|
if value {
|
|
settings.reactionSettings.messages = .contacts
|
|
} else {
|
|
settings.reactionSettings.messages = .nobody
|
|
}
|
|
return settings
|
|
}).start()
|
|
},
|
|
toggleStories: { value in
|
|
let _ = updateGlobalNotificationSettingsInteractively(postbox: context.account.postbox, { settings in
|
|
var settings = settings
|
|
if value {
|
|
settings.reactionSettings.stories = .contacts
|
|
} else {
|
|
settings.reactionSettings.stories = .nobody
|
|
}
|
|
return settings
|
|
}).start()
|
|
},
|
|
updatePreviews: { value in
|
|
let _ = updateGlobalNotificationSettingsInteractively(postbox: context.account.postbox, { settings in
|
|
var settings = settings
|
|
settings.reactionSettings.hideSender = value ? .show : .hide
|
|
return settings
|
|
}).start()
|
|
}, openSound: { sound in
|
|
let controller = notificationSoundSelectionController(context: context, isModal: true, currentSound: sound, defaultSound: nil, completion: { value in
|
|
let _ = updateGlobalNotificationSettingsInteractively(postbox: context.account.postbox, { settings in
|
|
var settings = settings
|
|
settings.reactionSettings.sound = value
|
|
return settings
|
|
}).start()
|
|
})
|
|
pushControllerImpl?(controller)
|
|
}
|
|
)
|
|
|
|
let preferences = context.account.postbox.preferencesView(keys: [PreferencesKeys.globalNotifications])
|
|
|
|
let signal = combineLatest(queue: .mainQueue(),
|
|
context.sharedContext.presentationData,
|
|
context.engine.peers.notificationSoundList(),
|
|
preferences,
|
|
statePromise.get()
|
|
)
|
|
|> map { presentationData, notificationSoundList, preferencesView, state -> (ItemListControllerState, (ItemListNodeState, Any)) in
|
|
let viewSettings: GlobalNotificationSettingsSet
|
|
if let settings = preferencesView.values[PreferencesKeys.globalNotifications]?.get(GlobalNotificationSettings.self) {
|
|
viewSettings = settings.effective
|
|
} else {
|
|
viewSettings = GlobalNotificationSettingsSet.defaultSettings
|
|
}
|
|
|
|
let entries = reactionNotificationSettingsEntries(
|
|
globalSettings: viewSettings,
|
|
state: state,
|
|
presentationData: presentationData,
|
|
notificationSoundList: notificationSoundList
|
|
)
|
|
|
|
let leftNavigationButton: ItemListNavigationButton?
|
|
let rightNavigationButton: ItemListNavigationButton?
|
|
|
|
leftNavigationButton = nil
|
|
rightNavigationButton = nil
|
|
|
|
let title: String = presentationData.strings.Notifications_Reactions_Title
|
|
|
|
let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(title), leftNavigationButton: leftNavigationButton, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: true)
|
|
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: entries, style: .blocks)
|
|
|
|
return (controllerState, (listState, arguments))
|
|
}
|
|
|
|
let controller = ItemListController(context: context, state: signal)
|
|
presentControllerImpl = { [weak controller] c, a in
|
|
controller?.present(c, in: .window(.root), with: a)
|
|
}
|
|
pushControllerImpl = { [weak controller] c in
|
|
(controller?.navigationController as? NavigationController)?.pushViewController(c)
|
|
}
|
|
return controller
|
|
}
|