mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
1085 lines
55 KiB
Swift
1085 lines
55 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
|
|
import ItemListPeerItem
|
|
import ItemListPeerActionItem
|
|
import NotificationPeerExceptionController
|
|
|
|
private extension EnginePeer.NotificationSettings.MuteState {
|
|
var timeInterval: Int32? {
|
|
switch self {
|
|
case .default:
|
|
return nil
|
|
case .unmuted:
|
|
return 0
|
|
case let .muted(until):
|
|
return until
|
|
}
|
|
}
|
|
}
|
|
|
|
private final class NotificationsPeerCategoryControllerArguments {
|
|
let context: AccountContext
|
|
let soundSelectionDisposable: MetaDisposable
|
|
|
|
let updateEnabled: (Bool) -> Void
|
|
let updateEnabledImportant: (Bool) -> Void
|
|
let updatePreviews: (Bool) -> Void
|
|
|
|
let openSound: (PeerMessageSound) -> Void
|
|
|
|
let addException: () -> Void
|
|
let openException: (EnginePeer) -> Void
|
|
let removeAllExceptions: () -> Void
|
|
let updateRevealedPeerId: (EnginePeer.Id?) -> Void
|
|
let removePeer: (EnginePeer) -> Void
|
|
|
|
let updatedExceptionMode: (NotificationExceptionMode) -> Void
|
|
|
|
init(context: AccountContext, soundSelectionDisposable: MetaDisposable, updateEnabled: @escaping (Bool) -> Void, updateEnabledImportant: @escaping (Bool) -> Void, updatePreviews: @escaping (Bool) -> Void, openSound: @escaping (PeerMessageSound) -> Void, addException: @escaping () -> Void, openException: @escaping (EnginePeer) -> Void, removeAllExceptions: @escaping () -> Void, updateRevealedPeerId: @escaping (EnginePeer.Id?) -> Void, removePeer: @escaping (EnginePeer) -> Void, updatedExceptionMode: @escaping (NotificationExceptionMode) -> Void) {
|
|
self.context = context
|
|
self.soundSelectionDisposable = soundSelectionDisposable
|
|
|
|
self.updateEnabled = updateEnabled
|
|
self.updateEnabledImportant = updateEnabledImportant
|
|
self.updatePreviews = updatePreviews
|
|
self.openSound = openSound
|
|
|
|
self.addException = addException
|
|
self.openException = openException
|
|
self.removeAllExceptions = removeAllExceptions
|
|
|
|
self.updateRevealedPeerId = updateRevealedPeerId
|
|
self.removePeer = removePeer
|
|
|
|
self.updatedExceptionMode = updatedExceptionMode
|
|
}
|
|
}
|
|
|
|
private enum NotificationsPeerCategorySection: Int32 {
|
|
case enable
|
|
case options
|
|
case exceptions
|
|
}
|
|
|
|
public enum NotificationsPeerCategoryEntryTag: ItemListItemTag {
|
|
case enable
|
|
case previews
|
|
case sound
|
|
|
|
public func isEqual(to other: ItemListItemTag) -> Bool {
|
|
if let other = other as? NotificationsPeerCategoryEntryTag, self == other {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
|
|
private enum NotificationsPeerCategoryEntry: ItemListNodeEntry {
|
|
enum StableId: Hashable {
|
|
case enable
|
|
case enableImportant
|
|
case importantInfo
|
|
case optionsHeader
|
|
case previews
|
|
case sound
|
|
case exceptionsHeader
|
|
case addException
|
|
case peer(EnginePeer.Id)
|
|
case removeAllExceptions
|
|
}
|
|
|
|
case enable(PresentationTheme, String, Bool)
|
|
case enableImportant(PresentationTheme, String, Bool)
|
|
case importantInfo(PresentationTheme, String)
|
|
case optionsHeader(PresentationTheme, String)
|
|
case previews(PresentationTheme, String, Bool)
|
|
case sound(PresentationTheme, String, String, PeerMessageSound)
|
|
|
|
case exceptionsHeader(PresentationTheme, String)
|
|
case addException(PresentationTheme, String)
|
|
case exception(Int32, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, PresentationPersonNameOrder, EnginePeer, String, TelegramPeerNotificationSettings, Bool, Bool, Bool)
|
|
case removeAllExceptions(PresentationTheme, String)
|
|
|
|
var section: ItemListSectionId {
|
|
switch self {
|
|
case .enable, .enableImportant, .importantInfo:
|
|
return NotificationsPeerCategorySection.enable.rawValue
|
|
case .optionsHeader, .previews, .sound:
|
|
return NotificationsPeerCategorySection.options.rawValue
|
|
case .exceptionsHeader, .addException, .exception, .removeAllExceptions:
|
|
return NotificationsPeerCategorySection.exceptions.rawValue
|
|
}
|
|
}
|
|
|
|
var stableId: StableId {
|
|
switch self {
|
|
case .enable:
|
|
return .enable
|
|
case .enableImportant:
|
|
return .enableImportant
|
|
case .importantInfo:
|
|
return .importantInfo
|
|
case .optionsHeader:
|
|
return .optionsHeader
|
|
case .previews:
|
|
return .previews
|
|
case .sound:
|
|
return .sound
|
|
case .exceptionsHeader:
|
|
return .exceptionsHeader
|
|
case .addException:
|
|
return .addException
|
|
case let .exception(_, _, _, _, _, peer, _, _, _, _, _):
|
|
return .peer(peer.id)
|
|
case .removeAllExceptions:
|
|
return .removeAllExceptions
|
|
}
|
|
}
|
|
|
|
var sortIndex: Int32 {
|
|
switch self {
|
|
case .enable:
|
|
return 0
|
|
case .enableImportant:
|
|
return 1
|
|
case .importantInfo:
|
|
return 2
|
|
case .optionsHeader:
|
|
return 3
|
|
case .previews:
|
|
return 4
|
|
case .sound:
|
|
return 5
|
|
case .exceptionsHeader:
|
|
return 6
|
|
case .addException:
|
|
return 7
|
|
case let .exception(index, _, _, _, _, _, _, _, _, _, _):
|
|
return 100 + index
|
|
case .removeAllExceptions:
|
|
return 10000
|
|
}
|
|
}
|
|
|
|
var tag: ItemListItemTag? {
|
|
switch self {
|
|
case .enable:
|
|
return NotificationsPeerCategoryEntryTag.enable
|
|
case .previews:
|
|
return NotificationsPeerCategoryEntryTag.previews
|
|
case .sound:
|
|
return NotificationsPeerCategoryEntryTag.sound
|
|
default:
|
|
return nil
|
|
}
|
|
}
|
|
|
|
static func ==(lhs: NotificationsPeerCategoryEntry, rhs: NotificationsPeerCategoryEntry) -> Bool {
|
|
switch lhs {
|
|
case let .enable(lhsTheme, lhsText, lhsValue):
|
|
if case let .enable(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
case let .enableImportant(lhsTheme, lhsText, lhsValue):
|
|
if case let .enableImportant(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
case let .importantInfo(lhsTheme, lhsText):
|
|
if case let .importantInfo(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
case let .optionsHeader(lhsTheme, lhsText):
|
|
if case let .optionsHeader(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
case let .previews(lhsTheme, lhsText, lhsValue):
|
|
if case let .previews(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
case let .sound(lhsTheme, lhsText, lhsValue, lhsSound):
|
|
if case let .sound(rhsTheme, rhsText, rhsValue, rhsSound) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue, lhsSound == rhsSound {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
case let .exceptionsHeader(lhsTheme, lhsText):
|
|
if case let .exceptionsHeader(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
case let .addException(lhsTheme, lhsText):
|
|
if case let .addException(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
case let .exception(lhsIndex, lhsTheme, lhsStrings, lhsDateTimeFormat, lhsDisplayNameOrder, lhsPeer, lhsDescription, lhsSettings, lhsEditing, lhsRevealed, lhsCanRemove):
|
|
if case let .exception(rhsIndex, rhsTheme, rhsStrings, rhsDateTimeFormat, rhsDisplayNameOrder, rhsPeer, rhsDescription, rhsSettings, rhsEditing, rhsRevealed, rhsCanRemove) = rhs, lhsIndex == rhsIndex, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsDateTimeFormat == rhsDateTimeFormat, lhsDisplayNameOrder == rhsDisplayNameOrder, lhsPeer == rhsPeer, lhsDescription == rhsDescription, lhsSettings == rhsSettings, lhsEditing == rhsEditing, lhsRevealed == rhsRevealed, lhsCanRemove == rhsCanRemove {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
case let .removeAllExceptions(lhsTheme, lhsText):
|
|
if case let .removeAllExceptions(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
static func <(lhs: NotificationsPeerCategoryEntry, rhs: NotificationsPeerCategoryEntry) -> Bool {
|
|
return lhs.sortIndex < rhs.sortIndex
|
|
}
|
|
|
|
func item(presentationData: ItemListPresentationData, arguments: Any) -> ListViewItem {
|
|
let arguments = arguments as! NotificationsPeerCategoryControllerArguments
|
|
switch self {
|
|
case let .enable(_, text, value):
|
|
return ItemListSwitchItem(presentationData: presentationData, title: text, value: value, sectionId: self.section, style: .blocks, updated: { updatedValue in
|
|
arguments.updateEnabled(updatedValue)
|
|
}, tag: self.tag)
|
|
case let .enableImportant(_, text, value):
|
|
return ItemListSwitchItem(presentationData: presentationData, title: text, value: value, sectionId: self.section, style: .blocks, updated: { updatedValue in
|
|
arguments.updateEnabledImportant(updatedValue)
|
|
}, tag: self.tag)
|
|
case let .importantInfo(_, text):
|
|
return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section)
|
|
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)
|
|
case let .exceptionsHeader(_, text):
|
|
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
|
|
case let .addException(theme, text):
|
|
return ItemListPeerActionItem(presentationData: presentationData, icon: PresentationResourcesItemList.plusIconImage(theme), title: text, sectionId: self.section, height: .peerList, color: .accent, editing: false, action: {
|
|
arguments.addException()
|
|
})
|
|
case let .exception(_, _, _, dateTimeFormat, nameDisplayOrder, peer, description, _, editing, revealed, canRemove):
|
|
return ItemListPeerItem(presentationData: presentationData, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, context: arguments.context, peer: peer, presence: nil, text: .text(description, .secondary), label: .none, editing: ItemListPeerItemEditing(editable: canRemove, editing: canRemove && editing, revealed: canRemove && revealed), switchValue: nil, enabled: true, selectable: true, sectionId: self.section, action: {
|
|
arguments.openException(peer)
|
|
}, setPeerIdWithRevealedOptions: { peerId, fromPeerId in
|
|
arguments.updateRevealedPeerId(peerId)
|
|
}, removePeer: { peerId in
|
|
arguments.removePeer(peer)
|
|
}, hasTopStripe: false, hasTopGroupInset: false, noInsets: false)
|
|
case let .removeAllExceptions(theme, text):
|
|
return ItemListPeerActionItem(presentationData: presentationData, icon: PresentationResourcesItemList.deleteIconImage(theme), title: text, sectionId: self.section, height: .generic, color: .destructive, editing: false, action: {
|
|
arguments.removeAllExceptions()
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
private func filteredGlobalSound(_ sound: PeerMessageSound) -> PeerMessageSound {
|
|
if case .default = sound {
|
|
return defaultCloudPeerNotificationSound
|
|
} else {
|
|
return sound
|
|
}
|
|
}
|
|
|
|
private func notificationsPeerCategoryEntries(category: NotificationsPeerCategory, globalSettings: GlobalNotificationSettingsSet, state: NotificationExceptionState, presentationData: PresentationData, notificationSoundList: NotificationSoundList?, automaticTopPeers: [EnginePeer], automaticNotificationSettings: [EnginePeer.Id: EnginePeer.NotificationSettings]) -> [NotificationsPeerCategoryEntry] {
|
|
var entries: [NotificationsPeerCategoryEntry] = []
|
|
|
|
let notificationSettings: MessageNotificationSettings
|
|
let notificationExceptions = state.mode
|
|
switch category {
|
|
case .privateChat:
|
|
notificationSettings = globalSettings.privateChats
|
|
case .group:
|
|
notificationSettings = globalSettings.groupChats
|
|
case .channel:
|
|
notificationSettings = globalSettings.channels
|
|
case .stories:
|
|
notificationSettings = globalSettings.privateChats
|
|
}
|
|
|
|
if case .stories = category {
|
|
var allEnabled = false
|
|
var importantEnabled = false
|
|
|
|
switch notificationSettings.storySettings.mute {
|
|
case .muted:
|
|
allEnabled = false
|
|
importantEnabled = false
|
|
case .unmuted:
|
|
allEnabled = true
|
|
importantEnabled = true
|
|
case .default:
|
|
allEnabled = false
|
|
importantEnabled = true
|
|
}
|
|
|
|
entries.append(.enable(presentationData.theme, presentationData.strings.NotificationSettings_Stories_ShowAll, allEnabled))
|
|
if !allEnabled {
|
|
entries.append(.enableImportant(presentationData.theme, presentationData.strings.NotificationSettings_Stories_ShowImportant, importantEnabled))
|
|
entries.append(.importantInfo(presentationData.theme, presentationData.strings.NotificationSettings_Stories_ShowImportantFooter))
|
|
}
|
|
|
|
if notificationSettings.enabled || !notificationExceptions.isEmpty {
|
|
entries.append(.optionsHeader(presentationData.theme, presentationData.strings.Notifications_Options.uppercased()))
|
|
|
|
entries.append(.previews(presentationData.theme, presentationData.strings.NotificationSettings_Stories_DisplayAuthorName, notificationSettings.storySettings.hideSender != .hide))
|
|
entries.append(.sound(presentationData.theme, presentationData.strings.Notifications_MessageNotificationsSound, localizedPeerNotificationSoundString(strings: presentationData.strings, notificationSoundList: notificationSoundList, sound: filteredGlobalSound(notificationSettings.storySettings.sound)), filteredGlobalSound(notificationSettings.storySettings.sound)))
|
|
}
|
|
} else {
|
|
entries.append(.enable(presentationData.theme, presentationData.strings.Notifications_MessageNotificationsAlert, notificationSettings.enabled))
|
|
|
|
if notificationSettings.enabled || !notificationExceptions.isEmpty {
|
|
entries.append(.optionsHeader(presentationData.theme, presentationData.strings.Notifications_Options.uppercased()))
|
|
entries.append(.previews(presentationData.theme, presentationData.strings.Notifications_MessageNotificationsPreview, notificationSettings.displayPreviews))
|
|
entries.append(.sound(presentationData.theme, presentationData.strings.Notifications_MessageNotificationsSound, localizedPeerNotificationSoundString(strings: presentationData.strings, notificationSoundList: notificationSoundList, sound: filteredGlobalSound(notificationSettings.sound)), filteredGlobalSound(notificationSettings.sound)))
|
|
}
|
|
}
|
|
|
|
entries.append(.exceptionsHeader(presentationData.theme, presentationData.strings.Notifications_MessageNotificationsExceptions.uppercased()))
|
|
entries.append(.addException(presentationData.theme, presentationData.strings.Notification_Exceptions_AddException))
|
|
|
|
var sortedExceptions = notificationExceptions.settings.sorted(by: { lhs, rhs in
|
|
let lhsName = lhs.value.peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
|
let rhsName = rhs.value.peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
|
|
|
if let lhsDate = lhs.value.date, let rhsDate = rhs.value.date {
|
|
return lhsDate > rhsDate
|
|
} else if lhs.value.date != nil && rhs.value.date == nil {
|
|
return true
|
|
} else if lhs.value.date == nil && rhs.value.date != nil {
|
|
return false
|
|
}
|
|
|
|
if case let .user(lhsPeer) = lhs.value.peer, case let .user(rhsPeer) = rhs.value.peer {
|
|
if lhsPeer.botInfo != nil && rhsPeer.botInfo == nil {
|
|
return false
|
|
} else if lhsPeer.botInfo == nil && rhsPeer.botInfo != nil {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return lhsName < rhsName
|
|
})
|
|
var automaticSet = Set<EnginePeer.Id>()
|
|
if globalSettings.privateChats.storySettings.mute == .default {
|
|
for peer in automaticTopPeers {
|
|
if sortedExceptions.contains(where: { $0.key == peer.id }) {
|
|
continue
|
|
}
|
|
sortedExceptions.append((peer.id, NotificationExceptionWrapper(settings: automaticNotificationSettings[peer.id]?._asNotificationSettings() ?? .defaultSettings, peer: peer, date: nil)))
|
|
automaticSet.insert(peer.id)
|
|
}
|
|
}
|
|
|
|
var existingPeerIds = Set<EnginePeer.Id>()
|
|
var index: Int = 0
|
|
|
|
for (_, value) in sortedExceptions {
|
|
if !value.peer.isDeleted {
|
|
var canRemove = true
|
|
var title: String = ""
|
|
|
|
if automaticSet.contains(value.peer.id) {
|
|
title = presentationData.strings.NotificationSettings_Stories_AutomaticValue(presentationData.strings.Notification_Exceptions_AlwaysOn).string
|
|
canRemove = false
|
|
} else {
|
|
if case .stories = category {
|
|
var muted = false
|
|
if value.settings.storySettings.mute == .muted {
|
|
muted = true
|
|
title.append(presentationData.strings.Notification_Exceptions_AlwaysOff)
|
|
} else {
|
|
title.append(presentationData.strings.Notification_Exceptions_AlwaysOn)
|
|
}
|
|
|
|
if !muted {
|
|
switch value.settings.storySettings.sound {
|
|
case .default:
|
|
break
|
|
default:
|
|
if !title.isEmpty {
|
|
title.append(", ")
|
|
}
|
|
title.append(presentationData.strings.Notification_Exceptions_SoundCustom)
|
|
}
|
|
switch value.settings.storySettings.hideSender {
|
|
case .default:
|
|
break
|
|
default:
|
|
if !title.isEmpty {
|
|
title += ", "
|
|
}
|
|
if case .show = value.settings.storySettings.hideSender {
|
|
title += presentationData.strings.NotificationSettings_Stories_CompactShowName
|
|
} else {
|
|
title += presentationData.strings.NotificationSettings_Stories_CompactHideName
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
var muted = false
|
|
switch value.settings.muteState {
|
|
case let .muted(until):
|
|
if until >= Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970) {
|
|
if until < Int32.max - 1 {
|
|
let formatter = DateFormatter()
|
|
formatter.locale = Locale(identifier: presentationData.strings.baseLanguageCode)
|
|
|
|
if Calendar.current.isDateInToday(Date(timeIntervalSince1970: Double(until))) {
|
|
formatter.dateFormat = "HH:mm"
|
|
} else {
|
|
formatter.dateFormat = "E, d MMM HH:mm"
|
|
}
|
|
|
|
let dateString = formatter.string(from: Date(timeIntervalSince1970: Double(until)))
|
|
|
|
title = presentationData.strings.Notification_Exceptions_MutedUntil(dateString).string
|
|
} else {
|
|
muted = true
|
|
title = presentationData.strings.Notification_Exceptions_AlwaysOff
|
|
}
|
|
} else {
|
|
title = presentationData.strings.Notification_Exceptions_AlwaysOn
|
|
}
|
|
case .unmuted:
|
|
title = presentationData.strings.Notification_Exceptions_AlwaysOn
|
|
default:
|
|
title = ""
|
|
}
|
|
if !muted {
|
|
switch value.settings.messageSound {
|
|
case .default:
|
|
break
|
|
default:
|
|
if !title.isEmpty {
|
|
title.append(", ")
|
|
}
|
|
title.append(presentationData.strings.Notification_Exceptions_SoundCustom)
|
|
}
|
|
switch value.settings.displayPreviews {
|
|
case .default:
|
|
break
|
|
default:
|
|
if !title.isEmpty {
|
|
title += ", "
|
|
}
|
|
if case .show = value.settings.displayPreviews {
|
|
title += presentationData.strings.Notification_Exceptions_PreviewAlwaysOn
|
|
} else {
|
|
title += presentationData.strings.Notification_Exceptions_PreviewAlwaysOff
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
existingPeerIds.insert(value.peer.id)
|
|
entries.append(.exception(Int32(index), presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameDisplayOrder, value.peer, title, value.settings, state.editing, state.revealedPeerId == value.peer.id, canRemove))
|
|
index += 1
|
|
}
|
|
}
|
|
|
|
if notificationExceptions.peerIds.count > 0 {
|
|
entries.append(.removeAllExceptions(presentationData.theme, presentationData.strings.Notifications_DeleteAllExceptions))
|
|
}
|
|
|
|
return entries
|
|
}
|
|
|
|
public enum NotificationsPeerCategory {
|
|
case privateChat
|
|
case group
|
|
case channel
|
|
case stories
|
|
}
|
|
|
|
private final class NotificationExceptionState : Equatable {
|
|
let mode: NotificationExceptionMode
|
|
let revealedPeerId: EnginePeer.Id?
|
|
let editing: Bool
|
|
|
|
init(mode: NotificationExceptionMode, revealedPeerId: EnginePeer.Id? = nil, editing: Bool = false) {
|
|
self.mode = mode
|
|
self.revealedPeerId = revealedPeerId
|
|
self.editing = editing
|
|
}
|
|
|
|
func withUpdatedMode(_ mode: NotificationExceptionMode) -> NotificationExceptionState {
|
|
return NotificationExceptionState(mode: mode, revealedPeerId: self.revealedPeerId, editing: self.editing)
|
|
}
|
|
|
|
func withUpdatedEditing(_ editing: Bool) -> NotificationExceptionState {
|
|
return NotificationExceptionState(mode: self.mode, revealedPeerId: self.revealedPeerId, editing: editing)
|
|
}
|
|
|
|
func withUpdatedRevealedPeerId(_ revealedPeerId: EnginePeer.Id?) -> NotificationExceptionState {
|
|
return NotificationExceptionState(mode: self.mode, revealedPeerId: revealedPeerId, editing: self.editing)
|
|
}
|
|
|
|
func withUpdatedPeerSound(_ peer: EnginePeer, _ sound: PeerMessageSound) -> NotificationExceptionState {
|
|
return NotificationExceptionState(mode: mode.withUpdatedPeerSound(peer, sound), revealedPeerId: self.revealedPeerId, editing: self.editing)
|
|
}
|
|
|
|
func withUpdatedPeerMuteInterval(_ peer: EnginePeer, _ muteInterval: Int32?) -> NotificationExceptionState {
|
|
return NotificationExceptionState(mode: mode.withUpdatedPeerMuteInterval(peer, muteInterval), revealedPeerId: self.revealedPeerId, editing: self.editing)
|
|
}
|
|
|
|
func withUpdatedPeerDisplayPreviews(_ peer: EnginePeer, _ displayPreviews: PeerNotificationDisplayPreviews) -> NotificationExceptionState {
|
|
return NotificationExceptionState(mode: mode.withUpdatedPeerDisplayPreviews(peer, displayPreviews), revealedPeerId: self.revealedPeerId, editing: self.editing)
|
|
}
|
|
|
|
func withUpdatedPeerStoriesMuted(_ peer: EnginePeer, _ mute: PeerStoryNotificationSettings.Mute) -> NotificationExceptionState {
|
|
return NotificationExceptionState(mode: mode.withUpdatedPeerStoriesMuted(peer, mute), revealedPeerId: self.revealedPeerId, editing: self.editing)
|
|
}
|
|
|
|
func withUpdatedPeerStoriesHideSender(_ peer: EnginePeer, _ hideSender: PeerStoryNotificationSettings.HideSender) -> NotificationExceptionState {
|
|
return NotificationExceptionState(mode: mode.withUpdatedPeerStoriesHideSender(peer, hideSender), revealedPeerId: self.revealedPeerId, editing: self.editing)
|
|
}
|
|
|
|
func withUpdatedPeerStorySound(_ peer: EnginePeer, _ sound: PeerMessageSound) -> NotificationExceptionState {
|
|
return NotificationExceptionState(mode: mode.withUpdatedPeerStorySound(peer, sound), revealedPeerId: self.revealedPeerId, editing: self.editing)
|
|
}
|
|
|
|
func removeStoryPeerIfDefault(id: EnginePeer.Id) -> NotificationExceptionState {
|
|
return NotificationExceptionState(mode: mode.removeStoryPeerIfDefault(id: id), revealedPeerId: self.revealedPeerId, editing: self.editing)
|
|
}
|
|
|
|
static func == (lhs: NotificationExceptionState, rhs: NotificationExceptionState) -> Bool {
|
|
return lhs.mode == rhs.mode && lhs.revealedPeerId == rhs.revealedPeerId && lhs.editing == rhs.editing
|
|
}
|
|
}
|
|
|
|
public func notificationsPeerCategoryController(context: AccountContext, category: NotificationsPeerCategory, mode: NotificationExceptionMode, updatedMode: @escaping (NotificationExceptionMode) -> Void, focusOnItemTag: NotificationsPeerCategoryEntryTag? = nil) -> ViewController {
|
|
var presentControllerImpl: ((ViewController, Any?) -> Void)?
|
|
var pushControllerImpl: ((ViewController) -> Void)?
|
|
|
|
let stateValue = Atomic<NotificationExceptionState>(value: NotificationExceptionState(mode: mode))
|
|
let statePromise: ValuePromise<NotificationExceptionState> = ValuePromise(ignoreRepeated: true)
|
|
|
|
statePromise.set(NotificationExceptionState(mode: mode))
|
|
|
|
let notificationExceptions: Promise<(users: NotificationExceptionMode, groups: NotificationExceptionMode, channels: NotificationExceptionMode, stories: NotificationExceptionMode)> = Promise()
|
|
|
|
let updateNotificationExceptions:((users: NotificationExceptionMode, groups: NotificationExceptionMode, channels: NotificationExceptionMode, stories: NotificationExceptionMode)) -> Void = { value in
|
|
notificationExceptions.set(.single(value))
|
|
}
|
|
|
|
let updateState: ((NotificationExceptionState) -> NotificationExceptionState) -> Void = { f in
|
|
let result = stateValue.modify { f($0) }
|
|
statePromise.set(result)
|
|
updatedMode(result.mode)
|
|
}
|
|
|
|
let updatePeerSound: (EnginePeer.Id, PeerMessageSound) -> Signal<Void, NoError> = { peerId, sound in
|
|
return context.engine.peers.updatePeerNotificationSoundInteractive(peerId: peerId, threadId: nil, sound: sound) |> deliverOnMainQueue
|
|
}
|
|
|
|
let updatePeerNotificationInterval: (EnginePeer.Id, Int32?) -> Signal<Void, NoError> = { peerId, muteInterval in
|
|
return context.engine.peers.updatePeerMuteSetting(peerId: peerId, threadId: nil, muteInterval: muteInterval) |> deliverOnMainQueue
|
|
}
|
|
|
|
let updatePeerDisplayPreviews:(EnginePeer.Id, PeerNotificationDisplayPreviews) -> Signal<Void, NoError> = {
|
|
peerId, displayPreviews in
|
|
return context.engine.peers.updatePeerDisplayPreviewsSetting(peerId: peerId, threadId: nil, displayPreviews: displayPreviews) |> deliverOnMainQueue
|
|
}
|
|
|
|
let updatePeerStoriesMuted: (EnginePeer.Id, PeerStoryNotificationSettings.Mute) -> Signal<Void, NoError> = {
|
|
peerId, mute in
|
|
return context.engine.peers.updatePeerStoriesMutedSetting(peerId: peerId, mute: mute) |> deliverOnMainQueue
|
|
}
|
|
|
|
let updatePeerStoriesHideSender: (EnginePeer.Id, PeerStoryNotificationSettings.HideSender) -> Signal<Void, NoError> = {
|
|
peerId, hideSender in
|
|
return context.engine.peers.updatePeerStoriesHideSenderSetting(peerId: peerId, hideSender: hideSender) |> deliverOnMainQueue
|
|
}
|
|
|
|
let updatePeerStorySound: (EnginePeer.Id, PeerMessageSound) -> Signal<Void, NoError> = { peerId, sound in
|
|
return context.engine.peers.updatePeerStorySoundInteractive(peerId: peerId, sound: sound) |> deliverOnMainQueue
|
|
}
|
|
|
|
var peerIds: Set<EnginePeer.Id> = Set(mode.peerIds)
|
|
let updateNotificationsDisposable = MetaDisposable()
|
|
let updateNotificationsView: (@escaping () -> Void) -> Void = { completion in
|
|
updateState { current in
|
|
peerIds = peerIds.union(current.mode.peerIds)
|
|
let combinedPeerNotificationSettings = context.engine.data.subscribe(EngineDataMap(
|
|
peerIds.map(TelegramEngine.EngineData.Item.Peer.NotificationSettings.init)
|
|
))
|
|
|
|
updateNotificationsDisposable.set((combinedPeerNotificationSettings
|
|
|> deliverOnMainQueue).start(next: { combinedPeerNotificationSettings in
|
|
let _ = (context.engine.data.get(
|
|
EngineDataMap(combinedPeerNotificationSettings.keys.map(TelegramEngine.EngineData.Item.Peer.Peer.init)),
|
|
EngineDataMap(combinedPeerNotificationSettings.keys.map(TelegramEngine.EngineData.Item.Peer.NotificationSettings.init))
|
|
)
|
|
|> deliverOnMainQueue).start(next: { peerMap, notificationSettingsMap in
|
|
updateState { current in
|
|
var current = current
|
|
for (key, value) in combinedPeerNotificationSettings {
|
|
if let local = current.mode.settings[key] {
|
|
if !value._asNotificationSettings().isEqual(to: local.settings), let maybePeer = peerMap[key], let peer = maybePeer, let settings = notificationSettingsMap[key], !settings._asNotificationSettings().isEqual(to: local.settings) {
|
|
current = current.withUpdatedPeerSound(peer, settings.messageSound._asMessageSound()).withUpdatedPeerMuteInterval(peer, settings.muteState.timeInterval).withUpdatedPeerDisplayPreviews(peer, settings.displayPreviews._asDisplayPreviews())
|
|
}
|
|
} else if let maybePeer = peerMap[key], let peer = maybePeer {
|
|
if case .default = value.messageSound, case .unmuted = value.muteState, case .default = value.displayPreviews {
|
|
} else {
|
|
current = current.withUpdatedPeerSound(peer, value.messageSound._asMessageSound()).withUpdatedPeerMuteInterval(peer, value.muteState.timeInterval).withUpdatedPeerDisplayPreviews(peer, value.displayPreviews._asDisplayPreviews())
|
|
}
|
|
}
|
|
}
|
|
return current
|
|
}
|
|
|
|
completion()
|
|
})
|
|
}))
|
|
return current
|
|
}
|
|
}
|
|
|
|
updateNotificationsView({})
|
|
|
|
let presentPeerSettings: (EnginePeer.Id, @escaping () -> Void) -> Void = { peerId, completion in
|
|
let _ = (context.engine.data.get(
|
|
TelegramEngine.EngineData.Item.Peer.Peer(id: peerId),
|
|
TelegramEngine.EngineData.Item.NotificationSettings.Global()
|
|
)
|
|
|> deliverOnMainQueue).start(next: { peer, globalSettings in
|
|
completion()
|
|
|
|
guard let peer = peer else {
|
|
return
|
|
}
|
|
|
|
let mode = stateValue.with { $0.mode }
|
|
|
|
let canRemove = mode.peerIds.contains(peerId)
|
|
|
|
let defaultSound: PeerMessageSound
|
|
var isStories = false
|
|
switch mode {
|
|
case .channels:
|
|
defaultSound = globalSettings.channels.sound._asMessageSound()
|
|
case .groups:
|
|
defaultSound = globalSettings.groupChats.sound._asMessageSound()
|
|
case .users:
|
|
defaultSound = globalSettings.privateChats.sound._asMessageSound()
|
|
case .stories:
|
|
defaultSound = globalSettings.privateChats.storySettings.sound
|
|
isStories = true
|
|
}
|
|
|
|
pushControllerImpl?(notificationPeerExceptionController(context: context, peer: peer, threadId: nil, isStories: isStories, canRemove: canRemove, defaultSound: defaultSound, defaultStoriesSound: defaultSound, updatePeerSound: { peerId, sound in
|
|
_ = updatePeerSound(peer.id, sound).start(next: { _ in
|
|
updateNotificationsDisposable.set(nil)
|
|
_ = combineLatest(updatePeerSound(peer.id, sound), context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)) |> deliverOnMainQueue).start(next: { _, peer in
|
|
if let peer = peer {
|
|
updateState { value in
|
|
return value.withUpdatedPeerSound(peer, sound)
|
|
}
|
|
}
|
|
updateNotificationsView({})
|
|
})
|
|
})
|
|
}, updatePeerNotificationInterval: { peerId, muteInterval in
|
|
updateNotificationsDisposable.set(nil)
|
|
_ = combineLatest(updatePeerNotificationInterval(peerId, muteInterval), context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)) |> deliverOnMainQueue).start(next: { _, peer in
|
|
if let peer = peer {
|
|
updateState { value in
|
|
return value.withUpdatedPeerMuteInterval(peer, muteInterval)
|
|
}
|
|
}
|
|
updateNotificationsView({})
|
|
})
|
|
}, updatePeerDisplayPreviews: { peerId, displayPreviews in
|
|
updateNotificationsDisposable.set(nil)
|
|
_ = combineLatest(updatePeerDisplayPreviews(peerId, displayPreviews), context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)) |> deliverOnMainQueue).start(next: { _, peer in
|
|
if let peer = peer {
|
|
updateState { value in
|
|
return value.withUpdatedPeerDisplayPreviews(peer, displayPreviews)
|
|
}
|
|
}
|
|
updateNotificationsView({})
|
|
})
|
|
}, updatePeerStoriesMuted: { peerId, mute in
|
|
updateNotificationsDisposable.set(nil)
|
|
let _ = combineLatest(updatePeerStoriesMuted(peerId, mute), context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)) |> deliverOnMainQueue).start(next: { _, peer in
|
|
if let peer = peer {
|
|
updateState { value in
|
|
return value.withUpdatedPeerStoriesMuted(peer, mute)
|
|
}
|
|
}
|
|
updateNotificationsView({})
|
|
})
|
|
}, updatePeerStoriesHideSender: { peerId, hideSender in
|
|
updateNotificationsDisposable.set(nil)
|
|
let _ = combineLatest(updatePeerStoriesHideSender(peerId, hideSender), context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)) |> deliverOnMainQueue).start(next: { _, peer in
|
|
if let peer = peer {
|
|
updateState { value in
|
|
return value.withUpdatedPeerStoriesHideSender(peer, hideSender)
|
|
}
|
|
}
|
|
updateNotificationsView({})
|
|
})
|
|
}, updatePeerStorySound: { peerId, sound in
|
|
updateNotificationsDisposable.set(nil)
|
|
let _ = combineLatest(updatePeerStorySound(peerId, sound), context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)) |> deliverOnMainQueue).start(next: { _, peer in
|
|
if let peer = peer {
|
|
updateState { value in
|
|
return value.withUpdatedPeerStorySound(peer, sound)
|
|
}
|
|
}
|
|
updateNotificationsView({})
|
|
})
|
|
}, removePeerFromExceptions: {
|
|
if case .stories = mode.mode {
|
|
let _ = (
|
|
context.engine.peers.removeCustomStoryNotificationSettings(peerIds: [peerId])
|
|
|> map { _ -> EnginePeer? in }
|
|
|> then(context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)))
|
|
).start(next: { peer in
|
|
guard let peer = peer else {
|
|
return
|
|
}
|
|
updateState { value in
|
|
var value = value.withUpdatedPeerStorySound(peer, .default).withUpdatedPeerStoriesMuted(peer, .default).withUpdatedPeerStoriesHideSender(peer, .default)
|
|
value = value.removeStoryPeerIfDefault(id: peer.id)
|
|
return value
|
|
}
|
|
updateNotificationsView({})
|
|
})
|
|
} else {
|
|
let _ = (
|
|
context.engine.peers.removeCustomNotificationSettings(peerIds: [peerId])
|
|
|> map { _ -> EnginePeer? in }
|
|
|> then(context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)))
|
|
).start(next: { peer in
|
|
guard let peer = peer else {
|
|
return
|
|
}
|
|
updateState { value in
|
|
return value.withUpdatedPeerDisplayPreviews(peer, .default).withUpdatedPeerSound(peer, .default).withUpdatedPeerMuteInterval(peer, nil)
|
|
}
|
|
updateNotificationsView({})
|
|
})
|
|
}
|
|
}, modifiedPeer: {
|
|
|
|
}))
|
|
})
|
|
}
|
|
|
|
let arguments = NotificationsPeerCategoryControllerArguments(context: context, soundSelectionDisposable: MetaDisposable(), updateEnabled: { value in
|
|
let _ = updateGlobalNotificationSettingsInteractively(postbox: context.account.postbox, { settings in
|
|
var settings = settings
|
|
switch category {
|
|
case .privateChat:
|
|
settings.privateChats.enabled = value
|
|
case .group:
|
|
settings.groupChats.enabled = value
|
|
case .channel:
|
|
settings.channels.enabled = value
|
|
case .stories:
|
|
settings.privateChats.storySettings.mute = value ? .unmuted : .default
|
|
}
|
|
return settings
|
|
}).start()
|
|
}, updateEnabledImportant: { value in
|
|
let _ = updateGlobalNotificationSettingsInteractively(postbox: context.account.postbox, { settings in
|
|
var settings = settings
|
|
switch category {
|
|
case .stories:
|
|
settings.privateChats.storySettings.mute = value ? .default : .muted
|
|
default:
|
|
break
|
|
}
|
|
return settings
|
|
}).start()
|
|
}, updatePreviews: { value in
|
|
let _ = updateGlobalNotificationSettingsInteractively(postbox: context.account.postbox, { settings in
|
|
var settings = settings
|
|
switch category {
|
|
case .privateChat:
|
|
settings.privateChats.displayPreviews = value
|
|
case .group:
|
|
settings.groupChats.displayPreviews = value
|
|
case .channel:
|
|
settings.channels.displayPreviews = value
|
|
case .stories:
|
|
settings.privateChats.storySettings.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
|
|
switch category {
|
|
case .privateChat:
|
|
settings.privateChats.sound = value
|
|
case .group:
|
|
settings.groupChats.sound = value
|
|
case .channel:
|
|
settings.channels.sound = value
|
|
case .stories:
|
|
settings.privateChats.storySettings.sound = value
|
|
}
|
|
return settings
|
|
}).start()
|
|
})
|
|
pushControllerImpl?(controller)
|
|
}, addException: {
|
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
|
var filter: ChatListNodePeersFilter = [.excludeRecent, .doNotSearchMessages, .removeSearchHeader]
|
|
switch category {
|
|
case .privateChat, .stories:
|
|
filter.insert(.onlyPrivateChats)
|
|
filter.insert(.excludeSavedMessages)
|
|
filter.insert(.excludeSecretChats)
|
|
case .group:
|
|
filter.insert(.onlyGroups)
|
|
case .channel:
|
|
filter.insert(.onlyChannels)
|
|
}
|
|
let controller = context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: context, filter: filter, hasContactSelector: false, title: presentationData.strings.Notifications_AddExceptionTitle))
|
|
controller.peerSelected = { [weak controller] peer, _ in
|
|
let peerId = peer.id
|
|
|
|
presentPeerSettings(peerId, {
|
|
controller?.dismiss()
|
|
})
|
|
}
|
|
pushControllerImpl?(controller)
|
|
}, openException: { peer in
|
|
presentPeerSettings(peer.id, {})
|
|
}, removeAllExceptions: {
|
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
|
let actionSheet = ActionSheetController(presentationData: presentationData)
|
|
actionSheet.setItemGroups([ActionSheetItemGroup(items: [
|
|
ActionSheetTextItem(title: presentationData.strings.Notification_Exceptions_DeleteAllConfirmation),
|
|
ActionSheetButtonItem(title: presentationData.strings.Notification_Exceptions_DeleteAll, color: .destructive, action: { [weak actionSheet] in
|
|
actionSheet?.dismissAnimated()
|
|
|
|
let values = stateValue.with { $0.mode.settings.values }
|
|
|
|
if case .stories = mode.mode {
|
|
let _ = (context.engine.peers.ensurePeersAreLocallyAvailable(peers: values.map { $0.peer })
|
|
|> deliverOnMainQueue).start(completed: {
|
|
updateNotificationsDisposable.set(nil)
|
|
updateState { state in
|
|
var state = state
|
|
for value in values {
|
|
state = state.withUpdatedPeerStorySound(value.peer, .default).withUpdatedPeerStoriesMuted(value.peer, .default).withUpdatedPeerStoriesHideSender(value.peer, .default)
|
|
state = state.removeStoryPeerIfDefault(id: value.peer.id)
|
|
}
|
|
return state
|
|
}
|
|
let _ = (context.engine.peers.removeCustomStoryNotificationSettings(peerIds: values.map(\.peer.id))
|
|
|> deliverOnMainQueue).start(completed: {
|
|
updateNotificationsView({})
|
|
})
|
|
})
|
|
} else {
|
|
let _ = (context.engine.peers.ensurePeersAreLocallyAvailable(peers: values.map { $0.peer })
|
|
|> deliverOnMainQueue).start(completed: {
|
|
updateNotificationsDisposable.set(nil)
|
|
updateState { state in
|
|
var state = state
|
|
for value in values {
|
|
state = state.withUpdatedPeerMuteInterval(value.peer, nil).withUpdatedPeerSound(value.peer, .default).withUpdatedPeerDisplayPreviews(value.peer, .default)
|
|
}
|
|
return state
|
|
}
|
|
let _ = (context.engine.peers.removeCustomNotificationSettings(peerIds: values.map(\.peer.id))
|
|
|> deliverOnMainQueue).start(completed: {
|
|
updateNotificationsView({})
|
|
})
|
|
})
|
|
}
|
|
})
|
|
]), ActionSheetItemGroup(items: [
|
|
ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak actionSheet] in
|
|
actionSheet?.dismissAnimated()
|
|
})
|
|
])])
|
|
presentControllerImpl?(actionSheet, nil)
|
|
}, updateRevealedPeerId: { peerId in
|
|
updateState { current in
|
|
return current.withUpdatedRevealedPeerId(peerId)
|
|
}
|
|
}, removePeer: { peer in
|
|
if case .stories = mode.mode {
|
|
let _ = (context.engine.peers.ensurePeersAreLocallyAvailable(peers: [peer])
|
|
|> deliverOnMainQueue).start(completed: {
|
|
updateNotificationsDisposable.set(nil)
|
|
updateState { value in
|
|
var value = value.withUpdatedPeerStorySound(peer, .default).withUpdatedPeerStoriesMuted(peer, .default).withUpdatedPeerStoriesHideSender(peer, .default)
|
|
value = value.removeStoryPeerIfDefault(id: peer.id)
|
|
return value
|
|
}
|
|
let _ = (context.engine.peers.removeCustomStoryNotificationSettings(peerIds: [peer.id])
|
|
|> deliverOnMainQueue).start(completed: {
|
|
updateNotificationsView({})
|
|
})
|
|
})
|
|
} else {
|
|
let _ = (context.engine.peers.ensurePeersAreLocallyAvailable(peers: [peer])
|
|
|> deliverOnMainQueue).start(completed: {
|
|
updateNotificationsDisposable.set(nil)
|
|
updateState { value in
|
|
return value.withUpdatedPeerMuteInterval(peer, nil).withUpdatedPeerSound(peer, .default).withUpdatedPeerDisplayPreviews(peer, .default)
|
|
}
|
|
let _ = (context.engine.peers.removeCustomNotificationSettings(peerIds: [peer.id])
|
|
|> deliverOnMainQueue).start(completed: {
|
|
updateNotificationsView({})
|
|
})
|
|
})
|
|
}
|
|
}, updatedExceptionMode: { mode in
|
|
_ = (notificationExceptions.get() |> take(1) |> deliverOnMainQueue).start(next: { (users, groups, channels, stories) in
|
|
switch mode {
|
|
case .users:
|
|
updateNotificationExceptions((mode, groups, channels, stories))
|
|
case .groups:
|
|
updateNotificationExceptions((users, mode, channels, stories))
|
|
case .channels:
|
|
updateNotificationExceptions((users, groups, mode, stories))
|
|
case .stories:
|
|
updateNotificationExceptions((users, groups, channels, mode))
|
|
}
|
|
})
|
|
})
|
|
|
|
let sharedData = context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.inAppNotificationSettings])
|
|
let preferences = context.account.postbox.preferencesView(keys: [PreferencesKeys.globalNotifications])
|
|
|
|
var automaticData: Signal<([EnginePeer], [EnginePeer.Id: EnginePeer.NotificationSettings]), NoError> = .single(([], [:]))
|
|
if case .stories = category {
|
|
automaticData = context.engine.peers.recentPeers()
|
|
|> mapToSignal { recentPeers -> Signal<([EnginePeer], [EnginePeer.Id: EnginePeer.NotificationSettings]), NoError> in
|
|
guard case let .peers(peersValue) = recentPeers else {
|
|
return .single(([], [:]))
|
|
}
|
|
let peers = peersValue.prefix(5).map(EnginePeer.init)
|
|
return context.engine.data.subscribe(
|
|
EngineDataMap(peers.map { peer in
|
|
return TelegramEngine.EngineData.Item.Peer.NotificationSettings(id: peer.id)
|
|
})
|
|
)
|
|
|> map { settings -> ([EnginePeer], [EnginePeer.Id: EnginePeer.NotificationSettings]) in
|
|
var settingsMap: [EnginePeer.Id: EnginePeer.NotificationSettings] = [:]
|
|
for peer in peers {
|
|
if let value = settings[peer.id] {
|
|
settingsMap[peer.id] = value
|
|
} else {
|
|
settingsMap[peer.id] = EnginePeer.NotificationSettings(TelegramPeerNotificationSettings.defaultSettings)
|
|
}
|
|
}
|
|
return (peers, settingsMap)
|
|
}
|
|
}
|
|
}
|
|
|
|
let signal = combineLatest(context.sharedContext.presentationData, context.engine.peers.notificationSoundList(), sharedData, preferences, statePromise.get(), automaticData)
|
|
|> map { presentationData, notificationSoundList, sharedData, view, state, automaticData -> (ItemListControllerState, (ItemListNodeState, Any)) in
|
|
let viewSettings: GlobalNotificationSettingsSet
|
|
if let settings = view.values[PreferencesKeys.globalNotifications]?.get(GlobalNotificationSettings.self) {
|
|
viewSettings = settings.effective
|
|
} else {
|
|
viewSettings = GlobalNotificationSettingsSet.defaultSettings
|
|
}
|
|
|
|
let entries = notificationsPeerCategoryEntries(category: category, globalSettings: viewSettings, state: state, presentationData: presentationData, notificationSoundList: notificationSoundList, automaticTopPeers: automaticData.0, automaticNotificationSettings: automaticData.1)
|
|
|
|
var index = 0
|
|
var scrollToItem: ListViewScrollToItem?
|
|
if let focusOnItemTag = focusOnItemTag {
|
|
for entry in entries {
|
|
if entry.tag?.isEqual(to: focusOnItemTag) ?? false {
|
|
scrollToItem = ListViewScrollToItem(index: index, position: .top(0.0), animated: false, curve: .Default(duration: 0.0), directionHint: .Up)
|
|
}
|
|
index += 1
|
|
}
|
|
}
|
|
|
|
let leftNavigationButton: ItemListNavigationButton?
|
|
let rightNavigationButton: ItemListNavigationButton?
|
|
if !state.mode.peerIds.isEmpty {
|
|
if state.editing {
|
|
leftNavigationButton = ItemListNavigationButton(content: .none, style: .regular, enabled: false, action: {})
|
|
rightNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Done), style: .bold, enabled: true, action: {
|
|
updateState { value in
|
|
return value.withUpdatedEditing(false)
|
|
}
|
|
})
|
|
} else {
|
|
leftNavigationButton = nil
|
|
rightNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Edit), style: .regular, enabled: true, action: {
|
|
updateState { value in
|
|
return value.withUpdatedEditing(true)
|
|
}
|
|
})
|
|
}
|
|
} else {
|
|
leftNavigationButton = nil
|
|
rightNavigationButton = nil
|
|
}
|
|
|
|
let title: String
|
|
switch category {
|
|
case .privateChat:
|
|
title = presentationData.strings.Notifications_PrivateChatsTitle
|
|
case .group:
|
|
title = presentationData.strings.Notifications_GroupChatsTitle
|
|
case .channel:
|
|
title = presentationData.strings.Notifications_ChannelsTitle
|
|
case .stories:
|
|
title = presentationData.strings.Notifications_StoriesTitle
|
|
}
|
|
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, ensureVisibleItemTag: focusOnItemTag, initialScrollToItem: scrollToItem)
|
|
|
|
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
|
|
}
|