mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-23 06:35:51 +00:00
notification exceptions ui
This commit is contained in:
@@ -63,6 +63,8 @@
|
||||
09D304152173C0E900C00567 /* WatchManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09D304142173C0E900C00567 /* WatchManager.swift */; };
|
||||
09D304182173C15700C00567 /* WatchSettingsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09D304172173C15700C00567 /* WatchSettingsController.swift */; };
|
||||
09FE756D2153F5F900A3120F /* CallRouteActionSheetItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09FE756C2153F5F900A3120F /* CallRouteActionSheetItem.swift */; };
|
||||
9F06830921A404AB001D8EDB /* NotificationExceptionControllerNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F06830821A404AB001D8EDB /* NotificationExceptionControllerNode.swift */; };
|
||||
9F06830B21A404C4001D8EDB /* NotificationExcetionSettingsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F06830A21A404C4001D8EDB /* NotificationExcetionSettingsController.swift */; };
|
||||
D0068FA821760FA300D1B315 /* StoreDownloadedMedia.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0068FA721760FA300D1B315 /* StoreDownloadedMedia.swift */; };
|
||||
D007019C2029E8F2006B9E34 /* LegqacyICloudFileController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D007019B2029E8F2006B9E34 /* LegqacyICloudFileController.swift */; };
|
||||
D007019E2029EFDD006B9E34 /* ICloudResources.swift in Sources */ = {isa = PBXBuildFile; fileRef = D007019D2029EFDD006B9E34 /* ICloudResources.swift */; };
|
||||
@@ -1124,6 +1126,8 @@
|
||||
09D304142173C0E900C00567 /* WatchManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WatchManager.swift; sourceTree = "<group>"; };
|
||||
09D304172173C15700C00567 /* WatchSettingsController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WatchSettingsController.swift; sourceTree = "<group>"; };
|
||||
09FE756C2153F5F900A3120F /* CallRouteActionSheetItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CallRouteActionSheetItem.swift; sourceTree = "<group>"; };
|
||||
9F06830821A404AB001D8EDB /* NotificationExceptionControllerNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NotificationExceptionControllerNode.swift; sourceTree = "<group>"; };
|
||||
9F06830A21A404C4001D8EDB /* NotificationExcetionSettingsController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NotificationExcetionSettingsController.swift; sourceTree = "<group>"; };
|
||||
D00219051DDD1C9E00BE708A /* ImageContainingNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageContainingNode.swift; sourceTree = "<group>"; };
|
||||
D002A0D01E9B99F500A81812 /* SoftwareVideoSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SoftwareVideoSource.swift; sourceTree = "<group>"; };
|
||||
D002A0D21E9BBE6700A81812 /* MultiplexedSoftwareVideoSourceManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MultiplexedSoftwareVideoSourceManager.swift; sourceTree = "<group>"; };
|
||||
@@ -2909,6 +2913,8 @@
|
||||
D0579E6D2179178700495DC7 /* exceptions */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
9F06830A21A404C4001D8EDB /* NotificationExcetionSettingsController.swift */,
|
||||
9F06830821A404AB001D8EDB /* NotificationExceptionControllerNode.swift */,
|
||||
D02C81702177729000CD1006 /* NotificationExceptions.swift */,
|
||||
);
|
||||
name = exceptions;
|
||||
@@ -5286,6 +5292,7 @@
|
||||
D0EC6D871EB9F58800EBF1C3 /* ChatTitleView.swift in Sources */,
|
||||
D04614372005094E00EC0EF2 /* DeviceLocationManager.swift in Sources */,
|
||||
D0EC6D881EB9F58800EBF1C3 /* ChatControllerTitlePanelNodeContainer.swift in Sources */,
|
||||
9F06830B21A404C4001D8EDB /* NotificationExcetionSettingsController.swift in Sources */,
|
||||
D0EC6D891EB9F58800EBF1C3 /* ChatSecretAutoremoveTimerActionSheet.swift in Sources */,
|
||||
D05D8B782195E0050064586F /* SetupTwoStepVerificationContentNode.swift in Sources */,
|
||||
D0EC6D8A1EB9F58800EBF1C3 /* ChatInfo.swift in Sources */,
|
||||
@@ -5331,6 +5338,7 @@
|
||||
D0E8174E2011FC3800B82BBB /* ChatMessageEventLogPreviousDescriptionContentNode.swift in Sources */,
|
||||
D0EC6D981EB9F58900EBF1C3 /* ChatMessageItemView.swift in Sources */,
|
||||
09D304152173C0E900C00567 /* WatchManager.swift in Sources */,
|
||||
9F06830921A404AB001D8EDB /* NotificationExceptionControllerNode.swift in Sources */,
|
||||
D039FB1921711B5D00BD1BAD /* PlatformVideoContent.swift in Sources */,
|
||||
D0CAD8FD20AE467D00ACD96E /* PeerChannelMemberCategoriesContextsManager.swift in Sources */,
|
||||
D073D2DB1FB61DA9009E1DA2 /* CallListSettings.swift in Sources */,
|
||||
|
||||
@@ -112,7 +112,7 @@ private enum BlockedPeersEntry: ItemListNodeEntry {
|
||||
func item(_ arguments: BlockedPeersControllerArguments) -> ListViewItem {
|
||||
switch self {
|
||||
case let .add(theme, text):
|
||||
return ItemListPeerActionItem(theme: theme, icon: PresentationResourcesItemList.addPersonIcon(theme), title: text, sectionId: self.section, editing: false, action: {
|
||||
return ItemListActionItem(theme: theme, title: text, kind: .generic, alignment: .natural, sectionId: self.section, style: .blocks, action: {
|
||||
arguments.addPeer()
|
||||
})
|
||||
case let .peerItem(_, theme, strings, dateTimeFormat, peer, editing, enabled):
|
||||
@@ -175,7 +175,7 @@ private func blockedPeersControllerEntries(presentationData: PresentationData, s
|
||||
var entries: [BlockedPeersEntry] = []
|
||||
|
||||
if let peers = peers {
|
||||
entries.append(.add(presentationData.theme, presentationData.strings.BlockedUsers_BlockUser))
|
||||
entries.append(.add(presentationData.theme, presentationData.strings.Conversation_BlockUser))
|
||||
|
||||
var index: Int32 = 0
|
||||
for peer in peers {
|
||||
@@ -213,8 +213,7 @@ public func blockedPeersController(account: Account) -> ViewController {
|
||||
}
|
||||
}
|
||||
}, addPeer: {
|
||||
let presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 }
|
||||
let controller = PeerSelectionController(account: account, filter: [.onlyUsers], title: presentationData.strings.BlockedUsers_SelectUserTitle)
|
||||
let controller = PeerSelectionController(account: account, filter: [.onlyPrivateChats])
|
||||
controller.peerSelected = { [weak controller] peerId in
|
||||
if let strongController = controller {
|
||||
strongController.inProgress = true
|
||||
|
||||
@@ -13,10 +13,14 @@ public struct ChatListNodePeersFilter: OptionSet {
|
||||
}
|
||||
|
||||
public static let onlyWriteable = ChatListNodePeersFilter(rawValue: 1 << 0)
|
||||
public static let onlyUsers = ChatListNodePeersFilter(rawValue: 1 << 1)
|
||||
public static let onlyPrivateChats = ChatListNodePeersFilter(rawValue: 1 << 1)
|
||||
public static let onlyGroups = ChatListNodePeersFilter(rawValue: 1 << 2)
|
||||
public static let onlyManageable = ChatListNodePeersFilter(rawValue: 1 << 3)
|
||||
public static let withoutSecretChats = ChatListNodePeersFilter(rawValue: 1 << 4)
|
||||
public static let onlyChannels = ChatListNodePeersFilter(rawValue: 1 << 3)
|
||||
public static let onlyManageable = ChatListNodePeersFilter(rawValue: 1 << 4)
|
||||
|
||||
public static let excludeSecretChats = ChatListNodePeersFilter(rawValue: 1 << 5)
|
||||
public static let excludeRecent = ChatListNodePeersFilter(rawValue: 1 << 6)
|
||||
public static let excludeSavedMessages = ChatListNodePeersFilter(rawValue: 1 << 7)
|
||||
|
||||
|
||||
}
|
||||
@@ -154,7 +158,7 @@ private func mappedInsertEntries(account: Account, nodeInteraction: ChatListNode
|
||||
enabled = false
|
||||
}
|
||||
}
|
||||
if filter.contains(.onlyUsers) {
|
||||
if filter.contains(.onlyPrivateChats) {
|
||||
if let peer = peer.peers[peer.peerId] {
|
||||
if !(peer is TelegramUser || peer is TelegramSecretChat) {
|
||||
enabled = false
|
||||
@@ -185,6 +189,7 @@ private func mappedInsertEntries(account: Account, nodeInteraction: ChatListNode
|
||||
enabled = false
|
||||
}
|
||||
}
|
||||
|
||||
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ContactsPeerItem(theme: presentationData.theme, strings: presentationData.strings, sortOrder: presentationData.nameSortOrder, displayOrder: presentationData.nameDisplayOrder, account: account, peerMode: .generalSearch, peer: .peer(peer: itemPeer, chatPeer: chatPeer), status: .none, enabled: enabled, selection: .none, editing: ContactsPeerItemEditing(editable: false, editing: false, revealed: false), index: nil, header: nil, action: { _ in
|
||||
if let chatPeer = chatPeer {
|
||||
nodeInteraction.peerSelected(chatPeer)
|
||||
@@ -226,26 +231,6 @@ private func mappedUpdateEntries(account: Account, nodeInteraction: ChatListNode
|
||||
enabled = false
|
||||
}
|
||||
}
|
||||
if filter.contains(.onlyUsers) {
|
||||
if let peer = peer.peers[peer.peerId] {
|
||||
if !(peer is TelegramUser || peer is TelegramSecretChat) {
|
||||
enabled = false
|
||||
}
|
||||
} else {
|
||||
enabled = false
|
||||
}
|
||||
}
|
||||
if filter.contains(.onlyGroups) {
|
||||
if let peer = peer.peers[peer.peerId] {
|
||||
if let _ = peer as? TelegramGroup {
|
||||
} else if let peer = peer as? TelegramChannel, case .group = peer.info {
|
||||
} else {
|
||||
enabled = false
|
||||
}
|
||||
} else {
|
||||
enabled = false
|
||||
}
|
||||
}
|
||||
return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ContactsPeerItem(theme: presentationData.theme, strings: presentationData.strings, sortOrder: presentationData.nameSortOrder, displayOrder: presentationData.nameDisplayOrder, account: account, peerMode: .generalSearch, peer: .peer(peer: itemPeer, chatPeer: chatPeer), status: .none, enabled: enabled, selection: .none, editing: ContactsPeerItemEditing(editable: false, editing: false, revealed: false), index: nil, header: nil, action: { _ in
|
||||
if let chatPeer = chatPeer {
|
||||
nodeInteraction.peerSelected(chatPeer)
|
||||
@@ -450,8 +435,51 @@ final class ChatListNode: ListView {
|
||||
savedMessagesPeer = .single(nil)
|
||||
}
|
||||
|
||||
let currentPeerId: PeerId = account.peerId
|
||||
|
||||
let chatListNodeViewTransition = combineLatest(savedMessagesPeer, chatListViewUpdate, self.statePromise.get()) |> mapToQueue { (savedMessagesPeer, update, state) -> Signal<ChatListNodeListViewTransition, NoError> in
|
||||
let processedView = ChatListNodeView(originalView: update.view, filteredEntries: chatListNodeEntriesForView(update.view, state: state, savedMessagesPeer: savedMessagesPeer, mode: mode))
|
||||
|
||||
let entries = chatListNodeEntriesForView(update.view, state: state, savedMessagesPeer: savedMessagesPeer, mode: mode).filter { entry in
|
||||
switch entry {
|
||||
case let .PeerEntry(index, _, _, _, _, _, peer, _, _, _, _, _):
|
||||
//ChatListNodePeersFilter
|
||||
switch mode {
|
||||
case .chatList:
|
||||
return true
|
||||
case let .peers(filter):
|
||||
|
||||
guard !filter.contains(.excludeSavedMessages) || peer.peerId != currentPeerId else { return false }
|
||||
guard !filter.contains(.excludeSecretChats) || peer.peerId.namespace != Namespaces.Peer.SecretChat else { return false }
|
||||
guard !filter.contains(.onlyPrivateChats) || peer.peerId.namespace == Namespaces.Peer.CloudUser else { return false }
|
||||
|
||||
if filter.contains(.onlyGroups) {
|
||||
var isGroup: Bool = false
|
||||
if let peer = peer.chatMainPeer as? TelegramChannel, case .group = peer.info {
|
||||
isGroup = true
|
||||
} else if peer.peerId.namespace == Namespaces.Peer.CloudGroup {
|
||||
isGroup = true
|
||||
}
|
||||
if !isGroup {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
if filter.contains(.onlyChannels) {
|
||||
if let peer = peer.chatMainPeer as? TelegramChannel, case .broadcast = peer.info {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
default:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
let processedView = ChatListNodeView(originalView: update.view, filteredEntries: entries)
|
||||
let previousView = previousView.swap(processedView)
|
||||
let previousState = previousState.swap(state)
|
||||
|
||||
@@ -587,7 +615,15 @@ final class ChatListNode: ListView {
|
||||
|
||||
self.chatListDisposable.set(appliedTransition.start())
|
||||
|
||||
let initialLocation: ChatListNodeLocation = .initial(count: 50)
|
||||
let initialLocation: ChatListNodeLocation
|
||||
|
||||
switch mode {
|
||||
case .chatList:
|
||||
initialLocation = .initial(count: 50)
|
||||
case .peers:
|
||||
initialLocation = .initial(count: 200)
|
||||
}
|
||||
|
||||
self.currentLocation = initialLocation
|
||||
self.chatListLocation.set(initialLocation)
|
||||
|
||||
|
||||
@@ -229,16 +229,6 @@ func chatListNodeEntriesForView(_ view: ChatListView, state: ChatListNodeState,
|
||||
if let savedMessagesPeer = savedMessagesPeer, savedMessagesPeer.id == index.messageIndex.id.peerId {
|
||||
continue loop
|
||||
}
|
||||
switch mode {
|
||||
case let .peers(filter):
|
||||
if filter.contains(.withoutSecretChats) {
|
||||
if index.messageIndex.id.peerId.namespace == Namespaces.Peer.SecretChat {
|
||||
continue
|
||||
}
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
result.append(.PeerEntry(index: offsetPinnedIndex(index, offset: pinnedIndexOffset), presentationData: state.presentationData, message: message, readState: combinedReadState, notificationSettings: notificationSettings, embeddedInterfaceState: embeddedState, peer: peer, summaryInfo: summaryInfo, editing: state.editing, hasActiveRevealControls: index.messageIndex.id.peerId == state.peerIdWithRevealedOptions, inputActivities: state.peerInputActivities?.activities[index.messageIndex.id.peerId], isAd: false))
|
||||
case let .HoleEntry(hole):
|
||||
result.append(.HoleEntry(hole, theme: state.presentationData.theme))
|
||||
|
||||
@@ -121,7 +121,7 @@ private enum ChatListRecentEntry: Comparable, Identifiable {
|
||||
enabled = canSendMessagesToPeer(primaryPeer)
|
||||
}
|
||||
}
|
||||
if filter.contains(.onlyUsers) {
|
||||
if filter.contains(.onlyPrivateChats) {
|
||||
if let peer = chatPeer {
|
||||
if !(peer is TelegramUser || peer is TelegramSecretChat) {
|
||||
enabled = false
|
||||
@@ -362,7 +362,7 @@ enum ChatListSearchEntry: Comparable, Identifiable {
|
||||
enabled = false
|
||||
}
|
||||
}
|
||||
if filter.contains(.onlyUsers) {
|
||||
if filter.contains(.onlyPrivateChats) {
|
||||
if let peer = chatPeer {
|
||||
if !(peer is TelegramUser || peer is TelegramSecretChat) {
|
||||
enabled = false
|
||||
@@ -396,7 +396,7 @@ enum ChatListSearchEntry: Comparable, Identifiable {
|
||||
if filter.contains(.onlyWriteable) {
|
||||
enabled = canSendMessagesToPeer(peer.peer)
|
||||
}
|
||||
if filter.contains(.onlyUsers) {
|
||||
if filter.contains(.onlyPrivateChats) {
|
||||
if !(peer.peer is TelegramUser || peer.peer is TelegramSecretChat) {
|
||||
enabled = false
|
||||
}
|
||||
@@ -495,7 +495,7 @@ private func doesPeerMatchFilter(peer: Peer, filter: ChatListNodePeersFilter) ->
|
||||
if filter.contains(.onlyWriteable), !canSendMessagesToPeer(peer) {
|
||||
enabled = false
|
||||
}
|
||||
if filter.contains(.onlyUsers), !(peer is TelegramUser || peer is TelegramSecretChat) {
|
||||
if filter.contains(.onlyPrivateChats), !(peer is TelegramUser || peer is TelegramSecretChat) {
|
||||
enabled = false
|
||||
}
|
||||
if filter.contains(.onlyGroups) {
|
||||
@@ -614,6 +614,35 @@ final class ChatListSearchContainerNode: SearchDisplayControllerContentNode {
|
||||
let isSearching = foundRemotePeers.2 || foundRemoteMessages.1
|
||||
var index = 0
|
||||
|
||||
|
||||
let filteredPeer:(Peer) -> Bool = { peer in
|
||||
guard !filter.contains(.excludeSavedMessages) || peer.id != accountPeer.id else { return false }
|
||||
guard !filter.contains(.excludeSecretChats) || peer.id.namespace != Namespaces.Peer.SecretChat else { return false }
|
||||
guard !filter.contains(.onlyPrivateChats) || peer.id.namespace == Namespaces.Peer.CloudUser else { return false }
|
||||
|
||||
if filter.contains(.onlyGroups) {
|
||||
var isGroup: Bool = false
|
||||
if let peer = peer as? TelegramChannel, case .group = peer.info {
|
||||
isGroup = true
|
||||
} else if peer.id.namespace == Namespaces.Peer.CloudGroup {
|
||||
isGroup = true
|
||||
}
|
||||
if !isGroup {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
if filter.contains(.onlyChannels) {
|
||||
if let peer = peer as? TelegramChannel, case .broadcast = peer.info {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
var existingPeerIds = Set<PeerId>()
|
||||
|
||||
if presentationData.strings.DialogList_SavedMessages.lowercased().hasPrefix(query.lowercased()) {
|
||||
@@ -625,7 +654,7 @@ final class ChatListSearchContainerNode: SearchDisplayControllerContentNode {
|
||||
}
|
||||
|
||||
for renderedPeer in foundLocalPeers.peers {
|
||||
if let peer = renderedPeer.peers[renderedPeer.peerId], peer.id != account.peerId {
|
||||
if let peer = renderedPeer.peers[renderedPeer.peerId], peer.id != account.peerId, filteredPeer(peer) {
|
||||
if !existingPeerIds.contains(peer.id) {
|
||||
existingPeerIds.insert(peer.id)
|
||||
var associatedPeer: Peer?
|
||||
@@ -639,7 +668,7 @@ final class ChatListSearchContainerNode: SearchDisplayControllerContentNode {
|
||||
}
|
||||
|
||||
for peer in foundRemotePeers.0 {
|
||||
if !existingPeerIds.contains(peer.peer.id) {
|
||||
if !existingPeerIds.contains(peer.peer.id), filteredPeer(peer.peer) {
|
||||
existingPeerIds.insert(peer.peer.id)
|
||||
entries.append(.localPeer(peer.peer, nil, nil, index, presentationData.theme, presentationData.strings, presentationData.nameSortOrder, presentationData.nameDisplayOrder))
|
||||
index += 1
|
||||
@@ -648,7 +677,7 @@ final class ChatListSearchContainerNode: SearchDisplayControllerContentNode {
|
||||
|
||||
index = 0
|
||||
for peer in foundRemotePeers.1 {
|
||||
if !existingPeerIds.contains(peer.peer.id) {
|
||||
if !existingPeerIds.contains(peer.peer.id), filteredPeer(peer.peer) {
|
||||
existingPeerIds.insert(peer.peer.id)
|
||||
entries.append(.globalPeer(peer, nil, index, presentationData.theme, presentationData.strings, presentationData.nameSortOrder, presentationData.nameDisplayOrder))
|
||||
index += 1
|
||||
@@ -718,7 +747,7 @@ final class ChatListSearchContainerNode: SearchDisplayControllerContentNode {
|
||||
}
|
||||
}
|
||||
|> distinctUntilChanged
|
||||
let recentItemsTransition = combineLatest(hasRecentPeers, recentlySearchedPeers(postbox: account.postbox), presentationDataPromise.get(), self.statePromise.get())
|
||||
var recentItemsTransition = combineLatest(hasRecentPeers, recentlySearchedPeers(postbox: account.postbox), presentationDataPromise.get(), self.statePromise.get())
|
||||
|> mapToSignal { [weak self] hasRecentPeers, peers, presentationData, state -> Signal<(ChatListSearchContainerRecentTransition, Bool), NoError> in
|
||||
var entries: [ChatListRecentEntry] = []
|
||||
if !filter.contains(.onlyGroups) {
|
||||
@@ -762,6 +791,10 @@ final class ChatListSearchContainerNode: SearchDisplayControllerContentNode {
|
||||
return .single((transition, previousEntries == nil))
|
||||
}
|
||||
|
||||
if filter.contains(.excludeRecent) {
|
||||
recentItemsTransition = .single((ChatListSearchContainerRecentTransition(deletions: [], insertions: [], updates: []), true))
|
||||
}
|
||||
|
||||
self.updatedRecentPeersDisposable.set(managedUpdatedRecentPeers(accountPeerId: account.peerId, postbox: account.postbox, network: account.network).start())
|
||||
|
||||
self.recentDisposable.set((recentItemsTransition |> deliverOnMainQueue).start(next: { [weak self] (transition, firstTime) in
|
||||
|
||||
@@ -80,7 +80,8 @@ final class ItemListPeerItem: ListViewItem, ItemListItem {
|
||||
let removePeer: (PeerId) -> Void
|
||||
let toggleUpdated: ((Bool) -> Void)?
|
||||
let hasTopStripe: Bool
|
||||
init(theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, account: Account, peer: Peer, aliasHandling: ItemListPeerItemAliasHandling = .standard, nameColor: ItemListPeerItemNameColor = .primary, presence: PeerPresence?, text: ItemListPeerItemText, label: ItemListPeerItemLabel, editing: ItemListPeerItemEditing, revealOptions: ItemListPeerItemRevealOptions? = nil, switchValue: ItemListPeerItemSwitch?, enabled: Bool, sectionId: ItemListSectionId, action: (() -> Void)?, setPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void, removePeer: @escaping (PeerId) -> Void, toggleUpdated: ((Bool) -> Void)? = nil, hasTopStripe: Bool = true) {
|
||||
let hasTopGroupInset: Bool
|
||||
init(theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, account: Account, peer: Peer, aliasHandling: ItemListPeerItemAliasHandling = .standard, nameColor: ItemListPeerItemNameColor = .primary, presence: PeerPresence?, text: ItemListPeerItemText, label: ItemListPeerItemLabel, editing: ItemListPeerItemEditing, revealOptions: ItemListPeerItemRevealOptions? = nil, switchValue: ItemListPeerItemSwitch?, enabled: Bool, sectionId: ItemListSectionId, action: (() -> Void)?, setPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void, removePeer: @escaping (PeerId) -> Void, toggleUpdated: ((Bool) -> Void)? = nil, hasTopStripe: Bool = true, hasTopGroupInset: Bool = true) {
|
||||
self.theme = theme
|
||||
self.strings = strings
|
||||
self.dateTimeFormat = dateTimeFormat
|
||||
@@ -101,6 +102,7 @@ final class ItemListPeerItem: ListViewItem, ItemListItem {
|
||||
self.removePeer = removePeer
|
||||
self.toggleUpdated = toggleUpdated
|
||||
self.hasTopStripe = hasTopStripe
|
||||
self.hasTopGroupInset = hasTopGroupInset
|
||||
}
|
||||
|
||||
func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal<Void, NoError>?, () -> Void)) -> Void) {
|
||||
@@ -404,7 +406,15 @@ class ItemListPeerItemNode: ItemListRevealOptionsItemNode {
|
||||
let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: titleAttributedString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - 12.0 - labelLayout.size.width - editingOffset - rightInset - labelInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
let (statusLayout, statusApply) = makeStatusLayout(TextNodeLayoutArguments(attributedString: statusAttributedString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - 8.0 - editingOffset - rightInset - labelInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
let insets = itemListNeighborsGroupedInsets(neighbors)
|
||||
var insets = itemListNeighborsGroupedInsets(neighbors)
|
||||
if !item.hasTopGroupInset {
|
||||
switch neighbors.top {
|
||||
case .none:
|
||||
insets.top = 0
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
let contentSize = CGSize(width: params.width, height: 48.0)
|
||||
let separatorHeight = UIScreenPixel
|
||||
|
||||
|
||||
1039
TelegramUI/NotificationExceptionControllerNode.swift
Normal file
1039
TelegramUI/NotificationExceptionControllerNode.swift
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
336
TelegramUI/NotificationExcetionSettingsController.swift
Normal file
336
TelegramUI/NotificationExcetionSettingsController.swift
Normal file
@@ -0,0 +1,336 @@
|
||||
import Foundation
|
||||
import Display
|
||||
import AsyncDisplayKit
|
||||
import Postbox
|
||||
import TelegramCore
|
||||
import SwiftSignalKit
|
||||
|
||||
|
||||
private enum NotificationPeerExceptionSection: Int32 {
|
||||
case switcher
|
||||
case soundModern
|
||||
case soundClassic
|
||||
}
|
||||
|
||||
private enum NotificationPeerExceptionSwitcher : Equatable {
|
||||
case alwaysOn
|
||||
case alwaysOff
|
||||
}
|
||||
|
||||
private enum NotificationPeerExceptionEntryId : Hashable {
|
||||
case switcher(NotificationPeerExceptionSwitcher)
|
||||
case sound(PeerMessageSound)
|
||||
case switcherHeader
|
||||
case soundModernHeader
|
||||
case soundClassicHeader
|
||||
case none
|
||||
case `default`
|
||||
|
||||
var hashValue: Int {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
private final class NotificationPeerExceptionArguments {
|
||||
let account: Account
|
||||
|
||||
let selectSound: (PeerMessageSound) -> Void
|
||||
let selectMode: (NotificationPeerExceptionSwitcher) -> Void
|
||||
let complete: () -> Void
|
||||
let cancel: () -> Void
|
||||
|
||||
init(account: Account, selectSound: @escaping(PeerMessageSound) -> Void, selectMode: @escaping(NotificationPeerExceptionSwitcher) -> Void, complete: @escaping()->Void, cancel: @escaping() -> Void) {
|
||||
self.account = account
|
||||
self.selectSound = selectSound
|
||||
self.selectMode = selectMode
|
||||
self.complete = complete
|
||||
self.cancel = cancel
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private enum NotificationPeerExceptionEntry: ItemListNodeEntry {
|
||||
|
||||
typealias ItemGenerationArguments = NotificationPeerExceptionArguments
|
||||
|
||||
case switcher(index:Int32, theme: PresentationTheme, strings: PresentationStrings, mode: NotificationPeerExceptionSwitcher, selected: Bool)
|
||||
case switcherHeader(index:Int32, theme: PresentationTheme, title: String)
|
||||
case soundModernHeader(index:Int32, theme: PresentationTheme, title: String)
|
||||
case soundClassicHeader(index:Int32, theme: PresentationTheme, title: String)
|
||||
case none(index:Int32, section: NotificationPeerExceptionSection, theme: PresentationTheme, text: String, selected: Bool)
|
||||
case `default`(index:Int32, section: NotificationPeerExceptionSection, theme: PresentationTheme, text: String, selected: Bool)
|
||||
case sound(index:Int32, section: NotificationPeerExceptionSection, theme: PresentationTheme, text: String, sound: PeerMessageSound, selected: Bool)
|
||||
|
||||
|
||||
var index: Int32 {
|
||||
switch self {
|
||||
case let .switcherHeader(index, _, _):
|
||||
return index
|
||||
case let .switcher(index, _, _, _, _):
|
||||
return index
|
||||
case let .soundModernHeader(index, _, _):
|
||||
return index
|
||||
case let .soundClassicHeader(index, _, _):
|
||||
return index
|
||||
case let .none(index, _, _, _, _):
|
||||
return index
|
||||
case let .default(index, _, _, _, _):
|
||||
return index
|
||||
case let .sound(index, _, _, _, _, _):
|
||||
return index
|
||||
}
|
||||
}
|
||||
|
||||
var section: ItemListSectionId {
|
||||
switch self {
|
||||
case .switcher, .switcherHeader:
|
||||
return NotificationPeerExceptionSection.switcher.rawValue
|
||||
case .soundModernHeader:
|
||||
return NotificationPeerExceptionSection.soundModern.rawValue
|
||||
case .soundClassicHeader:
|
||||
return NotificationPeerExceptionSection.soundClassic.rawValue
|
||||
case let .none(_, section, _, _, _):
|
||||
return section.rawValue
|
||||
case let .default(_, section, _, _, _):
|
||||
return section.rawValue
|
||||
case let .sound(_, section, _, _, _, _):
|
||||
return section.rawValue
|
||||
}
|
||||
}
|
||||
|
||||
var stableId: NotificationPeerExceptionEntryId {
|
||||
switch self {
|
||||
case let .switcher(_, _, _, mode, _):
|
||||
return .switcher(mode)
|
||||
case .switcherHeader:
|
||||
return .switcherHeader
|
||||
case .soundModernHeader:
|
||||
return .soundModernHeader
|
||||
case .soundClassicHeader:
|
||||
return .soundClassicHeader
|
||||
case .none:
|
||||
return .none
|
||||
case .default:
|
||||
return .default
|
||||
case let .sound(_, _, _, _, sound, _):
|
||||
return .sound(sound)
|
||||
}
|
||||
}
|
||||
|
||||
static func <(lhs: NotificationPeerExceptionEntry, rhs: NotificationPeerExceptionEntry) -> Bool {
|
||||
return lhs.index < rhs.index
|
||||
}
|
||||
|
||||
func item(_ arguments: NotificationPeerExceptionArguments) -> ListViewItem {
|
||||
switch self {
|
||||
case let .switcher(_, theme, strings, mode, selected):
|
||||
let title: String
|
||||
switch mode {
|
||||
case .alwaysOn:
|
||||
title = "Always On"
|
||||
case .alwaysOff:
|
||||
title = "Always Off"
|
||||
}
|
||||
return ItemListCheckboxItem(theme: theme, title: title, style: .left, checked: selected, zeroSeparatorInsets: false, sectionId: self.section, action: {
|
||||
arguments.selectMode(mode)
|
||||
})
|
||||
case let .switcherHeader(_, theme, text):
|
||||
return ItemListSectionHeaderItem(theme: theme, text: text, sectionId: self.section)
|
||||
case let .soundModernHeader(_, theme, text):
|
||||
return ItemListSectionHeaderItem(theme: theme, text: text, sectionId: self.section)
|
||||
case let .soundClassicHeader(_, theme, text):
|
||||
return ItemListSectionHeaderItem(theme: theme, text: text, sectionId: self.section)
|
||||
case let .none(_, _, theme, text, selected):
|
||||
return ItemListCheckboxItem(theme: theme, title: text, style: .left, checked: selected, zeroSeparatorInsets: true, sectionId: self.section, action: {
|
||||
arguments.selectSound(.none)
|
||||
})
|
||||
case let .default(_, _, theme, text, selected):
|
||||
return ItemListCheckboxItem(theme: theme, title: text, style: .left, checked: selected, zeroSeparatorInsets: false, sectionId: self.section, action: {
|
||||
arguments.selectSound(.default)
|
||||
})
|
||||
case let .sound(_, _, theme, text, sound, selected):
|
||||
return ItemListCheckboxItem(theme: theme, title: text, style: .left, checked: selected, zeroSeparatorInsets: false, sectionId: self.section, action: {
|
||||
arguments.selectSound(sound)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private func notificationPeerExceptionEntries(presentationData: PresentationData, state: NotificationExceptionPeerState) -> [NotificationPeerExceptionEntry] {
|
||||
var entries:[NotificationPeerExceptionEntry] = []
|
||||
|
||||
var index: Int32 = 0
|
||||
|
||||
entries.append(.switcherHeader(index: index, theme: presentationData.theme, title: "NOTIFICATIONS"))
|
||||
index += 1
|
||||
|
||||
|
||||
entries.append(.switcher(index: index, theme: presentationData.theme, strings: presentationData.strings, mode: .alwaysOn, selected: state.mode == .alwaysOn))
|
||||
index += 1
|
||||
entries.append(.switcher(index: index, theme: presentationData.theme, strings: presentationData.strings, mode: .alwaysOff, selected: state.mode == .alwaysOff))
|
||||
index += 1
|
||||
|
||||
|
||||
entries.append(.soundModernHeader(index: index, theme: presentationData.theme, title: presentationData.strings.Notifications_AlertTones))
|
||||
index += 1
|
||||
|
||||
if state.selectedSound == .default {
|
||||
var bp:Int = 0
|
||||
bp += 1
|
||||
}
|
||||
|
||||
entries.append(.default(index: index, section: .soundModern, theme: presentationData.theme, text: localizedPeerNotificationSoundString(strings: presentationData.strings, sound: .default, default: state.defaultSound), selected: state.selectedSound == .default))
|
||||
index += 1
|
||||
|
||||
entries.append(.none(index: index, section: .soundModern, theme: presentationData.theme, text: localizedPeerNotificationSoundString(strings: presentationData.strings, sound: .none), selected: state.selectedSound == .none))
|
||||
index += 1
|
||||
|
||||
for i in 0 ..< 12 {
|
||||
let sound: PeerMessageSound = .bundledModern(id: Int32(i))
|
||||
entries.append(.sound(index: index, section: .soundModern, theme: presentationData.theme, text: localizedPeerNotificationSoundString(strings: presentationData.strings, sound: sound), sound: sound, selected: sound == state.selectedSound))
|
||||
index += 1
|
||||
}
|
||||
|
||||
entries.append(.soundClassicHeader(index: index, theme: presentationData.theme, title: presentationData.strings.Notifications_ClassicTones))
|
||||
for i in 0 ..< 8 {
|
||||
let sound: PeerMessageSound = .bundledClassic(id: Int32(i))
|
||||
entries.append(.sound(index: index, section: .soundClassic, theme: presentationData.theme, text: localizedPeerNotificationSoundString(strings: presentationData.strings, sound: sound), sound: sound, selected: sound == state.selectedSound))
|
||||
index += 1
|
||||
}
|
||||
|
||||
return entries
|
||||
}
|
||||
|
||||
private struct NotificationExceptionPeerState : Equatable {
|
||||
let selectedSound: PeerMessageSound
|
||||
let mode: NotificationPeerExceptionSwitcher
|
||||
let defaultSound: PeerMessageSound
|
||||
init(notifications: TelegramPeerNotificationSettings? = nil) {
|
||||
|
||||
if let notifications = notifications {
|
||||
self.selectedSound = notifications.messageSound
|
||||
switch notifications.muteState {
|
||||
case .muted:
|
||||
self.mode = .alwaysOff
|
||||
case .unmuted:
|
||||
self.mode = .alwaysOn
|
||||
case .default:
|
||||
self.mode = .alwaysOn
|
||||
}
|
||||
} else {
|
||||
self.selectedSound = .default
|
||||
self.mode = .alwaysOn
|
||||
}
|
||||
|
||||
|
||||
self.defaultSound = .default
|
||||
}
|
||||
|
||||
init(selectedSound: PeerMessageSound, mode: NotificationPeerExceptionSwitcher, defaultSound: PeerMessageSound) {
|
||||
self.selectedSound = selectedSound
|
||||
self.mode = mode
|
||||
self.defaultSound = defaultSound
|
||||
}
|
||||
|
||||
func withUpdatedDefaultSound(_ defaultSound: PeerMessageSound) -> NotificationExceptionPeerState {
|
||||
return NotificationExceptionPeerState(selectedSound: self.selectedSound, mode: self.mode, defaultSound: defaultSound)
|
||||
}
|
||||
func withUpdatedSound(_ selectedSound: PeerMessageSound) -> NotificationExceptionPeerState {
|
||||
return NotificationExceptionPeerState(selectedSound: selectedSound, mode: self.mode, defaultSound: self.defaultSound)
|
||||
}
|
||||
func withUpdatedMode(_ mode: NotificationPeerExceptionSwitcher) -> NotificationExceptionPeerState {
|
||||
return NotificationExceptionPeerState(selectedSound: self.selectedSound, mode: mode, defaultSound: self.defaultSound)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func notificationPeerExceptionController(account: Account, peerId: PeerId, mode: NotificationExceptionMode, updatePeerSound: @escaping(PeerId, PeerMessageSound) -> Void, updatePeerNotificationInterval: @escaping(PeerId, Int32?) -> Void) -> ViewController {
|
||||
|
||||
|
||||
let initialState = NotificationExceptionPeerState()
|
||||
let statePromise = Promise(initialState)
|
||||
let stateValue = Atomic(value: initialState)
|
||||
let updateState: ((NotificationExceptionPeerState) -> NotificationExceptionPeerState) -> Void = { f in
|
||||
statePromise.set(.single(stateValue.modify { f($0) }))
|
||||
}
|
||||
|
||||
var completeImpl: (() -> Void)?
|
||||
var cancelImpl: (() -> Void)?
|
||||
let playSoundDisposable = MetaDisposable()
|
||||
|
||||
|
||||
let arguments = NotificationPeerExceptionArguments(account: account, selectSound: { sound in
|
||||
|
||||
updateState { state in
|
||||
playSoundDisposable.set(playSound(account: account, sound: sound, defaultSound: state.defaultSound).start())
|
||||
return state.withUpdatedSound(sound)
|
||||
}
|
||||
|
||||
}, selectMode: { mode in
|
||||
updateState { state in
|
||||
return state.withUpdatedMode(mode)
|
||||
}
|
||||
}, complete: {
|
||||
completeImpl?()
|
||||
}, cancel: {
|
||||
cancelImpl?()
|
||||
})
|
||||
|
||||
|
||||
|
||||
|
||||
statePromise.set(account.postbox.transaction { transaction -> NotificationExceptionPeerState in
|
||||
var state = NotificationExceptionPeerState(notifications: transaction.getPeerNotificationSettings(peerId) as? TelegramPeerNotificationSettings)
|
||||
let globalSettings: GlobalNotificationSettings = (transaction.getPreferencesEntry(key: PreferencesKeys.globalNotifications) as? GlobalNotificationSettings) ?? GlobalNotificationSettings.defaultSettings
|
||||
switch mode {
|
||||
case .channels:
|
||||
state = state.withUpdatedDefaultSound(globalSettings.effective.channels.sound)
|
||||
case .groups:
|
||||
state = state.withUpdatedDefaultSound(globalSettings.effective.groupChats.sound)
|
||||
case .users:
|
||||
state = state.withUpdatedDefaultSound(globalSettings.effective.privateChats.sound)
|
||||
}
|
||||
_ = stateValue.swap(state)
|
||||
return state
|
||||
})
|
||||
|
||||
|
||||
let signal = combineLatest((account.applicationContext as! TelegramApplicationContext).presentationData, statePromise.get() |> distinctUntilChanged)
|
||||
|> map { presentationData, state -> (ItemListControllerState, (ItemListNodeState<NotificationPeerExceptionEntry>, NotificationPeerExceptionEntry.ItemGenerationArguments)) in
|
||||
|
||||
let leftNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Cancel), style: .regular, enabled: true, action: {
|
||||
arguments.cancel()
|
||||
})
|
||||
|
||||
let rightNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Done), style: .bold, enabled: true, action: {
|
||||
arguments.complete()
|
||||
})
|
||||
|
||||
let controllerState = ItemListControllerState(theme: presentationData.theme, title: .text("New Exception"), leftNavigationButton: leftNavigationButton, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back))
|
||||
let listState = ItemListNodeState(entries: notificationPeerExceptionEntries(presentationData: presentationData, state: state), style: .blocks)
|
||||
|
||||
return (controllerState, (listState, arguments))
|
||||
}
|
||||
|
||||
let controller = ItemListController(account: account, state: signal |> afterDisposed {
|
||||
playSoundDisposable.dispose()
|
||||
})
|
||||
|
||||
controller.enableInteractiveDismiss = true
|
||||
|
||||
completeImpl = { [weak controller] in
|
||||
controller?.dismiss()
|
||||
updateState { state in
|
||||
updatePeerSound(peerId, state.selectedSound)
|
||||
updatePeerNotificationInterval(peerId, state.mode == .alwaysOn ? 0 : Int32.max)
|
||||
return state
|
||||
}
|
||||
}
|
||||
|
||||
cancelImpl = { [weak controller] in
|
||||
controller?.dismiss()
|
||||
}
|
||||
|
||||
return controller
|
||||
}
|
||||
@@ -209,7 +209,7 @@ public func fileNameForNotificationSound(_ sound: PeerMessageSound, defaultSound
|
||||
}
|
||||
}
|
||||
|
||||
private func playSound(account: Account, sound: PeerMessageSound, defaultSound: PeerMessageSound?) -> Signal<Void, NoError> {
|
||||
func playSound(account: Account, sound: PeerMessageSound, defaultSound: PeerMessageSound?) -> Signal<Void, NoError> {
|
||||
if case .none = sound {
|
||||
return .complete()
|
||||
} else {
|
||||
|
||||
@@ -422,7 +422,7 @@ private enum NotificationsAndSoundsEntry: ItemListNodeEntry {
|
||||
})
|
||||
case let .userExceptions(theme, strings, text, value):
|
||||
return ItemListDisclosureItem(theme: theme, title: text, label: strings.Notifications_Exceptions(Int32(value.settings.count)), sectionId: self.section, style: .blocks, action: {
|
||||
let controller = notificationExceptionsController(account: arguments.account, mode: value, updatedMode: arguments.updatedExceptionMode)
|
||||
let controller = NotificationExceptionsController(account: arguments.account, mode: value, updatedMode: arguments.updatedExceptionMode)
|
||||
arguments.pushController(controller)
|
||||
})
|
||||
case let .messageNotice(theme, text):
|
||||
@@ -446,7 +446,7 @@ private enum NotificationsAndSoundsEntry: ItemListNodeEntry {
|
||||
})
|
||||
case let .groupExceptions(theme, strings, text, value):
|
||||
return ItemListDisclosureItem(theme: theme, title: text, label: strings.Notifications_Exceptions(Int32(value.settings.count)), sectionId: self.section, style: .blocks, action: {
|
||||
let controller = notificationExceptionsController(account: arguments.account, mode: value, updatedMode: arguments.updatedExceptionMode)
|
||||
let controller = NotificationExceptionsController(account: arguments.account, mode: value, updatedMode: arguments.updatedExceptionMode)
|
||||
arguments.pushController(controller)
|
||||
})
|
||||
case let .groupNotice(theme, text):
|
||||
@@ -470,7 +470,7 @@ private enum NotificationsAndSoundsEntry: ItemListNodeEntry {
|
||||
})
|
||||
case let .channelExceptions(theme, strings, text, value):
|
||||
return ItemListDisclosureItem(theme: theme, title: text, label: strings.Notifications_Exceptions(Int32(value.settings.count)), sectionId: self.section, style: .blocks, action: {
|
||||
let controller = notificationExceptionsController(account: arguments.account, mode: value, updatedMode: arguments.updatedExceptionMode)
|
||||
let controller = NotificationExceptionsController(account: arguments.account, mode: value, updatedMode: arguments.updatedExceptionMode)
|
||||
arguments.pushController(controller)
|
||||
})
|
||||
case let .channelNotice(theme, text):
|
||||
@@ -533,33 +533,33 @@ private func filteredGlobalSound(_ sound: PeerMessageSound) -> PeerMessageSound
|
||||
}
|
||||
}
|
||||
|
||||
private func notificationsAndSoundsEntries(globalSettings: GlobalNotificationSettingsSet, inAppSettings: InAppNotificationSettings, exceptions: (NotificationExceptionMode, NotificationExceptionMode), presentationData: PresentationData) -> [NotificationsAndSoundsEntry] {
|
||||
private func notificationsAndSoundsEntries(globalSettings: GlobalNotificationSettingsSet, inAppSettings: InAppNotificationSettings, exceptions: (users: NotificationExceptionMode, groups: NotificationExceptionMode, channels: NotificationExceptionMode), presentationData: PresentationData) -> [NotificationsAndSoundsEntry] {
|
||||
var entries: [NotificationsAndSoundsEntry] = []
|
||||
|
||||
entries.append(.messageHeader(presentationData.theme, presentationData.strings.Notifications_MessageNotifications))
|
||||
entries.append(.messageAlerts(presentationData.theme, presentationData.strings.Notifications_MessageNotificationsAlert, globalSettings.privateChats.enabled))
|
||||
entries.append(.messagePreviews(presentationData.theme, presentationData.strings.Notifications_MessageNotificationsPreview, globalSettings.privateChats.displayPreviews))
|
||||
entries.append(.messageSound(presentationData.theme, presentationData.strings.Notifications_MessageNotificationsSound, localizedPeerNotificationSoundString(strings: presentationData.strings, sound: filteredGlobalSound(globalSettings.privateChats.sound)), filteredGlobalSound(globalSettings.privateChats.sound)))
|
||||
if !exceptions.0.isEmpty {
|
||||
// entries.append(.userExceptions(presentationData.theme, presentationData.strings, presentationData.strings.Notifications_MessageNotificationsExceptions, exceptions.0))
|
||||
}
|
||||
//if !exceptions.users.isEmpty {
|
||||
entries.append(.userExceptions(presentationData.theme, presentationData.strings, presentationData.strings.Notifications_MessageNotificationsExceptions, exceptions.users))
|
||||
// }
|
||||
entries.append(.messageNotice(presentationData.theme, presentationData.strings.Notifications_MessageNotificationsHelp))
|
||||
|
||||
entries.append(.groupHeader(presentationData.theme, presentationData.strings.Notifications_GroupNotifications))
|
||||
entries.append(.groupAlerts(presentationData.theme, presentationData.strings.Notifications_MessageNotificationsAlert, globalSettings.groupChats.enabled))
|
||||
entries.append(.groupPreviews(presentationData.theme, presentationData.strings.Notifications_MessageNotificationsPreview, globalSettings.groupChats.displayPreviews))
|
||||
entries.append(.groupSound(presentationData.theme, presentationData.strings.Notifications_MessageNotificationsSound, localizedPeerNotificationSoundString(strings: presentationData.strings, sound: filteredGlobalSound(globalSettings.groupChats.sound)), filteredGlobalSound(globalSettings.groupChats.sound)))
|
||||
if !exceptions.1.isEmpty {
|
||||
// entries.append(.groupExceptions(presentationData.theme, presentationData.strings, presentationData.strings.Notifications_MessageNotificationsExceptions, exceptions.1))
|
||||
}
|
||||
// if !exceptions.groups.isEmpty {
|
||||
entries.append(.groupExceptions(presentationData.theme, presentationData.strings, presentationData.strings.Notifications_MessageNotificationsExceptions, exceptions.groups))
|
||||
// }
|
||||
|
||||
entries.append(.channelHeader(presentationData.theme, presentationData.strings.Notifications_ChannelNotifications))
|
||||
entries.append(.channelAlerts(presentationData.theme, presentationData.strings.Notifications_MessageNotificationsAlert, globalSettings.channels.enabled))
|
||||
entries.append(.channelPreviews(presentationData.theme, presentationData.strings.Notifications_MessageNotificationsPreview, globalSettings.channels.displayPreviews))
|
||||
entries.append(.channelSound(presentationData.theme, presentationData.strings.Notifications_MessageNotificationsSound, localizedPeerNotificationSoundString(strings: presentationData.strings, sound: filteredGlobalSound(globalSettings.channels.sound)), filteredGlobalSound(globalSettings.channels.sound)))
|
||||
if !exceptions.1.isEmpty {
|
||||
// entries.append(.groupExceptions(presentationData.theme, presentationData.strings, presentationData.strings.Notifications_MessageNotificationsExceptions, exceptions.1))
|
||||
}
|
||||
// if !exceptions.channels.isEmpty {
|
||||
entries.append(.channelExceptions(presentationData.theme, presentationData.strings, presentationData.strings.Notifications_MessageNotificationsExceptions, exceptions.channels))
|
||||
// }
|
||||
|
||||
entries.append(.channelNotice(presentationData.theme, presentationData.strings.Notifications_ChannelNotificationsHelp))
|
||||
|
||||
@@ -590,9 +590,9 @@ public func notificationsAndSoundsController(account: Account) -> ViewController
|
||||
|
||||
|
||||
|
||||
let notificationExceptions: Promise<(NotificationExceptionMode, NotificationExceptionMode)> = Promise()
|
||||
let notificationExceptions: Promise<(users: NotificationExceptionMode, groups: NotificationExceptionMode, channels: NotificationExceptionMode)> = Promise()
|
||||
|
||||
let updateNotificationExceptions:((NotificationExceptionMode, NotificationExceptionMode)) -> Void = { value in
|
||||
let updateNotificationExceptions:((users: NotificationExceptionMode, groups: NotificationExceptionMode, channels: NotificationExceptionMode)) -> Void = { value in
|
||||
notificationExceptions.set(.single(value))
|
||||
}
|
||||
|
||||
@@ -724,12 +724,14 @@ public func notificationsAndSoundsController(account: Account) -> ViewController
|
||||
])])
|
||||
presentControllerImpl?(actionSheet, nil)
|
||||
}, updatedExceptionMode: { mode in
|
||||
_ = (notificationExceptions.get() |> take(1) |> deliverOnMainQueue).start(next: { (users, groups) in
|
||||
_ = (notificationExceptions.get() |> take(1) |> deliverOnMainQueue).start(next: { (users, groups, channels) in
|
||||
switch mode {
|
||||
case .users:
|
||||
updateNotificationExceptions((mode, groups))
|
||||
updateNotificationExceptions((mode, groups, channels))
|
||||
case .groups:
|
||||
updateNotificationExceptions((users, mode))
|
||||
updateNotificationExceptions((users, mode, channels))
|
||||
case .channels:
|
||||
updateNotificationExceptions((users, groups, mode))
|
||||
}
|
||||
})
|
||||
})
|
||||
@@ -738,11 +740,11 @@ public func notificationsAndSoundsController(account: Account) -> ViewController
|
||||
|
||||
|
||||
|
||||
notificationExceptions.set(account.postbox.transaction{ transaction -> (NotificationExceptionMode, NotificationExceptionMode) in
|
||||
notificationExceptions.set(account.postbox.transaction { transaction -> (NotificationExceptionMode, NotificationExceptionMode, NotificationExceptionMode) in
|
||||
let allSettings = transaction.getAllPeerNotificationSettings() ?? [:]
|
||||
var users:[PeerId : NotificationExceptionWrapper] = [:]
|
||||
var groups: [PeerId : NotificationExceptionWrapper] = [:]
|
||||
|
||||
var channels:[PeerId : NotificationExceptionWrapper] = [:]
|
||||
for (key, value) in allSettings {
|
||||
let peer = transaction.getPeer(key)
|
||||
if let value = value as? TelegramPeerNotificationSettings, let peer = peer, !peer.displayTitle.isEmpty, peer.id != account.peerId {
|
||||
@@ -756,21 +758,28 @@ public func notificationsAndSoundsController(account: Account) -> ViewController
|
||||
case Namespaces.Peer.CloudUser:
|
||||
users[key] = NotificationExceptionWrapper(settings: value)
|
||||
default:
|
||||
if let peer = peer as? TelegramChannel, case .broadcast = peer.info {
|
||||
channels[key] = NotificationExceptionWrapper(settings: value)
|
||||
} else {
|
||||
groups[key] = NotificationExceptionWrapper(settings: value)
|
||||
}
|
||||
}
|
||||
}
|
||||
default:
|
||||
switch key.namespace {
|
||||
case Namespaces.Peer.CloudUser:
|
||||
users[key] = NotificationExceptionWrapper(settings: value)
|
||||
default:
|
||||
if let peer = peer as? TelegramChannel, case .broadcast = peer.info {
|
||||
channels[key] = NotificationExceptionWrapper(settings: value)
|
||||
} else {
|
||||
groups[key] = NotificationExceptionWrapper(settings: value)
|
||||
}
|
||||
} }
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return (.users(users), .groups(groups))
|
||||
return (.users(users), .groups(groups), .channels(channels))
|
||||
})
|
||||
|
||||
let signal = combineLatest((account.applicationContext as! TelegramApplicationContext).presentationData, preferences, notificationExceptions.get())
|
||||
|
||||
@@ -40,10 +40,12 @@ public final class PeerSelectionController: ViewController {
|
||||
return self._ready
|
||||
}
|
||||
|
||||
public init(account: Account, filter: ChatListNodePeersFilter = [.onlyWriteable], title: String? = nil) {
|
||||
private let hasContactSelector: Bool
|
||||
|
||||
public init(account: Account, filter: ChatListNodePeersFilter = [.onlyWriteable], hasContactSelector: Bool = true, title: String? = nil) {
|
||||
self.account = account
|
||||
self.filter = filter
|
||||
|
||||
self.hasContactSelector = hasContactSelector
|
||||
self.presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 }
|
||||
|
||||
super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData))
|
||||
@@ -69,7 +71,7 @@ public final class PeerSelectionController: ViewController {
|
||||
}
|
||||
|
||||
override public func loadDisplayNode() {
|
||||
self.displayNode = PeerSelectionControllerNode(account: self.account, filter: self.filter, dismiss: { [weak self] in
|
||||
self.displayNode = PeerSelectionControllerNode(account: self.account, filter: self.filter, hasContactSelector: hasContactSelector, dismiss: { [weak self] in
|
||||
self?.presentingViewController?.dismiss(animated: false, completion: nil)
|
||||
})
|
||||
|
||||
@@ -152,7 +154,7 @@ public final class PeerSelectionController: ViewController {
|
||||
}
|
||||
}
|
||||
|
||||
override open func dismiss(completion: (() -> Void)? = nil) {
|
||||
override public func dismiss(completion: (() -> Void)? = nil) {
|
||||
self.peerSelectionNode.view.endEditing(true)
|
||||
self.peerSelectionNode.animateOut(completion: completion)
|
||||
}
|
||||
|
||||
@@ -18,9 +18,9 @@ final class PeerSelectionControllerNode: ASDisplayNode {
|
||||
|
||||
var navigationBar: NavigationBar?
|
||||
|
||||
private let toolbarBackgroundNode: ASDisplayNode
|
||||
private let toolbarSeparatorNode: ASDisplayNode
|
||||
private let segmentedControl: UISegmentedControl
|
||||
private let toolbarBackgroundNode: ASDisplayNode?
|
||||
private let toolbarSeparatorNode: ASDisplayNode?
|
||||
private let segmentedControl: UISegmentedControl?
|
||||
|
||||
private var contactListNode: ContactListNode?
|
||||
private let chatListNode: ChatListNode
|
||||
@@ -45,22 +45,30 @@ final class PeerSelectionControllerNode: ASDisplayNode {
|
||||
return self.readyValue.get()
|
||||
}
|
||||
|
||||
init(account: Account, filter: ChatListNodePeersFilter, dismiss: @escaping () -> Void) {
|
||||
|
||||
init(account: Account, filter: ChatListNodePeersFilter, hasContactSelector: Bool, dismiss: @escaping () -> Void) {
|
||||
self.account = account
|
||||
self.dismiss = dismiss
|
||||
self.filter = filter
|
||||
|
||||
self.presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 }
|
||||
|
||||
if hasContactSelector {
|
||||
self.toolbarBackgroundNode = ASDisplayNode()
|
||||
self.toolbarBackgroundNode.backgroundColor = self.presentationData.theme.rootController.navigationBar.backgroundColor
|
||||
self.toolbarBackgroundNode?.backgroundColor = self.presentationData.theme.rootController.navigationBar.backgroundColor
|
||||
|
||||
self.toolbarSeparatorNode = ASDisplayNode()
|
||||
self.toolbarSeparatorNode.backgroundColor = self.presentationData.theme.rootController.navigationBar.separatorColor
|
||||
self.toolbarSeparatorNode?.backgroundColor = self.presentationData.theme.rootController.navigationBar.separatorColor
|
||||
|
||||
self.segmentedControl = UISegmentedControl(items: [self.presentationData.strings.DialogList_TabTitle, self.presentationData.strings.Contacts_TabTitle])
|
||||
self.segmentedControl.tintColor = self.presentationData.theme.rootController.navigationBar.accentTextColor
|
||||
self.segmentedControl.selectedSegmentIndex = 0
|
||||
self.segmentedControl?.tintColor = self.presentationData.theme.rootController.navigationBar.accentTextColor
|
||||
self.segmentedControl?.selectedSegmentIndex = 0
|
||||
} else {
|
||||
self.toolbarBackgroundNode = nil
|
||||
self.toolbarSeparatorNode = nil
|
||||
self.segmentedControl = nil
|
||||
}
|
||||
|
||||
|
||||
self.chatListNode = ChatListNode(account: account, groupId: nil, controlsHistoryPreload: false, mode: .peers(filter: filter), theme: presentationData.theme, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, nameSortOrder: presentationData.nameSortOrder, nameDisplayOrder: presentationData.nameDisplayOrder, disableAnimations: presentationData.disableAnimations)
|
||||
|
||||
@@ -93,12 +101,13 @@ final class PeerSelectionControllerNode: ASDisplayNode {
|
||||
}
|
||||
})
|
||||
|
||||
self.addSubnode(self.toolbarBackgroundNode)
|
||||
self.addSubnode(self.toolbarSeparatorNode)
|
||||
if hasContactSelector {
|
||||
self.addSubnode(self.toolbarBackgroundNode!)
|
||||
self.addSubnode(self.toolbarSeparatorNode!)
|
||||
self.view.addSubview(self.segmentedControl!)
|
||||
self.segmentedControl!.addTarget(self, action: #selector(indexChanged), for: .valueChanged)
|
||||
}
|
||||
|
||||
self.view.addSubview(self.segmentedControl)
|
||||
|
||||
self.segmentedControl.addTarget(self, action: #selector(indexChanged), for: .valueChanged)
|
||||
|
||||
self.readyValue.set(self.chatListNode.ready)
|
||||
}
|
||||
@@ -118,14 +127,22 @@ final class PeerSelectionControllerNode: ASDisplayNode {
|
||||
|
||||
let cleanInsets = layout.insets(options: [])
|
||||
|
||||
let toolbarHeight: CGFloat = 44.0 + cleanInsets.bottom
|
||||
var toolbarHeight: CGFloat = cleanInsets.bottom
|
||||
|
||||
transition.updateFrame(node: self.toolbarBackgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - toolbarHeight), size: CGSize(width: layout.size.width, height: toolbarHeight)))
|
||||
transition.updateFrame(node: self.toolbarSeparatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - toolbarHeight), size: CGSize(width: layout.size.width, height: UIScreenPixel)))
|
||||
|
||||
var controlSize = self.segmentedControl.sizeThatFits(layout.size)
|
||||
if let segmentedControl = segmentedControl, let toolbarBackgroundNode = toolbarBackgroundNode, let toolbarSeparatorNode = toolbarSeparatorNode {
|
||||
toolbarHeight += 44
|
||||
transition.updateFrame(node: toolbarBackgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - toolbarHeight), size: CGSize(width: layout.size.width, height: toolbarHeight)))
|
||||
transition.updateFrame(node: toolbarSeparatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - toolbarHeight), size: CGSize(width: layout.size.width, height: UIScreenPixel)))
|
||||
|
||||
var controlSize = segmentedControl.sizeThatFits(layout.size)
|
||||
controlSize.width = min(layout.size.width, max(200.0, controlSize.width))
|
||||
transition.updateFrame(view: self.segmentedControl, frame: CGRect(origin: CGPoint(x: floor((layout.size.width - controlSize.width) / 2.0), y: layout.size.height - toolbarHeight + floor((44.0 - controlSize.height) / 2.0)), size: controlSize))
|
||||
transition.updateFrame(view: segmentedControl, frame: CGRect(origin: CGPoint(x: floor((layout.size.width - controlSize.width) / 2.0), y: layout.size.height - toolbarHeight + floor((44.0 - controlSize.height) / 2.0)), size: controlSize))
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
var insets = layout.insets(options: [.input])
|
||||
insets.top += max(navigationBarHeight, layout.insets(options: [.statusBar]).top)
|
||||
@@ -304,11 +321,11 @@ final class PeerSelectionControllerNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
@objc func indexChanged() {
|
||||
guard let (layout, navigationHeight) = self.containerLayout else {
|
||||
guard let (layout, navigationHeight) = self.containerLayout, let segmentedControl = self.segmentedControl else {
|
||||
return
|
||||
}
|
||||
|
||||
let contactListActive = self.segmentedControl.selectedSegmentIndex == 1
|
||||
let contactListActive = segmentedControl.selectedSegmentIndex == 1
|
||||
if contactListActive != self.contactListActive {
|
||||
self.contactListActive = contactListActive
|
||||
if contactListActive {
|
||||
|
||||
@@ -26,6 +26,18 @@ public enum PresentationPersonNameOrder {
|
||||
case lastFirst
|
||||
}
|
||||
|
||||
extension PresentationStrings : Equatable {
|
||||
public static func ==(lhs: PresentationStrings, rhs: PresentationStrings) -> Bool {
|
||||
return lhs === rhs
|
||||
}
|
||||
}
|
||||
//
|
||||
//extension PresentationTheme : Equatable {
|
||||
// public static func ==(lhs: PresentationTheme, rhs: PresentationTheme) -> Bool {
|
||||
// return lhs === rhs
|
||||
// }
|
||||
//}
|
||||
|
||||
public final class PresentationData: Equatable {
|
||||
public let strings: PresentationStrings
|
||||
public let theme: PresentationTheme
|
||||
|
||||
Reference in New Issue
Block a user