mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-10-09 03:20:48 +00:00
Various settings UI improvements
This commit is contained in:
parent
aadf9ceedf
commit
4fef982671
@ -6873,7 +6873,7 @@ Sorry for the inconvenience.";
|
||||
"InviteLink.Create.RequestApprovalOnInfoChannel" = "New users will be able to join the channel only after having been approved by the admins.";
|
||||
|
||||
"InviteLink.Create.LinkName" = "Link Name (Optional)";
|
||||
"InviteLink.Create.LinkNameInfo" = "You can provide an optional name for this link.";
|
||||
"InviteLink.Create.LinkNameInfo" = "Only you and other admins will see this name.";
|
||||
|
||||
"MemberRequests.Title" = "Member Requests";
|
||||
"MemberRequests.DescriptionGroup" = "Some [additional links]() are set up to accept requests to join the group.";
|
||||
@ -6962,3 +6962,19 @@ Sorry for the inconvenience.";
|
||||
"Time.AtDate" = "last seen %@";
|
||||
|
||||
"Stickers.ShowMore" = "Show More";
|
||||
|
||||
"Notifications.PrivateChats" = "Private Chats";
|
||||
"Notifications.GroupChats" = "Group Chats";
|
||||
"Notifications.Channels" = "Channels";
|
||||
|
||||
"Notifications.CategoryExceptions_0" = "%@ exceptions";
|
||||
"Notifications.CategoryExceptions_1" = "%@ exception";
|
||||
"Notifications.CategoryExceptions_2" = "%@ exceptions";
|
||||
"Notifications.CategoryExceptions_3_10" = "%@ exceptions";
|
||||
"Notifications.CategoryExceptions_many" = "%@ exceptions";
|
||||
"Notifications.CategoryExceptions_any" = "%@ exceptions";
|
||||
|
||||
"Notifications.DeleteAllExceptions" = "Delete All Exceptions";
|
||||
|
||||
"Notifications.On" = "On";
|
||||
"Notifications.Off" = "Off";
|
||||
|
@ -328,7 +328,7 @@ public class ItemListSwitchItemNode: ListViewItemNode, ItemListItemNode {
|
||||
strongSelf.insertSubnode(strongSelf.bottomStripeNode, at: 2)
|
||||
}
|
||||
if strongSelf.maskNode.supernode == nil {
|
||||
strongSelf.insertSubnode(strongSelf.maskNode, at: 3)
|
||||
strongSelf.insertSubnode(strongSelf.maskNode, aboveSubnode: strongSelf.switchGestureNode)
|
||||
}
|
||||
|
||||
let hasCorners = itemListHasRoundedBlockLayout(params) && !item.noCorners
|
||||
|
@ -61,18 +61,8 @@ private final class NotificationsAndSoundsArguments {
|
||||
let authorizeNotifications: () -> Void
|
||||
let suppressWarning: () -> Void
|
||||
|
||||
let updateMessageAlerts: (Bool) -> Void
|
||||
let updateMessagePreviews: (Bool) -> Void
|
||||
let updateMessageSound: (PeerMessageSound) -> Void
|
||||
|
||||
let updateGroupAlerts: (Bool) -> Void
|
||||
let updateGroupPreviews: (Bool) -> Void
|
||||
let updateGroupSound: (PeerMessageSound) -> Void
|
||||
|
||||
let updateChannelAlerts: (Bool) -> Void
|
||||
let updateChannelPreviews: (Bool) -> Void
|
||||
let updateChannelSound: (PeerMessageSound) -> Void
|
||||
|
||||
let openPeerCategory: (NotificationsPeerCategory) -> Void
|
||||
|
||||
let updateInAppSounds: (Bool) -> Void
|
||||
let updateInAppVibration: (Bool) -> Void
|
||||
let updateInAppPreviews: (Bool) -> Void
|
||||
@ -84,29 +74,19 @@ private final class NotificationsAndSoundsArguments {
|
||||
let updateJoinedNotifications: (Bool) -> Void
|
||||
|
||||
let resetNotifications: () -> Void
|
||||
|
||||
let updatedExceptionMode: (NotificationExceptionMode) -> Void
|
||||
|
||||
|
||||
let openAppSettings: () -> Void
|
||||
|
||||
let updateNotificationsFromAllAccounts: (Bool) -> Void
|
||||
|
||||
init(context: AccountContext, presentController: @escaping (ViewController, ViewControllerPresentationArguments?) -> Void, pushController: @escaping(ViewController)->Void, soundSelectionDisposable: MetaDisposable, authorizeNotifications: @escaping () -> Void, suppressWarning: @escaping () -> Void, updateMessageAlerts: @escaping (Bool) -> Void, updateMessagePreviews: @escaping (Bool) -> Void, updateMessageSound: @escaping (PeerMessageSound) -> Void, updateGroupAlerts: @escaping (Bool) -> Void, updateGroupPreviews: @escaping (Bool) -> Void, updateGroupSound: @escaping (PeerMessageSound) -> Void, updateChannelAlerts: @escaping (Bool) -> Void, updateChannelPreviews: @escaping (Bool) -> Void, updateChannelSound: @escaping (PeerMessageSound) -> Void, updateInAppSounds: @escaping (Bool) -> Void, updateInAppVibration: @escaping (Bool) -> Void, updateInAppPreviews: @escaping (Bool) -> Void, updateDisplayNameOnLockscreen: @escaping (Bool) -> Void, updateIncludeTag: @escaping (CounterTagSettings, Bool) -> Void, updateTotalUnreadCountCategory: @escaping (Bool) -> Void, resetNotifications: @escaping () -> Void, updatedExceptionMode: @escaping(NotificationExceptionMode) -> Void, openAppSettings: @escaping () -> Void, updateJoinedNotifications: @escaping (Bool) -> Void, updateNotificationsFromAllAccounts: @escaping (Bool) -> Void) {
|
||||
init(context: AccountContext, presentController: @escaping (ViewController, ViewControllerPresentationArguments?) -> Void, pushController: @escaping(ViewController)->Void, soundSelectionDisposable: MetaDisposable, authorizeNotifications: @escaping () -> Void, suppressWarning: @escaping () -> Void, openPeerCategory: @escaping (NotificationsPeerCategory) -> Void, updateInAppSounds: @escaping (Bool) -> Void, updateInAppVibration: @escaping (Bool) -> Void, updateInAppPreviews: @escaping (Bool) -> Void, updateDisplayNameOnLockscreen: @escaping (Bool) -> Void, updateIncludeTag: @escaping (CounterTagSettings, Bool) -> Void, updateTotalUnreadCountCategory: @escaping (Bool) -> Void, resetNotifications: @escaping () -> Void, openAppSettings: @escaping () -> Void, updateJoinedNotifications: @escaping (Bool) -> Void, updateNotificationsFromAllAccounts: @escaping (Bool) -> Void) {
|
||||
self.context = context
|
||||
self.presentController = presentController
|
||||
self.pushController = pushController
|
||||
self.soundSelectionDisposable = soundSelectionDisposable
|
||||
self.authorizeNotifications = authorizeNotifications
|
||||
self.suppressWarning = suppressWarning
|
||||
self.updateMessageAlerts = updateMessageAlerts
|
||||
self.updateMessagePreviews = updateMessagePreviews
|
||||
self.updateMessageSound = updateMessageSound
|
||||
self.updateGroupAlerts = updateGroupAlerts
|
||||
self.updateGroupPreviews = updateGroupPreviews
|
||||
self.updateGroupSound = updateGroupSound
|
||||
self.updateChannelAlerts = updateChannelAlerts
|
||||
self.updateChannelPreviews = updateChannelPreviews
|
||||
self.updateChannelSound = updateChannelSound
|
||||
self.openPeerCategory = openPeerCategory
|
||||
self.updateInAppSounds = updateInAppSounds
|
||||
self.updateInAppVibration = updateInAppVibration
|
||||
self.updateInAppPreviews = updateInAppPreviews
|
||||
@ -114,7 +94,6 @@ private final class NotificationsAndSoundsArguments {
|
||||
self.updateIncludeTag = updateIncludeTag
|
||||
self.updateTotalUnreadCountCategory = updateTotalUnreadCountCategory
|
||||
self.resetNotifications = resetNotifications
|
||||
self.updatedExceptionMode = updatedExceptionMode
|
||||
self.openAppSettings = openAppSettings
|
||||
self.updateJoinedNotifications = updateJoinedNotifications
|
||||
self.updateNotificationsFromAllAccounts = updateNotificationsFromAllAccounts
|
||||
@ -124,9 +103,7 @@ private final class NotificationsAndSoundsArguments {
|
||||
private enum NotificationsAndSoundsSection: Int32 {
|
||||
case accounts
|
||||
case permission
|
||||
case messages
|
||||
case groups
|
||||
case channels
|
||||
case categories
|
||||
case inApp
|
||||
case displayNamesOnLockscreen
|
||||
case badge
|
||||
@ -136,12 +113,6 @@ private enum NotificationsAndSoundsSection: Int32 {
|
||||
|
||||
public enum NotificationsAndSoundsEntryTag: ItemListItemTag {
|
||||
case allAccounts
|
||||
case messageAlerts
|
||||
case messagePreviews
|
||||
case groupAlerts
|
||||
case groupPreviews
|
||||
case channelAlerts
|
||||
case channelPreviews
|
||||
case inAppSounds
|
||||
case inAppVibrate
|
||||
case inAppPreviews
|
||||
@ -168,27 +139,10 @@ private enum NotificationsAndSoundsEntry: ItemListNodeEntry {
|
||||
case permissionInfo(PresentationTheme, String, String, Bool)
|
||||
case permissionEnable(PresentationTheme, String)
|
||||
|
||||
case messageHeader(PresentationTheme, String)
|
||||
case messageAlerts(PresentationTheme, String, Bool)
|
||||
case messagePreviews(PresentationTheme, String, Bool)
|
||||
case messageSound(PresentationTheme, String, String, PeerMessageSound)
|
||||
case userExceptions(PresentationTheme, PresentationStrings, String, NotificationExceptionMode)
|
||||
|
||||
case messageNotice(PresentationTheme, String)
|
||||
|
||||
case groupHeader(PresentationTheme, String)
|
||||
case groupAlerts(PresentationTheme, String, Bool)
|
||||
case groupPreviews(PresentationTheme, String, Bool)
|
||||
case groupSound(PresentationTheme, String, String, PeerMessageSound)
|
||||
case groupExceptions(PresentationTheme, PresentationStrings, String, NotificationExceptionMode)
|
||||
case groupNotice(PresentationTheme, String)
|
||||
|
||||
case channelHeader(PresentationTheme, String)
|
||||
case channelAlerts(PresentationTheme, String, Bool)
|
||||
case channelPreviews(PresentationTheme, String, Bool)
|
||||
case channelSound(PresentationTheme, String, String, PeerMessageSound)
|
||||
case channelExceptions(PresentationTheme, PresentationStrings, String, NotificationExceptionMode)
|
||||
case channelNotice(PresentationTheme, String)
|
||||
case categoriesHeader(PresentationTheme, String)
|
||||
case privateChats(PresentationTheme, String, String, String)
|
||||
case groupChats(PresentationTheme, String, String, String)
|
||||
case channels(PresentationTheme, String, String, String)
|
||||
|
||||
case inAppHeader(PresentationTheme, String)
|
||||
case inAppSounds(PresentationTheme, String, Bool)
|
||||
@ -215,12 +169,8 @@ private enum NotificationsAndSoundsEntry: ItemListNodeEntry {
|
||||
return NotificationsAndSoundsSection.accounts.rawValue
|
||||
case .permissionInfo, .permissionEnable:
|
||||
return NotificationsAndSoundsSection.permission.rawValue
|
||||
case .messageHeader, .messageAlerts, .messagePreviews, .messageSound, .messageNotice, .userExceptions:
|
||||
return NotificationsAndSoundsSection.messages.rawValue
|
||||
case .groupHeader, .groupAlerts, .groupPreviews, .groupSound, .groupNotice, .groupExceptions:
|
||||
return NotificationsAndSoundsSection.groups.rawValue
|
||||
case .channelHeader, .channelAlerts, .channelPreviews, .channelSound, .channelNotice, .channelExceptions:
|
||||
return NotificationsAndSoundsSection.channels.rawValue
|
||||
case .categoriesHeader, .privateChats, .groupChats, .channels:
|
||||
return NotificationsAndSoundsSection.categories.rawValue
|
||||
case .inAppHeader, .inAppSounds, .inAppVibrate, .inAppPreviews:
|
||||
return NotificationsAndSoundsSection.inApp.rawValue
|
||||
case .displayNamesOnLockscreen, .displayNamesOnLockscreenInfo:
|
||||
@ -246,70 +196,42 @@ private enum NotificationsAndSoundsEntry: ItemListNodeEntry {
|
||||
return 3
|
||||
case .permissionEnable:
|
||||
return 4
|
||||
case .messageHeader:
|
||||
case .categoriesHeader:
|
||||
return 5
|
||||
case .messageAlerts:
|
||||
case .privateChats:
|
||||
return 6
|
||||
case .messagePreviews:
|
||||
case .groupChats:
|
||||
return 7
|
||||
case .messageSound:
|
||||
case .channels:
|
||||
return 8
|
||||
case .userExceptions:
|
||||
return 9
|
||||
case .messageNotice:
|
||||
return 10
|
||||
case .groupHeader:
|
||||
return 11
|
||||
case .groupAlerts:
|
||||
return 12
|
||||
case .groupPreviews:
|
||||
return 13
|
||||
case .groupSound:
|
||||
return 14
|
||||
case .groupExceptions:
|
||||
return 15
|
||||
case .groupNotice:
|
||||
return 16
|
||||
case .channelHeader:
|
||||
return 17
|
||||
case .channelAlerts:
|
||||
return 18
|
||||
case .channelPreviews:
|
||||
return 19
|
||||
case .channelSound:
|
||||
return 20
|
||||
case .channelExceptions:
|
||||
return 21
|
||||
case .channelNotice:
|
||||
return 22
|
||||
case .inAppHeader:
|
||||
return 23
|
||||
return 14
|
||||
case .inAppSounds:
|
||||
return 24
|
||||
return 15
|
||||
case .inAppVibrate:
|
||||
return 25
|
||||
return 16
|
||||
case .inAppPreviews:
|
||||
return 26
|
||||
return 17
|
||||
case .displayNamesOnLockscreen:
|
||||
return 27
|
||||
return 18
|
||||
case .displayNamesOnLockscreenInfo:
|
||||
return 28
|
||||
return 19
|
||||
case .badgeHeader:
|
||||
return 29
|
||||
return 20
|
||||
case .includeChannels:
|
||||
return 32
|
||||
return 21
|
||||
case .unreadCountCategory:
|
||||
return 33
|
||||
return 22
|
||||
case .unreadCountCategoryInfo:
|
||||
return 34
|
||||
return 23
|
||||
case .joinedNotifications:
|
||||
return 35
|
||||
return 24
|
||||
case .joinedNotificationsInfo:
|
||||
return 36
|
||||
return 25
|
||||
case .reset:
|
||||
return 37
|
||||
return 26
|
||||
case .resetNotice:
|
||||
return 38
|
||||
return 27
|
||||
}
|
||||
}
|
||||
|
||||
@ -317,18 +239,6 @@ private enum NotificationsAndSoundsEntry: ItemListNodeEntry {
|
||||
switch self {
|
||||
case .allAccounts:
|
||||
return NotificationsAndSoundsEntryTag.allAccounts
|
||||
case .messageAlerts:
|
||||
return NotificationsAndSoundsEntryTag.messageAlerts
|
||||
case .messagePreviews:
|
||||
return NotificationsAndSoundsEntryTag.messagePreviews
|
||||
case .groupAlerts:
|
||||
return NotificationsAndSoundsEntryTag.groupAlerts
|
||||
case .groupPreviews:
|
||||
return NotificationsAndSoundsEntryTag.groupPreviews
|
||||
case .channelAlerts:
|
||||
return NotificationsAndSoundsEntryTag.channelAlerts
|
||||
case .channelPreviews:
|
||||
return NotificationsAndSoundsEntryTag.channelPreviews
|
||||
case .inAppSounds:
|
||||
return NotificationsAndSoundsEntryTag.inAppSounds
|
||||
case .inAppVibrate:
|
||||
@ -382,110 +292,26 @@ private enum NotificationsAndSoundsEntry: ItemListNodeEntry {
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .messageHeader(lhsTheme, lhsText):
|
||||
if case let .messageHeader(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
||||
case let .categoriesHeader(lhsTheme, lhsText):
|
||||
if case let .categoriesHeader(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .messageAlerts(lhsTheme, lhsText, lhsValue):
|
||||
if case let .messageAlerts(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
|
||||
case let .privateChats(lhsTheme, lhsTitle, lhsSubtitle, lhsLabel):
|
||||
if case let .privateChats(rhsTheme, rhsTitle, rhsSubtitle, rhsLabel) = rhs, lhsTheme === rhsTheme, lhsTitle == rhsTitle, lhsSubtitle == rhsSubtitle, lhsLabel == rhsLabel {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .messagePreviews(lhsTheme, lhsText, lhsValue):
|
||||
if case let .messagePreviews(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
|
||||
case let .groupChats(lhsTheme, lhsTitle, lhsSubtitle, lhsLabel):
|
||||
if case let .groupChats(rhsTheme, rhsTitle, rhsSubtitle, rhsLabel) = rhs, lhsTheme === rhsTheme, lhsTitle == rhsTitle, lhsSubtitle == rhsSubtitle, lhsLabel == rhsLabel {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .messageSound(lhsTheme, lhsText, lhsValue, lhsSound):
|
||||
if case let .messageSound(rhsTheme, rhsText, rhsValue, rhsSound) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue, lhsSound == rhsSound {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .userExceptions(lhsTheme, lhsStrings, lhsText, lhsValue):
|
||||
if case let .userExceptions(rhsTheme, rhsStrings, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsText == rhsText, lhsValue == rhsValue {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .messageNotice(lhsTheme, lhsText):
|
||||
if case let .messageNotice(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .groupHeader(lhsTheme, lhsText):
|
||||
if case let .groupHeader(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .groupAlerts(lhsTheme, lhsText, lhsValue):
|
||||
if case let .groupAlerts(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .groupPreviews(lhsTheme, lhsText, lhsValue):
|
||||
if case let .groupPreviews(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .groupSound(lhsTheme, lhsText, lhsValue, lhsSound):
|
||||
if case let .groupSound(rhsTheme, rhsText, rhsValue, rhsSound) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue, lhsSound == rhsSound {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .groupExceptions(lhsTheme, lhsStrings, lhsText, lhsValue):
|
||||
if case let .groupExceptions(rhsTheme, rhsStrings, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsText == rhsText, lhsValue == rhsValue {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .groupNotice(lhsTheme, lhsText):
|
||||
if case let .groupNotice(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .channelHeader(lhsTheme, lhsText):
|
||||
if case let .channelHeader(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .channelAlerts(lhsTheme, lhsText, lhsValue):
|
||||
if case let .channelAlerts(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .channelPreviews(lhsTheme, lhsText, lhsValue):
|
||||
if case let .channelPreviews(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .channelSound(lhsTheme, lhsText, lhsValue, lhsSound):
|
||||
if case let .channelSound(rhsTheme, rhsText, rhsValue, rhsSound) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue, lhsSound == rhsSound {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .channelExceptions(lhsTheme, lhsStrings, lhsText, lhsValue):
|
||||
if case let .channelExceptions(rhsTheme, rhsStrings, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsText == rhsText, lhsValue == rhsValue {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .channelNotice(lhsTheme, lhsText):
|
||||
if case let .channelNotice(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
||||
case let .channels(lhsTheme, lhsTitle, lhsSubtitle, lhsLabel):
|
||||
if case let .channels(rhsTheme, rhsTitle, rhsSubtitle, rhsLabel) = rhs, lhsTheme === rhsTheme, lhsTitle == rhsTitle, lhsSubtitle == rhsSubtitle, lhsLabel == rhsLabel {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
@ -600,81 +426,20 @@ private enum NotificationsAndSoundsEntry: ItemListNodeEntry {
|
||||
return ItemListActionItem(presentationData: presentationData, title: text, kind: .generic, alignment: .natural, sectionId: self.section, style: .blocks, action: {
|
||||
arguments.authorizeNotifications()
|
||||
})
|
||||
case let .messageHeader(_, text):
|
||||
case let .categoriesHeader(_, text):
|
||||
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
|
||||
case let .messageAlerts(_, text, value):
|
||||
return ItemListSwitchItem(presentationData: presentationData, title: text, value: value, sectionId: self.section, style: .blocks, updated: { updatedValue in
|
||||
arguments.updateMessageAlerts(updatedValue)
|
||||
}, tag: self.tag)
|
||||
case let .messagePreviews(_, text, value):
|
||||
return ItemListSwitchItem(presentationData: presentationData, title: text, value: value, sectionId: self.section, style: .blocks, updated: { updatedValue in
|
||||
arguments.updateMessagePreviews(updatedValue)
|
||||
}, tag: self.tag)
|
||||
case let .messageSound(_, text, value, sound):
|
||||
return ItemListDisclosureItem(presentationData: presentationData, title: text, label: value, sectionId: self.section, style: .blocks, action: {
|
||||
let controller = notificationSoundSelectionController(context: arguments.context, isModal: true, currentSound: sound, defaultSound: nil, completion: { [weak arguments] value in
|
||||
arguments?.updateMessageSound(value)
|
||||
})
|
||||
arguments.presentController(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||
case let .privateChats(_, title, subtitle, label):
|
||||
return NotificationsCategoryItemListItem(presentationData: presentationData, icon: UIImage(bundleImageName: "Settings/Menu/EditProfile"), title: title, subtitle: subtitle, label: label, sectionId: self.section, style: .blocks, action: {
|
||||
arguments.openPeerCategory(.privateChat)
|
||||
})
|
||||
case let .userExceptions(_, strings, text, value):
|
||||
let label = value.settings.count > 0 ? strings.Notifications_Exceptions(Int32(value.settings.count)) : strings.Notification_Exceptions_Add
|
||||
return ItemListDisclosureItem(presentationData: presentationData, title: text, label: label, sectionId: self.section, style: .blocks, action: {
|
||||
let controller = NotificationExceptionsController(context: arguments.context, mode: value, updatedMode: arguments.updatedExceptionMode)
|
||||
arguments.pushController(controller)
|
||||
case let .groupChats(_, title, subtitle, label):
|
||||
return NotificationsCategoryItemListItem(presentationData: presentationData, icon: UIImage(bundleImageName: "Settings/Menu/GroupChats"), title: title, subtitle: subtitle, label: label, sectionId: self.section, style: .blocks, action: {
|
||||
arguments.openPeerCategory(.group)
|
||||
})
|
||||
case let .messageNotice(_, text):
|
||||
return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section)
|
||||
case let .groupHeader(_, text):
|
||||
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
|
||||
case let .groupAlerts(_, text, value):
|
||||
return ItemListSwitchItem(presentationData: presentationData, title: text, value: value, sectionId: self.section, style: .blocks, updated: { updatedValue in
|
||||
arguments.updateGroupAlerts(updatedValue)
|
||||
}, tag: self.tag)
|
||||
case let .groupPreviews(_, text, value):
|
||||
return ItemListSwitchItem(presentationData: presentationData, title: text, value: value, sectionId: self.section, style: .blocks, updated: { updatedValue in
|
||||
arguments.updateGroupPreviews(updatedValue)
|
||||
}, tag: self.tag)
|
||||
case let .groupSound(_, text, value, sound):
|
||||
return ItemListDisclosureItem(presentationData: presentationData, title: text, label: value, sectionId: self.section, style: .blocks, action: {
|
||||
let controller = notificationSoundSelectionController(context: arguments.context, isModal: true, currentSound: sound, defaultSound: nil, completion: { [weak arguments] value in
|
||||
arguments?.updateGroupSound(value)
|
||||
})
|
||||
arguments.presentController(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||
case let .channels(_, title, subtitle, label):
|
||||
return NotificationsCategoryItemListItem(presentationData: presentationData, icon: UIImage(bundleImageName: "Settings/Menu/Channels"), title: title, subtitle: subtitle, label: label, sectionId: self.section, style: .blocks, action: {
|
||||
arguments.openPeerCategory(.channel)
|
||||
})
|
||||
case let .groupExceptions(_, strings, text, value):
|
||||
let label = value.settings.count > 0 ? strings.Notifications_Exceptions(Int32(value.settings.count)) : strings.Notification_Exceptions_Add
|
||||
return ItemListDisclosureItem(presentationData: presentationData, title: text, label: label, sectionId: self.section, style: .blocks, action: {
|
||||
let controller = NotificationExceptionsController(context: arguments.context, mode: value, updatedMode: arguments.updatedExceptionMode)
|
||||
arguments.pushController(controller)
|
||||
})
|
||||
case let .groupNotice(_, text):
|
||||
return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section)
|
||||
case let .channelHeader(_, text):
|
||||
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
|
||||
case let .channelAlerts(_, text, value):
|
||||
return ItemListSwitchItem(presentationData: presentationData, title: text, value: value, sectionId: self.section, style: .blocks, updated: { updatedValue in
|
||||
arguments.updateChannelAlerts(updatedValue)
|
||||
}, tag: self.tag)
|
||||
case let .channelPreviews(_, text, value):
|
||||
return ItemListSwitchItem(presentationData: presentationData, title: text, value: value, sectionId: self.section, style: .blocks, updated: { updatedValue in
|
||||
arguments.updateChannelPreviews(updatedValue)
|
||||
}, tag: self.tag)
|
||||
case let .channelSound(_, text, value, sound):
|
||||
return ItemListDisclosureItem(presentationData: presentationData, title: text, label: value, sectionId: self.section, style: .blocks, action: {
|
||||
let controller = notificationSoundSelectionController(context: arguments.context, isModal: true, currentSound: sound, defaultSound: nil, completion: { [weak arguments] value in
|
||||
arguments?.updateChannelSound(value)
|
||||
})
|
||||
arguments.presentController(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||
})
|
||||
case let .channelExceptions(_, strings, text, value):
|
||||
let label = value.settings.count > 0 ? strings.Notifications_Exceptions(Int32(value.settings.count)) : strings.Notification_Exceptions_Add
|
||||
return ItemListDisclosureItem(presentationData: presentationData, title: text, label: label, sectionId: self.section, style: .blocks, action: {
|
||||
let controller = NotificationExceptionsController(context: arguments.context, mode: value, updatedMode: arguments.updatedExceptionMode)
|
||||
arguments.pushController(controller)
|
||||
})
|
||||
case let .channelNotice(_, text):
|
||||
return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section)
|
||||
case let .inAppHeader(_, text):
|
||||
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
|
||||
case let .inAppSounds(_, text, value):
|
||||
@ -768,32 +533,10 @@ private func notificationsAndSoundsEntries(authorizationStatus: AccessType, warn
|
||||
}
|
||||
}
|
||||
|
||||
entries.append(.messageHeader(presentationData.theme, presentationData.strings.Notifications_MessageNotifications.uppercased()))
|
||||
entries.append(.messageAlerts(presentationData.theme, presentationData.strings.Notifications_MessageNotificationsAlert, globalSettings.privateChats.enabled))
|
||||
if globalSettings.privateChats.enabled || !exceptions.users.isEmpty {
|
||||
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)))
|
||||
}
|
||||
entries.append(.userExceptions(presentationData.theme, presentationData.strings, presentationData.strings.Notifications_MessageNotificationsExceptions, exceptions.users))
|
||||
entries.append(.messageNotice(presentationData.theme, presentationData.strings.Notifications_MessageNotificationsExceptionsHelp))
|
||||
|
||||
entries.append(.groupHeader(presentationData.theme, presentationData.strings.Notifications_GroupNotifications.uppercased()))
|
||||
entries.append(.groupAlerts(presentationData.theme, presentationData.strings.Notifications_MessageNotificationsAlert, globalSettings.groupChats.enabled))
|
||||
if globalSettings.groupChats.enabled || !exceptions.groups.isEmpty {
|
||||
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)))
|
||||
}
|
||||
entries.append(.groupExceptions(presentationData.theme, presentationData.strings, presentationData.strings.Notifications_MessageNotificationsExceptions, exceptions.groups))
|
||||
entries.append(.groupNotice(presentationData.theme, presentationData.strings.Notifications_GroupNotificationsExceptionsHelp))
|
||||
|
||||
entries.append(.channelHeader(presentationData.theme, presentationData.strings.Notifications_ChannelNotifications.uppercased()))
|
||||
entries.append(.channelAlerts(presentationData.theme, presentationData.strings.Notifications_MessageNotificationsAlert, globalSettings.channels.enabled))
|
||||
if globalSettings.channels.enabled || !exceptions.channels.isEmpty {
|
||||
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)))
|
||||
}
|
||||
entries.append(.channelExceptions(presentationData.theme, presentationData.strings, presentationData.strings.Notifications_MessageNotificationsExceptions, exceptions.channels))
|
||||
entries.append(.channelNotice(presentationData.theme, presentationData.strings.Notifications_ChannelNotificationsExceptionsHelp))
|
||||
entries.append(.categoriesHeader(presentationData.theme, presentationData.strings.Notifications_MessageNotifications.uppercased()))
|
||||
entries.append(.privateChats(presentationData.theme, presentationData.strings.Notifications_PrivateChats, !exceptions.users.isEmpty ? presentationData.strings.Notifications_CategoryExceptions(Int32(exceptions.users.peerIds.count)) : "", globalSettings.privateChats.enabled ? presentationData.strings.Notifications_On : presentationData.strings.Notifications_Off))
|
||||
entries.append(.groupChats(presentationData.theme, presentationData.strings.Notifications_GroupChats, !exceptions.groups.isEmpty ? presentationData.strings.Notifications_CategoryExceptions(Int32(exceptions.groups.peerIds.count)) : "", globalSettings.groupChats.enabled ? presentationData.strings.Notifications_On : presentationData.strings.Notifications_Off))
|
||||
entries.append(.channels(presentationData.theme, presentationData.strings.Notifications_Channels, !exceptions.channels.isEmpty ? presentationData.strings.Notifications_CategoryExceptions(Int32(exceptions.channels.peerIds.count)) : "", globalSettings.channels.enabled ? presentationData.strings.Notifications_On : presentationData.strings.Notifications_Off))
|
||||
|
||||
entries.append(.inAppHeader(presentationData.theme, presentationData.strings.Notifications_InAppNotifications.uppercased()))
|
||||
entries.append(.inAppSounds(presentationData.theme, presentationData.strings.Notifications_InAppNotificationsSounds, inAppSettings.playSounds))
|
||||
@ -858,60 +601,30 @@ public func notificationsAndSoundsController(context: AccountContext, exceptions
|
||||
}), TextAlertAction(type: .defaultAction, title: presentationData.strings.Notifications_PermissionsEnable, action: {
|
||||
context.sharedContext.applicationBindings.openSettings()
|
||||
})]), nil)
|
||||
}, updateMessageAlerts: { value in
|
||||
let _ = updateGlobalNotificationSettingsInteractively(postbox: context.account.postbox, { settings in
|
||||
var settings = settings
|
||||
settings.privateChats.enabled = value
|
||||
return settings
|
||||
}).start()
|
||||
}, updateMessagePreviews: { value in
|
||||
let _ = updateGlobalNotificationSettingsInteractively(postbox: context.account.postbox, { settings in
|
||||
var settings = settings
|
||||
settings.privateChats.displayPreviews = value
|
||||
return settings
|
||||
}).start()
|
||||
}, updateMessageSound: { value in
|
||||
let _ = updateGlobalNotificationSettingsInteractively(postbox: context.account.postbox, { settings in
|
||||
var settings = settings
|
||||
settings.privateChats.sound = value
|
||||
return settings
|
||||
}).start()
|
||||
}, updateGroupAlerts: { value in
|
||||
let _ = updateGlobalNotificationSettingsInteractively(postbox: context.account.postbox, { settings in
|
||||
var settings = settings
|
||||
settings.groupChats.enabled = value
|
||||
return settings
|
||||
}).start()
|
||||
}, updateGroupPreviews: { value in
|
||||
let _ = updateGlobalNotificationSettingsInteractively(postbox: context.account.postbox, { settings in
|
||||
var settings = settings
|
||||
settings.groupChats.displayPreviews = value
|
||||
return settings
|
||||
}).start()
|
||||
}, updateGroupSound: {value in
|
||||
let _ = updateGlobalNotificationSettingsInteractively(postbox: context.account.postbox, { settings in
|
||||
var settings = settings
|
||||
settings.groupChats.sound = value
|
||||
return settings
|
||||
}).start()
|
||||
}, updateChannelAlerts: { value in
|
||||
let _ = updateGlobalNotificationSettingsInteractively(postbox: context.account.postbox, { settings in
|
||||
var settings = settings
|
||||
settings.channels.enabled = value
|
||||
return settings
|
||||
}).start()
|
||||
}, updateChannelPreviews: { value in
|
||||
let _ = updateGlobalNotificationSettingsInteractively(postbox: context.account.postbox, { settings in
|
||||
var settings = settings
|
||||
settings.channels.displayPreviews = value
|
||||
return settings
|
||||
}).start()
|
||||
}, updateChannelSound: {value in
|
||||
let _ = updateGlobalNotificationSettingsInteractively(postbox: context.account.postbox, { settings in
|
||||
var settings = settings
|
||||
settings.channels.sound = value
|
||||
return settings
|
||||
}).start()
|
||||
}, openPeerCategory: { category in
|
||||
_ = (notificationExceptions.get() |> take(1) |> deliverOnMainQueue).start(next: { (users, groups, channels) in
|
||||
let mode: NotificationExceptionMode
|
||||
switch category {
|
||||
case .privateChat:
|
||||
mode = users
|
||||
case .group:
|
||||
mode = groups
|
||||
case .channel:
|
||||
mode = channels
|
||||
}
|
||||
pushControllerImpl?(notificationsPeerCategoryController(context: context, category: category, mode: mode, updatedMode: { mode in
|
||||
_ = (notificationExceptions.get() |> take(1) |> deliverOnMainQueue).start(next: { (users, groups, channels) in
|
||||
switch mode {
|
||||
case .users:
|
||||
updateNotificationExceptions((mode, groups, channels))
|
||||
case .groups:
|
||||
updateNotificationExceptions((users, mode, channels))
|
||||
case .channels:
|
||||
updateNotificationExceptions((users, groups, mode))
|
||||
}
|
||||
})
|
||||
}, focusOnItemTag: nil))
|
||||
})
|
||||
}, updateInAppSounds: { value in
|
||||
let _ = updateInAppNotificationSettingsInteractively(accountManager: context.sharedContext.accountManager, { settings in
|
||||
var settings = settings
|
||||
@ -977,17 +690,6 @@ public func notificationsAndSoundsController(context: AccountContext, exceptions
|
||||
})
|
||||
])])
|
||||
presentControllerImpl?(actionSheet, nil)
|
||||
}, updatedExceptionMode: { mode in
|
||||
_ = (notificationExceptions.get() |> take(1) |> deliverOnMainQueue).start(next: { (users, groups, channels) in
|
||||
switch mode {
|
||||
case .users:
|
||||
updateNotificationExceptions((mode, groups, channels))
|
||||
case .groups:
|
||||
updateNotificationExceptions((users, mode, channels))
|
||||
case .channels:
|
||||
updateNotificationExceptions((users, groups, mode))
|
||||
}
|
||||
})
|
||||
}, openAppSettings: {
|
||||
context.sharedContext.applicationBindings.openSettings()
|
||||
}, updateJoinedNotifications: { value in
|
||||
|
@ -0,0 +1,442 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import Display
|
||||
import AsyncDisplayKit
|
||||
import SwiftSignalKit
|
||||
import TelegramPresentationData
|
||||
import ShimmerEffect
|
||||
import ItemListUI
|
||||
|
||||
public class NotificationsCategoryItemListItem: ListViewItem, ItemListItem {
|
||||
let presentationData: ItemListPresentationData
|
||||
let icon: UIImage?
|
||||
let title: String
|
||||
let subtitle: String
|
||||
let enabled: Bool
|
||||
let label: String
|
||||
public let sectionId: ItemListSectionId
|
||||
let style: ItemListStyle
|
||||
let action: (() -> Void)?
|
||||
public let tag: ItemListItemTag?
|
||||
public let shimmeringIndex: Int?
|
||||
|
||||
public init(presentationData: ItemListPresentationData, icon: UIImage? = nil, title: String, subtitle: String, enabled: Bool = true, label: String, sectionId: ItemListSectionId, style: ItemListStyle, action: (() -> Void)?, tag: ItemListItemTag? = nil, shimmeringIndex: Int? = nil) {
|
||||
self.presentationData = presentationData
|
||||
self.icon = icon
|
||||
self.title = title
|
||||
self.subtitle = subtitle
|
||||
self.enabled = enabled
|
||||
self.label = label
|
||||
self.sectionId = sectionId
|
||||
self.style = style
|
||||
self.action = action
|
||||
self.tag = tag
|
||||
self.shimmeringIndex = shimmeringIndex
|
||||
}
|
||||
|
||||
public func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal<Void, NoError>?, (ListViewItemApply) -> Void)) -> Void) {
|
||||
async {
|
||||
let node = NotificationsCategoryItemListItemNode()
|
||||
let (layout, apply) = node.asyncLayout()(self, params, itemListNeighbors(item: self, topItem: previousItem as? ItemListItem, bottomItem: nextItem as? ItemListItem))
|
||||
|
||||
node.contentSize = layout.contentSize
|
||||
node.insets = layout.insets
|
||||
|
||||
Queue.mainQueue().async {
|
||||
completion(node, {
|
||||
return (nil, { _ in apply() })
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func updateNode(async: @escaping (@escaping () -> Void) -> Void, node: @escaping () -> ListViewItemNode, params: ListViewItemLayoutParams, previousItem: ListViewItem?, nextItem: ListViewItem?, animation: ListViewItemUpdateAnimation, completion: @escaping (ListViewItemNodeLayout, @escaping (ListViewItemApply) -> Void) -> Void) {
|
||||
Queue.mainQueue().async {
|
||||
if let nodeValue = node() as? NotificationsCategoryItemListItemNode {
|
||||
let makeLayout = nodeValue.asyncLayout()
|
||||
|
||||
async {
|
||||
let (layout, apply) = makeLayout(self, params, itemListNeighbors(item: self, topItem: previousItem as? ItemListItem, bottomItem: nextItem as? ItemListItem))
|
||||
Queue.mainQueue().async {
|
||||
completion(layout, { _ in
|
||||
apply()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public var selectable: Bool = true
|
||||
|
||||
public func selected(listView: ListView){
|
||||
listView.clearHighlightAnimated(true)
|
||||
|
||||
if self.enabled {
|
||||
self.action?()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class NotificationsCategoryItemListItemNode: ListViewItemNode, ItemListItemNode {
|
||||
private let backgroundNode: ASDisplayNode
|
||||
private let topStripeNode: ASDisplayNode
|
||||
private let bottomStripeNode: ASDisplayNode
|
||||
private let highlightedBackgroundNode: ASDisplayNode
|
||||
private let maskNode: ASImageNode
|
||||
|
||||
let iconNode: ASImageNode
|
||||
let titleNode: TextNode
|
||||
let subtitleNode: TextNode
|
||||
let labelNode: TextNode
|
||||
let arrowNode: ASImageNode
|
||||
|
||||
private let activateArea: AccessibilityAreaNode
|
||||
|
||||
private var item: NotificationsCategoryItemListItem?
|
||||
|
||||
override public var canBeSelected: Bool {
|
||||
if let item = self.item, let _ = item.action {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
public var tag: ItemListItemTag? {
|
||||
return self.item?.tag
|
||||
}
|
||||
|
||||
private var placeholderNode: ShimmerEffectNode?
|
||||
private var absoluteLocation: (CGRect, CGSize)?
|
||||
|
||||
public init() {
|
||||
self.backgroundNode = ASDisplayNode()
|
||||
self.backgroundNode.isLayerBacked = true
|
||||
self.backgroundNode.backgroundColor = .white
|
||||
|
||||
self.maskNode = ASImageNode()
|
||||
|
||||
self.topStripeNode = ASDisplayNode()
|
||||
self.topStripeNode.isLayerBacked = true
|
||||
|
||||
self.bottomStripeNode = ASDisplayNode()
|
||||
self.bottomStripeNode.isLayerBacked = true
|
||||
|
||||
self.iconNode = ASImageNode()
|
||||
self.iconNode.isLayerBacked = true
|
||||
self.iconNode.displaysAsynchronously = false
|
||||
|
||||
self.titleNode = TextNode()
|
||||
self.titleNode.isUserInteractionEnabled = false
|
||||
|
||||
self.subtitleNode = TextNode()
|
||||
self.subtitleNode.isUserInteractionEnabled = false
|
||||
|
||||
self.labelNode = TextNode()
|
||||
self.labelNode.isUserInteractionEnabled = false
|
||||
|
||||
self.arrowNode = ASImageNode()
|
||||
self.arrowNode.displayWithoutProcessing = true
|
||||
self.arrowNode.displaysAsynchronously = false
|
||||
self.arrowNode.isLayerBacked = true
|
||||
|
||||
self.highlightedBackgroundNode = ASDisplayNode()
|
||||
self.highlightedBackgroundNode.isLayerBacked = true
|
||||
|
||||
self.activateArea = AccessibilityAreaNode()
|
||||
|
||||
super.init(layerBacked: false, dynamicBounce: false)
|
||||
|
||||
self.addSubnode(self.titleNode)
|
||||
self.addSubnode(self.subtitleNode)
|
||||
self.addSubnode(self.labelNode)
|
||||
self.addSubnode(self.arrowNode)
|
||||
|
||||
self.addSubnode(self.activateArea)
|
||||
}
|
||||
|
||||
override public func updateAbsoluteRect(_ rect: CGRect, within containerSize: CGSize) {
|
||||
var rect = rect
|
||||
rect.origin.y += self.insets.top
|
||||
self.absoluteLocation = (rect, containerSize)
|
||||
if let shimmerNode = self.placeholderNode {
|
||||
shimmerNode.updateAbsoluteRect(rect, within: containerSize)
|
||||
}
|
||||
}
|
||||
|
||||
public func asyncLayout() -> (_ item: NotificationsCategoryItemListItem, _ params: ListViewItemLayoutParams, _ insets: ItemListNeighbors) -> (ListViewItemNodeLayout, () -> Void) {
|
||||
let makeTitleLayout = TextNode.asyncLayout(self.titleNode)
|
||||
let makeSubtitleLayout = TextNode.asyncLayout(self.subtitleNode)
|
||||
let makeLabelLayout = TextNode.asyncLayout(self.labelNode)
|
||||
|
||||
let currentItem = self.item
|
||||
|
||||
return { item, params, neighbors in
|
||||
let rightInset = 34.0 + params.rightInset
|
||||
|
||||
var updateArrowImage: UIImage?
|
||||
var updatedTheme: PresentationTheme?
|
||||
|
||||
if currentItem?.presentationData.theme !== item.presentationData.theme {
|
||||
updatedTheme = item.presentationData.theme
|
||||
updateArrowImage = PresentationResourcesItemList.disclosureArrowImage(item.presentationData.theme)
|
||||
}
|
||||
|
||||
var updateIcon = false
|
||||
if currentItem?.icon != item.icon {
|
||||
updateIcon = true
|
||||
}
|
||||
|
||||
let contentSize: CGSize
|
||||
let insets: UIEdgeInsets
|
||||
let separatorHeight = UIScreenPixel
|
||||
let itemBackgroundColor: UIColor
|
||||
let itemSeparatorColor: UIColor
|
||||
|
||||
var leftInset = 16.0 + params.leftInset
|
||||
if let _ = item.icon {
|
||||
leftInset += 43.0
|
||||
}
|
||||
|
||||
let titleFont = Font.regular(item.presentationData.fontSize.itemListBaseFontSize)
|
||||
let titleColor = item.presentationData.theme.list.itemPrimaryTextColor
|
||||
let detailFont = Font.regular(floor(item.presentationData.fontSize.itemListBaseFontSize * 15.0 / 17.0))
|
||||
let detailColor = item.presentationData.theme.list.itemSecondaryTextColor
|
||||
let labelFont = titleFont
|
||||
let labelColor = item.presentationData.theme.list.itemSecondaryTextColor
|
||||
|
||||
let (labelLayout, labelApply) = makeLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.label, font: labelFont, textColor: labelColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: 60.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
let additionalTextRightInset: CGFloat = labelLayout.size.width
|
||||
|
||||
let textConstrain = params.width - params.rightInset - 20.0 - leftInset - additionalTextRightInset
|
||||
let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.title, font: titleFont, textColor: titleColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: textConstrain, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
let (subtitleLayout, subtitleApply) = makeSubtitleLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.subtitle, font: detailFont, textColor: detailColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: textConstrain, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
let verticalInset: CGFloat = 11.0
|
||||
let titleSpacing: CGFloat = 1.0
|
||||
|
||||
let height: CGFloat
|
||||
if !item.subtitle.isEmpty {
|
||||
height = verticalInset * 2.0 + titleLayout.size.height + titleSpacing + subtitleLayout.size.height
|
||||
} else {
|
||||
height = verticalInset * 2.0 + titleLayout.size.height
|
||||
}
|
||||
|
||||
switch item.style {
|
||||
case .plain:
|
||||
itemBackgroundColor = item.presentationData.theme.list.plainBackgroundColor
|
||||
itemSeparatorColor = item.presentationData.theme.list.itemPlainSeparatorColor
|
||||
contentSize = CGSize(width: params.width, height: height)
|
||||
insets = itemListNeighborsPlainInsets(neighbors)
|
||||
case .blocks:
|
||||
itemBackgroundColor = item.presentationData.theme.list.itemBlocksBackgroundColor
|
||||
itemSeparatorColor = item.presentationData.theme.list.itemBlocksSeparatorColor
|
||||
contentSize = CGSize(width: params.width, height: height)
|
||||
insets = itemListNeighborsGroupedInsets(neighbors)
|
||||
}
|
||||
|
||||
let layout = ListViewItemNodeLayout(contentSize: contentSize, insets: insets)
|
||||
|
||||
return (ListViewItemNodeLayout(contentSize: contentSize, insets: insets), { [weak self] in
|
||||
if let strongSelf = self {
|
||||
strongSelf.item = item
|
||||
|
||||
strongSelf.activateArea.frame = CGRect(origin: CGPoint(x: params.leftInset, y: 0.0), size: CGSize(width: params.width - params.leftInset - params.rightInset, height: layout.contentSize.height))
|
||||
strongSelf.activateArea.accessibilityLabel = item.title
|
||||
strongSelf.activateArea.accessibilityValue = item.label
|
||||
if item.enabled {
|
||||
strongSelf.activateArea.accessibilityTraits = []
|
||||
} else {
|
||||
strongSelf.activateArea.accessibilityTraits = .notEnabled
|
||||
}
|
||||
|
||||
if let icon = item.icon {
|
||||
if strongSelf.iconNode.supernode == nil {
|
||||
strongSelf.addSubnode(strongSelf.iconNode)
|
||||
}
|
||||
if updateIcon {
|
||||
strongSelf.iconNode.image = icon
|
||||
}
|
||||
let iconY = floorToScreenPixels((layout.contentSize.height - icon.size.height) / 2.0)
|
||||
strongSelf.iconNode.frame = CGRect(origin: CGPoint(x: params.leftInset + floor((leftInset - params.leftInset - icon.size.width) / 2.0), y: iconY), size: icon.size)
|
||||
} else if strongSelf.iconNode.supernode != nil {
|
||||
strongSelf.iconNode.image = nil
|
||||
strongSelf.iconNode.removeFromSupernode()
|
||||
}
|
||||
|
||||
if let updateArrowImage = updateArrowImage {
|
||||
strongSelf.arrowNode.image = updateArrowImage
|
||||
}
|
||||
|
||||
if let _ = updatedTheme {
|
||||
strongSelf.topStripeNode.backgroundColor = itemSeparatorColor
|
||||
strongSelf.bottomStripeNode.backgroundColor = itemSeparatorColor
|
||||
strongSelf.backgroundNode.backgroundColor = itemBackgroundColor
|
||||
strongSelf.highlightedBackgroundNode.backgroundColor = item.presentationData.theme.list.itemHighlightedBackgroundColor
|
||||
}
|
||||
|
||||
let _ = titleApply()
|
||||
let _ = subtitleApply()
|
||||
let _ = labelApply()
|
||||
|
||||
switch item.style {
|
||||
case .plain:
|
||||
if strongSelf.backgroundNode.supernode != nil {
|
||||
strongSelf.backgroundNode.removeFromSupernode()
|
||||
}
|
||||
if strongSelf.topStripeNode.supernode != nil {
|
||||
strongSelf.topStripeNode.removeFromSupernode()
|
||||
}
|
||||
if strongSelf.bottomStripeNode.supernode == nil {
|
||||
strongSelf.insertSubnode(strongSelf.bottomStripeNode, at: 0)
|
||||
}
|
||||
if strongSelf.maskNode.supernode != nil {
|
||||
strongSelf.maskNode.removeFromSupernode()
|
||||
}
|
||||
strongSelf.bottomStripeNode.frame = CGRect(origin: CGPoint(x: leftInset, y: contentSize.height - separatorHeight), size: CGSize(width: params.width - leftInset, height: separatorHeight))
|
||||
case .blocks:
|
||||
if strongSelf.backgroundNode.supernode == nil {
|
||||
strongSelf.insertSubnode(strongSelf.backgroundNode, at: 0)
|
||||
}
|
||||
if strongSelf.topStripeNode.supernode == nil {
|
||||
strongSelf.insertSubnode(strongSelf.topStripeNode, at: 1)
|
||||
}
|
||||
if strongSelf.bottomStripeNode.supernode == nil {
|
||||
strongSelf.insertSubnode(strongSelf.bottomStripeNode, at: 2)
|
||||
}
|
||||
if strongSelf.maskNode.supernode == nil {
|
||||
strongSelf.insertSubnode(strongSelf.maskNode, at: 3)
|
||||
}
|
||||
|
||||
let hasCorners = itemListHasRoundedBlockLayout(params)
|
||||
var hasTopCorners = false
|
||||
var hasBottomCorners = false
|
||||
switch neighbors.top {
|
||||
case .sameSection(false):
|
||||
strongSelf.topStripeNode.isHidden = true
|
||||
default:
|
||||
hasTopCorners = true
|
||||
strongSelf.topStripeNode.isHidden = hasCorners
|
||||
}
|
||||
let bottomStripeInset: CGFloat
|
||||
switch neighbors.bottom {
|
||||
case .sameSection(false):
|
||||
bottomStripeInset = leftInset
|
||||
default:
|
||||
bottomStripeInset = 0.0
|
||||
hasBottomCorners = true
|
||||
strongSelf.bottomStripeNode.isHidden = hasCorners
|
||||
}
|
||||
|
||||
strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.presentationData.theme, top: hasTopCorners, bottom: hasBottomCorners) : nil
|
||||
|
||||
strongSelf.backgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: contentSize.height + min(insets.top, separatorHeight) + min(insets.bottom, separatorHeight)))
|
||||
strongSelf.maskNode.frame = strongSelf.backgroundNode.frame.insetBy(dx: params.leftInset, dy: 0.0)
|
||||
strongSelf.topStripeNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: separatorHeight))
|
||||
strongSelf.bottomStripeNode.frame = CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height - separatorHeight), size: CGSize(width: params.width - bottomStripeInset, height: separatorHeight))
|
||||
}
|
||||
|
||||
let titleFrame = CGRect(origin: CGPoint(x: leftInset, y: 11.0), size: titleLayout.size)
|
||||
strongSelf.titleNode.frame = titleFrame
|
||||
|
||||
let subtitleFrame = CGRect(origin: CGPoint(x: leftInset, y: titleFrame.maxY + titleSpacing), size: subtitleLayout.size)
|
||||
strongSelf.subtitleNode.frame = subtitleFrame
|
||||
|
||||
let labelFrame = CGRect(origin: CGPoint(x: params.width - rightInset - labelLayout.size.width, y: floorToScreenPixels((height - labelLayout.size.height) / 2.0)), size: labelLayout.size)
|
||||
strongSelf.labelNode.frame = labelFrame
|
||||
|
||||
if let arrowImage = strongSelf.arrowNode.image {
|
||||
strongSelf.arrowNode.frame = CGRect(origin: CGPoint(x: params.width - params.rightInset - 7.0 - arrowImage.size.width, y: floorToScreenPixels((height - arrowImage.size.height) / 2.0)), size: arrowImage.size)
|
||||
}
|
||||
|
||||
strongSelf.highlightedBackgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -UIScreenPixel), size: CGSize(width: params.width, height: height + UIScreenPixel))
|
||||
|
||||
if let shimmeringIndex = item.shimmeringIndex {
|
||||
let shimmerNode: ShimmerEffectNode
|
||||
if let current = strongSelf.placeholderNode {
|
||||
shimmerNode = current
|
||||
} else {
|
||||
shimmerNode = ShimmerEffectNode()
|
||||
strongSelf.placeholderNode = shimmerNode
|
||||
if strongSelf.backgroundNode.supernode != nil {
|
||||
strongSelf.insertSubnode(shimmerNode, aboveSubnode: strongSelf.backgroundNode)
|
||||
} else {
|
||||
strongSelf.addSubnode(shimmerNode)
|
||||
}
|
||||
}
|
||||
shimmerNode.frame = CGRect(origin: CGPoint(), size: contentSize)
|
||||
if let (rect, size) = strongSelf.absoluteLocation {
|
||||
shimmerNode.updateAbsoluteRect(rect, within: size)
|
||||
}
|
||||
|
||||
var shapes: [ShimmerEffectNode.Shape] = []
|
||||
|
||||
let titleLineWidth: CGFloat = (shimmeringIndex % 2 == 0) ? 120.0 : 80.0
|
||||
let lineDiameter: CGFloat = 8.0
|
||||
|
||||
let titleFrame = strongSelf.titleNode.frame
|
||||
shapes.append(.roundedRectLine(startPoint: CGPoint(x: titleFrame.minX, y: titleFrame.minY + floor((titleFrame.height - lineDiameter) / 2.0)), width: titleLineWidth, diameter: lineDiameter))
|
||||
|
||||
shimmerNode.update(backgroundColor: item.presentationData.theme.list.itemBlocksBackgroundColor, foregroundColor: item.presentationData.theme.list.mediaPlaceholderColor, shimmeringColor: item.presentationData.theme.list.itemBlocksBackgroundColor.withAlphaComponent(0.4), shapes: shapes, size: contentSize)
|
||||
} else if let shimmerNode = strongSelf.placeholderNode {
|
||||
strongSelf.placeholderNode = nil
|
||||
shimmerNode.removeFromSupernode()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
override public func setHighlighted(_ highlighted: Bool, at point: CGPoint, animated: Bool) {
|
||||
super.setHighlighted(highlighted, at: point, animated: animated)
|
||||
|
||||
if highlighted && (self.item?.enabled ?? false) {
|
||||
self.highlightedBackgroundNode.alpha = 1.0
|
||||
if self.highlightedBackgroundNode.supernode == nil {
|
||||
var anchorNode: ASDisplayNode?
|
||||
if self.bottomStripeNode.supernode != nil {
|
||||
anchorNode = self.bottomStripeNode
|
||||
} else if self.topStripeNode.supernode != nil {
|
||||
anchorNode = self.topStripeNode
|
||||
} else if self.backgroundNode.supernode != nil {
|
||||
anchorNode = self.backgroundNode
|
||||
}
|
||||
if let anchorNode = anchorNode {
|
||||
self.insertSubnode(self.highlightedBackgroundNode, aboveSubnode: anchorNode)
|
||||
} else {
|
||||
self.addSubnode(self.highlightedBackgroundNode)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if self.highlightedBackgroundNode.supernode != nil {
|
||||
if animated {
|
||||
self.highlightedBackgroundNode.layer.animateAlpha(from: self.highlightedBackgroundNode.alpha, to: 0.0, duration: 0.4, completion: { [weak self] completed in
|
||||
if let strongSelf = self {
|
||||
if completed {
|
||||
strongSelf.highlightedBackgroundNode.removeFromSupernode()
|
||||
}
|
||||
}
|
||||
})
|
||||
self.highlightedBackgroundNode.alpha = 0.0
|
||||
} else {
|
||||
self.highlightedBackgroundNode.removeFromSupernode()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override public func animateInsertion(_ currentTimestamp: Double, duration: Double, short: Bool) {
|
||||
self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.4)
|
||||
}
|
||||
|
||||
override public func animateAdded(_ currentTimestamp: Double, duration: Double) {
|
||||
self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
}
|
||||
|
||||
override public func animateRemoved(_ currentTimestamp: Double, duration: Double) {
|
||||
self.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false)
|
||||
}
|
||||
}
|
@ -18,15 +18,27 @@ import TelegramStringFormatting
|
||||
import ItemListPeerItem
|
||||
import ItemListPeerActionItem
|
||||
|
||||
private extension PeerMuteState {
|
||||
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 presentController: (ViewController, ViewControllerPresentationArguments?) -> Void
|
||||
let pushController: (ViewController) -> Void
|
||||
let soundSelectionDisposable: MetaDisposable
|
||||
|
||||
let updateEnabled: (Bool) -> Void
|
||||
let updatePreviews: (Bool) -> Void
|
||||
let updateSound: (PeerMessageSound) -> Void
|
||||
|
||||
let openSound: (PeerMessageSound) -> Void
|
||||
|
||||
let addException: () -> Void
|
||||
let openException: (Peer) -> Void
|
||||
@ -36,15 +48,13 @@ private final class NotificationsPeerCategoryControllerArguments {
|
||||
|
||||
let updatedExceptionMode: (NotificationExceptionMode) -> Void
|
||||
|
||||
init(context: AccountContext, presentController: @escaping (ViewController, ViewControllerPresentationArguments?) -> Void, pushController: @escaping (ViewController) -> Void, soundSelectionDisposable: MetaDisposable, updateEnabled: @escaping (Bool) -> Void, updatePreviews: @escaping (Bool) -> Void, updateSound: @escaping (PeerMessageSound) -> Void, addException: @escaping () -> Void, openException: @escaping (Peer) -> Void, removeAllExceptions: @escaping () -> Void, updateRevealedPeerId: @escaping (PeerId?) -> Void, removePeer: @escaping (Peer) -> Void, updatedExceptionMode: @escaping (NotificationExceptionMode) -> Void) {
|
||||
init(context: AccountContext, soundSelectionDisposable: MetaDisposable, updateEnabled: @escaping (Bool) -> Void, updatePreviews: @escaping (Bool) -> Void, openSound: @escaping (PeerMessageSound) -> Void, addException: @escaping () -> Void, openException: @escaping (Peer) -> Void, removeAllExceptions: @escaping () -> Void, updateRevealedPeerId: @escaping (PeerId?) -> Void, removePeer: @escaping (Peer) -> Void, updatedExceptionMode: @escaping (NotificationExceptionMode) -> Void) {
|
||||
self.context = context
|
||||
self.presentController = presentController
|
||||
self.pushController = pushController
|
||||
self.soundSelectionDisposable = soundSelectionDisposable
|
||||
|
||||
self.updateEnabled = updateEnabled
|
||||
self.updatePreviews = updatePreviews
|
||||
self.updateSound = updateSound
|
||||
self.openSound = openSound
|
||||
|
||||
self.addException = addException
|
||||
self.openException = openException
|
||||
@ -85,7 +95,7 @@ private enum NotificationsPeerCategoryEntry: ItemListNodeEntry {
|
||||
|
||||
case exceptionsHeader(PresentationTheme, String)
|
||||
case addException(PresentationTheme, String)
|
||||
case exception(Int32, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, PresentationPersonNameOrder, Peer, TelegramPeerNotificationSettings, Bool, Bool)
|
||||
case exception(Int32, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, PresentationPersonNameOrder, Peer, String, TelegramPeerNotificationSettings, Bool, Bool)
|
||||
case removeAllExceptions(PresentationTheme, String)
|
||||
|
||||
var section: ItemListSectionId {
|
||||
@ -113,7 +123,7 @@ private enum NotificationsPeerCategoryEntry: ItemListNodeEntry {
|
||||
return 4
|
||||
case .addException:
|
||||
return 5
|
||||
case let .exception(index, _, _, _, _, _, _, _, _):
|
||||
case let .exception(index, _, _, _, _, _, _, _, _, _):
|
||||
return 6 + index
|
||||
case .removeAllExceptions:
|
||||
return 100000
|
||||
@ -171,8 +181,8 @@ private enum NotificationsPeerCategoryEntry: ItemListNodeEntry {
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .exception(lhsIndex, lhsTheme, lhsStrings, lhsDateTimeFormat, lhsDisplayNameOrder, lhsPeer, lhsSettings, lhsEditing, lhsRevealed):
|
||||
if case let .exception(rhsIndex, rhsTheme, rhsStrings, rhsDateTimeFormat, rhsDisplayNameOrder, rhsPeer, rhsSettings, rhsEditing, rhsRevealed) = rhs, lhsIndex == rhsIndex, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsDateTimeFormat == rhsDateTimeFormat, lhsDisplayNameOrder == rhsDisplayNameOrder, arePeersEqual(lhsPeer, rhsPeer), lhsSettings == rhsSettings, lhsEditing == rhsEditing, lhsRevealed == rhsRevealed {
|
||||
case let .exception(lhsIndex, lhsTheme, lhsStrings, lhsDateTimeFormat, lhsDisplayNameOrder, lhsPeer, lhsDescription, lhsSettings, lhsEditing, lhsRevealed):
|
||||
if case let .exception(rhsIndex, rhsTheme, rhsStrings, rhsDateTimeFormat, rhsDisplayNameOrder, rhsPeer, rhsDescription, rhsSettings, rhsEditing, rhsRevealed) = rhs, lhsIndex == rhsIndex, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsDateTimeFormat == rhsDateTimeFormat, lhsDisplayNameOrder == rhsDisplayNameOrder, arePeersEqual(lhsPeer, rhsPeer), lhsDescription == rhsDescription, lhsSettings == rhsSettings, lhsEditing == rhsEditing, lhsRevealed == rhsRevealed {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
@ -204,9 +214,9 @@ private enum NotificationsPeerCategoryEntry: ItemListNodeEntry {
|
||||
return ItemListSwitchItem(presentationData: presentationData, title: text, value: value, sectionId: self.section, style: .blocks, updated: { value in
|
||||
arguments.updatePreviews(value)
|
||||
})
|
||||
case let .sound(_, text, 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)
|
||||
@ -214,18 +224,18 @@ private enum NotificationsPeerCategoryEntry: ItemListNodeEntry {
|
||||
return ItemListPeerActionItem(presentationData: presentationData, icon: PresentationResourcesItemList.addPersonIcon(theme), title: text, sectionId: self.section, height: .generic, color: .accent, editing: false, action: {
|
||||
arguments.addException()
|
||||
})
|
||||
case let .removeAllExceptions(theme, text):
|
||||
return ItemListPeerActionItem(presentationData: presentationData, icon: PresentationResourcesItemList.addPersonIcon(theme), title: text, sectionId: self.section, height: .generic, color: .destructive, editing: false, action: {
|
||||
arguments.removeAllExceptions()
|
||||
})
|
||||
case let .exception(_, _, _, dateTimeFormat, nameDisplayOrder, peer, _, editing, revealed):
|
||||
return ItemListPeerItem(presentationData: presentationData, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, context: arguments.context, peer: EnginePeer(peer), presence: nil, text: .text("", .secondary), label: .none, editing: ItemListPeerItemEditing(editable: true, editing: editing, revealed: revealed), switchValue: nil, enabled: true, selectable: true, sectionId: self.section, action: {
|
||||
case let .exception(_, _, _, dateTimeFormat, nameDisplayOrder, peer, description, _, editing, revealed):
|
||||
return ItemListPeerItem(presentationData: presentationData, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, context: arguments.context, peer: EnginePeer(peer), presence: nil, text: .text(description, .secondary), label: .none, editing: ItemListPeerItemEditing(editable: true, editing: editing, revealed: 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()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -238,10 +248,11 @@ private func filteredGlobalSound(_ sound: PeerMessageSound) -> PeerMessageSound
|
||||
}
|
||||
}
|
||||
|
||||
private func notificationsPeerCategoryEntries(category: NotificationsPeerCategory, globalSettings: GlobalNotificationSettingsSet, exceptions: (users: NotificationExceptionMode, groups: NotificationExceptionMode, channels: NotificationExceptionMode), presentationData: PresentationData) -> [NotificationsPeerCategoryEntry] {
|
||||
private func notificationsPeerCategoryEntries(category: NotificationsPeerCategory, globalSettings: GlobalNotificationSettingsSet, state: NotificationExceptionState, presentationData: PresentationData) -> [NotificationsPeerCategoryEntry] {
|
||||
var entries: [NotificationsPeerCategoryEntry] = []
|
||||
|
||||
let notificationSettings: MessageNotificationSettings
|
||||
let notificationExceptions = state.mode
|
||||
switch category {
|
||||
case .privateChat:
|
||||
notificationSettings = globalSettings.privateChats
|
||||
@ -253,14 +264,106 @@ private func notificationsPeerCategoryEntries(category: NotificationsPeerCategor
|
||||
|
||||
entries.append(.enable(presentationData.theme, presentationData.strings.Notifications_MessageNotificationsAlert, notificationSettings.enabled))
|
||||
|
||||
if notificationSettings.enabled || !exceptions.users.isEmpty {
|
||||
if notificationSettings.enabled || !notificationExceptions.isEmpty {
|
||||
entries.append(.optionsHeader(presentationData.theme, presentationData.strings.Notifications_MessageNotifications.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, sound: filteredGlobalSound(notificationSettings.sound)), filteredGlobalSound(notificationSettings.sound)))
|
||||
}
|
||||
|
||||
|
||||
entries.append(.exceptionsHeader(presentationData.theme, presentationData.strings.Notifications_MessageNotifications.uppercased()))
|
||||
|
||||
entries.append(.addException(presentationData.theme, presentationData.strings.Notification_Exceptions_AddException))
|
||||
|
||||
|
||||
let sortedExceptions = notificationExceptions.settings.sorted(by: { lhs, rhs in
|
||||
let lhsName = EnginePeer(lhs.value.peer).displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||
let rhsName = EnginePeer(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 let lhsPeer = lhs.value.peer as? TelegramUser, let rhsPeer = rhs.value.peer as? TelegramUser {
|
||||
if lhsPeer.botInfo != nil && rhsPeer.botInfo == nil {
|
||||
return false
|
||||
} else if lhsPeer.botInfo == nil && rhsPeer.botInfo != nil {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return lhsName < rhsName
|
||||
})
|
||||
|
||||
var existingPeerIds = Set<PeerId>()
|
||||
var index: Int = 0
|
||||
|
||||
for (_, value) in sortedExceptions {
|
||||
if !value.peer.isDeleted {
|
||||
var title: String
|
||||
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:
|
||||
let soundName = localizedPeerNotificationSoundString(strings: presentationData.strings, sound: value.settings.messageSound)
|
||||
title += (title.isEmpty ? presentationData.strings.Notification_Exceptions_Sound(soundName).string : ", \(presentationData.strings.Notification_Exceptions_Sound(soundName).string)")
|
||||
}
|
||||
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))
|
||||
index += 1
|
||||
}
|
||||
}
|
||||
|
||||
if notificationExceptions.peerIds.count > 0 {
|
||||
entries.append(.removeAllExceptions(presentationData.theme, presentationData.strings.Notifications_DeleteAllExceptions))
|
||||
}
|
||||
|
||||
return entries
|
||||
}
|
||||
|
||||
@ -270,21 +373,186 @@ public enum NotificationsPeerCategory {
|
||||
case channel
|
||||
}
|
||||
|
||||
public func notificationsPeerCategoryController(context: AccountContext, category: NotificationsPeerCategory, exceptionsList: NotificationExceptionsList?, focusOnItemTag: NotificationsPeerCategoryEntryTag? = nil) -> ViewController {
|
||||
var presentControllerImpl: ((ViewController, ViewControllerPresentationArguments?) -> Void)?
|
||||
private final class NotificationExceptionState : Equatable {
|
||||
let mode: NotificationExceptionMode
|
||||
let isSearchMode: Bool
|
||||
let revealedPeerId: PeerId?
|
||||
let editing: Bool
|
||||
|
||||
init(mode: NotificationExceptionMode, isSearchMode: Bool = false, revealedPeerId: PeerId? = nil, editing: Bool = false) {
|
||||
self.mode = mode
|
||||
self.isSearchMode = isSearchMode
|
||||
self.revealedPeerId = revealedPeerId
|
||||
self.editing = editing
|
||||
}
|
||||
|
||||
func withUpdatedMode(_ mode: NotificationExceptionMode) -> NotificationExceptionState {
|
||||
return NotificationExceptionState(mode: mode, isSearchMode: self.isSearchMode, revealedPeerId: self.revealedPeerId, editing: self.editing)
|
||||
}
|
||||
|
||||
func withUpdatedSearchMode(_ isSearchMode: Bool) -> NotificationExceptionState {
|
||||
return NotificationExceptionState(mode: self.mode, isSearchMode: isSearchMode, revealedPeerId: self.revealedPeerId, editing: self.editing)
|
||||
}
|
||||
|
||||
func withUpdatedEditing(_ editing: Bool) -> NotificationExceptionState {
|
||||
return NotificationExceptionState(mode: self.mode, isSearchMode: self.isSearchMode, revealedPeerId: self.revealedPeerId, editing: editing)
|
||||
}
|
||||
|
||||
func withUpdatedRevealedPeerId(_ revealedPeerId: PeerId?) -> NotificationExceptionState {
|
||||
return NotificationExceptionState(mode: self.mode, isSearchMode: self.isSearchMode, revealedPeerId: revealedPeerId, editing: self.editing)
|
||||
}
|
||||
|
||||
func withUpdatedPeerSound(_ peer: Peer, _ sound: PeerMessageSound) -> NotificationExceptionState {
|
||||
return NotificationExceptionState(mode: mode.withUpdatedPeerSound(peer, sound), isSearchMode: isSearchMode, revealedPeerId: self.revealedPeerId, editing: self.editing)
|
||||
}
|
||||
|
||||
func withUpdatedPeerMuteInterval(_ peer: Peer, _ muteInterval: Int32?) -> NotificationExceptionState {
|
||||
return NotificationExceptionState(mode: mode.withUpdatedPeerMuteInterval(peer, muteInterval), isSearchMode: isSearchMode, revealedPeerId: self.revealedPeerId, editing: self.editing)
|
||||
}
|
||||
|
||||
func withUpdatedPeerDisplayPreviews(_ peer: Peer, _ displayPreviews: PeerNotificationDisplayPreviews) -> NotificationExceptionState {
|
||||
return NotificationExceptionState(mode: mode.withUpdatedPeerDisplayPreviews(peer, displayPreviews), isSearchMode: isSearchMode, revealedPeerId: self.revealedPeerId, editing: self.editing)
|
||||
}
|
||||
|
||||
static func == (lhs: NotificationExceptionState, rhs: NotificationExceptionState) -> Bool {
|
||||
return lhs.mode == rhs.mode && lhs.isSearchMode == rhs.isSearchMode && 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)> = Promise()
|
||||
|
||||
let updateNotificationExceptions:((users: NotificationExceptionMode, groups: NotificationExceptionMode, channels: NotificationExceptionMode)) -> Void = { value in
|
||||
notificationExceptions.set(.single(value))
|
||||
}
|
||||
|
||||
let arguments = NotificationsPeerCategoryControllerArguments(context: context, presentController: { controller, arguments in
|
||||
presentControllerImpl?(controller, arguments)
|
||||
}, pushController: { controller in
|
||||
pushControllerImpl?(controller)
|
||||
}, soundSelectionDisposable: MetaDisposable(), updateEnabled: { value in
|
||||
let updateState: ((NotificationExceptionState) -> NotificationExceptionState) -> Void = { f in
|
||||
let result = stateValue.modify { f($0) }
|
||||
statePromise.set(result)
|
||||
updatedMode(result.mode)
|
||||
}
|
||||
|
||||
let updatePeerSound: (PeerId, PeerMessageSound) -> Signal<Void, NoError> = { peerId, sound in
|
||||
return context.engine.peers.updatePeerNotificationSoundInteractive(peerId: peerId, sound: sound) |> deliverOnMainQueue
|
||||
}
|
||||
|
||||
let updatePeerNotificationInterval: (PeerId, Int32?) -> Signal<Void, NoError> = { peerId, muteInterval in
|
||||
return context.engine.peers.updatePeerMuteSetting(peerId: peerId, muteInterval: muteInterval) |> deliverOnMainQueue
|
||||
}
|
||||
|
||||
let updatePeerDisplayPreviews:(PeerId, PeerNotificationDisplayPreviews) -> Signal<Void, NoError> = {
|
||||
peerId, displayPreviews in
|
||||
return context.engine.peers.updatePeerDisplayPreviewsSetting(peerId: peerId, displayPreviews: displayPreviews) |> deliverOnMainQueue
|
||||
}
|
||||
|
||||
var peerIds: Set<PeerId> = Set(mode.peerIds)
|
||||
let updateNotificationsDisposable = MetaDisposable()
|
||||
let updateNotificationsView: (@escaping () -> Void) -> Void = { completion in
|
||||
updateState { current in
|
||||
peerIds = peerIds.union(current.mode.peerIds)
|
||||
let key: PostboxViewKey = .peerNotificationSettings(peerIds: peerIds)
|
||||
updateNotificationsDisposable.set((context.account.postbox.combinedView(keys: [key])
|
||||
|> deliverOnMainQueue).start(next: { view in
|
||||
if let view = view.views[key] as? PeerNotificationSettingsView {
|
||||
_ = context.account.postbox.transaction { transaction in
|
||||
updateState { current in
|
||||
var current = current
|
||||
for (key, value) in view.notificationSettings {
|
||||
if let value = value as? TelegramPeerNotificationSettings {
|
||||
if let local = current.mode.settings[key] {
|
||||
if !value.isEqual(to: local.settings), let peer = transaction.getPeer(key), let settings = transaction.getPeerNotificationSettings(key) as? TelegramPeerNotificationSettings, !settings.isEqual(to: local.settings) {
|
||||
current = current.withUpdatedPeerSound(peer, settings.messageSound).withUpdatedPeerMuteInterval(peer, settings.muteState.timeInterval).withUpdatedPeerDisplayPreviews(peer, settings.displayPreviews)
|
||||
}
|
||||
} else if let peer = transaction.getPeer(key) {
|
||||
if case .default = value.messageSound, case .unmuted = value.muteState, case .default = value.displayPreviews {
|
||||
} else {
|
||||
current = current.withUpdatedPeerSound(peer, value.messageSound).withUpdatedPeerMuteInterval(peer, value.muteState.timeInterval).withUpdatedPeerDisplayPreviews(peer, value.displayPreviews)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return current
|
||||
}
|
||||
}.start(completed: {
|
||||
completion()
|
||||
})
|
||||
} else {
|
||||
completion()
|
||||
}
|
||||
}))
|
||||
return current
|
||||
}
|
||||
}
|
||||
|
||||
updateNotificationsView({})
|
||||
|
||||
let presentPeerSettings: (PeerId, @escaping () -> Void) -> Void = { peerId, completion in
|
||||
let _ = (context.account.postbox.transaction { transaction -> Peer? in
|
||||
return transaction.getPeer(peerId)
|
||||
}
|
||||
|> deliverOnMainQueue).start(next: { peer in
|
||||
completion()
|
||||
|
||||
guard let peer = peer else {
|
||||
return
|
||||
}
|
||||
|
||||
let mode = stateValue.with { $0.mode }
|
||||
pushControllerImpl?(notificationPeerExceptionController(context: context, peer: peer, mode: mode, updatePeerSound: { peerId, sound in
|
||||
_ = updatePeerSound(peer.id, sound).start(next: { _ in
|
||||
updateNotificationsDisposable.set(nil)
|
||||
_ = combineLatest(updatePeerSound(peer.id, sound), context.account.postbox.loadedPeerWithId(peerId) |> deliverOnMainQueue).start(next: { _, peer in
|
||||
updateState { value in
|
||||
return value.withUpdatedPeerSound(peer, sound)
|
||||
}
|
||||
updateNotificationsView({})
|
||||
})
|
||||
})
|
||||
}, updatePeerNotificationInterval: { peerId, muteInterval in
|
||||
updateNotificationsDisposable.set(nil)
|
||||
_ = combineLatest(updatePeerNotificationInterval(peerId, muteInterval), context.account.postbox.loadedPeerWithId(peerId) |> deliverOnMainQueue).start(next: { _, peer in
|
||||
updateState { value in
|
||||
return value.withUpdatedPeerMuteInterval(peer, muteInterval)
|
||||
}
|
||||
updateNotificationsView({})
|
||||
})
|
||||
}, updatePeerDisplayPreviews: { peerId, displayPreviews in
|
||||
updateNotificationsDisposable.set(nil)
|
||||
_ = combineLatest(updatePeerDisplayPreviews(peerId, displayPreviews), context.account.postbox.loadedPeerWithId(peerId) |> deliverOnMainQueue).start(next: { _, peer in
|
||||
updateState { value in
|
||||
return value.withUpdatedPeerDisplayPreviews(peer, displayPreviews)
|
||||
}
|
||||
updateNotificationsView({})
|
||||
})
|
||||
}, removePeerFromExceptions: {
|
||||
let _ = (context.engine.peers.removeCustomNotificationSettings(peerIds: [peerId])
|
||||
|> map { _ -> Peer? in }
|
||||
|> then(context.account.postbox.transaction { transaction -> Peer? in
|
||||
return transaction.getPeer(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 {
|
||||
@ -310,29 +578,104 @@ public func notificationsPeerCategoryController(context: AccountContext, categor
|
||||
}
|
||||
return settings
|
||||
}).start()
|
||||
}, updateSound: { 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
|
||||
}
|
||||
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
|
||||
}
|
||||
return settings
|
||||
}).start()
|
||||
})
|
||||
pushControllerImpl?(controller)
|
||||
}, addException: {
|
||||
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
var filter: ChatListNodePeersFilter = [.excludeRecent, .doNotSearchMessages, .removeSearchHeader]
|
||||
switch category {
|
||||
case .privateChat:
|
||||
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 }
|
||||
let _ = (context.account.postbox.transaction { transaction -> Void in
|
||||
for value in values {
|
||||
if transaction.getPeer(value.peer.id) == nil {
|
||||
updatePeers(transaction: transaction, peers: [value.peer], update: { _, updated in
|
||||
updated
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|> 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
|
||||
|
||||
_ = (context.account.postbox.transaction { transaction in
|
||||
if transaction.getPeer(peer.id) == nil {
|
||||
updatePeers(transaction: transaction, peers: [peer], update: { _, updated in return updated})
|
||||
}
|
||||
} |> 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) in
|
||||
switch mode {
|
||||
@ -349,77 +692,32 @@ public func notificationsPeerCategoryController(context: AccountContext, categor
|
||||
let sharedData = context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.inAppNotificationSettings])
|
||||
let preferences = context.account.postbox.preferencesView(keys: [PreferencesKeys.globalNotifications])
|
||||
|
||||
let exceptionsSignal = Signal<NotificationExceptionsList?, NoError>.single(exceptionsList) |> then(context.engine.peers.notificationExceptionsList() |> map(Optional.init))
|
||||
|
||||
notificationExceptions.set(exceptionsSignal |> map { list -> (NotificationExceptionMode, NotificationExceptionMode, NotificationExceptionMode) in
|
||||
var users:[PeerId : NotificationExceptionWrapper] = [:]
|
||||
var groups: [PeerId : NotificationExceptionWrapper] = [:]
|
||||
var channels:[PeerId : NotificationExceptionWrapper] = [:]
|
||||
if let list = list {
|
||||
for (key, value) in list.settings {
|
||||
if let peer = list.peers[key], !peer.debugDisplayTitle.isEmpty, peer.id != context.account.peerId {
|
||||
switch value.muteState {
|
||||
case .default:
|
||||
switch value.messageSound {
|
||||
case .default:
|
||||
break
|
||||
default:
|
||||
switch key.namespace {
|
||||
case Namespaces.Peer.CloudUser:
|
||||
users[key] = NotificationExceptionWrapper(settings: value, peer: peer)
|
||||
default:
|
||||
if let peer = peer as? TelegramChannel, case .broadcast = peer.info {
|
||||
channels[key] = NotificationExceptionWrapper(settings: value, peer: peer)
|
||||
} else {
|
||||
groups[key] = NotificationExceptionWrapper(settings: value, peer: peer)
|
||||
}
|
||||
}
|
||||
}
|
||||
default:
|
||||
switch key.namespace {
|
||||
case Namespaces.Peer.CloudUser:
|
||||
users[key] = NotificationExceptionWrapper(settings: value, peer: peer)
|
||||
default:
|
||||
if let peer = peer as? TelegramChannel, case .broadcast = peer.info {
|
||||
channels[key] = NotificationExceptionWrapper(settings: value, peer: peer)
|
||||
} else {
|
||||
groups[key] = NotificationExceptionWrapper(settings: value, peer: peer)
|
||||
}
|
||||
}
|
||||
}
|
||||
let signal = combineLatest(context.sharedContext.presentationData, sharedData, preferences, statePromise.get())
|
||||
|> map { presentationData, sharedData, view, state -> (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)
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
return (.users(users), .groups(groups), .channels(channels))
|
||||
})
|
||||
|
||||
let signal = combineLatest(context.sharedContext.presentationData, sharedData, preferences, notificationExceptions.get())
|
||||
|> map { presentationData, sharedData, view, exceptions -> (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, exceptions: exceptions, presentationData: presentationData)
|
||||
|
||||
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 controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(presentationData.strings.Notifications_Title), leftNavigationButton: nil, rightNavigationButton: nil, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back))
|
||||
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: entries, style: .blocks, ensureVisibleItemTag: focusOnItemTag, initialScrollToItem: scrollToItem)
|
||||
|
||||
return (controllerState, (listState, arguments))
|
||||
let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(presentationData.strings.Notifications_Title), leftNavigationButton: nil, rightNavigationButton: nil, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back))
|
||||
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)
|
||||
|
@ -331,12 +331,12 @@ private func notificationSearchableItems(context: AccountContext, settings: Glob
|
||||
SettingsSearchableItem(id: .notifications(0), title: strings.Settings_NotificationsAndSounds, alternate: synonyms(strings.SettingsSearch_Synonyms_Notifications_Title), icon: icon, breadcrumbs: [], present: { context, _, present in
|
||||
presentNotificationSettings(context, present, nil)
|
||||
}),
|
||||
SettingsSearchableItem(id: .notifications(1), title: strings.Notifications_MessageNotificationsAlert, alternate: synonyms(strings.SettingsSearch_Synonyms_Notifications_MessageNotificationsAlert), icon: icon, breadcrumbs: [strings.Settings_NotificationsAndSounds, strings.Notifications_MessageNotifications], present: { context, _, present in
|
||||
presentNotificationSettings(context, present, .messageAlerts)
|
||||
}),
|
||||
SettingsSearchableItem(id: .notifications(2), title: strings.Notifications_MessageNotificationsPreview, alternate: synonyms(strings.SettingsSearch_Synonyms_Notifications_MessageNotificationsPreview), icon: icon, breadcrumbs: [strings.Settings_NotificationsAndSounds, strings.Notifications_MessageNotifications], present: { context, _, present in
|
||||
presentNotificationSettings(context, present, .messagePreviews)
|
||||
}),
|
||||
// SettingsSearchableItem(id: .notifications(1), title: strings.Notifications_MessageNotificationsAlert, alternate: synonyms(strings.SettingsSearch_Synonyms_Notifications_MessageNotificationsAlert), icon: icon, breadcrumbs: [strings.Settings_NotificationsAndSounds, strings.Notifications_MessageNotifications], present: { context, _, present in
|
||||
// presentNotificationSettings(context, present, .messageAlerts)
|
||||
// }),
|
||||
// SettingsSearchableItem(id: .notifications(2), title: strings.Notifications_MessageNotificationsPreview, alternate: synonyms(strings.SettingsSearch_Synonyms_Notifications_MessageNotificationsPreview), icon: icon, breadcrumbs: [strings.Settings_NotificationsAndSounds, strings.Notifications_MessageNotifications], present: { context, _, present in
|
||||
// presentNotificationSettings(context, present, .messagePreviews)
|
||||
// }),
|
||||
SettingsSearchableItem(id: .notifications(3), title: strings.Notifications_MessageNotificationsSound, alternate: synonyms(strings.SettingsSearch_Synonyms_Notifications_MessageNotificationsSound), icon: icon, breadcrumbs: [strings.Settings_NotificationsAndSounds, strings.Notifications_MessageNotifications], present: { context, _, present in
|
||||
|
||||
let controller = notificationSoundSelectionController(context: context, isModal: true, currentSound: filteredGlobalSound(settings.privateChats.sound), defaultSound: nil, completion: { value in
|
||||
@ -351,12 +351,12 @@ private func notificationSearchableItems(context: AccountContext, settings: Glob
|
||||
SettingsSearchableItem(id: .notifications(4), title: strings.Notifications_MessageNotificationsExceptions, alternate: synonyms(strings.SettingsSearch_Synonyms_Notifications_MessageNotificationsExceptions), icon: icon, breadcrumbs: [strings.Settings_NotificationsAndSounds, strings.Notifications_MessageNotifications], present: { context, _, present in
|
||||
present(.push, NotificationExceptionsController(context: context, mode: exceptions().0, updatedMode: { _ in}))
|
||||
}),
|
||||
SettingsSearchableItem(id: .notifications(5), title: strings.Notifications_GroupNotificationsAlert, alternate: synonyms(strings.SettingsSearch_Synonyms_Notifications_GroupNotificationsAlert), icon: icon, breadcrumbs: [strings.Settings_NotificationsAndSounds, strings.Notifications_GroupNotifications], present: { context, _, present in
|
||||
presentNotificationSettings(context, present, .groupAlerts)
|
||||
}),
|
||||
SettingsSearchableItem(id: .notifications(6), title: strings.Notifications_GroupNotificationsPreview, alternate: synonyms(strings.SettingsSearch_Synonyms_Notifications_GroupNotificationsPreview), icon: icon, breadcrumbs: [strings.Settings_NotificationsAndSounds, strings.Notifications_GroupNotifications], present: { context, _, present in
|
||||
presentNotificationSettings(context, present, .groupPreviews)
|
||||
}),
|
||||
// SettingsSearchableItem(id: .notifications(5), title: strings.Notifications_GroupNotificationsAlert, alternate: synonyms(strings.SettingsSearch_Synonyms_Notifications_GroupNotificationsAlert), icon: icon, breadcrumbs: [strings.Settings_NotificationsAndSounds, strings.Notifications_GroupNotifications], present: { context, _, present in
|
||||
// presentNotificationSettings(context, present, .groupAlerts)
|
||||
// }),
|
||||
// SettingsSearchableItem(id: .notifications(6), title: strings.Notifications_GroupNotificationsPreview, alternate: synonyms(strings.SettingsSearch_Synonyms_Notifications_GroupNotificationsPreview), icon: icon, breadcrumbs: [strings.Settings_NotificationsAndSounds, strings.Notifications_GroupNotifications], present: { context, _, present in
|
||||
// presentNotificationSettings(context, present, .groupPreviews)
|
||||
// }),
|
||||
SettingsSearchableItem(id: .notifications(7), title: strings.Notifications_GroupNotificationsSound, alternate: synonyms(strings.SettingsSearch_Synonyms_Notifications_GroupNotificationsSound), icon: icon, breadcrumbs: [strings.Settings_NotificationsAndSounds, strings.Notifications_GroupNotifications], present: { context, _, present in
|
||||
let controller = notificationSoundSelectionController(context: context, isModal: true, currentSound: filteredGlobalSound(settings.groupChats.sound), defaultSound: nil, completion: { value in
|
||||
let _ = updateGlobalNotificationSettingsInteractively(postbox: context.account.postbox, { settings in
|
||||
@ -370,12 +370,12 @@ private func notificationSearchableItems(context: AccountContext, settings: Glob
|
||||
SettingsSearchableItem(id: .notifications(8), title: strings.Notifications_GroupNotificationsExceptions, alternate: synonyms(strings.SettingsSearch_Synonyms_Notifications_GroupNotificationsExceptions), icon: icon, breadcrumbs: [strings.Settings_NotificationsAndSounds, strings.Notifications_GroupNotifications], present: { context, _, present in
|
||||
present(.push, NotificationExceptionsController(context: context, mode: exceptions().1, updatedMode: { _ in}))
|
||||
}),
|
||||
SettingsSearchableItem(id: .notifications(9), title: strings.Notifications_ChannelNotificationsAlert, alternate: synonyms(strings.SettingsSearch_Synonyms_Notifications_ChannelNotificationsAlert), icon: icon, breadcrumbs: [strings.Settings_NotificationsAndSounds, strings.Notifications_ChannelNotifications], present: { context, _, present in
|
||||
presentNotificationSettings(context, present, .channelAlerts)
|
||||
}),
|
||||
SettingsSearchableItem(id: .notifications(10), title: strings.Notifications_ChannelNotificationsPreview, alternate: synonyms(strings.SettingsSearch_Synonyms_Notifications_ChannelNotificationsPreview), icon: icon, breadcrumbs: [strings.Settings_NotificationsAndSounds, strings.Notifications_ChannelNotifications], present: { context, _, present in
|
||||
presentNotificationSettings(context, present, .channelPreviews)
|
||||
}),
|
||||
// SettingsSearchableItem(id: .notifications(9), title: strings.Notifications_ChannelNotificationsAlert, alternate: synonyms(strings.SettingsSearch_Synonyms_Notifications_ChannelNotificationsAlert), icon: icon, breadcrumbs: [strings.Settings_NotificationsAndSounds, strings.Notifications_ChannelNotifications], present: { context, _, present in
|
||||
// presentNotificationSettings(context, present, .channelAlerts)
|
||||
// }),
|
||||
// SettingsSearchableItem(id: .notifications(10), title: strings.Notifications_ChannelNotificationsPreview, alternate: synonyms(strings.SettingsSearch_Synonyms_Notifications_ChannelNotificationsPreview), icon: icon, breadcrumbs: [strings.Settings_NotificationsAndSounds, strings.Notifications_ChannelNotifications], present: { context, _, present in
|
||||
// presentNotificationSettings(context, present, .channelPreviews)
|
||||
// }),
|
||||
SettingsSearchableItem(id: .notifications(11), title: strings.Notifications_ChannelNotificationsSound, alternate: synonyms(strings.SettingsSearch_Synonyms_Notifications_ChannelNotificationsSound), icon: icon, breadcrumbs: [strings.Settings_NotificationsAndSounds, strings.Notifications_ChannelNotifications], present: { context, _, present in
|
||||
let controller = notificationSoundSelectionController(context: context, isModal: true, currentSound: filteredGlobalSound(settings.channels.sound), defaultSound: nil, completion: { value in
|
||||
let _ = updateGlobalNotificationSettingsInteractively(postbox: context.account.postbox, { settings in
|
||||
|
12
submodules/TelegramUI/Images.xcassets/Settings/Menu/Channels.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Settings/Menu/Channels.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "Icon-35.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
126
submodules/TelegramUI/Images.xcassets/Settings/Menu/Channels.imageset/Icon-35.pdf
vendored
Normal file
126
submodules/TelegramUI/Images.xcassets/Settings/Menu/Channels.imageset/Icon-35.pdf
vendored
Normal file
@ -0,0 +1,126 @@
|
||||
%PDF-1.7
|
||||
|
||||
1 0 obj
|
||||
<< >>
|
||||
endobj
|
||||
|
||||
2 0 obj
|
||||
<< /Length 3 0 R >>
|
||||
stream
|
||||
/DeviceRGB CS
|
||||
/DeviceRGB cs
|
||||
q
|
||||
1.000000 0.000000 -0.000000 1.000000 0.000000 0.000000 cm
|
||||
1.000000 0.584314 0.000000 scn
|
||||
0.000000 18.799999 m
|
||||
0.000000 22.720367 0.000000 24.680552 0.762954 26.177933 c
|
||||
1.434068 27.495068 2.504932 28.565931 3.822066 29.237045 c
|
||||
5.319448 30.000000 7.279633 30.000000 11.200000 30.000000 c
|
||||
18.799999 30.000000 l
|
||||
22.720367 30.000000 24.680552 30.000000 26.177933 29.237045 c
|
||||
27.495068 28.565931 28.565931 27.495068 29.237045 26.177933 c
|
||||
30.000000 24.680552 30.000000 22.720367 30.000000 18.799999 c
|
||||
30.000000 11.200001 l
|
||||
30.000000 7.279633 30.000000 5.319448 29.237045 3.822067 c
|
||||
28.565931 2.504932 27.495068 1.434069 26.177933 0.762955 c
|
||||
24.680552 0.000000 22.720367 0.000000 18.799999 0.000000 c
|
||||
11.200000 0.000000 l
|
||||
7.279633 0.000000 5.319448 0.000000 3.822066 0.762955 c
|
||||
2.504932 1.434069 1.434068 2.504932 0.762954 3.822067 c
|
||||
0.000000 5.319448 0.000000 7.279633 0.000000 11.200001 c
|
||||
0.000000 18.799999 l
|
||||
h
|
||||
f
|
||||
n
|
||||
Q
|
||||
q
|
||||
1.000000 0.000000 -0.000000 1.000000 6.000000 5.493408 cm
|
||||
1.000000 1.000000 1.000000 scn
|
||||
4.000000 6.005815 m
|
||||
6.634244 6.005815 l
|
||||
6.966668 6.005815 7.132880 6.005815 7.294138 5.988326 c
|
||||
7.738360 5.940148 8.166211 5.793349 8.546436 5.558653 c
|
||||
8.684463 5.473454 8.815662 5.371410 9.078063 5.167321 c
|
||||
11.835391 3.022733 l
|
||||
11.835403 3.022722 l
|
||||
13.492669 1.733738 14.321304 1.089245 15.017017 1.095165 c
|
||||
15.622235 1.100315 16.192566 1.379257 16.568199 1.853825 c
|
||||
17.000000 2.399353 17.000000 3.449122 17.000000 5.548661 c
|
||||
17.000000 14.462967 l
|
||||
17.000000 16.562506 17.000000 17.612276 16.568199 18.157804 c
|
||||
16.192566 18.632374 15.622235 18.911314 15.017017 18.916464 c
|
||||
14.321305 18.922384 13.492673 18.277893 11.835412 16.988913 c
|
||||
11.835398 16.988903 l
|
||||
11.835389 16.988895 l
|
||||
9.078062 14.844307 l
|
||||
8.815662 14.640219 8.684463 14.538175 8.546436 14.452976 c
|
||||
8.166211 14.218279 7.738360 14.071481 7.294138 14.023304 c
|
||||
7.132880 14.005815 6.966668 14.005815 6.634244 14.005815 c
|
||||
4.000000 14.005815 l
|
||||
1.790861 14.005815 0.000000 12.214953 0.000000 10.005815 c
|
||||
0.000000 7.796676 1.790861 6.005815 4.000000 6.005815 c
|
||||
h
|
||||
4.094136 4.100051 m
|
||||
4.000000 3.898727 4.000000 3.639016 4.000000 3.119593 c
|
||||
4.000000 2.005812 l
|
||||
4.000000 0.901243 4.895431 0.005812 6.000000 0.005812 c
|
||||
7.104569 0.005812 8.000000 0.901243 8.000000 2.005812 c
|
||||
8.000000 3.119593 l
|
||||
8.000000 3.639016 8.000000 3.898727 7.905864 4.100051 c
|
||||
7.806588 4.312369 7.635887 4.483070 7.423568 4.582348 c
|
||||
7.222244 4.676483 6.962533 4.676483 6.443110 4.676483 c
|
||||
5.556890 4.676483 l
|
||||
5.037467 4.676483 4.777756 4.676483 4.576432 4.582348 c
|
||||
4.364113 4.483070 4.193412 4.312369 4.094136 4.100051 c
|
||||
h
|
||||
f*
|
||||
n
|
||||
Q
|
||||
|
||||
endstream
|
||||
endobj
|
||||
|
||||
3 0 obj
|
||||
2587
|
||||
endobj
|
||||
|
||||
4 0 obj
|
||||
<< /Annots []
|
||||
/Type /Page
|
||||
/MediaBox [ 0.000000 0.000000 30.000000 30.000000 ]
|
||||
/Resources 1 0 R
|
||||
/Contents 2 0 R
|
||||
/Parent 5 0 R
|
||||
>>
|
||||
endobj
|
||||
|
||||
5 0 obj
|
||||
<< /Kids [ 4 0 R ]
|
||||
/Count 1
|
||||
/Type /Pages
|
||||
>>
|
||||
endobj
|
||||
|
||||
6 0 obj
|
||||
<< /Type /Catalog
|
||||
/Pages 5 0 R
|
||||
>>
|
||||
endobj
|
||||
|
||||
xref
|
||||
0 7
|
||||
0000000000 65535 f
|
||||
0000000010 00000 n
|
||||
0000000034 00000 n
|
||||
0000002677 00000 n
|
||||
0000002700 00000 n
|
||||
0000002873 00000 n
|
||||
0000002947 00000 n
|
||||
trailer
|
||||
<< /ID [ (some) (id) ]
|
||||
/Root 6 0 R
|
||||
/Size 7
|
||||
>>
|
||||
startxref
|
||||
3006
|
||||
%%EOF
|
12
submodules/TelegramUI/Images.xcassets/Settings/Menu/GroupChats.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Settings/Menu/GroupChats.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "Icon-34.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
136
submodules/TelegramUI/Images.xcassets/Settings/Menu/GroupChats.imageset/Icon-34.pdf
vendored
Normal file
136
submodules/TelegramUI/Images.xcassets/Settings/Menu/GroupChats.imageset/Icon-34.pdf
vendored
Normal file
@ -0,0 +1,136 @@
|
||||
%PDF-1.7
|
||||
|
||||
1 0 obj
|
||||
<< >>
|
||||
endobj
|
||||
|
||||
2 0 obj
|
||||
<< /Length 3 0 R >>
|
||||
stream
|
||||
/DeviceRGB CS
|
||||
/DeviceRGB cs
|
||||
q
|
||||
1.000000 0.000000 -0.000000 1.000000 0.000000 0.000000 cm
|
||||
0.203922 0.780392 0.349020 scn
|
||||
0.000000 18.799999 m
|
||||
0.000000 22.720367 0.000000 24.680552 0.762954 26.177933 c
|
||||
1.434068 27.495068 2.504932 28.565931 3.822066 29.237045 c
|
||||
5.319448 30.000000 7.279633 30.000000 11.200000 30.000000 c
|
||||
18.799999 30.000000 l
|
||||
22.720367 30.000000 24.680552 30.000000 26.177933 29.237045 c
|
||||
27.495068 28.565931 28.565931 27.495068 29.237045 26.177933 c
|
||||
30.000000 24.680552 30.000000 22.720367 30.000000 18.799999 c
|
||||
30.000000 11.200001 l
|
||||
30.000000 7.279633 30.000000 5.319448 29.237045 3.822067 c
|
||||
28.565931 2.504932 27.495068 1.434069 26.177933 0.762955 c
|
||||
24.680552 0.000000 22.720367 0.000000 18.799999 0.000000 c
|
||||
11.200000 0.000000 l
|
||||
7.279633 0.000000 5.319448 0.000000 3.822066 0.762955 c
|
||||
2.504932 1.434069 1.434068 2.504932 0.762954 3.822067 c
|
||||
0.000000 5.319448 0.000000 7.279633 0.000000 11.200001 c
|
||||
0.000000 18.799999 l
|
||||
h
|
||||
f
|
||||
n
|
||||
Q
|
||||
q
|
||||
1.000000 0.000000 -0.000000 1.000000 2.652100 8.000000 cm
|
||||
1.000000 1.000000 1.000000 scn
|
||||
15.347914 11.500000 m
|
||||
15.347914 9.843145 14.004768 8.500000 12.347914 8.500000 c
|
||||
10.691060 8.500000 9.347914 9.843145 9.347914 11.500000 c
|
||||
9.347914 13.156855 10.691060 14.500000 12.347914 14.500000 c
|
||||
14.004768 14.500000 15.347914 13.156855 15.347914 11.500000 c
|
||||
h
|
||||
16.206648 5.096860 m
|
||||
17.131365 4.487454 17.732334 3.680024 18.122902 2.887923 c
|
||||
18.464052 2.196046 18.353537 1.522406 17.970762 1.000000 c
|
||||
17.531406 0.400373 16.733355 -0.000002 15.847914 -0.000002 c
|
||||
8.847914 -0.000002 l
|
||||
7.962472 -0.000002 7.164421 0.400373 6.725066 1.000000 c
|
||||
6.701973 1.031517 6.679871 1.063584 6.658800 1.096172 c
|
||||
6.330600 1.603753 6.252357 2.237787 6.572926 2.887924 c
|
||||
6.987937 3.729597 7.640509 4.588578 8.666627 5.208897 c
|
||||
9.571874 5.756145 10.767847 6.117645 12.347914 6.117645 c
|
||||
13.927979 6.117645 15.123951 5.756145 16.029198 5.208899 c
|
||||
16.089634 5.172363 16.148775 5.134999 16.206648 5.096860 c
|
||||
h
|
||||
5.347910 6.000000 m
|
||||
6.038868 6.000000 6.650654 5.923974 7.192287 5.790671 c
|
||||
6.346507 5.094717 5.770174 4.267296 5.380054 3.476105 c
|
||||
4.957832 2.619810 4.950275 1.751801 5.228493 1.000000 c
|
||||
2.054037 1.000000 l
|
||||
0.595407 1.000000 -0.530585 2.298071 0.261390 3.522970 c
|
||||
1.064612 4.765267 2.563349 6.000000 5.347910 6.000000 c
|
||||
h
|
||||
19.315775 3.476104 m
|
||||
19.737995 2.619809 19.745552 1.751801 19.467335 1.000000 c
|
||||
22.641785 1.000000 l
|
||||
24.100414 1.000000 25.226404 2.298071 24.434431 3.522971 c
|
||||
23.631208 4.765267 22.132471 6.000000 19.347910 6.000000 c
|
||||
18.656954 6.000000 18.045172 5.923975 17.503540 5.790672 c
|
||||
18.349321 5.094717 18.925655 4.267296 19.315775 3.476104 c
|
||||
h
|
||||
7.847914 10.500000 m
|
||||
7.847914 9.119288 6.728626 8.000000 5.347914 8.000000 c
|
||||
3.967202 8.000000 2.847914 9.119288 2.847914 10.500000 c
|
||||
2.847914 11.880713 3.967202 13.000000 5.347914 13.000000 c
|
||||
6.728626 13.000000 7.847914 11.880713 7.847914 10.500000 c
|
||||
h
|
||||
21.847914 10.500000 m
|
||||
21.847914 9.119288 20.728626 8.000000 19.347914 8.000000 c
|
||||
17.967201 8.000000 16.847914 9.119288 16.847914 10.500000 c
|
||||
16.847914 11.880713 17.967201 13.000000 19.347914 13.000000 c
|
||||
20.728626 13.000000 21.847914 11.880713 21.847914 10.500000 c
|
||||
h
|
||||
f*
|
||||
n
|
||||
Q
|
||||
|
||||
endstream
|
||||
endobj
|
||||
|
||||
3 0 obj
|
||||
3113
|
||||
endobj
|
||||
|
||||
4 0 obj
|
||||
<< /Annots []
|
||||
/Type /Page
|
||||
/MediaBox [ 0.000000 0.000000 30.000000 30.000000 ]
|
||||
/Resources 1 0 R
|
||||
/Contents 2 0 R
|
||||
/Parent 5 0 R
|
||||
>>
|
||||
endobj
|
||||
|
||||
5 0 obj
|
||||
<< /Kids [ 4 0 R ]
|
||||
/Count 1
|
||||
/Type /Pages
|
||||
>>
|
||||
endobj
|
||||
|
||||
6 0 obj
|
||||
<< /Type /Catalog
|
||||
/Pages 5 0 R
|
||||
>>
|
||||
endobj
|
||||
|
||||
xref
|
||||
0 7
|
||||
0000000000 65535 f
|
||||
0000000010 00000 n
|
||||
0000000034 00000 n
|
||||
0000003203 00000 n
|
||||
0000003226 00000 n
|
||||
0000003399 00000 n
|
||||
0000003473 00000 n
|
||||
trailer
|
||||
<< /ID [ (some) (id) ]
|
||||
/Root 6 0 R
|
||||
/Size 7
|
||||
>>
|
||||
startxref
|
||||
3532
|
||||
%%EOF
|
@ -6356,7 +6356,9 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
let items = self.isSettings ? settingsItems(data: self.data, context: self.context, presentationData: self.presentationData, interaction: self.interaction, isExpanded: self.headerNode.isAvatarExpanded) : infoItems(data: self.data, context: self.context, presentationData: self.presentationData, interaction: self.interaction, nearbyPeerDistance: self.nearbyPeerDistance, callMessages: self.callMessages)
|
||||
|
||||
contentHeight += headerHeight
|
||||
contentHeight += sectionSpacing
|
||||
if !(self.isSettings && self.state.isEditing) {
|
||||
contentHeight += sectionSpacing
|
||||
}
|
||||
|
||||
for (sectionId, sectionItems) in items {
|
||||
validRegularSections.append(sectionId)
|
||||
|
Loading…
x
Reference in New Issue
Block a user