diff --git a/.gitmodules b/.gitmodules index a222259271..15453bd728 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,7 +1,7 @@ [submodule "submodules/rlottie/rlottie"] path = submodules/rlottie/rlottie - url = https://github.com/laktyushin/rlottie.git +url=../rlottie.git [submodule "submodules/libtgvoip/libtgvoip"] - path = submodules/libtgvoip/libtgvoip - url = https://github.com/peter-iakovlev/libtgvoip.git +path = submodules/libtgvoip/libtgvoip +url = https://github.com/telegramdesktop/libtgvoip.git \ No newline at end of file diff --git a/Makefile b/Makefile index beb06ee055..169f5aa33f 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ include Utils.makefile BUCK_OPTIONS=\ - --config custom.appVersion="5.13.1" \ + --config custom.appVersion="5.14" \ --config custom.developmentCodeSignIdentity="${DEVELOPMENT_CODE_SIGN_IDENTITY}" \ --config custom.distributionCodeSignIdentity="${DISTRIBUTION_CODE_SIGN_IDENTITY}" \ --config custom.developmentTeam="${DEVELOPMENT_TEAM}" \ diff --git a/NotificationService/Serialization.m b/NotificationService/Serialization.m index cd0732944d..00a0621e3e 100644 --- a/NotificationService/Serialization.m +++ b/NotificationService/Serialization.m @@ -3,7 +3,7 @@ @implementation Serialization - (NSUInteger)currentLayer { - return 108; + return 109; } - (id _Nullable)parseMessage:(NSData * _Nullable)data { diff --git a/Telegram-iOS/Telegram-iOS-AppStoreLLC.entitlements b/Telegram-iOS/Telegram-iOS-AppStoreLLC.entitlements index a710d1b1fb..042b86f4cf 100644 --- a/Telegram-iOS/Telegram-iOS-AppStoreLLC.entitlements +++ b/Telegram-iOS/Telegram-iOS-AppStoreLLC.entitlements @@ -32,6 +32,7 @@ merchant.sberbank.test.ph.telegra.Telegraph merchant.privatbank.test.telergramios merchant.privatbank.prod.telergram + merchant.telegram.tranzzo.test com.apple.developer.pushkit.unrestricted-voip diff --git a/Telegram-iOS/en.lproj/Localizable.strings b/Telegram-iOS/en.lproj/Localizable.strings index d16fcf1a8e..2a8e328ead 100644 --- a/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram-iOS/en.lproj/Localizable.strings @@ -22,6 +22,10 @@ "PUSH_CHANNEL_MESSAGE_POLL" = "%1$@|posted a poll %2$@"; "PUSH_PINNED_POLL" = "%1$@|pinned a poll"; +"PUSH_MESSAGE_QUIZ" = "%1$@|sent you a quiz %2$@"; +"PUSH_CHANNEL_MESSAGE_QUIZ" = "%1$@|posted a quiz %2$@"; +"PUSH_PINNED_QUIZ" = "%1$@|pinned a quiz"; + "PUSH_CHAT_MESSAGE_TEXT" = "%2$@|%1$@: %3$@"; "PUSH_CHAT_MESSAGE_NOTEXT" = "%2$@|%1$@ sent a message"; "PUSH_CHAT_MESSAGE_PHOTO" = "%2$@|%1$@ sent a photo"; @@ -93,6 +97,7 @@ "PUSH_MESSAGE_GEO" = "%1$@|sent you a map"; "PUSH_MESSAGE_GEOLIVE" = "%1$@|started sharing their live location"; "PUSH_MESSAGE_POLL" = "%1$@|sent you a poll"; +"PUSH_MESSAGE_QUIZ" = "%1$@|sent you a quiz"; "PUSH_MESSAGE_GIF" = "%1$@|sent you a GIF"; "PUSH_MESSAGE_GAME" = "%1$@|invited you to play %2$@"; "PUSH_MESSAGE_INVOICE" = "%1$@|sent you an invoice for %2$@"; @@ -125,6 +130,7 @@ "PUSH_CHANNEL_MESSAGE_GEO" = "%1$@|posted a map"; "PUSH_CHANNEL_MESSAGE_GEOLIVE" = "%1$@|posted a live location"; "PUSH_CHANNEL_MESSAGE_POLL" = "%1$@|posted a poll"; +"PUSH_CHANNEL_MESSAGE_QUIZ" = "%1$@|posted a quiz"; "PUSH_CHANNEL_MESSAGE_GIF" = "%1$@|posted a GIF"; "PUSH_CHANNEL_MESSAGE_GAME" = "%1$@|invited you to play %2$@"; "PUSH_CHANNEL_MESSAGE_FWD" = "%1$@|posted a forwarded message"; @@ -155,6 +161,7 @@ "PUSH_CHAT_MESSAGE_GEO" = "%2$@|%1$@ sent a map"; "PUSH_CHAT_MESSAGE_GEOLIVE" = "%2$@|%1$@ started sharing their live location"; "PUSH_CHAT_MESSAGE_POLL" = "%2$@|%1$@ sent a poll %3$@ to the group"; +"PUSH_CHAT_MESSAGE_QUIZ" = "%2$@|%1$@ sent a quiz %3$@ to the group"; "PUSH_CHAT_MESSAGE_GIF" = "%2$@|%1$@ sent a GIF"; "PUSH_CHAT_MESSAGE_GAME" = "%2$@|%1$@ invited the group to play %3$@"; "PUSH_CHAT_MESSAGE_INVOICE" = "%2$@|%1$@ sent an invoice for %3$@"; @@ -197,6 +204,7 @@ "PUSH_PINNED_GEO" = "%1$@|pinned a map"; "PUSH_PINNED_GEOLIVE" = "%1$@|pinned a live location"; "PUSH_PINNED_POLL" = "|%1$@|pinned a poll %2$@"; +"PUSH_PINNED_QUIZ" = "|%1$@|pinned a quiz %2$@"; "PUSH_PINNED_GAME" = "%1$@|pinned a game"; "PUSH_PINNED_INVOICE" = "%1$@|pinned an invoice"; "PUSH_PINNED_GIF" = "%1$@|pinned a GIF"; @@ -1927,6 +1935,7 @@ "Notification.PinnedContactMessage" = "%@ pinned a contact"; "Notification.PinnedDeletedMessage" = "%@ pinned deleted message"; "Notification.PinnedPollMessage" = "%@ pinned a poll"; +"Notification.PinnedQuizMessage" = "%@ pinned a quiz"; "Message.PinnedTextMessage" = "pinned \"%@\" "; "Message.PinnedPhotoMessage" = "pinned photo"; @@ -1937,7 +1946,6 @@ "Message.PinnedStickerMessage" = "pinned sticker"; "Message.PinnedLocationMessage" = "pinned location"; "Message.PinnedContactMessage" = "pinned contact"; -"Message.PinnedPollMessage" = "pinned poll"; "Notification.PinnedMessage" = "pinned message"; @@ -3793,6 +3801,7 @@ Unused sets are archived when you add more."; "MessagePoll.VotedCount_any" = "%@ votes"; "AttachmentMenu.Poll" = "Poll"; "Conversation.PinnedPoll" = "Pinned Poll"; +"Conversation.PinnedQuiz" = "Pinned Quiz"; "CreatePoll.Title" = "New Poll"; "CreatePoll.Create" = "Send"; @@ -5245,3 +5254,47 @@ Any member of this group will be able to see messages in the channel."; "Conversation.ContextMenuCancelEditing" = "Cancel Editing"; "Map.NoPlacesNearby" = "There are no known places nearby.\nTry a different location."; + +"CreatePoll.QuizTitle" = "New Quiz"; +"CreatePoll.QuizOptionsHeader" = "QUIZ ANSWERS"; +"CreatePoll.Anonymous" = "Anonymous Voting"; +"CreatePoll.MultipleChoice" = "Multiple Choice"; +"CreatePoll.MultipleChoiceQuizAlert" = "A quiz has one correct answer."; +"CreatePoll.Quiz" = "Quiz Mode"; +"CreatePoll.QuizInfo" = "Polls in Quiz Mode have one correct answer. Users can't revoke their answers."; +"CreatePoll.QuizTip" = "Tap to choose the correct answer"; + +"MessagePoll.LabelPoll" = "Public Poll"; +"MessagePoll.LabelAnonymousQuiz" = "Anonymous Quiz"; +"MessagePoll.LabelQuiz" = "Quiz"; +"MessagePoll.SubmitVote" = "Vote"; +"MessagePoll.ViewResults" = "View Results"; +"MessagePoll.QuizNoUsers" = "Nobody answered yet"; +"MessagePoll.QuizCount_0" = "%@ answered"; +"MessagePoll.QuizCount_1" = "1 answered"; +"MessagePoll.QuizCount_2" = "2 answered"; +"MessagePoll.QuizCount_3_10" = "%@ answered"; +"MessagePoll.QuizCount_many" = "%@ answered"; +"MessagePoll.QuizCount_any" = "%@ answered"; + +"PollResults.Title" = "Poll Results"; +"PollResults.Collapse" = "COLLAPSE"; +"PollResults.ShowMore_1" = "Show More (%@)"; +"PollResults.ShowMore_any" = "Show More (%@)"; + +"Conversation.StopQuiz" = "Stop Quiz"; +"Conversation.StopQuizConfirmationTitle" = "If you stop this quiz now, nobody will be able to submit answers. This action cannot be undone."; +"Conversation.StopQuizConfirmation" = "Stop Quiz"; + +"Forward.ErrorDisabledForChat" = "Sorry, you can't forward messages to this chat."; +"Forward.ErrorPublicPollDisabledInChannels" = "Sorry, public polls can’t be forwarded to channels."; +"Forward.ErrorPublicQuizDisabledInChannels" = "Sorry, public polls can’t be forwarded to channels."; + +"Map.PlacesInThisArea" = "Places In This Area"; + +"Appearance.BubbleCornersSetting" = "Message Corners"; +"Appearance.BubbleCorners.Title" = "Message Corners"; +"Appearance.BubbleCorners.AdjustAdjacent" = "Adjust Adjacent Corners"; +"Appearance.BubbleCorners.Apply" = "Set"; + +"Conversation.LiveLocationYouAndOther" = "**You** and %@"; diff --git a/submodules/AccountContext/Sources/AccountContext.swift b/submodules/AccountContext/Sources/AccountContext.swift index 063be71577..620bf03962 100644 --- a/submodules/AccountContext/Sources/AccountContext.swift +++ b/submodules/AccountContext/Sources/AccountContext.swift @@ -446,8 +446,8 @@ public protocol SharedAccountContext: class { func makeComposeController(context: AccountContext) -> ViewController func makeChatListController(context: AccountContext, groupId: PeerGroupId, controlsHistoryPreload: Bool, hideNetworkActivityStatus: Bool, previewing: Bool, enableDebugActions: Bool) -> ChatListController func makeChatController(context: AccountContext, chatLocation: ChatLocation, subject: ChatControllerSubject?, botStart: ChatControllerInitialBotStart?, mode: ChatControllerPresentationMode) -> ChatController - func makeChatMessagePreviewItem(context: AccountContext, message: Message, theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, dateTimeFormat: PresentationDateTimeFormat, nameOrder: PresentationPersonNameOrder, forcedResourceStatus: FileMediaResourceStatus?, tapMessage: ((Message) -> Void)?, clickThroughMessage: (() -> Void)?) -> ListViewItem - func makeChatMessageDateHeaderItem(context: AccountContext, timestamp: Int32, theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, dateTimeFormat: PresentationDateTimeFormat, nameOrder: PresentationPersonNameOrder) -> ListViewItemHeader + func makeChatMessagePreviewItem(context: AccountContext, message: Message, theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, chatBubbleCorners: PresentationChatBubbleCorners, dateTimeFormat: PresentationDateTimeFormat, nameOrder: PresentationPersonNameOrder, forcedResourceStatus: FileMediaResourceStatus?, tapMessage: ((Message) -> Void)?, clickThroughMessage: (() -> Void)?) -> ListViewItem + func makeChatMessageDateHeaderItem(context: AccountContext, timestamp: Int32, theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, chatBubbleCorners: PresentationChatBubbleCorners, dateTimeFormat: PresentationDateTimeFormat, nameOrder: PresentationPersonNameOrder) -> ListViewItemHeader func makePeerSharedMediaController(context: AccountContext, peerId: PeerId) -> ViewController? func makeContactSelectionController(_ params: ContactSelectionControllerParams) -> ContactSelectionController func makeContactMultiselectionController(_ params: ContactMultiselectionControllerParams) -> ContactMultiselectionController diff --git a/submodules/AccountContext/Sources/ChatController.swift b/submodules/AccountContext/Sources/ChatController.swift index 9e21f14875..da9d89bc63 100644 --- a/submodules/AccountContext/Sources/ChatController.swift +++ b/submodules/AccountContext/Sources/ChatController.swift @@ -246,7 +246,7 @@ public enum ChatControllerSubject: Equatable { public enum ChatControllerPresentationMode: Equatable { case standard(previewing: Bool) - case overlay + case overlay(NavigationController?) case inline(NavigationController?) } diff --git a/submodules/AccountContext/Sources/PeerSelectionController.swift b/submodules/AccountContext/Sources/PeerSelectionController.swift index fee175e265..5037f67c93 100644 --- a/submodules/AccountContext/Sources/PeerSelectionController.swift +++ b/submodules/AccountContext/Sources/PeerSelectionController.swift @@ -25,6 +25,8 @@ public struct ChatListNodePeersFilter: OptionSet { public static let excludeDisabled = ChatListNodePeersFilter(rawValue: 1 << 10) public static let includeSavedMessages = ChatListNodePeersFilter(rawValue: 1 << 11) + + public static let excludeChannels = ChatListNodePeersFilter(rawValue: 1 << 12) } public final class PeerSelectionControllerParams { @@ -32,12 +34,14 @@ public final class PeerSelectionControllerParams { public let filter: ChatListNodePeersFilter public let hasContactSelector: Bool public let title: String? + public let attemptSelection: ((Peer) -> Void)? - public init(context: AccountContext, filter: ChatListNodePeersFilter = [.onlyWriteable], hasContactSelector: Bool = true, title: String? = nil) { + public init(context: AccountContext, filter: ChatListNodePeersFilter = [.onlyWriteable], hasContactSelector: Bool = true, title: String? = nil, attemptSelection: ((Peer) -> Void)? = nil) { self.context = context self.filter = filter self.hasContactSelector = hasContactSelector self.title = title + self.attemptSelection = attemptSelection } } diff --git a/submodules/AppLock/Sources/AppLock.swift b/submodules/AppLock/Sources/AppLock.swift index 8e8ecda201..e339ed19ad 100644 --- a/submodules/AppLock/Sources/AppLock.swift +++ b/submodules/AppLock/Sources/AppLock.swift @@ -44,19 +44,19 @@ private func getCoveringViewSnaphot(window: Window1) -> UIImage? { context.clear(CGRect(origin: CGPoint(), size: size)) context.scaleBy(x: scale, y: scale) UIGraphicsPushContext(context) - window.forEachViewController { controller in + window.forEachViewController({ controller in if let controller = controller as? PasscodeEntryController { controller.displayNode.alpha = 0.0 } return true - } + }) window.hostView.containerView.drawHierarchy(in: CGRect(origin: CGPoint(), size: unscaledSize), afterScreenUpdates: false) - window.forEachViewController { controller in + window.forEachViewController({ controller in if let controller = controller as? PasscodeEntryController { controller.displayNode.alpha = 1.0 } return true - } + }) UIGraphicsPopContext() }).flatMap(applyScreenshotEffectToImage) } @@ -201,6 +201,7 @@ public final class AppLockContextImpl: AppLockContext { } } passcodeController.presentedOverCoveringView = true + passcodeController.isOpaqueWhenInOverlay = true strongSelf.passcodeController = passcodeController if let rootViewController = strongSelf.rootController { if let presentedViewController = rootViewController.presentedViewController as? UIActivityViewController { diff --git a/submodules/ArchivedStickerPacksNotice/BUCK b/submodules/ArchivedStickerPacksNotice/BUCK new file mode 100644 index 0000000000..d544ffbad2 --- /dev/null +++ b/submodules/ArchivedStickerPacksNotice/BUCK @@ -0,0 +1,29 @@ +load("//Config:buck_rule_macros.bzl", "static_library") + +static_library( + name = "ArchivedStickerPacksNotice", + srcs = glob([ + "Sources/**/*.swift", + ]), + deps = [ + "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit#shared", + "//submodules/AsyncDisplayKit:AsyncDisplayKit#shared", + "//submodules/Display:Display#shared", + "//submodules/Postbox:Postbox#shared", + "//submodules/TelegramCore:TelegramCore#shared", + "//submodules/SyncCore:SyncCore#shared", + "//submodules/TelegramPresentationData:TelegramPresentationData", + "//submodules/AccountContext:AccountContext", + "//submodules/TelegramUIPreferences:TelegramUIPreferences", + "//submodules/StickerResources:StickerResources", + "//submodules/AlertUI:AlertUI", + "//submodules/PresentationDataUtils:PresentationDataUtils", + "//submodules/MergeLists:MergeLists", + "//submodules/ItemListUI:ItemListUI", + "//submodules/ItemListStickerPackItem:ItemListStickerPackItem", + ], + frameworks = [ + "$SDKROOT/System/Library/Frameworks/Foundation.framework", + "$SDKROOT/System/Library/Frameworks/UIKit.framework", + ], +) diff --git a/submodules/ArchivedStickerPacksNotice/Sources/ArchivedStickerPacksNoticeController.swift b/submodules/ArchivedStickerPacksNotice/Sources/ArchivedStickerPacksNoticeController.swift new file mode 100644 index 0000000000..4cc7acdcb2 --- /dev/null +++ b/submodules/ArchivedStickerPacksNotice/Sources/ArchivedStickerPacksNoticeController.swift @@ -0,0 +1,316 @@ +import Foundation +import UIKit +import AsyncDisplayKit +import Display +import SwiftSignalKit +import Postbox +import TelegramCore +import SyncCore +import TelegramPresentationData +import ActivityIndicator +import AccountContext +import AlertUI +import PresentationDataUtils +import MergeLists +import ItemListUI +import ItemListStickerPackItem + +private struct ArchivedStickersNoticeEntry: Comparable, Identifiable { + let index: Int + let info: StickerPackCollectionInfo + let topItem: StickerPackItem? + let count: String + + var stableId: ItemCollectionId { + return info.id + } + + static func ==(lhs: ArchivedStickersNoticeEntry, rhs: ArchivedStickersNoticeEntry) -> Bool { + return lhs.index == rhs.index && lhs.info.id == rhs.info.id && lhs.count == rhs.count + } + + static func <(lhs: ArchivedStickersNoticeEntry, rhs: ArchivedStickersNoticeEntry) -> Bool { + return lhs.index < rhs.index + } + + func item(account: Account, presentationData: PresentationData) -> ListViewItem { + return ItemListStickerPackItem(presentationData: ItemListPresentationData(presentationData), account: account, packInfo: info, itemCount: self.count, topItem: topItem, unread: false, control: .none, editing: ItemListStickerPackItemEditing(editable: false, editing: false, revealed: false, reorderable: false), enabled: true, playAnimatedStickers: true, sectionId: 0, action: { + }, setPackIdWithRevealedOptions: { current, previous in + }, addPack: { + }, removePack: { + }) + } +} + +private struct ArchivedStickersNoticeTransition { + let deletions: [ListViewDeleteItem] + let insertions: [ListViewInsertItem] + let updates: [ListViewUpdateItem] +} + +private func preparedTransition(from fromEntries: [ArchivedStickersNoticeEntry], to toEntries: [ArchivedStickersNoticeEntry], account: Account, presentationData: PresentationData) -> ArchivedStickersNoticeTransition { + let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries) + + let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) } + let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, presentationData: presentationData), directionHint: nil) } + let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, presentationData: presentationData), directionHint: nil) } + + return ArchivedStickersNoticeTransition(deletions: deletions, insertions: insertions, updates: updates) +} + + +private final class ArchivedStickersNoticeAlertContentNode: AlertContentNode { + private let presentationData: PresentationData + private let archivedStickerPacks: [(StickerPackCollectionInfo, StickerPackItem?)] + + private let textNode: ASTextNode + private let listView: ListView + + private var enqueuedTransitions: [ArchivedStickersNoticeTransition] = [] + + private let actionNodesSeparator: ASDisplayNode + private let actionNodes: [TextAlertContentActionNode] + private let actionVerticalSeparators: [ASDisplayNode] + + private let disposable = MetaDisposable() + + private var validLayout: CGSize? + + override var dismissOnOutsideTap: Bool { + return self.isUserInteractionEnabled + } + + init(theme: AlertControllerTheme, account: Account, presentationData: PresentationData, archivedStickerPacks: [(StickerPackCollectionInfo, StickerPackItem?)], actions: [TextAlertAction]) { + self.presentationData = presentationData + self.archivedStickerPacks = archivedStickerPacks + + self.textNode = ASTextNode() + self.textNode.maximumNumberOfLines = 4 + + self.listView = ListView() + self.listView.isOpaque = false + + self.actionNodesSeparator = ASDisplayNode() + self.actionNodesSeparator.isLayerBacked = true + + self.actionNodes = actions.map { action -> TextAlertContentActionNode in + return TextAlertContentActionNode(theme: theme, action: action) + } + + var actionVerticalSeparators: [ASDisplayNode] = [] + if actions.count > 1 { + for _ in 0 ..< actions.count - 1 { + let separatorNode = ASDisplayNode() + separatorNode.isLayerBacked = true + actionVerticalSeparators.append(separatorNode) + } + } + self.actionVerticalSeparators = actionVerticalSeparators + + super.init() + + self.addSubnode(self.textNode) + self.addSubnode(self.listView) + + self.addSubnode(self.actionNodesSeparator) + + for actionNode in self.actionNodes { + self.addSubnode(actionNode) + } + self.actionNodes.last?.actionEnabled = false + + for separatorNode in self.actionVerticalSeparators { + self.addSubnode(separatorNode) + } + + self.updateTheme(theme) + + var index: Int = 0 + var entries: [ArchivedStickersNoticeEntry] = [] + for pack in archivedStickerPacks { + entries.append(ArchivedStickersNoticeEntry(index: index, info: pack.0, topItem: pack.1, count: presentationData.strings.StickerPack_StickerCount(pack.0.count))) + index += 1 + } + + let transition = preparedTransition(from: [], to: entries, account: account, presentationData: presentationData) + self.enqueueTransition(transition) + } + + deinit { + self.disposable.dispose() + } + + private func enqueueTransition(_ transition: ArchivedStickersNoticeTransition) { + self.enqueuedTransitions.append(transition) + + if let _ = self.validLayout { + while !self.enqueuedTransitions.isEmpty { + self.dequeueTransition() + } + } + } + + private func dequeueTransition() { + guard let layout = self.validLayout, let transition = self.enqueuedTransitions.first else { + return + } + self.enqueuedTransitions.remove(at: 0) + + var options = ListViewDeleteAndInsertOptions() + + self.listView.transaction(deleteIndices: transition.deletions, insertIndicesAndItems: transition.insertions, updateIndicesAndItems: transition.updates, options: options, updateSizeAndInsets: nil, updateOpaqueState: nil, completion: { [weak self] _ in + }) + } + + override func updateTheme(_ theme: AlertControllerTheme) { + self.textNode.attributedText = NSAttributedString(string: self.presentationData.strings.ArchivedPacksAlert_Title, font: Font.regular(13.0), textColor: theme.primaryColor, paragraphAlignment: .center) + + self.actionNodesSeparator.backgroundColor = theme.separatorColor + for actionNode in self.actionNodes { + actionNode.updateTheme(theme) + } + for separatorNode in self.actionVerticalSeparators { + separatorNode.backgroundColor = theme.separatorColor + } + + if let size = self.validLayout { + _ = self.updateLayout(size: size, transition: .immediate) + } + } + + override func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize { + var size = size + size.width = min(size.width, 270.0) + let measureSize = CGSize(width: size.width - 16.0 * 2.0, height: CGFloat.greatestFiniteMagnitude) + + let hadValidLayout = self.validLayout != nil + + self.validLayout = size + + var origin: CGPoint = CGPoint(x: 0.0, y: 20.0) + + let textSize = self.textNode.measure(measureSize) + transition.updateFrame(node: self.textNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - textSize.width) / 2.0), y: origin.y), size: textSize)) + origin.y += textSize.height + 16.0 + + let actionButtonHeight: CGFloat = 44.0 + var minActionsWidth: CGFloat = 0.0 + let maxActionWidth: CGFloat = floor(size.width / CGFloat(self.actionNodes.count)) + let actionTitleInsets: CGFloat = 8.0 + + var effectiveActionLayout = TextAlertContentActionLayout.horizontal + for actionNode in self.actionNodes { + let actionTitleSize = actionNode.titleNode.measure(CGSize(width: maxActionWidth, height: actionButtonHeight)) + if case .horizontal = effectiveActionLayout, actionTitleSize.height > actionButtonHeight * 0.6667 { + effectiveActionLayout = .vertical + } + switch effectiveActionLayout { + case .horizontal: + minActionsWidth += actionTitleSize.width + actionTitleInsets + case .vertical: + minActionsWidth = max(minActionsWidth, actionTitleSize.width + actionTitleInsets) + } + } + + let insets = UIEdgeInsets(top: 18.0, left: 18.0, bottom: 18.0, right: 18.0) + + var contentWidth = max(textSize.width, minActionsWidth) + contentWidth = max(contentWidth, 234.0) + + var actionsHeight: CGFloat = 0.0 + switch effectiveActionLayout { + case .horizontal: + actionsHeight = actionButtonHeight + case .vertical: + actionsHeight = actionButtonHeight * CGFloat(self.actionNodes.count) + } + + let resultWidth = contentWidth + insets.left + insets.right + + let listHeight: CGFloat = CGFloat(min(3, self.archivedStickerPacks.count)) * 56.0 + + let (duration, curve) = listViewAnimationDurationAndCurve(transition: transition) + self.listView.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous, .LowLatency], scrollToItem: nil, updateSizeAndInsets: ListViewUpdateSizeAndInsets(size: CGSize(width: resultWidth, height: listHeight), insets: UIEdgeInsets(top: -35.0, left: 0.0, bottom: 0.0, right: 0.0), headerInsets: UIEdgeInsets(), scrollIndicatorInsets: UIEdgeInsets(), duration: duration, curve: curve), stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in }) + transition.updateFrame(node: self.listView, frame: CGRect(x: 0.0, y: origin.y, width: resultWidth, height: listHeight)) + + let resultSize = CGSize(width: resultWidth, height: textSize.height + actionsHeight + listHeight + 10.0 + insets.top + insets.bottom) + + transition.updateFrame(node: self.actionNodesSeparator, frame: CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel))) + + var actionOffset: CGFloat = 0.0 + let actionWidth: CGFloat = floor(resultSize.width / CGFloat(self.actionNodes.count)) + var separatorIndex = -1 + var nodeIndex = 0 + for actionNode in self.actionNodes { + if separatorIndex >= 0 { + let separatorNode = self.actionVerticalSeparators[separatorIndex] + switch effectiveActionLayout { + case .horizontal: + transition.updateFrame(node: separatorNode, frame: CGRect(origin: CGPoint(x: actionOffset - UIScreenPixel, y: resultSize.height - actionsHeight), size: CGSize(width: UIScreenPixel, height: actionsHeight - UIScreenPixel))) + case .vertical: + transition.updateFrame(node: separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight + actionOffset - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel))) + } + } + separatorIndex += 1 + + let currentActionWidth: CGFloat + switch effectiveActionLayout { + case .horizontal: + if nodeIndex == self.actionNodes.count - 1 { + currentActionWidth = resultSize.width - actionOffset + } else { + currentActionWidth = actionWidth + } + case .vertical: + currentActionWidth = resultSize.width + } + + let actionNodeFrame: CGRect + switch effectiveActionLayout { + case .horizontal: + actionNodeFrame = CGRect(origin: CGPoint(x: actionOffset, y: resultSize.height - actionsHeight), size: CGSize(width: currentActionWidth, height: actionButtonHeight)) + actionOffset += currentActionWidth + case .vertical: + actionNodeFrame = CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight + actionOffset), size: CGSize(width: currentActionWidth, height: actionButtonHeight)) + actionOffset += actionButtonHeight + } + + transition.updateFrame(node: actionNode, frame: actionNodeFrame) + + nodeIndex += 1 + } + + if !hadValidLayout { + while !self.enqueuedTransitions.isEmpty { + self.dequeueTransition() + } + } + + return resultSize + } +} + +public func archivedStickerPacksNoticeController(context: AccountContext, archivedStickerPacks: [(StickerPackCollectionInfo, StickerPackItem?)]) -> ViewController { + let presentationData = context.sharedContext.currentPresentationData.with { $0 } + + var dismissImpl: (() -> Void)? + + let disposable = MetaDisposable() + + let contentNode = ArchivedStickersNoticeAlertContentNode(theme: AlertControllerTheme(presentationData: presentationData), account: context.account, presentationData: presentationData, archivedStickerPacks: archivedStickerPacks, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: { + dismissImpl?() + })]) + + let controller = AlertController(theme: AlertControllerTheme(presentationData: presentationData), contentNode: contentNode) + let presentationDataDisposable = context.sharedContext.presentationData.start(next: { [weak controller, weak contentNode] presentationData in + controller?.theme = AlertControllerTheme(presentationData: presentationData) + }) + controller.dismissed = { + presentationDataDisposable.dispose() + disposable.dispose() + } + dismissImpl = { [weak controller, weak contentNode] in + controller?.dismissAnimated() + } + return controller +} diff --git a/submodules/AvatarNode/Sources/AvatarNode.swift b/submodules/AvatarNode/Sources/AvatarNode.swift index 4b8924e900..18f61800f6 100644 --- a/submodules/AvatarNode/Sources/AvatarNode.swift +++ b/submodules/AvatarNode/Sources/AvatarNode.swift @@ -54,16 +54,6 @@ private class AvatarNodeParameters: NSObject { } } -private let gradientColors: [NSArray] = [ - [UIColor(rgb: 0xff516a).cgColor, UIColor(rgb: 0xff885e).cgColor], - [UIColor(rgb: 0xffa85c).cgColor, UIColor(rgb: 0xffcd6a).cgColor], - [UIColor(rgb: 0x665fff).cgColor, UIColor(rgb: 0x82b1ff).cgColor], - [UIColor(rgb: 0x54cb68).cgColor, UIColor(rgb: 0xa0de7e).cgColor], - [UIColor(rgb: 0x4acccd).cgColor, UIColor(rgb: 0x00fcfd).cgColor], - [UIColor(rgb: 0x2a9ef1).cgColor, UIColor(rgb: 0x72d5fd).cgColor], - [UIColor(rgb: 0xd669ed).cgColor, UIColor(rgb: 0xe0a2f3).cgColor], -] - private func generateGradientFilledCircleImage(diameter: CGFloat, colors: NSArray) -> UIImage? { return generateImage(CGSize(width: diameter, height: diameter), contextGenerator: { size, context in let bounds = CGRect(origin: CGPoint(), size: size) @@ -167,6 +157,16 @@ public final class AvatarEditOverlayNode: ASDisplayNode { } public final class AvatarNode: ASDisplayNode { + public static let gradientColors: [NSArray] = [ + [UIColor(rgb: 0xff516a).cgColor, UIColor(rgb: 0xff885e).cgColor], + [UIColor(rgb: 0xffa85c).cgColor, UIColor(rgb: 0xffcd6a).cgColor], + [UIColor(rgb: 0x665fff).cgColor, UIColor(rgb: 0x82b1ff).cgColor], + [UIColor(rgb: 0x54cb68).cgColor, UIColor(rgb: 0xa0de7e).cgColor], + [UIColor(rgb: 0x4acccd).cgColor, UIColor(rgb: 0x00fcfd).cgColor], + [UIColor(rgb: 0x2a9ef1).cgColor, UIColor(rgb: 0x72d5fd).cgColor], + [UIColor(rgb: 0xd669ed).cgColor, UIColor(rgb: 0xe0a2f3).cgColor], + ] + public var font: UIFont { didSet { if oldValue !== font { @@ -318,7 +318,7 @@ public final class AvatarNode: ASDisplayNode { let parameters: AvatarNodeParameters - if let peer = peer, let signal = peerAvatarImage(account: context.account, peer: peer, authorOfMessage: authorOfMessage, representation: representation, displayDimensions: displayDimensions, emptyColor: emptyColor, synchronousLoad: synchronousLoad) { + if let peer = peer, let signal = peerAvatarImage(account: context.account, peerReference: PeerReference(peer), authorOfMessage: authorOfMessage, representation: representation, displayDimensions: displayDimensions, emptyColor: emptyColor, synchronousLoad: synchronousLoad) { self.contents = nil self.displaySuspended = true self.imageReady.set(self.imageNode.ready) @@ -416,7 +416,7 @@ public final class AvatarNode: ASDisplayNode { if peerId.namespace == -1 { colorIndex = -1 } else { - colorIndex = abs(Int(clamping: accountPeerId.id &+ peerId.id)) + colorIndex = abs(Int(clamping: peerId.id)) } } else { colorIndex = -1 @@ -456,7 +456,7 @@ public final class AvatarNode: ASDisplayNode { colorsArray = grayscaleColors } } else { - colorsArray = gradientColors[colorIndex % gradientColors.count] + colorsArray = AvatarNode.gradientColors[colorIndex % AvatarNode.gradientColors.count] } var locations: [CGFloat] = [1.0, 0.0] @@ -555,7 +555,7 @@ public final class AvatarNode: ASDisplayNode { } } -public func drawPeerAvatarLetters(context: CGContext, size: CGSize, font: UIFont, letters: [String], accountPeerId: PeerId, peerId: PeerId) { +public func drawPeerAvatarLetters(context: CGContext, size: CGSize, font: UIFont, letters: [String], peerId: PeerId) { context.beginPath() context.addEllipse(in: CGRect(x: 0.0, y: 0.0, width: size.width, height: size.height)) @@ -572,7 +572,7 @@ public func drawPeerAvatarLetters(context: CGContext, size: CGSize, font: UIFont if colorIndex == -1 { colorsArray = grayscaleColors } else { - colorsArray = gradientColors[colorIndex % gradientColors.count] + colorsArray = AvatarNode.gradientColors[colorIndex % AvatarNode.gradientColors.count] } var locations: [CGFloat] = [1.0, 0.0] @@ -582,6 +582,8 @@ public func drawPeerAvatarLetters(context: CGContext, size: CGSize, font: UIFont context.drawLinearGradient(gradient, start: CGPoint(), end: CGPoint(x: 0.0, y: size.height), options: CGGradientDrawingOptions()) + context.resetClip() + context.setBlendMode(.normal) let string = letters.count == 0 ? "" : (letters[0] + (letters.count == 1 ? "" : letters[1])) @@ -597,7 +599,9 @@ public func drawPeerAvatarLetters(context: CGContext, size: CGSize, font: UIFont context.scaleBy(x: 1.0, y: -1.0) context.translateBy(x: -size.width / 2.0, y: -size.height / 2.0) + let textPosition = context.textPosition context.translateBy(x: lineOrigin.x, y: lineOrigin.y) CTLineDraw(line, context) context.translateBy(x: -lineOrigin.x, y: -lineOrigin.y) + context.textPosition = textPosition } diff --git a/submodules/AvatarNode/Sources/PeerAvatar.swift b/submodules/AvatarNode/Sources/PeerAvatar.swift index 751ecfbd8c..183a06137a 100644 --- a/submodules/AvatarNode/Sources/PeerAvatar.swift +++ b/submodules/AvatarNode/Sources/PeerAvatar.swift @@ -21,7 +21,7 @@ private let roundCorners = { () -> UIImage in return image }() -public func peerAvatarImageData(account: Account, peer: Peer, authorOfMessage: MessageReference?, representation: TelegramMediaImageRepresentation?, synchronousLoad: Bool) -> Signal? { +public func peerAvatarImageData(account: Account, peerReference: PeerReference?, authorOfMessage: MessageReference?, representation: TelegramMediaImageRepresentation?, synchronousLoad: Bool) -> Signal? { if let smallProfileImage = representation { let resourceData = account.postbox.mediaBox.resourceData(smallProfileImage.resource, attemptSynchronously: synchronousLoad) let imageData = resourceData @@ -44,7 +44,7 @@ public func peerAvatarImageData(account: Account, peer: Peer, authorOfMessage: M subscriber.putCompletion() }) var fetchedDataDisposable: Disposable? - if let peerReference = PeerReference(peer) { + if let peerReference = peerReference { fetchedDataDisposable = fetchedMediaResource(mediaBox: account.postbox.mediaBox, reference: .avatar(peer: peerReference, resource: smallProfileImage.resource), statsCategory: .generic).start() } else if let authorOfMessage = authorOfMessage { fetchedDataDisposable = fetchedMediaResource(mediaBox: account.postbox.mediaBox, reference: .messageAuthorAvatar(message: authorOfMessage, resource: smallProfileImage.resource), statsCategory: .generic).start() @@ -64,8 +64,8 @@ public func peerAvatarImageData(account: Account, peer: Peer, authorOfMessage: M } } -public func peerAvatarImage(account: Account, peer: Peer, authorOfMessage: MessageReference?, representation: TelegramMediaImageRepresentation?, displayDimensions: CGSize = CGSize(width: 60.0, height: 60.0), round: Bool = true, inset: CGFloat = 0.0, emptyColor: UIColor? = nil, synchronousLoad: Bool = false) -> Signal? { - if let imageData = peerAvatarImageData(account: account, peer: peer, authorOfMessage: authorOfMessage, representation: representation, synchronousLoad: synchronousLoad) { +public func peerAvatarImage(account: Account, peerReference: PeerReference?, authorOfMessage: MessageReference?, representation: TelegramMediaImageRepresentation?, displayDimensions: CGSize = CGSize(width: 60.0, height: 60.0), round: Bool = true, inset: CGFloat = 0.0, emptyColor: UIColor? = nil, synchronousLoad: Bool = false) -> Signal? { + if let imageData = peerAvatarImageData(account: account, peerReference: peerReference, authorOfMessage: authorOfMessage, representation: representation, synchronousLoad: synchronousLoad) { return imageData |> mapToSignal { data -> Signal in let generate = deferred { () -> Signal in diff --git a/submodules/CallListUI/Sources/CallListControllerNode.swift b/submodules/CallListUI/Sources/CallListControllerNode.swift index bf90cd5308..96c2880606 100644 --- a/submodules/CallListUI/Sources/CallListControllerNode.swift +++ b/submodules/CallListUI/Sources/CallListControllerNode.swift @@ -288,7 +288,7 @@ final class CallListControllerNode: ASDisplayNode { let showCallsTab = context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.callListSettings]) |> map { sharedData -> Bool in - var value = true + var value = CallListSettings.defaultSettings.showTab if let settings = sharedData.entries[ApplicationSpecificSharedDataKeys.callListSettings] as? CallListSettings { value = settings.showTab } diff --git a/submodules/ChatListUI/Sources/ChatListController.swift b/submodules/ChatListUI/Sources/ChatListController.swift index f1854fdfec..f2049711c5 100644 --- a/submodules/ChatListUI/Sources/ChatListController.swift +++ b/submodules/ChatListUI/Sources/ChatListController.swift @@ -306,7 +306,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController, strongSelf.titleView.title = NetworkStatusTitle(text: defaultTitle, activity: false, hasProxy: isRoot && hasProxy, connectsViaProxy: connectsViaProxy, isPasscodeSet: isRoot && isPasscodeSet, isManuallyLocked: isRoot && isManuallyLocked) } if case .root = groupId, checkProxy { - if strongSelf.proxyUnavailableTooltipController == nil && !strongSelf.didShowProxyUnavailableTooltipController && strongSelf.isNodeLoaded && strongSelf.displayNode.view.window != nil { + if strongSelf.proxyUnavailableTooltipController == nil && !strongSelf.didShowProxyUnavailableTooltipController && strongSelf.isNodeLoaded && strongSelf.displayNode.view.window != nil && strongSelf.navigationController?.topViewController === self { strongSelf.didShowProxyUnavailableTooltipController = true let tooltipController = TooltipController(content: .text(strongSelf.presentationData.strings.Proxy_TooltipUnavailable), baseFontSize: strongSelf.presentationData.listsFontSize.baseDisplaySize, timeout: 60.0, dismissByTapOutside: true) strongSelf.proxyUnavailableTooltipController = tooltipController diff --git a/submodules/ChatListUI/Sources/ChatListControllerNode.swift b/submodules/ChatListUI/Sources/ChatListControllerNode.swift index 6065cc3b7c..3dd78330a6 100644 --- a/submodules/ChatListUI/Sources/ChatListControllerNode.swift +++ b/submodules/ChatListUI/Sources/ChatListControllerNode.swift @@ -244,6 +244,7 @@ final class ChatListControllerNode: ASDisplayNode { self.searchDisplayController = SearchDisplayController(presentationData: self.presentationData, contentNode: ChatListSearchContainerNode(context: self.context, filter: [], groupId: self.groupId, openPeer: { [weak self] peer, dismissSearch in self?.requestOpenPeerFromSearch?(peer, dismissSearch) + }, openDisabledPeer: { _ in }, openRecentPeerOptions: { [weak self] peer in self?.requestOpenRecentPeerOptions?(peer) }, openMessage: { [weak self] peer, messageId in diff --git a/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift b/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift index 0c91905017..79fa26f3f7 100644 --- a/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift +++ b/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift @@ -81,7 +81,7 @@ private enum ChatListRecentEntry: Comparable, Identifiable { } } - func item(context: AccountContext, presentationData: ChatListPresentationData, filter: ChatListNodePeersFilter, peerSelected: @escaping (Peer) -> Void, peerContextAction: ((Peer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?) -> Void)?, clearRecentlySearchedPeers: @escaping () -> Void, setPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void, deletePeer: @escaping (PeerId) -> Void) -> ListViewItem { + func item(context: AccountContext, presentationData: ChatListPresentationData, filter: ChatListNodePeersFilter, peerSelected: @escaping (Peer) -> Void, disaledPeerSelected: @escaping (Peer) -> Void, peerContextAction: ((Peer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?) -> Void)?, clearRecentlySearchedPeers: @escaping () -> Void, setPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void, deletePeer: @escaping (PeerId) -> Void) -> ListViewItem { switch self { case let .topPeers(peers, theme, strings): return ChatListRecentPeersListItem(theme: theme, strings: strings, context: context, peers: peers, peerSelected: { peer in @@ -133,6 +133,12 @@ private enum ChatListRecentEntry: Comparable, Identifiable { } } + if filter.contains(.excludeChannels) { + if let channel = primaryPeer as? TelegramChannel, case .broadcast = channel.info { + enabled = false + } + } + let status: ContactsPeerItemStatus if let user = primaryPeer as? TelegramUser { let servicePeer = isServicePeer(primaryPeer) @@ -181,6 +187,10 @@ private enum ChatListRecentEntry: Comparable, Identifiable { if let chatPeer = peer.peer.peers[peer.peer.peerId] { peerSelected(chatPeer) } + }, disabledAction: { _ in + if let chatPeer = peer.peer.peers[peer.peer.peerId] { + disaledPeerSelected(chatPeer) + } }, setPeerIdWithRevealedOptions: setPeerIdWithRevealedOptions, deletePeer: deletePeer, contextAction: peerContextAction.flatMap { peerContextAction in return { node, gesture in if let chatPeer = peer.peer.peers[peer.peer.peerId] { @@ -501,12 +511,12 @@ public struct ChatListSearchContainerTransition { } } -private func chatListSearchContainerPreparedRecentTransition(from fromEntries: [ChatListRecentEntry], to toEntries: [ChatListRecentEntry], context: AccountContext, presentationData: ChatListPresentationData, filter: ChatListNodePeersFilter, peerSelected: @escaping (Peer) -> Void, peerContextAction: ((Peer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?) -> Void)?, clearRecentlySearchedPeers: @escaping () -> Void, setPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void, deletePeer: @escaping (PeerId) -> Void) -> ChatListSearchContainerRecentTransition { +private func chatListSearchContainerPreparedRecentTransition(from fromEntries: [ChatListRecentEntry], to toEntries: [ChatListRecentEntry], context: AccountContext, presentationData: ChatListPresentationData, filter: ChatListNodePeersFilter, peerSelected: @escaping (Peer) -> Void, disaledPeerSelected: @escaping (Peer) -> Void, peerContextAction: ((Peer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?) -> Void)?, clearRecentlySearchedPeers: @escaping () -> Void, setPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void, deletePeer: @escaping (PeerId) -> Void) -> ChatListSearchContainerRecentTransition { let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries) let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) } - let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, presentationData: presentationData, filter: filter, peerSelected: peerSelected, peerContextAction: peerContextAction, clearRecentlySearchedPeers: clearRecentlySearchedPeers, setPeerIdWithRevealedOptions: setPeerIdWithRevealedOptions, deletePeer: deletePeer), directionHint: nil) } - let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, presentationData: presentationData, filter: filter, peerSelected: peerSelected, peerContextAction: peerContextAction, clearRecentlySearchedPeers: clearRecentlySearchedPeers, setPeerIdWithRevealedOptions: setPeerIdWithRevealedOptions, deletePeer: deletePeer), directionHint: nil) } + let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, presentationData: presentationData, filter: filter, peerSelected: peerSelected, disaledPeerSelected: disaledPeerSelected, peerContextAction: peerContextAction, clearRecentlySearchedPeers: clearRecentlySearchedPeers, setPeerIdWithRevealedOptions: setPeerIdWithRevealedOptions, deletePeer: deletePeer), directionHint: nil) } + let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, presentationData: presentationData, filter: filter, peerSelected: peerSelected, disaledPeerSelected: disaledPeerSelected, peerContextAction: peerContextAction, clearRecentlySearchedPeers: clearRecentlySearchedPeers, setPeerIdWithRevealedOptions: setPeerIdWithRevealedOptions, deletePeer: deletePeer), directionHint: nil) } return ChatListSearchContainerRecentTransition(deletions: deletions, insertions: insertions, updates: updates) } @@ -615,7 +625,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo private let filter: ChatListNodePeersFilter - public init(context: AccountContext, filter: ChatListNodePeersFilter, groupId: PeerGroupId, openPeer originalOpenPeer: @escaping (Peer, Bool) -> Void, openRecentPeerOptions: @escaping (Peer) -> Void, openMessage originalOpenMessage: @escaping (Peer, MessageId) -> Void, addContact: ((String) -> Void)?, peerContextAction: ((Peer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?) -> Void)?) { + public init(context: AccountContext, filter: ChatListNodePeersFilter, groupId: PeerGroupId, openPeer originalOpenPeer: @escaping (Peer, Bool) -> Void, openDisabledPeer: @escaping (Peer) -> Void, openRecentPeerOptions: @escaping (Peer) -> Void, openMessage originalOpenMessage: @escaping (Peer, MessageId) -> Void, addContact: ((String) -> Void)?, peerContextAction: ((Peer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?) -> Void)?) { self.context = context self.filter = filter self.dimNode = ASDisplayNode() @@ -837,6 +847,12 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo } } + if filter.contains(.excludeChannels) { + if let peer = peer as? TelegramChannel, case .broadcast = peer.info { + return false + } + } + return true } @@ -962,6 +978,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo openPeer(peer, false) let _ = addRecentlySearchedPeer(postbox: context.account.postbox, peerId: peer.id).start() self?.listNode.clearHighlightAnimated(true) + }, disabledPeerSelected: { _ in }, togglePeerSelected: { _ in }, messageSelected: { [weak self] peer, message, _ in self?.view.endEditing(true) @@ -1091,6 +1108,8 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo openPeer(peer, true) let _ = addRecentlySearchedPeer(postbox: context.account.postbox, peerId: peer.id).start() self?.recentListNode.clearHighlightAnimated(true) + }, disaledPeerSelected: { peer in + openDisabledPeer(peer) }, peerContextAction: peerContextAction, clearRecentlySearchedPeers: { self?.clearRecentSearch() diff --git a/submodules/ChatListUI/Sources/Node/ChatListItem.swift b/submodules/ChatListUI/Sources/Node/ChatListItem.swift index c44aa5fc0a..8803effaeb 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListItem.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListItem.swift @@ -755,7 +755,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { var currentSecretIconImage: UIImage? var selectableControlSizeAndApply: (CGFloat, (CGSize, Bool) -> ItemListSelectableControlNode)? - var reorderControlSizeAndApply: (CGFloat, (CGFloat, Bool) -> ItemListEditableReorderControlNode)? + var reorderControlSizeAndApply: (CGFloat, (CGFloat, Bool, ContainedViewLayoutTransition) -> ItemListEditableReorderControlNode)? let editingOffset: CGFloat var reorderInset: CGFloat = 0.0 @@ -1331,7 +1331,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { if let reorderControlSizeAndApply = reorderControlSizeAndApply { let reorderControlFrame = CGRect(origin: CGPoint(x: params.width + revealOffset - params.rightInset - reorderControlSizeAndApply.0, y: layoutOffset), size: CGSize(width: reorderControlSizeAndApply.0, height: layout.contentSize.height)) if strongSelf.reorderControlNode == nil { - let reorderControlNode = reorderControlSizeAndApply.1(layout.contentSize.height, false) + let reorderControlNode = reorderControlSizeAndApply.1(layout.contentSize.height, false, .immediate) strongSelf.reorderControlNode = reorderControlNode strongSelf.addSubnode(reorderControlNode) reorderControlNode.frame = reorderControlFrame @@ -1344,7 +1344,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { transition.updateAlpha(node: strongSelf.pinnedIconNode, alpha: 0.0) transition.updateAlpha(node: strongSelf.statusNode, alpha: 0.0) } else if let reorderControlNode = strongSelf.reorderControlNode { - let _ = reorderControlSizeAndApply.1(layout.contentSize.height, false) + let _ = reorderControlSizeAndApply.1(layout.contentSize.height, false, .immediate) transition.updateFrame(node: reorderControlNode, frame: reorderControlFrame) } } else if let reorderControlNode = strongSelf.reorderControlNode { diff --git a/submodules/ChatListUI/Sources/Node/ChatListNode.swift b/submodules/ChatListUI/Sources/Node/ChatListNode.swift index 763d9279e8..0276a2bd8b 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListNode.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListNode.swift @@ -46,6 +46,7 @@ final class ChatListHighlightedLocation { public final class ChatListNodeInteraction { let activateSearch: () -> Void let peerSelected: (Peer) -> Void + let disabledPeerSelected: (Peer) -> Void let togglePeerSelected: (PeerId) -> Void let messageSelected: (Peer, Message, Bool) -> Void let groupSelected: (PeerGroupId) -> Void @@ -62,9 +63,10 @@ public final class ChatListNodeInteraction { public var searchTextHighightState: String? var highlightedChatLocation: ChatListHighlightedLocation? - public init(activateSearch: @escaping () -> Void, peerSelected: @escaping (Peer) -> Void, togglePeerSelected: @escaping (PeerId) -> Void, messageSelected: @escaping (Peer, Message, Bool) -> Void, groupSelected: @escaping (PeerGroupId) -> Void, addContact: @escaping (String) -> Void, setPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void, setItemPinned: @escaping (PinnedItemId, Bool) -> Void, setPeerMuted: @escaping (PeerId, Bool) -> Void, deletePeer: @escaping (PeerId) -> Void, updatePeerGrouping: @escaping (PeerId, Bool) -> Void, togglePeerMarkedUnread: @escaping (PeerId, Bool) -> Void, toggleArchivedFolderHiddenByDefault: @escaping () -> Void, activateChatPreview: @escaping (ChatListItem, ASDisplayNode, ContextGesture?) -> Void) { + public init(activateSearch: @escaping () -> Void, peerSelected: @escaping (Peer) -> Void, disabledPeerSelected: @escaping (Peer) -> Void, togglePeerSelected: @escaping (PeerId) -> Void, messageSelected: @escaping (Peer, Message, Bool) -> Void, groupSelected: @escaping (PeerGroupId) -> Void, addContact: @escaping (String) -> Void, setPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void, setItemPinned: @escaping (PinnedItemId, Bool) -> Void, setPeerMuted: @escaping (PeerId, Bool) -> Void, deletePeer: @escaping (PeerId) -> Void, updatePeerGrouping: @escaping (PeerId, Bool) -> Void, togglePeerMarkedUnread: @escaping (PeerId, Bool) -> Void, toggleArchivedFolderHiddenByDefault: @escaping () -> Void, activateChatPreview: @escaping (ChatListItem, ASDisplayNode, ContextGesture?) -> Void) { self.activateSearch = activateSearch self.peerSelected = peerSelected + self.disabledPeerSelected = disabledPeerSelected self.togglePeerSelected = togglePeerSelected self.messageSelected = messageSelected self.groupSelected = groupSelected @@ -202,11 +204,22 @@ private func mappedInsertEntries(context: AccountContext, nodeInteraction: ChatL enabled = false } } + if filter.contains(.excludeChannels) { + if let peer = peer.peers[peer.peerId] { + if let peer = peer as? TelegramChannel, case .broadcast = peer.info { + enabled = false + } + } + } return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ContactsPeerItem(presentationData: ItemListPresentationData(theme: presentationData.theme, fontSize: presentationData.fontSize, strings: presentationData.strings), sortOrder: presentationData.nameSortOrder, displayOrder: presentationData.nameDisplayOrder, context: context, peerMode: .generalSearch, peer: .peer(peer: itemPeer, chatPeer: chatPeer), status: .none, enabled: enabled, selection: .none, editing: ContactsPeerItemEditing(editable: false, editing: false, revealed: false), index: nil, header: nil, action: { _ in if let chatPeer = chatPeer { nodeInteraction.peerSelected(chatPeer) } + }, disabledAction: { _ in + if let chatPeer = chatPeer { + nodeInteraction.disabledPeerSelected(chatPeer) + } }), directionHint: entry.directionHint) } case let .HoleEntry(_, theme): @@ -242,10 +255,19 @@ private func mappedUpdateEntries(context: AccountContext, nodeInteraction: ChatL enabled = false } } + if filter.contains(.excludeChannels) { + if let peer = peer.chatMainPeer as? TelegramChannel, case .broadcast = peer.info { + enabled = false + } + } return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ContactsPeerItem(presentationData: ItemListPresentationData(theme: presentationData.theme, fontSize: presentationData.fontSize, strings: presentationData.strings), sortOrder: presentationData.nameSortOrder, displayOrder: presentationData.nameDisplayOrder, context: context, peerMode: .generalSearch, peer: .peer(peer: itemPeer, chatPeer: chatPeer), status: .none, enabled: enabled, selection: .none, editing: ContactsPeerItemEditing(editable: false, editing: false, revealed: false), index: nil, header: nil, action: { _ in if let chatPeer = chatPeer { nodeInteraction.peerSelected(chatPeer) } + }, disabledAction: { _ in + if let chatPeer = chatPeer { + nodeInteraction.disabledPeerSelected(chatPeer) + } }), directionHint: entry.directionHint) } case let .HoleEntry(_, theme): @@ -318,6 +340,7 @@ public final class ChatListNode: ListView { } public var peerSelected: ((PeerId, Bool, Bool) -> Void)? + public var disabledPeerSelected: ((Peer) -> Void)? public var groupSelected: ((PeerGroupId) -> Void)? public var addContact: ((String) -> Void)? public var activateSearch: (() -> Void)? @@ -409,6 +432,10 @@ public final class ChatListNode: ListView { if let strongSelf = self, let peerSelected = strongSelf.peerSelected { peerSelected(peer.id, true, false) } + }, disabledPeerSelected: { [weak self] peer in + if let strongSelf = self, let disabledPeerSelected = strongSelf.disabledPeerSelected { + disabledPeerSelected(peer) + } }, togglePeerSelected: { [weak self] peerId in self?.updateState { state in var state = state @@ -584,6 +611,11 @@ public final class ChatListNode: ListView { } } + if filter.contains(.excludeChannels) { + if let peer = peer.chatMainPeer as? TelegramChannel, case .broadcast = peer.info { + } + } + if filter.contains(.onlyWriteable) && filter.contains(.excludeDisabled) { if let peer = peer.peers[peer.peerId] { if !canSendMessagesToPeer(peer) { diff --git a/submodules/ComposePollUI/Sources/CreatePollController.swift b/submodules/ComposePollUI/Sources/CreatePollController.swift index cbf36c2929..af294cccda 100644 --- a/submodules/ComposePollUI/Sources/CreatePollController.swift +++ b/submodules/ComposePollUI/Sources/CreatePollController.swift @@ -12,8 +12,131 @@ import AccountContext import AlertUI import PresentationDataUtils +private struct OrderedLinkedListItemOrderingId: RawRepresentable, Hashable { + var rawValue: Int +} + +private struct OrderedLinkedListItemOrdering: Comparable { + var id: OrderedLinkedListItemOrderingId + var lowerItemIds: Set + var higherItemIds: Set + + static func <(lhs: OrderedLinkedListItemOrdering, rhs: OrderedLinkedListItemOrdering) -> Bool { + if rhs.lowerItemIds.contains(lhs.id) { + return true + } + if rhs.higherItemIds.contains(lhs.id) { + return false + } + if lhs.lowerItemIds.contains(rhs.id) { + return false + } + if lhs.higherItemIds.contains(rhs.id) { + return true + } + assertionFailure() + return false + } +} + +private struct OrderedLinkedListItem { + var item: T + var ordering: OrderedLinkedListItemOrdering +} + +private struct OrderedLinkedList: Sequence, Equatable { + private var items: [OrderedLinkedListItem] = [] + private var nextId: Int = 0 + + init(items: [T]) { + for i in 0 ..< items.count { + self.insert(items[i], at: i, id: nil) + } + } + + static func ==(lhs: OrderedLinkedList, rhs: OrderedLinkedList) -> Bool { + if lhs.items.count != rhs.items.count { + return false + } + for i in 0 ..< lhs.items.count { + if lhs.items[i].item != rhs.items[i].item { + return false + } + } + return true + } + + func makeIterator() -> AnyIterator> { + var index = 0 + return AnyIterator { () -> OrderedLinkedListItem? in + if index < self.items.count { + let currentIndex = index + index += 1 + return self.items[currentIndex] + } + return nil + } + } + + subscript(index: Int) -> OrderedLinkedListItem { + return self.items[index] + } + + mutating func update(at index: Int, _ f: (inout T) -> Void) { + f(&self.items[index].item) + } + + var count: Int { + return self.items.count + } + + var isEmpty: Bool { + return self.items.isEmpty + } + + var last: OrderedLinkedListItem? { + return self.items.last + } + + mutating func append(_ item: T, id: OrderedLinkedListItemOrderingId?) { + self.insert(item, at: self.items.count, id: id) + } + + mutating func insert(_ item: T, at index: Int, id: OrderedLinkedListItemOrderingId?) { + let previousId = id + let id = previousId ?? OrderedLinkedListItemOrderingId(rawValue: self.nextId) + self.nextId += 1 + + if let previousId = previousId { + for i in 0 ..< self.items.count { + self.items[i].ordering.higherItemIds.remove(previousId) + self.items[i].ordering.lowerItemIds.remove(previousId) + } + } + + var lowerItemIds = Set() + var higherItemIds = Set() + for i in 0 ..< self.items.count { + if i < index { + lowerItemIds.insert(self.items[i].ordering.id) + self.items[i].ordering.higherItemIds.insert(id) + } else { + higherItemIds.insert(self.items[i].ordering.id) + self.items[i].ordering.lowerItemIds.insert(id) + } + } + + self.items.insert(OrderedLinkedListItem(item: item, ordering: OrderedLinkedListItemOrdering(id: id, lowerItemIds: lowerItemIds, higherItemIds: higherItemIds)), at: index) + } + + mutating func remove(at index: Int) { + self.items.remove(at: index) + } +} + private let maxTextLength = 255 private let maxOptionLength = 100 +private let maxOptionCount = 10 private func processPollText(_ text: String) -> String { var text = text.trimmingCharacters(in: .whitespacesAndNewlines) @@ -25,33 +148,56 @@ private func processPollText(_ text: String) -> String { private final class CreatePollControllerArguments { let updatePollText: (String) -> Void - let updateOptionText: (Int, String) -> Void + let updateOptionText: (Int, String, Bool) -> Void let moveToNextOption: (Int) -> Void - let addOption: () -> Void + let moveToPreviousOption: (Int) -> Void let removeOption: (Int, Bool) -> Void - let optionFocused: (Int) -> Void + let optionFocused: (Int, Bool) -> Void let setItemIdWithRevealedOptions: (Int?, Int?) -> Void + let toggleOptionSelected: (Int) -> Void + let updateAnonymous: (Bool) -> Void + let updateMultipleChoice: (Bool) -> Void + let displayMultipleChoiceDisabled: () -> Void + let updateQuiz: (Bool) -> Void - init(updatePollText: @escaping (String) -> Void, updateOptionText: @escaping (Int, String) -> Void, moveToNextOption: @escaping (Int) -> Void, addOption: @escaping () -> Void, removeOption: @escaping (Int, Bool) -> Void, optionFocused: @escaping (Int) -> Void, setItemIdWithRevealedOptions: @escaping (Int?, Int?) -> Void) { + init(updatePollText: @escaping (String) -> Void, updateOptionText: @escaping (Int, String, Bool) -> Void, moveToNextOption: @escaping (Int) -> Void, moveToPreviousOption: @escaping (Int) -> Void, removeOption: @escaping (Int, Bool) -> Void, optionFocused: @escaping (Int, Bool) -> Void, setItemIdWithRevealedOptions: @escaping (Int?, Int?) -> Void, toggleOptionSelected: @escaping (Int) -> Void, updateAnonymous: @escaping (Bool) -> Void, updateMultipleChoice: @escaping (Bool) -> Void, displayMultipleChoiceDisabled: @escaping () -> Void, updateQuiz: @escaping (Bool) -> Void) { self.updatePollText = updatePollText self.updateOptionText = updateOptionText self.moveToNextOption = moveToNextOption - self.addOption = addOption + self.moveToPreviousOption = moveToPreviousOption self.removeOption = removeOption self.optionFocused = optionFocused self.setItemIdWithRevealedOptions = setItemIdWithRevealedOptions + self.toggleOptionSelected = toggleOptionSelected + self.updateAnonymous = updateAnonymous + self.updateMultipleChoice = updateMultipleChoice + self.displayMultipleChoiceDisabled = displayMultipleChoiceDisabled + self.updateQuiz = updateQuiz } } private enum CreatePollSection: Int32 { case text case options + case settings +} + +private enum CreatePollEntryId: Hashable { + case textHeader + case text + case optionsHeader + case option(Int) + case optionsInfo + case anonymousVotes + case multipleChoice + case quiz + case quizInfo } private enum CreatePollEntryTag: Equatable, ItemListItemTag { case text case option(Int) - case addOption(Int) + case optionsInfo func isEqual(to other: ItemListItemTag) -> Bool { if let other = other as? CreatePollEntryTag { @@ -63,103 +209,148 @@ private enum CreatePollEntryTag: Equatable, ItemListItemTag { } private enum CreatePollEntry: ItemListNodeEntry { - case textHeader(PresentationTheme, String, ItemListSectionHeaderAccessoryText) - case text(PresentationTheme, String, String, Int) - case optionsHeader(PresentationTheme, String) - case option(PresentationTheme, PresentationStrings, Int, Int, String, String, Bool, Bool) - case addOption(PresentationTheme, String, Bool, Int) - case optionsInfo(PresentationTheme, String) + case textHeader(String, ItemListSectionHeaderAccessoryText) + case text(String, String, Int) + case optionsHeader(String) + case option(id: Int, ordering: OrderedLinkedListItemOrdering, placeholder: String, text: String, revealed: Bool, hasNext: Bool, isLast: Bool, isSelected: Bool?) + case optionsInfo(String) + case anonymousVotes(String, Bool) + case multipleChoice(String, Bool, Bool) + case quiz(String, Bool) + case quizInfo(String) var section: ItemListSectionId { switch self { - case .textHeader, .text: - return CreatePollSection.text.rawValue - case .optionsHeader, .option, .addOption, .optionsInfo: - return CreatePollSection.options.rawValue + case .textHeader, .text: + return CreatePollSection.text.rawValue + case .optionsHeader, .option, .optionsInfo: + return CreatePollSection.options.rawValue + case .anonymousVotes, .multipleChoice, .quiz, .quizInfo: + return CreatePollSection.settings.rawValue } } var tag: ItemListItemTag? { switch self { - case .text: - return CreatePollEntryTag.text - case let .option(_, _, id, _, _, _, _, _): - return CreatePollEntryTag.option(id) - case let .addOption(_, _, _, id): - return CreatePollEntryTag.addOption(id) - default: - break + case .text: + return CreatePollEntryTag.text + case let .option(option): + return CreatePollEntryTag.option(option.id) + default: + break } return nil } - var stableId: Int { + var stableId: CreatePollEntryId { switch self { - case .textHeader: - return 0 - case .text: - return 1 - case .optionsHeader: - return 2 - case let .option(_, _, id, _, _, _, _, _): - return 3 + id - case .addOption: - return 1000 - case .optionsInfo: - return 1001 + case .textHeader: + return .textHeader + case .text: + return .text + case .optionsHeader: + return .optionsHeader + case let .option(option): + return .option(option.id) + case .optionsInfo: + return .optionsInfo + case .anonymousVotes: + return .anonymousVotes + case .multipleChoice: + return .multipleChoice + case .quiz: + return .quiz + case .quizInfo: + return .quizInfo } } private var sortId: Int { switch self { - case .textHeader: - return 0 - case .text: - return 1 - case .optionsHeader: - return 2 - case let .option(_, _, _, index, _, _, _, _): - return 3 + index - case .addOption: - return 1000 - case .optionsInfo: - return 1001 + case .textHeader: + return 0 + case .text: + return 1 + case .optionsHeader: + return 2 + case let .option(option): + return 3 + case .optionsInfo: + return 1001 + case .anonymousVotes: + return 1002 + case .multipleChoice: + return 1003 + case .quiz: + return 1004 + case .quizInfo: + return 1005 } } static func <(lhs: CreatePollEntry, rhs: CreatePollEntry) -> Bool { + switch lhs { + case let .option(lhsOption): + switch rhs { + case let .option(rhsOption): + return lhsOption.ordering < rhsOption.ordering + default: + break + } + default: + break + } return lhs.sortId < rhs.sortId } func item(presentationData: ItemListPresentationData, arguments: Any) -> ListViewItem { let arguments = arguments as! CreatePollControllerArguments switch self { - case let .textHeader(theme, text, accessoryText): - return ItemListSectionHeaderItem(presentationData: presentationData, text: text, accessoryText: accessoryText, sectionId: self.section) - case let .text(theme, placeholder, text, maxLength): - return ItemListMultilineInputItem(presentationData: presentationData, text: text, placeholder: placeholder, maxLength: ItemListMultilineInputItemTextLimit(value: maxLength, display: false), sectionId: self.section, style: .blocks, textUpdated: { value in - arguments.updatePollText(value) - }, tag: CreatePollEntryTag.text) - case let .optionsHeader(theme, text): - return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section) - case let .option(theme, strings, id, _, placeholder, text, revealed, hasNext): - return CreatePollOptionItem(theme: theme, strings: strings, id: id, placeholder: placeholder, value: text, maxLength: maxOptionLength, editing: CreatePollOptionItemEditing(editable: true, hasActiveRevealControls: revealed), sectionId: self.section, setItemIdWithRevealedOptions: { id, fromId in - arguments.setItemIdWithRevealedOptions(id, fromId) - }, updated: { value in - arguments.updateOptionText(id, value) - }, next: hasNext ? { - arguments.moveToNextOption(id) - } : nil, delete: { focused in + case let .textHeader(text, accessoryText): + return ItemListSectionHeaderItem(presentationData: presentationData, text: text, accessoryText: accessoryText, sectionId: self.section) + case let .text(placeholder, text, maxLength): + return ItemListMultilineInputItem(presentationData: presentationData, text: text, placeholder: placeholder, maxLength: ItemListMultilineInputItemTextLimit(value: maxLength, display: false), sectionId: self.section, style: .blocks, textUpdated: { value in + arguments.updatePollText(value) + }, tag: CreatePollEntryTag.text) + case let .optionsHeader(text): + return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section) + case let .option(id, _, placeholder, text, revealed, hasNext, isLast, isSelected): + return CreatePollOptionItem(presentationData: presentationData, id: id, placeholder: placeholder, value: text, isSelected: isSelected, maxLength: maxOptionLength, editing: CreatePollOptionItemEditing(editable: true, hasActiveRevealControls: revealed), sectionId: self.section, setItemIdWithRevealedOptions: { id, fromId in + arguments.setItemIdWithRevealedOptions(id, fromId) + }, updated: { value, isFocused in + arguments.updateOptionText(id, value, isFocused) + }, next: hasNext ? { + arguments.moveToNextOption(id) + } : nil, delete: { focused in + if !isLast { arguments.removeOption(id, focused) - }, focused: { - arguments.optionFocused(id) - }, tag: CreatePollEntryTag.option(id)) - case let .addOption(theme, title, enabled, id): - return CreatePollOptionActionItem(theme: theme, title: title, enabled: enabled, tag: CreatePollEntryTag.addOption(id), sectionId: self.section, action: { - arguments.addOption() - }) - case let .optionsInfo(theme, text): - return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section) + } else { + arguments.moveToPreviousOption(id) + } + }, canDelete: !isLast, + focused: { isFocused in + arguments.optionFocused(id, isFocused) + }, toggleSelected: { + arguments.toggleOptionSelected(id) + }, tag: CreatePollEntryTag.option(id)) + case let .optionsInfo(text): + return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section, tag: CreatePollEntryTag.optionsInfo) + case let .anonymousVotes(text, value): + return ItemListSwitchItem(presentationData: presentationData, title: text, value: value, sectionId: self.section, style: .blocks, updated: { value in + arguments.updateAnonymous(value) + }) + case let .multipleChoice(text, value, enabled): + return ItemListSwitchItem(presentationData: presentationData, title: text, value: value, enabled: enabled, sectionId: self.section, style: .blocks, updated: { value in + arguments.updateMultipleChoice(value) + }, activatedWhileDisabled: { + arguments.displayMultipleChoiceDisabled() + }) + case let .quiz(text, value): + return ItemListSwitchItem(presentationData: presentationData, title: text, value: value, sectionId: self.section, style: .blocks, updated: { value in + arguments.updateQuiz(value) + }) + case let .quizInfo(text): + return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section) } } } @@ -167,17 +358,21 @@ private enum CreatePollEntry: ItemListNodeEntry { private struct CreatePollControllerOption: Equatable { var text: String let id: Int + var isSelected: Bool } private struct CreatePollControllerState: Equatable { var text: String = "" - var options: [CreatePollControllerOption] = [CreatePollControllerOption(text: "", id: 0), CreatePollControllerOption(text: "", id: 1)] + var options = OrderedLinkedList(items: [CreatePollControllerOption(text: "", id: 0, isSelected: false), CreatePollControllerOption(text: "", id: 1, isSelected: false)]) var nextOptionId: Int = 2 var focusOptionId: Int? var optionIdWithRevealControls: Int? + var isAnonymous: Bool = true + var isMultipleChoice: Bool = false + var isQuiz: Bool = false } -private func createPollControllerEntries(presentationData: PresentationData, state: CreatePollControllerState, limitsConfiguration: LimitsConfiguration) -> [CreatePollEntry] { +private func createPollControllerEntries(presentationData: PresentationData, peer: Peer, state: CreatePollControllerState, limitsConfiguration: LimitsConfiguration, defaultIsQuiz: Bool?) -> [CreatePollEntry] { var entries: [CreatePollEntry] = [] var textLimitText = ItemListSectionHeaderAccessoryText(value: "", color: .generic) @@ -185,25 +380,55 @@ private func createPollControllerEntries(presentationData: PresentationData, sta let remainingCount = Int(maxTextLength) - state.text.count textLimitText = ItemListSectionHeaderAccessoryText(value: "\(remainingCount)", color: remainingCount < 0 ? .destructive : .generic) } - entries.append(.textHeader(presentationData.theme, presentationData.strings.CreatePoll_TextHeader, textLimitText)) - entries.append(.text(presentationData.theme, presentationData.strings.CreatePoll_TextPlaceholder, state.text, Int(limitsConfiguration.maxMediaCaptionLength))) - entries.append(.optionsHeader(presentationData.theme, presentationData.strings.CreatePoll_OptionsHeader)) - for i in 0 ..< state.options.count { - entries.append(.option(presentationData.theme, presentationData.strings, state.options[i].id, i, presentationData.strings.CreatePoll_OptionPlaceholder, state.options[i].text, state.optionIdWithRevealControls == state.options[i].id, i != 9)) - } - if state.options.count < 10 { - entries.append(.addOption(presentationData.theme, presentationData.strings.CreatePoll_AddOption, true, state.options.last?.id ?? -1)) - entries.append(.optionsInfo(presentationData.theme, presentationData.strings.CreatePoll_AddMoreOptions(Int32(10 - state.options.count)))) + entries.append(.textHeader(presentationData.strings.CreatePoll_TextHeader, textLimitText)) + entries.append(.text(presentationData.strings.CreatePoll_TextPlaceholder, state.text, Int(limitsConfiguration.maxMediaCaptionLength))) + let optionsHeaderTitle: String + if let defaultIsQuiz = defaultIsQuiz, defaultIsQuiz { + optionsHeaderTitle = presentationData.strings.CreatePoll_QuizOptionsHeader } else { - entries.append(.optionsInfo(presentationData.theme, presentationData.strings.CreatePoll_AllOptionsAdded)) + optionsHeaderTitle = presentationData.strings.CreatePoll_OptionsHeader + } + entries.append(.optionsHeader(optionsHeaderTitle)) + for i in 0 ..< state.options.count { + let isSecondLast = state.options.count == 2 && i == 0 + let isLast = i == state.options.count - 1 + let option = state.options[i].item + entries.append(.option(id: option.id, ordering: state.options[i].ordering, placeholder: isLast ? presentationData.strings.CreatePoll_AddOption : presentationData.strings.CreatePoll_OptionPlaceholder, text: option.text, revealed: state.optionIdWithRevealControls == option.id, hasNext: i != 9, isLast: isLast || isSecondLast, isSelected: state.isQuiz ? option.isSelected : nil)) + } + if state.options.count < maxOptionCount { + entries.append(.optionsInfo(presentationData.strings.CreatePoll_AddMoreOptions(Int32(maxOptionCount - state.options.count)))) + } else { + entries.append(.optionsInfo(presentationData.strings.CreatePoll_AllOptionsAdded)) + } + + var canBePublic = true + if let channel = peer as? TelegramChannel, case .broadcast = channel.info { + canBePublic = false + } + + if canBePublic { + entries.append(.anonymousVotes(presentationData.strings.CreatePoll_Anonymous, state.isAnonymous)) + } + if let defaultIsQuiz = defaultIsQuiz { + if !defaultIsQuiz { + entries.append(.multipleChoice(presentationData.strings.CreatePoll_MultipleChoice, state.isMultipleChoice && !state.isQuiz, !state.isQuiz)) + } + } else { + entries.append(.multipleChoice(presentationData.strings.CreatePoll_MultipleChoice, state.isMultipleChoice && !state.isQuiz, !state.isQuiz)) + entries.append(.quiz(presentationData.strings.CreatePoll_Quiz, state.isQuiz)) + entries.append(.quizInfo(presentationData.strings.CreatePoll_QuizInfo)) } return entries } -public func createPollController(context: AccountContext, peerId: PeerId, completion: @escaping (EnqueueMessage) -> Void) -> ViewController { - let statePromise = ValuePromise(CreatePollControllerState(), ignoreRepeated: true) - let stateValue = Atomic(value: CreatePollControllerState()) +public func createPollController(context: AccountContext, peer: Peer, isQuiz: Bool? = nil, completion: @escaping (EnqueueMessage) -> Void) -> ViewController { + var initialState = CreatePollControllerState() + if let isQuiz = isQuiz { + initialState.isQuiz = isQuiz + } + let statePromise = ValuePromise(initialState, ignoreRepeated: true) + let stateValue = Atomic(value: initialState) let updateState: ((CreatePollControllerState) -> CreatePollControllerState) -> Void = { f in statePromise.set(stateValue.modify { f($0) }) } @@ -212,6 +437,8 @@ public func createPollController(context: AccountContext, peerId: PeerId, comple var dismissImpl: (() -> Void)? var ensureTextVisibleImpl: (() -> Void)? var ensureOptionVisibleImpl: ((Int) -> Void)? + var displayQuizTooltipImpl: ((Bool) -> Void)? + var attemptNavigationImpl: (() -> Bool)? let actionsDisposable = DisposableSet() @@ -224,37 +451,54 @@ public func createPollController(context: AccountContext, peerId: PeerId, comple let arguments = CreatePollControllerArguments(updatePollText: { value in updateState { state in var state = state + state.focusOptionId = nil state.text = value return state } ensureTextVisibleImpl?() - }, updateOptionText: { id, value in + }, updateOptionText: { id, value, isFocused in + var ensureVisibleId = id updateState { state in var state = state for i in 0 ..< state.options.count { - if state.options[i].id == id { - state.options[i].text = value + if state.options[i].item.id == id { + if isFocused { + state.focusOptionId = id + } + state.options.update(at: i, { option in + option.text = value + }) + if !value.isEmpty && i == state.options.count - 1 && state.options.count < maxOptionCount { + state.options.append(CreatePollControllerOption(text: "", id: state.nextOptionId, isSelected: false), id: nil) + state.nextOptionId += 1 + } + if i != state.options.count - 1 { + ensureVisibleId = state.options[i + 1].item.id + } + break } } return state } - ensureOptionVisibleImpl?(id) + if isFocused { + ensureOptionVisibleImpl?(ensureVisibleId) + } }, moveToNextOption: { id in var resetFocusOptionId: Int? updateState { state in var state = state for i in 0 ..< state.options.count { - if state.options[i].id == id { + if state.options[i].item.id == id { if i == state.options.count - 1 { - state.options.append(CreatePollControllerOption(text: "", id: state.nextOptionId)) + /*state.options.append(CreatePollControllerOption(text: "", id: state.nextOptionId, isSelected: false)) state.focusOptionId = state.nextOptionId - state.nextOptionId += 1 + state.nextOptionId += 1*/ } else { - if state.focusOptionId == state.options[i + 1].id { - resetFocusOptionId = state.options[i + 1].id + if state.focusOptionId == state.options[i + 1].item.id { + resetFocusOptionId = state.options[i + 1].item.id state.focusOptionId = -1 } else { - state.focusOptionId = state.options[i + 1].id + state.focusOptionId = state.options[i + 1].item.id } } break @@ -269,30 +513,78 @@ public func createPollController(context: AccountContext, peerId: PeerId, comple return state } } - }, addOption: { - updateState { state in - var state = state - state.options.append(CreatePollControllerOption(text: "", id: state.nextOptionId)) - state.focusOptionId = state.nextOptionId - state.nextOptionId += 1 - return state - } - }, removeOption: { id, focused in + }, moveToPreviousOption: { id in + var resetFocusOptionId: Int? updateState { state in var state = state for i in 0 ..< state.options.count { - if state.options[i].id == id { - state.options.remove(at: i) - if focused && i != 0 { - state.focusOptionId = state.options[i - 1].id + if state.options[i].item.id == id { + if i != 0 { + if state.focusOptionId == state.options[i - 1].item.id { + resetFocusOptionId = state.options[i - 1].item.id + state.focusOptionId = -1 + } else { + state.focusOptionId = state.options[i - 1].item.id + } } break } } return state } - }, optionFocused: { id in - ensureOptionVisibleImpl?(id) + if let resetFocusOptionId = resetFocusOptionId { + updateState { state in + var state = state + state.focusOptionId = resetFocusOptionId + return state + } + } + }, removeOption: { id, focused in + updateState { state in + var state = state + for i in 0 ..< state.options.count { + if state.options[i].item.id == id { + state.options.remove(at: i) + if focused && i != 0 { + state.focusOptionId = state.options[i - 1].item.id + } + break + } + } + let focusOnFirst = state.options.isEmpty + if state.options.count < 2 { + for i in 0 ..< (2 - state.options.count) { + if i == 0 && focusOnFirst { + state.options.append(CreatePollControllerOption(text: "", id: state.nextOptionId, isSelected: false), id: nil) + state.focusOptionId = state.nextOptionId + state.nextOptionId += 1 + } else { + state.options.append(CreatePollControllerOption(text: "", id: state.nextOptionId, isSelected: false), id: nil) + state.nextOptionId += 1 + } + } + } + return state + } + }, optionFocused: { id, isFocused in + if isFocused { + ensureOptionVisibleImpl?(id) + } else { + updateState { state in + var state = state + if state.options.count > 2 { + for i in 0 ..< state.options.count { + if state.options[i].item.id == id { + if state.options[i].item.text.isEmpty && i != state.options.count - 1 { + state.options.remove(at: i) + } + break + } + } + } + return state + } + } }, setItemIdWithRevealedOptions: { id, fromId in updateState { state in var state = state @@ -303,6 +595,73 @@ public func createPollController(context: AccountContext, peerId: PeerId, comple return state } } + }, toggleOptionSelected: { id in + updateState { state in + var state = state + for i in 0 ..< state.options.count { + if state.options[i].item.id == id { + state.options.update(at: i, { option in + option.isSelected = !option.isSelected + }) + if state.options[i].item.isSelected && state.isQuiz { + for j in 0 ..< state.options.count { + if i != j { + state.options.update(at: j, { option in + option.isSelected = false + }) + } + } + } + break + } + } + return state + } + }, updateAnonymous: { value in + updateState { state in + var state = state + state.focusOptionId = -1 + state.isAnonymous = value + return state + } + }, updateMultipleChoice: { value in + updateState { state in + var state = state + state.focusOptionId = -1 + state.isMultipleChoice = value + return state + } + }, displayMultipleChoiceDisabled: { + let presentationData = context.sharedContext.currentPresentationData.with { $0 } + presentControllerImpl?(textAlertController(context: context, title: nil, text: presentationData.strings.CreatePoll_MultipleChoiceQuizAlert, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: {})]), nil) + }, updateQuiz: { value in + if !value { + displayQuizTooltipImpl?(value) + } + updateState { state in + var state = state + state.focusOptionId = -1 + state.isQuiz = value + if value { + state.isMultipleChoice = false + var foundSelectedOption = false + for i in 0 ..< state.options.count { + if state.options[i].item.isSelected { + if !foundSelectedOption { + foundSelectedOption = true + } else { + state.options.update(at: i, { option in + option.isSelected = false + }) + } + } + } + } + return state + } + if value { + displayQuizTooltipImpl?(value) + } }) let previousOptionIds = Atomic<[Int]?>(value: nil) @@ -320,11 +679,25 @@ public func createPollController(context: AccountContext, peerId: PeerId, comple enabled = false } var nonEmptyOptionCount = 0 + var hasSelectedOptions = false for option in state.options { - if !option.text.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty { + if !option.item.text.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty { nonEmptyOptionCount += 1 } - if option.text.count > maxOptionLength { + if option.item.text.count > maxOptionLength { + enabled = false + } + if option.item.isSelected { + hasSelectedOptions = true + } + if state.isQuiz { + if option.item.text.isEmpty && option.item.isSelected { + enabled = false + } + } + } + if state.isQuiz { + if !hasSelectedOptions { enabled = false } } @@ -335,44 +708,48 @@ public func createPollController(context: AccountContext, peerId: PeerId, comple rightNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.CreatePoll_Create), style: .bold, enabled: enabled, action: { let state = stateValue.with { $0 } var options: [TelegramMediaPollOption] = [] + var correctAnswers: [Data]? for i in 0 ..< state.options.count { - let optionText = state.options[i].text.trimmingCharacters(in: .whitespacesAndNewlines) + let optionText = state.options[i].item.text.trimmingCharacters(in: .whitespacesAndNewlines) if !optionText.isEmpty { - options.append(TelegramMediaPollOption(text: optionText, opaqueIdentifier: "\(i)".data(using: .utf8)!)) + let optionData = "\(i)".data(using: .utf8)! + options.append(TelegramMediaPollOption(text: optionText, opaqueIdentifier: optionData)) + if state.isQuiz && state.options[i].item.isSelected { + correctAnswers = [optionData] + } } } - completion(.message(text: "", attributes: [], mediaReference: .standalone(media: TelegramMediaPoll(pollId: MediaId(namespace: Namespaces.Media.LocalPoll, id: arc4random64()), text: processPollText(state.text), options: options, results: TelegramMediaPollResults(voters: nil, totalVoters: nil), isClosed: false)), replyToMessageId: nil, localGroupingKey: nil)) + let publicity: TelegramMediaPollPublicity + if state.isAnonymous { + publicity = .anonymous + } else { + publicity = .public + } + let kind: TelegramMediaPollKind + if state.isQuiz { + kind = .quiz + } else { + kind = .poll(multipleAnswers: state.isMultipleChoice) + } dismissImpl?() + completion(.message(text: "", attributes: [], mediaReference: .standalone(media: TelegramMediaPoll(pollId: MediaId(namespace: Namespaces.Media.LocalPoll, id: arc4random64()), publicity: publicity, kind: kind, text: processPollText(state.text), options: options, correctAnswers: correctAnswers, results: TelegramMediaPollResults(voters: nil, totalVoters: nil, recentVoters: []), isClosed: false)), replyToMessageId: nil, localGroupingKey: nil)) }) let leftNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Cancel), style: .regular, enabled: true, action: { - let state = stateValue.with { $0 } - var hasNonEmptyOptions = false - for i in 0 ..< state.options.count { - let optionText = state.options[i].text.trimmingCharacters(in: .whitespacesAndNewlines) - if !optionText.isEmpty { - hasNonEmptyOptions = true - } - } - if hasNonEmptyOptions || !state.text.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty { - let presentationData = context.sharedContext.currentPresentationData.with { $0 } - presentControllerImpl?(textAlertController(context: context, title: nil, text: presentationData.strings.CreatePoll_CancelConfirmation, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_No, action: {}), TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Yes, action: { - dismissImpl?() - })]), nil) - } else { + if let attemptNavigationImpl = attemptNavigationImpl, attemptNavigationImpl() { dismissImpl?() } }) - let optionIds = state.options.map { $0.id } + let optionIds = state.options.map { $0.item.id } let previousIds = previousOptionIds.swap(optionIds) var focusItemTag: ItemListItemTag? var ensureVisibleItemTag: ItemListItemTag? if let focusOptionId = state.focusOptionId { focusItemTag = CreatePollEntryTag.option(focusOptionId) - if focusOptionId == state.options.last?.id { - ensureVisibleItemTag = CreatePollEntryTag.addOption(focusOptionId) + if focusOptionId == state.options.last?.item.id { + ensureVisibleItemTag = nil } else { ensureVisibleItemTag = focusItemTag } @@ -381,8 +758,15 @@ public func createPollController(context: AccountContext, peerId: PeerId, comple ensureVisibleItemTag = focusItemTag } - let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(presentationData.strings.CreatePoll_Title), leftNavigationButton: leftNavigationButton, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: false) - let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: createPollControllerEntries(presentationData: presentationData, state: state, limitsConfiguration: limitsConfiguration), style: .blocks, focusItemTag: focusItemTag, ensureVisibleItemTag: ensureVisibleItemTag, animateChanges: previousIds != nil && previousIds != optionIds) + let title: String + if let isQuiz = isQuiz, isQuiz { + title = presentationData.strings.CreatePoll_QuizTitle + } else { + title = presentationData.strings.CreatePoll_Title + } + + let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(title), leftNavigationButton: leftNavigationButton, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: false) + let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: createPollControllerEntries(presentationData: presentationData, peer: peer, state: state, limitsConfiguration: limitsConfiguration, defaultIsQuiz: isQuiz), style: .blocks, focusItemTag: focusItemTag, ensureVisibleItemTag: ensureVisibleItemTag, animateChanges: previousIds != nil) return (controllerState, (listState, arguments)) } @@ -390,13 +774,13 @@ public func createPollController(context: AccountContext, peerId: PeerId, comple actionsDisposable.dispose() } + weak var currentTooltipController: TooltipController? let controller = ItemListController(context: context, state: signal) controller.navigationPresentation = .modal presentControllerImpl = { [weak controller] c, a in controller?.present(c, in: .window(.root), with: a) } dismissImpl = { [weak controller] in - //controller?.view.endEditing(true) controller?.dismiss() } ensureTextVisibleImpl = { [weak controller] in @@ -428,23 +812,23 @@ public func createPollController(context: AccountContext, peerId: PeerId, comple var resultItemNode: ListViewItemNode? let state = stateValue.with({ $0 }) - if state.options.last?.id == id { - let _ = controller.frameForItemNode({ itemNode in - if let itemNode = itemNode as? ItemListItemNode { - if let tag = itemNode.tag, tag.isEqual(to: CreatePollEntryTag.addOption(id)) { - resultItemNode = itemNode as? ListViewItemNode - return true - } - } - return false - }) + var isLast = false + if state.options.last?.item.id == id { + isLast = true } if resultItemNode == nil { let _ = controller.frameForItemNode({ itemNode in - if let itemNode = itemNode as? ItemListItemNode { - if let tag = itemNode.tag, tag.isEqual(to: CreatePollEntryTag.option(id)) { - resultItemNode = itemNode as? ListViewItemNode - return true + if let itemNode = itemNode as? ItemListItemNode, let tag = itemNode.tag { + if isLast { + if tag.isEqual(to: CreatePollEntryTag.optionsInfo) { + resultItemNode = itemNode as? ListViewItemNode + return true + } + } else { + if tag.isEqual(to: CreatePollEntryTag.option(id)) { + resultItemNode = itemNode as? ListViewItemNode + return true + } } } return false @@ -456,19 +840,53 @@ public func createPollController(context: AccountContext, peerId: PeerId, comple } }) } - - controller.setReorderEntry({ (fromIndex: Int, toIndex: Int, entries: [CreatePollEntry]) -> Void in - let fromEntry = entries[fromIndex] - guard case let .option(_, _, id, _, _, _, _, _) = fromEntry else { + displayQuizTooltipImpl = { [weak controller] display in + guard let controller = controller else { return } + var resultItemNode: CreatePollOptionItemNode? + let insets = controller.listInsets + let _ = controller.frameForItemNode({ itemNode in + if resultItemNode == nil, let itemNode = itemNode as? CreatePollOptionItemNode { + if itemNode.frame.minY >= insets.top { + resultItemNode = itemNode + return true + } + } + return false + }) + if let resultItemNode = resultItemNode, let localCheckNodeFrame = resultItemNode.checkNodeFrame { + let checkNodeFrame = resultItemNode.view.convert(localCheckNodeFrame, to: controller.view) + if display { + let presentationData = context.sharedContext.currentPresentationData.with { $0 } + let tooltipController = TooltipController(content: .text(presentationData.strings.CreatePoll_QuizTip), baseFontSize: presentationData.listsFontSize.baseDisplaySize, dismissByTapOutside: true) + controller.present(tooltipController, in: .window(.root), with: TooltipControllerPresentationArguments(sourceViewAndRect: { [weak controller] in + if let controller = controller { + return (controller.view, checkNodeFrame.insetBy(dx: 0.0, dy: 0.0)) + } + return nil + })) + tooltipController.displayNode.layer.animatePosition(from: CGPoint(x: -checkNodeFrame.maxX, y: 0.0), to: CGPoint(), duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, additive: true) + currentTooltipController = tooltipController + } else if let tooltipController = currentTooltipController{ + currentTooltipController = nil + tooltipController.displayNode.layer.animatePosition(from: CGPoint(), to: CGPoint(x: -checkNodeFrame.maxX, y: 0.0), duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, additive: true) + } + } + } + controller.setReorderEntry({ (fromIndex: Int, toIndex: Int, entries: [CreatePollEntry]) -> Signal in + let fromEntry = entries[fromIndex] + guard case let .option(option) = fromEntry else { + return .single(false) + } + let id = option.id var referenceId: Int? var beforeAll = false var afterAll = false if toIndex < entries.count { switch entries[toIndex] { - case let .option(_, _, toId, _, _, _, _, _): - referenceId = toId + case let .option(toOption): + referenceId = toOption.id default: if entries[toIndex] < fromEntry { beforeAll = true @@ -479,13 +897,18 @@ public func createPollController(context: AccountContext, peerId: PeerId, comple } else { afterAll = true } + + var didReorder = false + updateState { state in var state = state var options = state.options - var reorderOption: CreatePollControllerOption? + var reorderOption: OrderedLinkedListItem? + var previousIndex: Int? for i in 0 ..< options.count { - if options[i].id == id { + if options[i].item.id == id { reorderOption = options[i] + previousIndex = i options.remove(at: i) break } @@ -493,33 +916,76 @@ public func createPollController(context: AccountContext, peerId: PeerId, comple if let reorderOption = reorderOption { if let referenceId = referenceId { var inserted = false - for i in 0 ..< options.count { - if options[i].id == referenceId { + for i in 0 ..< options.count - 1 { + if options[i].item.id == referenceId { if fromIndex < toIndex { - options.insert(reorderOption, at: i + 1) + didReorder = previousIndex != i + 1 + options.insert(reorderOption.item, at: i + 1, id: reorderOption.ordering.id) } else { - options.insert(reorderOption, at: i) + didReorder = previousIndex != i + options.insert(reorderOption.item, at: i, id: reorderOption.ordering.id) } inserted = true break } } if !inserted { - options.append(reorderOption) + if options.count >= 2 { + didReorder = previousIndex != options.count - 1 + options.insert(reorderOption.item, at: options.count - 1, id: reorderOption.ordering.id) + } else { + didReorder = previousIndex != options.count + options.append(reorderOption.item, id: reorderOption.ordering.id) + } } } else if beforeAll { - options.insert(reorderOption, at: 0) + didReorder = previousIndex != 0 + options.insert(reorderOption.item, at: 0, id: reorderOption.ordering.id) } else if afterAll { - options.append(reorderOption) + if options.count >= 2 { + didReorder = previousIndex != options.count - 1 + options.insert(reorderOption.item, at: options.count - 1, id: reorderOption.ordering.id) + } else { + didReorder = previousIndex != options.count + options.append(reorderOption.item, id: reorderOption.ordering.id) + } } state.options = options } return state } + + return .single(didReorder) }) + attemptNavigationImpl = { + let state = stateValue.with { $0 } + var hasNonEmptyOptions = false + for i in 0 ..< state.options.count { + let optionText = state.options[i].item.text.trimmingCharacters(in: .whitespacesAndNewlines) + if !optionText.isEmpty { + hasNonEmptyOptions = true + } + } + if hasNonEmptyOptions || !state.text.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty { + let presentationData = context.sharedContext.currentPresentationData.with { $0 } + presentControllerImpl?(textAlertController(context: context, title: nil, text: presentationData.strings.CreatePoll_CancelConfirmation, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_No, action: {}), TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Yes, action: { + dismissImpl?() + })]), nil) + return false + } else { + return true + } + } + controller.attemptNavigation = { _ in + if let attemptNavigationImpl = attemptNavigationImpl, attemptNavigationImpl() { + return true + } + return false + } controller.isOpaqueWhenInOverlay = true controller.blocksBackgroundWhenInOverlay = true controller.experimentalSnapScrollToItem = true + controller.alwaysSynchronous = true return controller } diff --git a/submodules/ComposePollUI/Sources/CreatePollOptionActionItem.swift b/submodules/ComposePollUI/Sources/CreatePollOptionActionItem.swift index ba1dae70be..e7cc231c00 100644 --- a/submodules/ComposePollUI/Sources/CreatePollOptionActionItem.swift +++ b/submodules/ComposePollUI/Sources/CreatePollOptionActionItem.swift @@ -170,7 +170,7 @@ class CreatePollOptionActionItemNode: ListViewItemNode, ItemListItemNode { strongSelf.iconNode.image = updatedIcon } if let image = strongSelf.iconNode.image { - transition.updateFrame(node: strongSelf.iconNode, frame: CGRect(origin: CGPoint(x: params.leftInset + floor((leftInset - params.leftInset - image.size.width) / 2.0 - 1.0), y: floor((contentSize.height - image.size.height) / 2.0)), size: image.size)) + transition.updateFrame(node: strongSelf.iconNode, frame: CGRect(origin: CGPoint(x: params.leftInset + floor((leftInset - params.leftInset - image.size.width) / 2.0 - 3.0), y: floor((contentSize.height - image.size.height) / 2.0)), size: image.size)) } if strongSelf.backgroundNode.supernode == nil { diff --git a/submodules/ComposePollUI/Sources/CreatePollOptionItem.swift b/submodules/ComposePollUI/Sources/CreatePollOptionItem.swift index f322e1c478..6a658bac2a 100644 --- a/submodules/ComposePollUI/Sources/CreatePollOptionItem.swift +++ b/submodules/ComposePollUI/Sources/CreatePollOptionItem.swift @@ -6,6 +6,7 @@ import SwiftSignalKit import TelegramPresentationData import ItemListUI import PresentationDataUtils +import CheckNode struct CreatePollOptionItemEditing { let editable: Bool @@ -13,27 +14,29 @@ struct CreatePollOptionItemEditing { } class CreatePollOptionItem: ListViewItem, ItemListItem { - let theme: PresentationTheme - let strings: PresentationStrings + let presentationData: ItemListPresentationData let id: Int let placeholder: String let value: String + let isSelected: Bool? let maxLength: Int let editing: CreatePollOptionItemEditing let sectionId: ItemListSectionId let setItemIdWithRevealedOptions: (Int?, Int?) -> Void - let updated: (String) -> Void + let updated: (String, Bool) -> Void let next: (() -> Void)? let delete: (Bool) -> Void - let focused: () -> Void + let canDelete: Bool + let focused: (Bool) -> Void + let toggleSelected: () -> Void let tag: ItemListItemTag? - init(theme: PresentationTheme, strings: PresentationStrings, id: Int, placeholder: String, value: String, maxLength: Int, editing: CreatePollOptionItemEditing, sectionId: ItemListSectionId, setItemIdWithRevealedOptions: @escaping (Int?, Int?) -> Void, updated: @escaping (String) -> Void, next: (() -> Void)?, delete: @escaping (Bool) -> Void, focused: @escaping () -> Void, tag: ItemListItemTag?) { - self.theme = theme - self.strings = strings + init(presentationData: ItemListPresentationData, id: Int, placeholder: String, value: String, isSelected: Bool?, maxLength: Int, editing: CreatePollOptionItemEditing, sectionId: ItemListSectionId, setItemIdWithRevealedOptions: @escaping (Int?, Int?) -> Void, updated: @escaping (String, Bool) -> Void, next: (() -> Void)?, delete: @escaping (Bool) -> Void, canDelete: Bool, focused: @escaping (Bool) -> Void, toggleSelected: @escaping () -> Void, tag: ItemListItemTag?) { + self.presentationData = presentationData self.id = id self.placeholder = placeholder self.value = value + self.isSelected = isSelected self.maxLength = maxLength self.editing = editing self.sectionId = sectionId @@ -41,7 +44,9 @@ class CreatePollOptionItem: ListViewItem, ItemListItem { self.updated = updated self.next = next self.delete = delete + self.canDelete = canDelete self.focused = focused + self.toggleSelected = toggleSelected self.tag = tag } @@ -55,7 +60,7 @@ class CreatePollOptionItem: ListViewItem, ItemListItem { Queue.mainQueue().async { completion(node, { - return (nil, { _ in apply() }) + return (nil, { _ in apply(.None) }) }) } } @@ -70,7 +75,7 @@ class CreatePollOptionItem: ListViewItem, ItemListItem { let (layout, apply) = makeLayout(self, params, itemListNeighbors(item: self, topItem: previousItem as? ItemListItem, bottomItem: nextItem as? ItemListItem)) Queue.mainQueue().async { completion(layout, { _ in - apply() + apply(animation) }) } } @@ -84,17 +89,19 @@ class CreatePollOptionItem: ListViewItem, ItemListItem { private let titleFont = Font.regular(15.0) class CreatePollOptionItemNode: ItemListRevealOptionsItemNode, ItemListItemNode, ItemListItemFocusableNode, ASEditableTextNodeDelegate { + private let containerNode: ASDisplayNode private let backgroundNode: ASDisplayNode private let topStripeNode: ASDisplayNode private let bottomStripeNode: ASDisplayNode private let maskNode: ASImageNode + private var checkNode: CheckNode? + private let textClippingNode: ASDisplayNode private let textNode: EditableTextNode private let measureTextNode: TextNode private let textLimitNode: TextNode - private let editableControlNode: ItemListEditableControlNode private let reorderControlNode: ItemListEditableReorderControlNode private var item: CreatePollOptionItem? @@ -104,7 +111,21 @@ class CreatePollOptionItemNode: ItemListRevealOptionsItemNode, ItemListItemNode, return self.item?.tag } + override var controlsContainer: ASDisplayNode { + return self.containerNode + } + + var checkNodeFrame: CGRect? { + guard let _ = self.layoutParams, let checkNode = self.checkNode else { + return nil + } + return checkNode.frame + } + init() { + self.containerNode = ASDisplayNode() + self.containerNode.clipsToBounds = true + self.backgroundNode = ASDisplayNode() self.backgroundNode.isLayerBacked = true self.backgroundNode.backgroundColor = .white @@ -117,7 +138,6 @@ class CreatePollOptionItemNode: ItemListRevealOptionsItemNode, ItemListItemNode, self.maskNode = ASImageNode() - self.editableControlNode = ItemListEditableControlNode() self.reorderControlNode = ItemListEditableReorderControlNode() self.textClippingNode = ASDisplayNode() @@ -131,21 +151,17 @@ class CreatePollOptionItemNode: ItemListRevealOptionsItemNode, ItemListItemNode, super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false) - self.clipsToBounds = true + self.addSubnode(self.containerNode) self.textClippingNode.addSubnode(self.textNode) - self.addSubnode(self.textClippingNode) + self.containerNode.addSubnode(self.textClippingNode) - self.addSubnode(self.editableControlNode) - self.addSubnode(self.reorderControlNode) - self.addSubnode(self.textLimitNode) - - self.editableControlNode.tapped = { [weak self] in - if let strongSelf = self { - strongSelf.setRevealOptionsOpened(true, animated: true) - strongSelf.revealOptionsInteractivelyOpened() - } - } + self.containerNode.addSubnode(self.reorderControlNode) + self.containerNode.addSubnode(self.textLimitNode) + } + + @objc private func checkNodePressed() { + self.item?.toggleSelected() } override func didLoad() { @@ -153,7 +169,7 @@ class CreatePollOptionItemNode: ItemListRevealOptionsItemNode, ItemListItemNode, var textColor: UIColor = .black if let item = self.item { - textColor = item.theme.list.itemPrimaryTextColor + textColor = item.presentationData.theme.list.itemPrimaryTextColor } self.textNode.typingAttributes = [NSAttributedString.Key.font.rawValue: Font.regular(17.0), NSAttributedString.Key.foregroundColor.rawValue: textColor] self.textNode.clipsToBounds = true @@ -162,7 +178,12 @@ class CreatePollOptionItemNode: ItemListRevealOptionsItemNode, ItemListItemNode, } func editableTextNodeDidBeginEditing(_ editableTextNode: ASEditableTextNode) { - self.item?.focused() + self.item?.focused(true) + } + + func editableTextNodeDidFinishEditing(_ editableTextNode: ASEditableTextNode) { + self.internalEditableTextNodeDidUpdateText(editableTextNode, isLosingFocus: true) + self.item?.focused(false) } func editableTextNode(_ editableTextNode: ASEditableTextNode, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool { @@ -177,7 +198,7 @@ class CreatePollOptionItemNode: ItemListRevealOptionsItemNode, ItemListItemNode, if updatedText.count == 1 { updatedText = "" } - let updatedAttributedText = NSAttributedString(string: updatedText, font: Font.regular(17.0), textColor: item.theme.list.itemPrimaryTextColor) + let updatedAttributedText = NSAttributedString(string: updatedText, font: Font.regular(17.0), textColor: item.presentationData.theme.list.itemPrimaryTextColor) self.textNode.attributedText = updatedAttributedText self.editableTextNodeDidUpdateText(editableTextNode) } @@ -192,6 +213,10 @@ class CreatePollOptionItemNode: ItemListRevealOptionsItemNode, ItemListItemNode, } func editableTextNodeDidUpdateText(_ editableTextNode: ASEditableTextNode) { + self.internalEditableTextNodeDidUpdateText(editableTextNode, isLosingFocus: false) + } + + private func internalEditableTextNodeDidUpdateText(_ editableTextNode: ASEditableTextNode, isLosingFocus: Bool) { if let item = self.item { let text = self.textNode.attributedText ?? NSAttributedString() @@ -201,15 +226,15 @@ class CreatePollOptionItemNode: ItemListRevealOptionsItemNode, ItemListItemNode, hadReturn = true updatedText = updatedText.replacingOccurrences(of: "\n", with: " ") } - let updatedAttributedText = NSAttributedString(string: updatedText, font: Font.regular(17.0), textColor: item.theme.list.itemPrimaryTextColor) + let updatedAttributedText = NSAttributedString(string: updatedText, font: Font.regular(17.0), textColor: item.presentationData.theme.list.itemPrimaryTextColor) if text.string != updatedAttributedText.string { self.textNode.attributedText = updatedAttributedText } - item.updated(updatedText) + item.updated(updatedText, !isLosingFocus && editableTextNode.isFirstResponder()) if hadReturn { if let next = item.next { next() - } else { + } else if !isLosingFocus { editableTextNode.resignFirstResponder() } } @@ -220,8 +245,7 @@ class CreatePollOptionItemNode: ItemListRevealOptionsItemNode, ItemListItemNode, self.item?.delete(editableTextNode.isFirstResponder()) } - func asyncLayout() -> (_ item: CreatePollOptionItem, _ params: ListViewItemLayoutParams, _ neighbors: ItemListNeighbors) -> (ListViewItemNodeLayout, () -> Void) { - let editableControlLayout = ItemListEditableControlNode.asyncLayout(self.editableControlNode) + func asyncLayout() -> (_ item: CreatePollOptionItem, _ params: ListViewItemLayoutParams, _ neighbors: ItemListNeighbors) -> (ListViewItemNodeLayout, (ListViewItemUpdateAnimation) -> Void) { let reorderControlLayout = ItemListEditableReorderControlNode.asyncLayout(self.reorderControlNode) let makeTextLayout = TextNode.asyncLayout(self.measureTextNode) let makeTextLimitLayout = TextNode.asyncLayout(self.textLimitNode) @@ -231,32 +255,31 @@ class CreatePollOptionItemNode: ItemListRevealOptionsItemNode, ItemListItemNode, return { item, params, neighbors in var updatedTheme: PresentationTheme? - if currentItem?.theme !== item.theme { - updatedTheme = item.theme + if currentItem?.presentationData.theme !== item.presentationData.theme { + updatedTheme = item.presentationData.theme } - let controlSizeAndApply = editableControlLayout(item.theme, false) - let reorderSizeAndApply = reorderControlLayout(item.theme) + let reorderSizeAndApply = reorderControlLayout(item.presentationData.theme) let separatorHeight = UIScreenPixel let insets = itemListNeighborsGroupedInsets(neighbors) - let leftInset: CGFloat = 60.0 + params.leftInset + let leftInset: CGFloat = params.leftInset + (item.isSelected != nil ? 60.0 : 16.0) let rightInset: CGFloat = 44.0 + params.rightInset let textLength = item.value.count let displayTextLimit = textLength > item.maxLength * 70 / 100 let remainingCount = item.maxLength - textLength - let (textLimitLayout, textLimitApply) = makeTextLimitLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: "\(remainingCount)", font: Font.regular(13.0), textColor: remainingCount < 0 ? item.theme.list.itemDestructiveColor : item.theme.list.itemSecondaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: 100.0, height: .greatestFiniteMagnitude), alignment: .left, lineSpacing: 0.0, cutout: nil, insets: UIEdgeInsets())) + let (textLimitLayout, textLimitApply) = makeTextLimitLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: "\(remainingCount)", font: Font.regular(13.0), textColor: remainingCount < 0 ? item.presentationData.theme.list.itemDestructiveColor : item.presentationData.theme.list.itemSecondaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: 100.0, height: .greatestFiniteMagnitude), alignment: .left, lineSpacing: 0.0, cutout: nil, insets: UIEdgeInsets())) var measureText = item.value if measureText.hasSuffix("\n") || measureText.isEmpty { measureText += "|" } let attributedMeasureText = NSAttributedString(string: measureText, font: Font.regular(17.0), textColor: .black) - let attributedText = NSAttributedString(string: item.value, font: Font.regular(17.0), textColor: item.theme.list.itemPrimaryTextColor) + let attributedText = NSAttributedString(string: item.value, font: Font.regular(17.0), textColor: item.presentationData.theme.list.itemPrimaryTextColor) let (textLayout, textApply) = makeTextLayout(TextNodeLayoutArguments(attributedString: attributedMeasureText, backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - rightInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, lineSpacing: 0.05, cutout: nil, insets: UIEdgeInsets())) let textTopInset: CGFloat = 11.0 @@ -265,21 +288,29 @@ class CreatePollOptionItemNode: ItemListRevealOptionsItemNode, ItemListItemNode, let contentSize = CGSize(width: params.width, height: textLayout.size.height + textTopInset + textBottomInset) let layout = ListViewItemNodeLayout(contentSize: contentSize, insets: insets) - let attributedPlaceholderText = NSAttributedString(string: item.placeholder, font: Font.regular(17.0), textColor: item.theme.list.itemPlaceholderTextColor) + let attributedPlaceholderText = NSAttributedString(string: item.placeholder, font: Font.regular(17.0), textColor: item.presentationData.theme.list.itemPlaceholderTextColor) - return (layout, { [weak self] in + return (layout, { [weak self] animation in if let strongSelf = self { + let transition: ContainedViewLayoutTransition + switch animation { + case .System: + transition = .animated(duration: 0.3, curve: .spring) + default: + transition = .immediate + } + strongSelf.item = item strongSelf.layoutParams = params if let _ = updatedTheme { - strongSelf.topStripeNode.backgroundColor = item.theme.list.itemBlocksSeparatorColor - strongSelf.bottomStripeNode.backgroundColor = item.theme.list.itemBlocksSeparatorColor - strongSelf.backgroundNode.backgroundColor = item.theme.list.itemBlocksBackgroundColor + strongSelf.topStripeNode.backgroundColor = item.presentationData.theme.list.itemBlocksSeparatorColor + strongSelf.bottomStripeNode.backgroundColor = item.presentationData.theme.list.itemBlocksSeparatorColor + strongSelf.backgroundNode.backgroundColor = item.presentationData.theme.list.itemBlocksBackgroundColor if strongSelf.isNodeLoaded { - strongSelf.textNode.typingAttributes = [NSAttributedString.Key.font.rawValue: Font.regular(17.0), NSAttributedString.Key.foregroundColor.rawValue: item.theme.list.itemPrimaryTextColor] - strongSelf.textNode.tintColor = item.theme.list.itemAccentColor + strongSelf.textNode.typingAttributes = [NSAttributedString.Key.font.rawValue: Font.regular(17.0), NSAttributedString.Key.foregroundColor.rawValue: item.presentationData.theme.list.itemPrimaryTextColor] + strongSelf.textNode.tintColor = item.presentationData.theme.list.itemAccentColor } } @@ -295,7 +326,7 @@ class CreatePollOptionItemNode: ItemListRevealOptionsItemNode, ItemListItemNode, let _ = textApply() if let currentText = strongSelf.textNode.attributedText { - if currentText.string != attributedText.string { + if currentText.string != attributedText.string || updatedTheme != nil { strongSelf.textNode.attributedText = attributedText } } else { @@ -325,58 +356,93 @@ class CreatePollOptionItemNode: ItemListRevealOptionsItemNode, ItemListItemNode, strongSelf.textNode.attributedPlaceholderText = attributedPlaceholderText } - strongSelf.textNode.keyboardAppearance = item.theme.rootController.keyboardColor.keyboardAppearance + strongSelf.textNode.keyboardAppearance = item.presentationData.theme.rootController.keyboardColor.keyboardAppearance - strongSelf.textClippingNode.frame = CGRect(origin: CGPoint(x: revealOffset + leftInset, y: textTopInset), size: CGSize(width: params.width - leftInset - params.rightInset, height: textLayout.size.height)) - strongSelf.textNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: params.width - leftInset - rightInset, height: textLayout.size.height + 1.0)) + let checkSize = CGSize(width: 32.0, height: 32.0) + let checkFrame = CGRect(origin: CGPoint(x: params.leftInset + revealOffset + 11.0, y: floor((layout.contentSize.height - checkSize.height) / 2.0)), size: checkSize) + if let isSelected = item.isSelected { + if let checkNode = strongSelf.checkNode { + transition.updateFrame(node: checkNode, frame: checkFrame) + checkNode.setIsChecked(isSelected, animated: true) + } else { + let checkNode = CheckNode(strokeColor: item.presentationData.theme.list.itemCheckColors.strokeColor, fillColor: item.presentationData.theme.list.itemSwitchColors.positiveColor, foregroundColor: item.presentationData.theme.list.itemCheckColors.foregroundColor, style: .plain) + checkNode.addTarget(target: strongSelf, action: #selector(strongSelf.checkNodePressed)) + checkNode.setIsChecked(isSelected, animated: false) + strongSelf.checkNode = checkNode + strongSelf.containerNode.addSubnode(checkNode) + checkNode.frame = checkFrame + transition.animatePositionAdditive(node: checkNode, offset: CGPoint(x: -checkFrame.maxX, y: 0.0)) + } + } else if let checkNode = strongSelf.checkNode { + strongSelf.checkNode = nil + transition.updateFrame(node: checkNode, frame: checkFrame.offsetBy(dx: -checkFrame.maxX, dy: 0.0), completion: { [weak checkNode] _ in + checkNode?.removeFromSupernode() + }) + } + + transition.updateFrame(node: strongSelf.textClippingNode, frame: CGRect(origin: CGPoint(x: revealOffset + leftInset, y: textTopInset), size: CGSize(width: params.width - leftInset - params.rightInset, height: textLayout.size.height))) + transition.updateFrame(node: strongSelf.textNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: params.width - leftInset - rightInset, height: textLayout.size.height + 1.0))) if strongSelf.backgroundNode.supernode == nil { - strongSelf.insertSubnode(strongSelf.backgroundNode, at: 0) + strongSelf.containerNode.insertSubnode(strongSelf.backgroundNode, at: 0) } if strongSelf.topStripeNode.supernode == nil { - strongSelf.insertSubnode(strongSelf.topStripeNode, at: 1) + strongSelf.containerNode.insertSubnode(strongSelf.topStripeNode, at: 1) } if strongSelf.bottomStripeNode.supernode == nil { - strongSelf.insertSubnode(strongSelf.bottomStripeNode, at: 2) + strongSelf.containerNode.insertSubnode(strongSelf.bottomStripeNode, at: 2) } if strongSelf.maskNode.supernode == nil { - strongSelf.insertSubnode(strongSelf.maskNode, at: 3) + strongSelf.containerNode.insertSubnode(strongSelf.maskNode, at: 3) } + let bottomStripeWasHidden = strongSelf.bottomStripeNode.isHidden + 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 + 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 + case .sameSection(false): + bottomStripeInset = leftInset + strongSelf.bottomStripeNode.isHidden = false + default: + bottomStripeInset = 0.0 + hasBottomCorners = true + strongSelf.bottomStripeNode.isHidden = hasCorners } - strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.theme, top: hasTopCorners, bottom: hasBottomCorners) : nil + 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: layout.contentSize.width, height: separatorHeight)) - strongSelf.bottomStripeNode.frame = CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height - UIScreenPixel), size: CGSize(width: layout.contentSize.width - bottomStripeInset, height: separatorHeight)) + if strongSelf.animationForKey("apparentHeight") == nil { + strongSelf.containerNode.frame = CGRect(origin: CGPoint(), size: layout.contentSize) + 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) + let previousX = strongSelf.bottomStripeNode.frame.minX + strongSelf.bottomStripeNode.frame = CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height - UIScreenPixel), size: CGSize(width: layout.contentSize.width, height: separatorHeight)) + if !bottomStripeWasHidden { + transition.animatePositionAdditive(node: strongSelf.bottomStripeNode, offset: CGPoint(x: previousX - strongSelf.bottomStripeNode.frame.minX, y: 0.0)) + } + } else { + let previousX = strongSelf.bottomStripeNode.frame.minX + strongSelf.bottomStripeNode.frame = CGRect(origin: CGPoint(x: bottomStripeInset, y: strongSelf.bottomStripeNode.frame.minY), size: CGSize(width: layout.contentSize.width, height: separatorHeight)) + if !bottomStripeWasHidden { + transition.animatePositionAdditive(node: strongSelf.bottomStripeNode, offset: CGPoint(x: previousX - strongSelf.bottomStripeNode.frame.minX, y: 0.0)) + } + } - let _ = controlSizeAndApply.1(layout.contentSize.height) - let editableControlFrame = CGRect(origin: CGPoint(x: params.leftInset + 6.0 + revealOffset, y: 0.0), size: CGSize(width: controlSizeAndApply.0, height: contentSize.height)) - strongSelf.editableControlNode.frame = editableControlFrame - - let _ = reorderSizeAndApply.1(layout.contentSize.height, displayTextLimit && layout.contentSize.height <= 44.0) + let _ = reorderSizeAndApply.1(layout.contentSize.height, displayTextLimit, transition) let reorderControlFrame = CGRect(origin: CGPoint(x: params.width + revealOffset - params.rightInset - reorderSizeAndApply.0, y: 0.0), size: CGSize(width: reorderSizeAndApply.0, height: layout.contentSize.height)) strongSelf.reorderControlNode.frame = reorderControlFrame + strongSelf.reorderControlNode.isHidden = !item.canDelete let _ = textLimitApply() strongSelf.textLimitNode.frame = CGRect(origin: CGPoint(x: reorderControlFrame.minX + floor((reorderControlFrame.width - textLimitLayout.size.width) / 2.0) - 4.0 - UIScreenPixel, y: max(floor(reorderControlFrame.midY + 2.0), layout.contentSize.height - 15.0 - textLimitLayout.size.height)), size: textLimitLayout.size) @@ -384,7 +450,7 @@ class CreatePollOptionItemNode: ItemListRevealOptionsItemNode, ItemListItemNode, strongSelf.updateLayout(size: layout.contentSize, leftInset: params.leftInset, rightInset: params.rightInset) - strongSelf.setRevealOptions((left: [], right: [ItemListRevealOption(key: 0, title: item.strings.Common_Delete, icon: .none, color: item.theme.list.itemDisclosureActions.destructive.fillColor, textColor: item.theme.list.itemDisclosureActions.destructive.foregroundColor)])) + strongSelf.setRevealOptions((left: [], right: item.canDelete ? [ItemListRevealOption(key: 0, title: item.presentationData.strings.Common_Delete, icon: .none, color: item.presentationData.theme.list.itemDisclosureActions.destructive.fillColor, textColor: item.presentationData.theme.list.itemDisclosureActions.destructive.foregroundColor)] : [])) } }) } @@ -393,18 +459,20 @@ class CreatePollOptionItemNode: ItemListRevealOptionsItemNode, ItemListItemNode, override func updateRevealOffset(offset: CGFloat, transition: ContainedViewLayoutTransition) { super.updateRevealOffset(offset: offset, transition: transition) - guard let params = self.layoutParams else { + guard let params = self.layoutParams, let item = self.item else { return } let revealOffset = offset let leftInset: CGFloat - leftInset = 60.0 + params.leftInset + leftInset = params.leftInset + (item.isSelected != nil ? 60.0 : 16.0) - var controlFrame = self.editableControlNode.frame - controlFrame.origin.x = params.leftInset + 6.0 + revealOffset - transition.updateFrame(node: self.editableControlNode, frame: controlFrame) + if let checkNode = self.checkNode { + var checkNodeFrame = checkNode.frame + checkNodeFrame.origin.x = params.leftInset + 11.0 + revealOffset + transition.updateFrame(node: checkNode, frame: checkNodeFrame) + } var reorderFrame = self.reorderControlNode.frame reorderFrame.origin.x = params.width + revealOffset - params.rightInset - reorderFrame.width @@ -436,7 +504,7 @@ class CreatePollOptionItemNode: ItemListRevealOptionsItemNode, ItemListItemNode, } override func isReorderable(at point: CGPoint) -> Bool { - if self.reorderControlNode.frame.contains(point), !self.isDisplayingRevealedOptions { + if self.reorderControlNode.frame.contains(point), !self.reorderControlNode.isHidden, !self.isDisplayingRevealedOptions { return true } return false @@ -448,5 +516,16 @@ class CreatePollOptionItemNode: ItemListRevealOptionsItemNode, ItemListItemNode, var separatorFrame = self.bottomStripeNode.frame separatorFrame.origin.y = currentValue - UIScreenPixel self.bottomStripeNode.frame = separatorFrame + + self.containerNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: self.containerNode.bounds.width, height: currentValue)) + + let insets = self.insets + let separatorHeight = UIScreenPixel + guard let params = self.layoutParams else { + return + } + + self.backgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: self.containerNode.bounds.width, height: currentValue + min(insets.top, separatorHeight) + min(insets.bottom, separatorHeight))) + self.maskNode.frame = self.backgroundNode.frame.insetBy(dx: params.leftInset, dy: 0.0) } } diff --git a/submodules/ContactsPeerItem/Sources/ContactsPeerItem.swift b/submodules/ContactsPeerItem/Sources/ContactsPeerItem.swift index 7e9ef44c72..183287730b 100644 --- a/submodules/ContactsPeerItem/Sources/ContactsPeerItem.swift +++ b/submodules/ContactsPeerItem/Sources/ContactsPeerItem.swift @@ -122,6 +122,7 @@ public class ContactsPeerItem: ItemListItem, ListViewItemWithHeader { let options: [ItemListPeerItemRevealOption] let actionIcon: ContactsPeerItemActionIcon let action: (ContactsPeerItemPeer) -> Void + let disabledAction: ((ContactsPeerItemPeer) -> Void)? let setPeerIdWithRevealedOptions: ((PeerId?, PeerId?) -> Void)? let deletePeer: ((PeerId) -> Void)? let itemHighlighting: ContactItemHighlighting? @@ -133,7 +134,7 @@ public class ContactsPeerItem: ItemListItem, ListViewItemWithHeader { public let header: ListViewItemHeader? - public init(presentationData: ItemListPresentationData, style: ItemListStyle = .plain, sectionId: ItemListSectionId = 0, sortOrder: PresentationPersonNameOrder, displayOrder: PresentationPersonNameOrder, context: AccountContext, peerMode: ContactsPeerItemPeerMode, peer: ContactsPeerItemPeer, status: ContactsPeerItemStatus, badge: ContactsPeerItemBadge? = nil, enabled: Bool, selection: ContactsPeerItemSelection, editing: ContactsPeerItemEditing, options: [ItemListPeerItemRevealOption] = [], actionIcon: ContactsPeerItemActionIcon = .none, index: PeerNameIndex?, header: ListViewItemHeader?, action: @escaping (ContactsPeerItemPeer) -> Void, setPeerIdWithRevealedOptions: ((PeerId?, PeerId?) -> Void)? = nil, deletePeer: ((PeerId) -> Void)? = nil, itemHighlighting: ContactItemHighlighting? = nil, contextAction: ((ASDisplayNode, ContextGesture?) -> Void)? = nil) { + public init(presentationData: ItemListPresentationData, style: ItemListStyle = .plain, sectionId: ItemListSectionId = 0, sortOrder: PresentationPersonNameOrder, displayOrder: PresentationPersonNameOrder, context: AccountContext, peerMode: ContactsPeerItemPeerMode, peer: ContactsPeerItemPeer, status: ContactsPeerItemStatus, badge: ContactsPeerItemBadge? = nil, enabled: Bool, selection: ContactsPeerItemSelection, editing: ContactsPeerItemEditing, options: [ItemListPeerItemRevealOption] = [], actionIcon: ContactsPeerItemActionIcon = .none, index: PeerNameIndex?, header: ListViewItemHeader?, action: @escaping (ContactsPeerItemPeer) -> Void, disabledAction: ((ContactsPeerItemPeer) -> Void)? = nil, setPeerIdWithRevealedOptions: ((PeerId?, PeerId?) -> Void)? = nil, deletePeer: ((PeerId) -> Void)? = nil, itemHighlighting: ContactItemHighlighting? = nil, contextAction: ((ASDisplayNode, ContextGesture?) -> Void)? = nil) { self.presentationData = presentationData self.style = style self.sectionId = sectionId @@ -150,11 +151,12 @@ public class ContactsPeerItem: ItemListItem, ListViewItemWithHeader { self.options = options self.actionIcon = actionIcon self.action = action + self.disabledAction = disabledAction self.setPeerIdWithRevealedOptions = setPeerIdWithRevealedOptions self.deletePeer = deletePeer self.header = header self.itemHighlighting = itemHighlighting - self.selectable = enabled + self.selectable = enabled || disabledAction != nil self.contextAction = contextAction if let index = index { @@ -245,7 +247,12 @@ public class ContactsPeerItem: ItemListItem, ListViewItemWithHeader { } public func selected(listView: ListView) { - self.action(self.peer) + if self.enabled { + self.action(self.peer) + } else { + listView.clearHighlightAnimated(true) + self.disabledAction?(self.peer) + } } static func mergeType(item: ContactsPeerItem, previousItem: ListViewItem?, nextItem: ListViewItem?) -> (first: Bool, last: Bool, firstWithHeader: Bool) { diff --git a/submodules/DirectionalPanGesture/Sources/DirectionalPanGestureRecognizer.swift b/submodules/DirectionalPanGesture/Sources/DirectionalPanGestureRecognizer.swift index 74ec8d23ca..ce7a0fc69b 100644 --- a/submodules/DirectionalPanGesture/Sources/DirectionalPanGestureRecognizer.swift +++ b/submodules/DirectionalPanGesture/Sources/DirectionalPanGestureRecognizer.swift @@ -5,6 +5,8 @@ public class DirectionalPanGestureRecognizer: UIPanGestureRecognizer { private var validatedGesture = false private var firstLocation: CGPoint = CGPoint() + public var shouldBegin: ((CGPoint) -> Bool)? + override public init(target: Any?, action: Selector?) { super.init(target: target, action: action) @@ -21,7 +23,13 @@ public class DirectionalPanGestureRecognizer: UIPanGestureRecognizer { super.touchesBegan(touches, with: event) let touch = touches.first! - self.firstLocation = touch.location(in: self.view) + let point = touch.location(in: self.view) + if let shouldBegin = self.shouldBegin, !shouldBegin(point) { + self.state = .failed + return + } + + self.firstLocation = point if let target = self.view?.hitTest(self.firstLocation, with: event) { if target == self.view { diff --git a/submodules/Display/Display/ImageCorners.swift b/submodules/Display/Display/ImageCorners.swift index ce45a9565a..948a4d385d 100644 --- a/submodules/Display/Display/ImageCorners.swift +++ b/submodules/Display/Display/ImageCorners.swift @@ -109,7 +109,6 @@ private func ==(lhs: Tail, rhs: Tail) -> Bool { } private var cachedCorners = Atomic<[Corner: DrawingContext]>(value: [:]) -private var cachedTails = Atomic<[Tail: DrawingContext]>(value: [:]) private func cornerContext(_ corner: Corner) -> DrawingContext { let cached: DrawingContext? = cachedCorners.with { @@ -122,20 +121,22 @@ private func cornerContext(_ corner: Corner) -> DrawingContext { let context = DrawingContext(size: CGSize(width: CGFloat(corner.radius), height: CGFloat(corner.radius)), clear: true) context.withContext { c in - c.setBlendMode(.copy) + c.clear(CGRect(origin: CGPoint(), size: CGSize(width: CGFloat(corner.radius), height: CGFloat(corner.radius)))) c.setFillColor(UIColor.black.cgColor) - let rect: CGRect switch corner { - case let .TopLeft(radius): - rect = CGRect(origin: CGPoint(), size: CGSize(width: CGFloat(radius << 1), height: CGFloat(radius << 1))) - case let .TopRight(radius): - rect = CGRect(origin: CGPoint(x: -CGFloat(radius), y: 0.0), size: CGSize(width: CGFloat(radius << 1), height: CGFloat(radius << 1))) - case let .BottomLeft(radius): - rect = CGRect(origin: CGPoint(x: 0.0, y: -CGFloat(radius)), size: CGSize(width: CGFloat(radius << 1), height: CGFloat(radius << 1))) - case let .BottomRight(radius): - rect = CGRect(origin: CGPoint(x: -CGFloat(radius), y: -CGFloat(radius)), size: CGSize(width: CGFloat(radius << 1), height: CGFloat(radius << 1))) + case let .TopLeft(radius): + let rect = CGRect(origin: CGPoint(), size: CGSize(width: CGFloat(radius * 2), height: CGFloat(radius * 2))) + c.fillEllipse(in: rect) + case let .TopRight(radius): + let rect = CGRect(origin: CGPoint(x: -CGFloat(radius), y: 0.0), size: CGSize(width: CGFloat(radius * 2), height: CGFloat(radius * 2))) + c.fillEllipse(in: rect) + case let .BottomLeft(radius): + let rect = CGRect(origin: CGPoint(x: 0.0, y: -CGFloat(radius)), size: CGSize(width: CGFloat(radius * 2), height: CGFloat(radius * 2))) + c.fillEllipse(in: rect) + case let .BottomRight(radius): + let rect = CGRect(origin: CGPoint(x: -CGFloat(radius), y: -CGFloat(radius)), size: CGSize(width: CGFloat(radius * 2), height: CGFloat(radius * 2))) + c.fillEllipse(in: rect) } - c.fillEllipse(in: rect) } let _ = cachedCorners.modify { current in @@ -148,62 +149,6 @@ private func cornerContext(_ corner: Corner) -> DrawingContext { } } -private func tailContext(_ tail: Tail) -> DrawingContext { - let cached: DrawingContext? = cachedTails.with { - return $0[tail] - } - - if let cached = cached { - return cached - } else { - let context = DrawingContext(size: CGSize(width: CGFloat(tail.radius) + 3.0, height: CGFloat(tail.radius)), clear: true) - - context.withContext { c in - c.setBlendMode(.copy) - c.setFillColor(UIColor.black.cgColor) - let rect: CGRect - switch tail { - case let .BottomLeft(radius): - rect = CGRect(origin: CGPoint(x: 3.0, y: -CGFloat(radius)), size: CGSize(width: CGFloat(radius << 1), height: CGFloat(radius << 1))) - - c.move(to: CGPoint(x: 3.0, y: 1.0)) - c.addLine(to: CGPoint(x: 3.0, y: 11.0)) - c.addLine(to: CGPoint(x: 2.3, y: 13.0)) - c.addLine(to: CGPoint(x: 0.0, y: 16.6)) - c.addLine(to: CGPoint(x: 4.5, y: 15.5)) - c.addLine(to: CGPoint(x: 6.5, y: 14.3)) - c.addLine(to: CGPoint(x: 9.0, y: 12.5)) - c.closePath() - c.fillPath() - case let .BottomRight(radius): - rect = CGRect(origin: CGPoint(x: 3.0, y: -CGFloat(radius)), size: CGSize(width: CGFloat(radius << 1), height: CGFloat(radius << 1))) - - c.translateBy(x: context.size.width / 2.0, y: context.size.height / 2.0) - c.scaleBy(x: -1.0, y: 1.0) - c.translateBy(x: -context.size.width / 2.0, y: -context.size.height / 2.0) - - c.move(to: CGPoint(x: 3.0, y: 1.0)) - c.addLine(to: CGPoint(x: 3.0, y: 11.0)) - c.addLine(to: CGPoint(x: 2.3, y: 13.0)) - c.addLine(to: CGPoint(x: 0.0, y: 16.6)) - c.addLine(to: CGPoint(x: 4.5, y: 15.5)) - c.addLine(to: CGPoint(x: 6.5, y: 14.3)) - c.addLine(to: CGPoint(x: 9.0, y: 12.5)) - c.closePath() - c.fillPath() - } - c.fillEllipse(in: rect) - } - - let _ = cachedTails.modify { current in - var current = current - current[tail] = context - return current - } - return context - } -} - public func addCorners(_ context: DrawingContext, arguments: TransformImageArguments) { let corners = arguments.corners let drawingRect = arguments.drawingRect @@ -223,23 +168,24 @@ public func addCorners(_ context: DrawingContext, arguments: TransformImageArgum let corner = cornerContext(.BottomLeft(Int(radius))) context.blt(corner, at: CGPoint(x: drawingRect.minX, y: drawingRect.maxY - radius)) } - case let .Tail(radius, enabled): + case let .Tail(radius, image): if radius > CGFloat.ulpOfOne { - if enabled { - let tail = tailContext(.BottomLeft(Int(radius))) - let color = context.colorAt(CGPoint(x: drawingRect.minX, y: drawingRect.maxY - 1.0)) - context.withContext { c in - c.clear(CGRect(x: drawingRect.minX - 3.0, y: 0.0, width: 3.0, height: drawingRect.maxY - 6.0)) - c.setFillColor(color.cgColor) - c.fill(CGRect(x: 0.0, y: drawingRect.maxY - 6.0, width: 3.0, height: 6.0)) - } - context.blt(tail, at: CGPoint(x: drawingRect.minX - 3.0, y: drawingRect.maxY - radius)) - } else { - let corner = cornerContext(.BottomLeft(Int(radius))) - context.blt(corner, at: CGPoint(x: drawingRect.minX, y: drawingRect.maxY - radius)) + let color = context.colorAt(CGPoint(x: drawingRect.minX, y: drawingRect.maxY - 1.0)) + context.withContext { c in + c.clear(CGRect(x: drawingRect.minX - 4.0, y: 0.0, width: 4.0, height: drawingRect.maxY - 6.0)) + c.setFillColor(color.cgColor) + c.fill(CGRect(x: 0.0, y: drawingRect.maxY - 7.0, width: 4.0, height: 7.0)) + c.setBlendMode(.destinationIn) + let cornerRect = CGRect(origin: CGPoint(x: drawingRect.minX - 6.0, y: drawingRect.maxY - image.size.height), size: image.size) + c.translateBy(x: cornerRect.midX, y: cornerRect.midY) + c.scaleBy(x: 1.0, y: -1.0) + c.translateBy(x: -cornerRect.midX, y: -cornerRect.midY) + c.draw(image.cgImage!, in: cornerRect) + c.translateBy(x: cornerRect.midX, y: cornerRect.midY) + c.scaleBy(x: 1.0, y: -1.0) + c.translateBy(x: -cornerRect.midX, y: -cornerRect.midY) } } - } switch corners.bottomRight { @@ -248,20 +194,22 @@ public func addCorners(_ context: DrawingContext, arguments: TransformImageArgum let corner = cornerContext(.BottomRight(Int(radius))) context.blt(corner, at: CGPoint(x: drawingRect.maxX - radius, y: drawingRect.maxY - radius)) } - case let .Tail(radius, enabled): + case let .Tail(radius, image): if radius > CGFloat.ulpOfOne { - if enabled { - let tail = tailContext(.BottomRight(Int(radius))) - let color = context.colorAt(CGPoint(x: drawingRect.maxX - 1.0, y: drawingRect.maxY - 1.0)) - context.withContext { c in - c.clear(CGRect(x: drawingRect.maxX, y: 0.0, width: 3.0, height: drawingRect.maxY - 6.0)) - c.setFillColor(color.cgColor) - c.fill(CGRect(x: drawingRect.maxX, y: drawingRect.maxY - 6.0, width: 3.0, height: 6.0)) - } - context.blt(tail, at: CGPoint(x: drawingRect.maxX - radius, y: drawingRect.maxY - radius)) - } else { - let corner = cornerContext(.BottomRight(Int(radius))) - context.blt(corner, at: CGPoint(x: drawingRect.maxX - radius, y: drawingRect.maxY - radius)) + let color = context.colorAt(CGPoint(x: drawingRect.maxX - 1.0, y: drawingRect.maxY - 1.0)) + context.withContext { c in + c.clear(CGRect(x: drawingRect.maxX, y: 0.0, width: 4.0, height: drawingRect.maxY - image.size.height)) + c.setFillColor(color.cgColor) + c.fill(CGRect(x: drawingRect.maxX, y: drawingRect.maxY - 7.0, width: 5.0, height: 7.0)) + c.setBlendMode(.destinationIn) + let cornerRect = CGRect(origin: CGPoint(x: drawingRect.maxX - image.size.width + 6.0, y: drawingRect.maxY - image.size.height), size: image.size) + c.translateBy(x: cornerRect.midX, y: cornerRect.midY) + c.scaleBy(x: 1.0, y: -1.0) + c.translateBy(x: -cornerRect.midX, y: -cornerRect.midY) + c.draw(image.cgImage!, in: cornerRect) + c.translateBy(x: cornerRect.midX, y: cornerRect.midY) + c.scaleBy(x: 1.0, y: -1.0) + c.translateBy(x: -cornerRect.midX, y: -cornerRect.midY) } } } diff --git a/submodules/Display/Display/ImageNode.swift b/submodules/Display/Display/ImageNode.swift index 99192e4895..87422abbc6 100644 --- a/submodules/Display/Display/ImageNode.swift +++ b/submodules/Display/Display/ImageNode.swift @@ -8,12 +8,12 @@ private let dispatcher = displayLinkDispatcher public enum ImageCorner: Equatable { case Corner(CGFloat) - case Tail(CGFloat, Bool) + case Tail(CGFloat, UIImage) public var extendedInsets: CGSize { switch self { case .Tail: - return CGSize(width: 3.0, height: 0.0) + return CGSize(width: 4.0, height: 0.0) default: return CGSize() } @@ -36,15 +36,6 @@ public enum ImageCorner: Equatable { return radius } } - - public func scaledBy(_ scale: CGFloat) -> ImageCorner { - switch self { - case let .Corner(radius): - return .Corner(radius * scale) - case let .Tail(radius, enabled): - return .Tail(radius * scale, enabled) - } - } } public func ==(lhs: ImageCorner, rhs: ImageCorner) -> Bool { @@ -56,8 +47,8 @@ public func ==(lhs: ImageCorner, rhs: ImageCorner) -> Bool { default: return false } - case let .Tail(lhsRadius, lhsEnabled): - if case let .Tail(rhsRadius, rhsEnabled) = rhs, lhsRadius.isEqual(to: rhsRadius), lhsEnabled == rhsEnabled { + case let .Tail(lhsRadius, lhsImage): + if case let .Tail(rhsRadius, rhsImage) = rhs, lhsRadius.isEqual(to: rhsRadius), lhsImage === rhsImage { return true } else { return false @@ -124,10 +115,6 @@ public struct ImageCorners: Equatable { public func withRemovedTails() -> ImageCorners { return ImageCorners(topLeft: self.topLeft.withoutTail, topRight: self.topRight.withoutTail, bottomLeft: self.bottomLeft.withoutTail, bottomRight: self.bottomRight.withoutTail) } - - public func scaledBy(_ scale: CGFloat) -> ImageCorners { - return ImageCorners(topLeft: self.topLeft.scaledBy(scale), topRight: self.topRight.scaledBy(scale), bottomLeft: self.bottomLeft.scaledBy(scale), bottomRight: self.bottomRight.scaledBy(scale)) - } } public func ==(lhs: ImageCorners, rhs: ImageCorners) -> Bool { diff --git a/submodules/Display/Display/ListView.swift b/submodules/Display/Display/ListView.swift index 5330a53cb7..964435ecb7 100644 --- a/submodules/Display/Display/ListView.swift +++ b/submodules/Display/Display/ListView.swift @@ -435,6 +435,10 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture } private func endReordering() { + self.itemReorderingTimer?.invalidate() + self.itemReorderingTimer = nil + self.lastReorderingOffset = nil + let f: () -> Void = { [weak self] in guard let strongSelf = self else { return @@ -467,11 +471,32 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture } } - private func checkItemReordering() { - if let reorderNode = self.reorderNode, let reorderItemNode = reorderNode.itemNode, let reorderItemIndex = reorderItemNode.index, reorderItemNode.supernode == self { - guard let verticalTopOffset = reorderNode.currentOffset() else { - return - } + private var itemReorderingTimer: SwiftSignalKit.Timer? + private var lastReorderingOffset: CGFloat? + + private func checkItemReordering(force: Bool = false) { + guard let reorderNode = self.reorderNode, let verticalTopOffset = reorderNode.currentOffset() else { + return + } + + if let lastReorderingOffset = self.lastReorderingOffset, abs(lastReorderingOffset - verticalTopOffset) < 4.0 && !force { + return + } + + self.itemReorderingTimer?.invalidate() + self.itemReorderingTimer = nil + + self.lastReorderingOffset = verticalTopOffset + + if !force { + self.itemReorderingTimer = SwiftSignalKit.Timer(timeout: 0.025, repeat: false, completion: { [weak self] in + self?.checkItemReordering(force: true) + }, queue: Queue.mainQueue()) + self.itemReorderingTimer?.start() + return + } + + if let reorderItemNode = reorderNode.itemNode, let reorderItemIndex = reorderItemNode.index, reorderItemNode.supernode == self { let verticalOffset = verticalTopOffset var closestIndex: (Int, CGFloat)? for i in 0 ..< self.itemNodes.count { @@ -2011,7 +2036,11 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture node.addHeightAnimation(0.0, duration: insertionAnimationDuration * UIView.animationDurationFactor(), beginAt: timestamp) } if node.animationForKey("apparentHeight") == nil || !(node is ListViewTempItemNode) { - node.addApparentHeightAnimation(0.0, duration: insertionAnimationDuration * UIView.animationDurationFactor(), beginAt: timestamp) + node.addApparentHeightAnimation(0.0, duration: insertionAnimationDuration * UIView.animationDurationFactor(), beginAt: timestamp, update: { [weak node] progress, currentValue in + if let node = node { + node.animateFrameTransition(progress, currentValue) + } + }) } node.animateRemoved(timestamp, duration: insertionAnimationDuration * UIView.animationDurationFactor()) } else if animated { @@ -2035,8 +2064,9 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture } } else { if !nodeFrame.size.height.isEqual(to: node.apparentHeight) { + let addAnimation = previousFrame?.height != nodeFrame.size.height node.addApparentHeightAnimation(nodeFrame.size.height, duration: insertionAnimationDuration * UIView.animationDurationFactor(), beginAt: timestamp, update: { [weak node] progress, currentValue in - if let node = node { + if let node = node, addAnimation { node.animateFrameTransition(progress, currentValue) } }) @@ -3007,21 +3037,25 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture let flashing = self.headerItemsAreFlashing() - let addHeader: (_ id: Int64, _ upperBound: CGFloat, _ lowerBound: CGFloat, _ item: ListViewItemHeader, _ hasValidNodes: Bool) -> Void = { id, upperBound, lowerBound, item, hasValidNodes in + func addHeader(id: Int64, upperBound: CGFloat, upperBoundEdge: CGFloat, lowerBound: CGFloat, item: ListViewItemHeader, hasValidNodes: Bool) { let itemHeaderHeight: CGFloat = item.height let headerFrame: CGRect let stickLocationDistanceFactor: CGFloat let stickLocationDistance: CGFloat switch item.stickDirection { - case .top: - headerFrame = CGRect(origin: CGPoint(x: 0.0, y: min(max(upperDisplayBound, upperBound), lowerBound - itemHeaderHeight)), size: CGSize(width: self.visibleSize.width, height: itemHeaderHeight)) - stickLocationDistance = headerFrame.minY - upperBound - stickLocationDistanceFactor = max(0.0, min(1.0, stickLocationDistance / itemHeaderHeight)) - case .bottom: - headerFrame = CGRect(origin: CGPoint(x: 0.0, y: max(upperBound, min(lowerBound, lowerDisplayBound) - itemHeaderHeight)), size: CGSize(width: self.visibleSize.width, height: itemHeaderHeight)) - stickLocationDistance = lowerBound - headerFrame.maxY - stickLocationDistanceFactor = max(0.0, min(1.0, stickLocationDistance / itemHeaderHeight)) + case .top: + headerFrame = CGRect(origin: CGPoint(x: 0.0, y: min(max(upperDisplayBound, upperBound), lowerBound - itemHeaderHeight)), size: CGSize(width: self.visibleSize.width, height: itemHeaderHeight)) + stickLocationDistance = headerFrame.minY - upperBound + stickLocationDistanceFactor = max(0.0, min(1.0, stickLocationDistance / itemHeaderHeight)) + case .topEdge: + headerFrame = CGRect(origin: CGPoint(x: 0.0, y: min(max(upperDisplayBound, upperBoundEdge - itemHeaderHeight), lowerBound - itemHeaderHeight)), size: CGSize(width: self.visibleSize.width, height: itemHeaderHeight)) + stickLocationDistance = headerFrame.minY - upperBoundEdge + itemHeaderHeight + stickLocationDistanceFactor = max(0.0, min(1.0, stickLocationDistance / itemHeaderHeight)) + case .bottom: + headerFrame = CGRect(origin: CGPoint(x: 0.0, y: max(upperBound, min(lowerBound, lowerDisplayBound) - itemHeaderHeight)), size: CGSize(width: self.visibleSize.width, height: itemHeaderHeight)) + stickLocationDistance = lowerBound - headerFrame.maxY + stickLocationDistanceFactor = max(0.0, min(1.0, stickLocationDistance / itemHeaderHeight)) } visibleHeaderNodes.insert(id) if let headerNode = self.itemHeaderNodes[id] { @@ -3092,31 +3126,32 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture } } - var previousHeader: (Int64, CGFloat, CGFloat, ListViewItemHeader, Bool)? + var previousHeader: (id: Int64, upperBound: CGFloat, upperBoundEdge: CGFloat, lowerBound: CGFloat, item: ListViewItemHeader, hasValidNodes: Bool)? for itemNode in self.itemNodes { let itemFrame = itemNode.apparentFrame + let itemTopInset = itemNode.insets.top if let itemHeader = itemNode.header() { - if let (previousHeaderId, previousUpperBound, previousLowerBound, previousHeaderItem, hasValidNodes) = previousHeader { + if let (previousHeaderId, previousUpperBound, previousUpperBoundEdge, previousLowerBound, previousHeaderItem, hasValidNodes) = previousHeader { if previousHeaderId == itemHeader.id { - previousHeader = (previousHeaderId, previousUpperBound, itemFrame.maxY, previousHeaderItem, hasValidNodes || itemNode.index != nil) + previousHeader = (previousHeaderId, previousUpperBound, previousUpperBoundEdge, itemFrame.maxY, previousHeaderItem, hasValidNodes || itemNode.index != nil) } else { - addHeader(previousHeaderId, previousUpperBound, previousLowerBound, previousHeaderItem, hasValidNodes) + addHeader(id: previousHeaderId, upperBound: previousUpperBound, upperBoundEdge: previousUpperBoundEdge, lowerBound: previousLowerBound, item: previousHeaderItem, hasValidNodes: hasValidNodes) - previousHeader = (itemHeader.id, itemFrame.minY, itemFrame.maxY, itemHeader, itemNode.index != nil) + previousHeader = (itemHeader.id, itemFrame.minY, itemFrame.minY + itemTopInset, itemFrame.maxY, itemHeader, itemNode.index != nil) } } else { - previousHeader = (itemHeader.id, itemFrame.minY, itemFrame.maxY, itemHeader, itemNode.index != nil) + previousHeader = (itemHeader.id, itemFrame.minY, itemFrame.minY + itemTopInset, itemFrame.maxY, itemHeader, itemNode.index != nil) } } else { - if let (previousHeaderId, previousUpperBound, previousLowerBound, previousHeaderItem, hasValidNodes) = previousHeader { - addHeader(previousHeaderId, previousUpperBound, previousLowerBound, previousHeaderItem, hasValidNodes) + if let (previousHeaderId, previousUpperBound, previousUpperBoundEdge, previousLowerBound, previousHeaderItem, hasValidNodes) = previousHeader { + addHeader(id: previousHeaderId, upperBound: previousUpperBound, upperBoundEdge: previousUpperBoundEdge, lowerBound: previousLowerBound, item: previousHeaderItem, hasValidNodes: hasValidNodes) } previousHeader = nil } } - if let (previousHeaderId, previousUpperBound, previousLowerBound, previousHeaderItem, hasValidNodes) = previousHeader { - addHeader(previousHeaderId, previousUpperBound, previousLowerBound, previousHeaderItem, hasValidNodes) + if let (previousHeaderId, previousUpperBound, previousUpperBoundEdge, previousLowerBound, previousHeaderItem, hasValidNodes) = previousHeader { + addHeader(id: previousHeaderId, upperBound: previousUpperBound, upperBoundEdge: previousUpperBoundEdge, lowerBound: previousLowerBound, item: previousHeaderItem, hasValidNodes: hasValidNodes) } let currentIds = Set(self.itemHeaderNodes.keys) diff --git a/submodules/Display/Display/ListViewItemHeader.swift b/submodules/Display/Display/ListViewItemHeader.swift index 1c985bc621..fb288b3187 100644 --- a/submodules/Display/Display/ListViewItemHeader.swift +++ b/submodules/Display/Display/ListViewItemHeader.swift @@ -4,6 +4,7 @@ import AsyncDisplayKit public enum ListViewItemHeaderStickDirection { case top + case topEdge case bottom } diff --git a/submodules/Display/Display/ListViewItemNode.swift b/submodules/Display/Display/ListViewItemNode.swift index a2a920f641..608580a667 100644 --- a/submodules/Display/Display/ListViewItemNode.swift +++ b/submodules/Display/Display/ListViewItemNode.swift @@ -564,6 +564,6 @@ open class ListViewItemNode: ASDisplayNode { } open func snapshotForReordering() -> UIView? { - return self.view.snapshotContentTree() + return self.view.snapshotContentTree(keepTransform: true) } } diff --git a/submodules/Display/Display/Navigation/NavigationModalContainer.swift b/submodules/Display/Display/Navigation/NavigationModalContainer.swift index 65c7d2233c..0f3f0dee97 100644 --- a/submodules/Display/Display/Navigation/NavigationModalContainer.swift +++ b/submodules/Display/Display/Navigation/NavigationModalContainer.swift @@ -131,6 +131,16 @@ final class NavigationModalContainer: ASDisplayNode, UIScrollViewDelegate, UIGes return false } + private func checkInteractiveDismissWithControllers() -> Bool { + if let controller = self.container.controllers.last { + if !controller.attemptNavigation({ + }) { + return false + } + } + return true + } + @objc private func panGesture(_ recognizer: UIPanGestureRecognizer) { switch recognizer.state { case .began: @@ -147,7 +157,7 @@ final class NavigationModalContainer: ASDisplayNode, UIScrollViewDelegate, UIGes let progress = translation / self.bounds.width let velocity = recognizer.velocity(in: self.view).x - if velocity > 1000 || progress > 0.2 { + if (velocity > 1000 || progress > 0.2) && self.checkInteractiveDismissWithControllers() { self.isDismissed = true self.horizontalDismissOffset = self.bounds.width self.dismissProgress = 1.0 @@ -243,7 +253,7 @@ final class NavigationModalContainer: ASDisplayNode, UIScrollViewDelegate, UIGes let duration = Double(min(0.3, velocityFactor)) let transition: ContainedViewLayoutTransition let dismissProgress: CGFloat - if velocity.y < -0.5 || progress >= 0.5 { + if (velocity.y < -0.5 || progress >= 0.5) && self.checkInteractiveDismissWithControllers() { dismissProgress = 1.0 targetOffset = 0.0 transition = .animated(duration: duration, curve: .easeInOut) diff --git a/submodules/Display/Display/TextAlertController.swift b/submodules/Display/Display/TextAlertController.swift index c6369e1d7b..d1d4d3f99e 100644 --- a/submodules/Display/Display/TextAlertController.swift +++ b/submodules/Display/Display/TextAlertController.swift @@ -360,8 +360,8 @@ public func standardTextAlertController(theme: AlertControllerTheme, title: Stri var dismissImpl: (() -> Void)? let attributedText: NSAttributedString if parseMarkdown { - let font = title == nil ? Font.semibold(theme.baseFontSize) : Font.regular(floor(theme.baseFontSize * 13.0 / 17.0)) - let boldFont = title == nil ? Font.bold(theme.baseFontSize) : Font.semibold(floor(theme.baseFontSize * 13.0 / 17.0)) + let font = title == nil ? Font.semibold(theme.baseFontSize * 13.0 / 17.0) : Font.regular(floor(theme.baseFontSize * 13.0 / 17.0)) + let boldFont = title == nil ? Font.bold(theme.baseFontSize * 13.0 / 17.0) : Font.semibold(floor(theme.baseFontSize * 13.0 / 17.0)) let body = MarkdownAttributeSet(font: font, textColor: theme.primaryColor) let bold = MarkdownAttributeSet(font: boldFont, textColor: theme.primaryColor) attributedText = parseMarkdownIntoAttributedString(text, attributes: MarkdownAttributes(body: body, bold: bold, link: body, linkAttribute: { _ in nil }), textAlignment: .center) diff --git a/submodules/Display/Display/TooltipController.swift b/submodules/Display/Display/TooltipController.swift index f1c103fbcb..34c320883d 100644 --- a/submodules/Display/Display/TooltipController.swift +++ b/submodules/Display/Display/TooltipController.swift @@ -185,6 +185,7 @@ open class TooltipController: ViewController, StandalonePresentableController { open func dismissImmediately() { self.dismissed?(false) + self.controllerNode.hide() self.presentingViewController?.dismiss(animated: false) } } diff --git a/submodules/Display/Display/TooltipControllerNode.swift b/submodules/Display/Display/TooltipControllerNode.swift index 62b790ca8f..ba0650c6ee 100644 --- a/submodules/Display/Display/TooltipControllerNode.swift +++ b/submodules/Display/Display/TooltipControllerNode.swift @@ -126,6 +126,10 @@ final class TooltipControllerNode: ASDisplayNode { }) } + func hide() { + self.containerNode.alpha = 0.0 + } + override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { if let event = event { var eventIsPresses = false diff --git a/submodules/Display/Display/WindowContent.swift b/submodules/Display/Display/WindowContent.swift index 868b94b6eb..cbf67d23e9 100644 --- a/submodules/Display/Display/WindowContent.swift +++ b/submodules/Display/Display/WindowContent.swift @@ -337,8 +337,17 @@ public class Window1 { self?.isInteractionBlocked = value } + let updateOpaqueOverlays: () -> Void = { [weak self] in + guard let strongSelf = self else { + return + } + strongSelf._rootController?.displayNode.accessibilityElementsHidden = strongSelf.presentationContext.hasOpaqueOverlay || strongSelf.topPresentationContext.hasOpaqueOverlay + } self.presentationContext.updateHasOpaqueOverlay = { [weak self] value in - self?._rootController?.displayNode.accessibilityElementsHidden = value + updateOpaqueOverlays() + } + self.topPresentationContext.updateHasOpaqueOverlay = { [weak self] value in + updateOpaqueOverlays() } self.hostView.present = { [weak self] controller, level, blockInteraction, completion in @@ -1222,10 +1231,12 @@ public class Window1 { return hidden } - public func forEachViewController(_ f: (ContainableController) -> Bool) { + public func forEachViewController(_ f: (ContainableController) -> Bool, excludeNavigationSubControllers: Bool = false) { if let navigationController = self._rootController as? NavigationController { - for case let controller as ContainableController in navigationController.viewControllers { - !f(controller) + if !excludeNavigationSubControllers { + for case let controller as ContainableController in navigationController.viewControllers { + !f(controller) + } } if let controller = navigationController.topOverlayController { !f(controller) diff --git a/submodules/GalleryUI/Sources/GalleryController.swift b/submodules/GalleryUI/Sources/GalleryController.swift index 53b10ecb22..bdc0a7b29c 100644 --- a/submodules/GalleryUI/Sources/GalleryController.swift +++ b/submodules/GalleryUI/Sources/GalleryController.swift @@ -396,7 +396,7 @@ public class GalleryController: ViewController, StandalonePresentableController } else { namespaces = .not(Namespaces.Message.allScheduled) } - return context.account.postbox.aroundMessageHistoryViewForLocation(.peer(message!.id.peerId), anchor: .index(message!.index), count: 50, fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: [], tagMask: tags, namespaces: namespaces, orderStatistics: [.combinedLocation]) + return context.account.postbox.aroundMessageHistoryViewForLocation(.peer(message!.id.peerId), anchor: .index(message!.index), count: 50, clipHoles: false, fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: [], tagMask: tags, namespaces: namespaces, orderStatistics: [.combinedLocation]) |> mapToSignal { (view, _, _) -> Signal in let mapped = GalleryMessageHistoryView.view(view) return .single(mapped) diff --git a/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift b/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift index 75fafb1291..183bce8efd 100644 --- a/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift +++ b/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift @@ -238,7 +238,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { } self.statusButtonNode.addSubnode(self.statusNode) - self.statusButtonNode.addTarget(self, action: #selector(statusButtonPressed), forControlEvents: .touchUpInside) + self.statusButtonNode.addTarget(self, action: #selector(self.statusButtonPressed), forControlEvents: .touchUpInside) self.addSubnode(self.statusButtonNode) diff --git a/submodules/GalleryUI/Sources/SecretMediaPreviewController.swift b/submodules/GalleryUI/Sources/SecretMediaPreviewController.swift index 4a2df1e638..3c36aeaa18 100644 --- a/submodules/GalleryUI/Sources/SecretMediaPreviewController.swift +++ b/submodules/GalleryUI/Sources/SecretMediaPreviewController.swift @@ -122,6 +122,7 @@ private final class SecretMediaPreviewControllerNode: GalleryControllerNode { public final class SecretMediaPreviewController: ViewController { private let context: AccountContext + private let messageId: MessageId private let _ready = Promise() override public var ready: Promise { @@ -150,6 +151,7 @@ public final class SecretMediaPreviewController: ViewController { public init(context: AccountContext, messageId: MessageId) { self.context = context + self.messageId = messageId self.presentationData = context.sharedContext.currentPresentationData.with { $0 } super.init(navigationBarPresentationData: NavigationBarPresentationData(theme: GalleryController.darkNavigationTheme, strings: NavigationBarStrings(presentationStrings: self.presentationData.strings))) @@ -159,8 +161,6 @@ public final class SecretMediaPreviewController: ViewController { self.statusBar.statusBarStyle = .White - - self.disposable.set((context.account.postbox.messageView(messageId) |> deliverOnMainQueue).start(next: { [weak self] view in if let strongSelf = self { strongSelf.messageView = view @@ -178,17 +178,6 @@ public final class SecretMediaPreviewController: ViewController { return nil } }) - - self.screenCaptureEventsDisposable = (screenCaptureEvents() - |> deliverOnMainQueue).start(next: { [weak self] _ in - if let strongSelf = self, strongSelf.traceVisibility() { - if messageId.peerId.namespace == Namespaces.Peer.CloudUser { - let _ = enqueueMessages(account: context.account, peerId: messageId.peerId, messages: [.message(text: "", attributes: [], mediaReference: .standalone(media: TelegramMediaAction(action: TelegramMediaActionType.historyScreenshot)), replyToMessageId: nil, localGroupingKey: nil)]).start() - } else if messageId.peerId.namespace == Namespaces.Peer.SecretChat { - let _ = addSecretChatMessageScreenshot(account: context.account, peerId: messageId.peerId).start() - } - } - }) } required public init(coder aDecoder: NSCoder) { @@ -348,6 +337,19 @@ public final class SecretMediaPreviewController: ViewController { override public func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) + if self.screenCaptureEventsDisposable == nil { + self.screenCaptureEventsDisposable = (screenCaptureEvents() + |> deliverOnMainQueue).start(next: { [weak self] _ in + if let strongSelf = self, strongSelf.traceVisibility() { + if strongSelf.messageId.peerId.namespace == Namespaces.Peer.CloudUser { + let _ = enqueueMessages(account: strongSelf.context.account, peerId: strongSelf.messageId.peerId, messages: [.message(text: "", attributes: [], mediaReference: .standalone(media: TelegramMediaAction(action: TelegramMediaActionType.historyScreenshot)), replyToMessageId: nil, localGroupingKey: nil)]).start() + } else if strongSelf.messageId.peerId.namespace == Namespaces.Peer.SecretChat { + let _ = addSecretChatMessageScreenshot(account: strongSelf.context.account, peerId: strongSelf.messageId.peerId).start() + } + } + }) + } + var nodeAnimatesItself = false if let centralItemNode = self.controllerNode.pager.centralItemNode(), let message = self.messageView?.message { diff --git a/submodules/HashtagSearchUI/Sources/HashtagSearchController.swift b/submodules/HashtagSearchUI/Sources/HashtagSearchController.swift index 5d2b266ad4..a6e14bb87e 100644 --- a/submodules/HashtagSearchUI/Sources/HashtagSearchController.swift +++ b/submodules/HashtagSearchUI/Sources/HashtagSearchController.swift @@ -49,6 +49,7 @@ public final class HashtagSearchController: TelegramBaseController { } let interaction = ChatListNodeInteraction(activateSearch: { }, peerSelected: { peer in + }, disabledPeerSelected: { _ in }, togglePeerSelected: { _ in }, messageSelected: { [weak self] peer, message, _ in if let strongSelf = self { diff --git a/submodules/ItemListPeerActionItem/Sources/ItemListPeerActionItem.swift b/submodules/ItemListPeerActionItem/Sources/ItemListPeerActionItem.swift index 91e9fdf027..2b0988c6d8 100644 --- a/submodules/ItemListPeerActionItem/Sources/ItemListPeerActionItem.swift +++ b/submodules/ItemListPeerActionItem/Sources/ItemListPeerActionItem.swift @@ -20,9 +20,9 @@ public class ItemListPeerActionItem: ListViewItem, ItemListItem { let editing: Bool let height: ItemListPeerActionItemHeight public let sectionId: ItemListSectionId - let action: () -> Void + let action: (() -> Void)? - public init(presentationData: ItemListPresentationData, icon: UIImage?, title: String, alwaysPlain: Bool = false, sectionId: ItemListSectionId, height: ItemListPeerActionItemHeight = .peerList, editing: Bool, action: @escaping () -> Void) { + public init(presentationData: ItemListPresentationData, icon: UIImage?, title: String, alwaysPlain: Bool = false, sectionId: ItemListSectionId, height: ItemListPeerActionItemHeight = .peerList, editing: Bool, action: (() -> Void)?) { self.presentationData = presentationData self.icon = icon self.title = title @@ -79,11 +79,13 @@ public class ItemListPeerActionItem: ListViewItem, ItemListItem { } } - public var selectable: Bool = true + public var selectable: Bool { + return self.action != nil + } public func selected(listView: ListView){ listView.clearHighlightAnimated(true) - self.action() + self.action?() } } @@ -150,15 +152,15 @@ class ItemListPeerActionItemNode: ListViewItemNode { updatedTheme = item.presentationData.theme } let leftInset: CGFloat - let vertcalInset: CGFloat + let verticalInset: CGFloat let verticalOffset: CGFloat switch item.height { case .generic: - vertcalInset = 11.0 + verticalInset = 11.0 verticalOffset = 0.0 leftInset = 59.0 + params.leftInset case .peerList: - vertcalInset = 14.0 + verticalInset = 14.0 verticalOffset = 0.0 leftInset = 65.0 + params.leftInset } @@ -170,7 +172,7 @@ class ItemListPeerActionItemNode: ListViewItemNode { let separatorHeight = UIScreenPixel let insets = itemListNeighborsGroupedInsets(neighbors) - let contentSize = CGSize(width: params.width, height: titleLayout.size.height + vertcalInset * 2.0) + let contentSize = CGSize(width: params.width, height: titleLayout.size.height + verticalInset * 2.0) let layout = ListViewItemNodeLayout(contentSize: contentSize, insets: insets) let layoutSize = layout.size @@ -247,7 +249,7 @@ class ItemListPeerActionItemNode: ListViewItemNode { strongSelf.topStripeNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: layoutSize.width, height: separatorHeight)) transition.updateFrame(node: strongSelf.bottomStripeNode, frame: CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height + bottomStripeOffset), size: CGSize(width: layoutSize.width - bottomStripeInset, height: separatorHeight))) - transition.updateFrame(node: strongSelf.titleNode, frame: CGRect(origin: CGPoint(x: leftInset + editingOffset, y: vertcalInset + verticalOffset), size: titleLayout.size)) + transition.updateFrame(node: strongSelf.titleNode, frame: CGRect(origin: CGPoint(x: leftInset + editingOffset, y: verticalInset + verticalOffset), size: titleLayout.size)) strongSelf.highlightedBackgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -UIScreenPixel), size: CGSize(width: params.width, height: layout.contentSize.height + UIScreenPixel + UIScreenPixel)) } diff --git a/submodules/ItemListPeerItem/Sources/ItemListPeerItem.swift b/submodules/ItemListPeerItem/Sources/ItemListPeerItem.swift index c8018f245a..a40405c95f 100644 --- a/submodules/ItemListPeerItem/Sources/ItemListPeerItem.swift +++ b/submodules/ItemListPeerItem/Sources/ItemListPeerItem.swift @@ -16,6 +16,195 @@ import PeerPresenceStatusManager import ContextUI import AccountContext +private final class ShimmerEffectNode: ASDisplayNode { + private var currentBackgroundColor: UIColor? + private var currentForegroundColor: UIColor? + private let imageNodeContainer: ASDisplayNode + private let imageNode: ASImageNode + + private var absoluteLocation: (CGRect, CGSize)? + private var isCurrentlyInHierarchy = false + private var shouldBeAnimating = false + + override init() { + self.imageNodeContainer = ASDisplayNode() + self.imageNodeContainer.isLayerBacked = true + + self.imageNode = ASImageNode() + self.imageNode.isLayerBacked = true + self.imageNode.displaysAsynchronously = false + self.imageNode.displayWithoutProcessing = true + self.imageNode.contentMode = .scaleToFill + + super.init() + + self.isLayerBacked = true + self.clipsToBounds = true + + self.imageNodeContainer.addSubnode(self.imageNode) + self.addSubnode(self.imageNodeContainer) + } + + override func didEnterHierarchy() { + super.didEnterHierarchy() + + self.isCurrentlyInHierarchy = true + self.updateAnimation() + } + + override func didExitHierarchy() { + super.didExitHierarchy() + + self.isCurrentlyInHierarchy = false + self.updateAnimation() + } + + func update(backgroundColor: UIColor, foregroundColor: UIColor) { + if let currentBackgroundColor = self.currentBackgroundColor, currentBackgroundColor.isEqual(backgroundColor), let currentForegroundColor = self.currentForegroundColor, currentForegroundColor.isEqual(foregroundColor) { + return + } + self.currentBackgroundColor = backgroundColor + self.currentForegroundColor = foregroundColor + + self.imageNode.image = generateImage(CGSize(width: 4.0, height: 320.0), opaque: true, scale: 1.0, rotatedContext: { size, context in + context.setFillColor(backgroundColor.cgColor) + context.fill(CGRect(origin: CGPoint(), size: size)) + + context.clip(to: CGRect(origin: CGPoint(), size: size)) + + let transparentColor = foregroundColor.withAlphaComponent(0.0).cgColor + let peakColor = foregroundColor.cgColor + + var locations: [CGFloat] = [0.0, 0.5, 1.0] + let colors: [CGColor] = [transparentColor, peakColor, transparentColor] + + let colorSpace = CGColorSpaceCreateDeviceRGB() + let gradient = CGGradient(colorsSpace: colorSpace, colors: colors as CFArray, locations: &locations)! + + context.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: 0.0), end: CGPoint(x: 0.0, y: size.height), options: CGGradientDrawingOptions()) + }) + } + + func updateAbsoluteRect(_ rect: CGRect, within containerSize: CGSize) { + if let absoluteLocation = self.absoluteLocation, absoluteLocation.0 == rect && absoluteLocation.1 == containerSize { + return + } + let sizeUpdated = self.absoluteLocation?.1 != containerSize + let frameUpdated = self.absoluteLocation?.0 != rect + self.absoluteLocation = (rect, containerSize) + + if sizeUpdated { + if self.shouldBeAnimating { + self.imageNode.layer.removeAnimation(forKey: "shimmer") + self.addImageAnimation() + } + } + + if frameUpdated { + self.imageNodeContainer.frame = CGRect(origin: CGPoint(x: -rect.minX, y: -rect.minY), size: containerSize) + } + } + + private func updateAnimation() { + let shouldBeAnimating = self.isCurrentlyInHierarchy && self.absoluteLocation != nil + if shouldBeAnimating != self.shouldBeAnimating { + self.shouldBeAnimating = shouldBeAnimating + if shouldBeAnimating { + self.addImageAnimation() + } else { + self.imageNode.layer.removeAnimation(forKey: "shimmer") + } + } + } + + private func addImageAnimation() { + guard let containerSize = self.absoluteLocation?.1 else { + return + } + let gradientHeight: CGFloat = 250.0 + self.imageNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -gradientHeight), size: CGSize(width: containerSize.width, height: gradientHeight)) + let animation = self.imageNode.layer.makeAnimation(from: 0.0 as NSNumber, to: (containerSize.height + gradientHeight) as NSNumber, keyPath: "position.y", timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, duration: 1.3 * 1.0, delay: 0.0, mediaTimingFunction: nil, removeOnCompletion: true, additive: true) + animation.repeatCount = Float.infinity + animation.beginTime = 1.0 + self.imageNode.layer.add(animation, forKey: "shimmer") + } +} + +private final class LoadingShimmerNode: ASDisplayNode { + enum Shape: Equatable { + case circle(CGRect) + case roundedRectLine(startPoint: CGPoint, width: CGFloat, diameter: CGFloat) + } + + private let backgroundNode: ASDisplayNode + private let effectNode: ShimmerEffectNode + private let foregroundNode: ASImageNode + + private var currentShapes: [Shape] = [] + private var currentBackgroundColor: UIColor? + private var currentForegroundColor: UIColor? + private var currentShimmeringColor: UIColor? + private var currentSize = CGSize() + + override init() { + self.backgroundNode = ASDisplayNode() + + self.effectNode = ShimmerEffectNode() + + self.foregroundNode = ASImageNode() + self.foregroundNode.displaysAsynchronously = false + self.foregroundNode.displayWithoutProcessing = true + + super.init() + + self.addSubnode(self.backgroundNode) + self.addSubnode(self.effectNode) + self.addSubnode(self.foregroundNode) + } + + func updateAbsoluteRect(_ rect: CGRect, within containerSize: CGSize) { + self.effectNode.updateAbsoluteRect(rect, within: containerSize) + } + + func update(backgroundColor: UIColor, foregroundColor: UIColor, shimmeringColor: UIColor, shapes: [Shape], size: CGSize) { + if self.currentShapes == shapes, let currentBackgroundColor = self.currentBackgroundColor, currentBackgroundColor.isEqual(backgroundColor), let currentForegroundColor = self.currentForegroundColor, currentForegroundColor.isEqual(foregroundColor), let currentShimmeringColor = self.currentShimmeringColor, currentShimmeringColor.isEqual(shimmeringColor), self.currentSize == size { + return + } + + self.currentBackgroundColor = backgroundColor + self.currentForegroundColor = foregroundColor + self.currentShimmeringColor = shimmeringColor + self.currentShapes = shapes + self.currentSize = size + + self.backgroundNode.backgroundColor = foregroundColor + + self.effectNode.update(backgroundColor: foregroundColor, foregroundColor: shimmeringColor) + + self.foregroundNode.image = generateImage(size, rotatedContext: { size, context in + context.setFillColor(backgroundColor.cgColor) + context.setBlendMode(.copy) + context.fill(CGRect(origin: CGPoint(), size: size)) + + context.setFillColor(UIColor.clear.cgColor) + for shape in shapes { + switch shape { + case let .circle(frame): + context.fillEllipse(in: frame) + case let .roundedRectLine(startPoint, width, diameter): + context.fillEllipse(in: CGRect(origin: startPoint, size: CGSize(width: diameter, height: diameter))) + context.fillEllipse(in: CGRect(origin: CGPoint(x: startPoint.x + width - diameter, y: startPoint.y), size: CGSize(width: diameter, height: diameter))) + context.fill(CGRect(origin: CGPoint(x: startPoint.x + diameter / 2.0, y: startPoint.y), size: CGSize(width: width - diameter, height: diameter))) + } + } + }) + + self.backgroundNode.frame = CGRect(origin: CGPoint(), size: size) + self.foregroundNode.frame = CGRect(origin: CGPoint(), size: size) + self.effectNode.frame = CGRect(origin: CGPoint(), size: size) + } +} + public struct ItemListPeerItemEditing: Equatable { public var editable: Bool public var editing: Bool @@ -107,6 +296,14 @@ public struct ItemListPeerItemRevealOptions { } } +public struct ItemListPeerItemShimmering { + public var alternationIndex: Int + + public init(alternationIndex: Int) { + self.alternationIndex = alternationIndex + } +} + public final class ItemListPeerItem: ListViewItem, ItemListItem { let presentationData: ItemListPresentationData let dateTimeFormat: PresentationDateTimeFormat @@ -135,8 +332,10 @@ public final class ItemListPeerItem: ListViewItem, ItemListItem { let hasTopGroupInset: Bool let noInsets: Bool public let tag: ItemListItemTag? + let header: ListViewItemHeader? + let shimmering: ItemListPeerItemShimmering? - public init(presentationData: ItemListPresentationData, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, context: AccountContext, peer: Peer, height: ItemListPeerItemHeight = .peerList, aliasHandling: ItemListPeerItemAliasHandling = .standard, nameColor: ItemListPeerItemNameColor = .primary, nameStyle: ItemListPeerItemNameStyle = .distinctBold, presence: PeerPresence?, text: ItemListPeerItemText, label: ItemListPeerItemLabel, editing: ItemListPeerItemEditing, revealOptions: ItemListPeerItemRevealOptions? = nil, switchValue: ItemListPeerItemSwitch?, enabled: Bool, selectable: Bool, sectionId: ItemListSectionId, action: (() -> Void)?, setPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void, removePeer: @escaping (PeerId) -> Void, toggleUpdated: ((Bool) -> Void)? = nil, contextAction: ((ASDisplayNode, ContextGesture?) -> Void)? = nil, hasTopStripe: Bool = true, hasTopGroupInset: Bool = true, noInsets: Bool = false, tag: ItemListItemTag? = nil) { + public init(presentationData: ItemListPresentationData, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, context: AccountContext, peer: Peer, height: ItemListPeerItemHeight = .peerList, aliasHandling: ItemListPeerItemAliasHandling = .standard, nameColor: ItemListPeerItemNameColor = .primary, nameStyle: ItemListPeerItemNameStyle = .distinctBold, presence: PeerPresence?, text: ItemListPeerItemText, label: ItemListPeerItemLabel, editing: ItemListPeerItemEditing, revealOptions: ItemListPeerItemRevealOptions? = nil, switchValue: ItemListPeerItemSwitch?, enabled: Bool, selectable: Bool, sectionId: ItemListSectionId, action: (() -> Void)?, setPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void, removePeer: @escaping (PeerId) -> Void, toggleUpdated: ((Bool) -> Void)? = nil, contextAction: ((ASDisplayNode, ContextGesture?) -> Void)? = nil, hasTopStripe: Bool = true, hasTopGroupInset: Bool = true, noInsets: Bool = false, tag: ItemListItemTag? = nil, header: ListViewItemHeader? = nil, shimmering: ItemListPeerItemShimmering? = nil) { self.presentationData = presentationData self.dateTimeFormat = dateTimeFormat self.nameDisplayOrder = nameDisplayOrder @@ -164,12 +363,14 @@ public final class ItemListPeerItem: ListViewItem, ItemListItem { self.hasTopGroupInset = hasTopGroupInset self.noInsets = noInsets self.tag = tag + self.header = header + self.shimmering = shimmering } public func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal?, (ListViewItemApply) -> Void)) -> Void) { async { let node = ItemListPeerItemNode() - let (layout, apply) = node.asyncLayout()(self, params, itemListNeighbors(item: self, topItem: previousItem as? ItemListItem, bottomItem: nextItem as? ItemListItem)) + let (layout, apply) = node.asyncLayout()(self, params, itemListNeighbors(item: self, topItem: previousItem as? ItemListItem, bottomItem: nextItem as? ItemListItem), self.getHeaderAtTop(top: previousItem, bottom: nextItem)) node.contentSize = layout.contentSize node.insets = layout.insets @@ -182,6 +383,19 @@ public final class ItemListPeerItem: ListViewItem, ItemListItem { } } + private func getHeaderAtTop(top: ListViewItem?, bottom: ListViewItem?) -> Bool { + var headerAtTop = false + if let top = top as? ItemListPeerItem, top.header != nil { + if top.header?.id != self.header?.id { + headerAtTop = true + } + } else if self.header != nil { + headerAtTop = true + } + + return headerAtTop + } + 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? ItemListPeerItemNode { @@ -193,7 +407,7 @@ public final class ItemListPeerItem: ListViewItem, ItemListItem { } async { - let (layout, apply) = makeLayout(self, params, itemListNeighbors(item: self, topItem: previousItem as? ItemListItem, bottomItem: nextItem as? ItemListItem)) + let (layout, apply) = makeLayout(self, params, itemListNeighbors(item: self, topItem: previousItem as? ItemListItem, bottomItem: nextItem as? ItemListItem), self.getHeaderAtTop(top: previousItem, bottom: nextItem)) Queue.mainQueue().async { completion(layout, { _ in apply(false, animated) @@ -232,8 +446,11 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo private var switchNode: SwitchNode? private var checkNode: ASImageNode? + private var shimmerNode: LoadingShimmerNode? + private var absoluteLocation: (CGRect, CGSize)? + private var peerPresenceManager: PeerPresenceStatusManager? - private var layoutParams: (ItemListPeerItem, ListViewItemLayoutParams, ItemListNeighbors)? + private var layoutParams: (ItemListPeerItem, ListViewItemLayoutParams, ItemListNeighbors, Bool)? private var editableControlNode: ItemListEditableControlNode? @@ -303,7 +520,7 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo self.peerPresenceManager = PeerPresenceStatusManager(update: { [weak self] in if let strongSelf = self, let layoutParams = strongSelf.layoutParams { - let (_, apply) = strongSelf.asyncLayout()(layoutParams.0, layoutParams.1, layoutParams.2) + let (_, apply) = strongSelf.asyncLayout()(layoutParams.0, layoutParams.1, layoutParams.2, layoutParams.3) apply(false, true) } }) @@ -317,7 +534,7 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo } } - public func asyncLayout() -> (_ item: ItemListPeerItem, _ params: ListViewItemLayoutParams, _ neighbors: ItemListNeighbors) -> (ListViewItemNodeLayout, (Bool, Bool) -> Void) { + public func asyncLayout() -> (_ item: ItemListPeerItem, _ params: ListViewItemLayoutParams, _ neighbors: ItemListNeighbors, _ headerAtTop: Bool) -> (ListViewItemNodeLayout, (Bool, Bool) -> Void) { let makeTitleLayout = TextNode.asyncLayout(self.titleNode) let makeStatusLayout = TextNode.asyncLayout(self.statusNode) let makeLabelLayout = TextNode.asyncLayout(self.labelNode) @@ -334,7 +551,7 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo let currentHasBadge = self.labelBadgeNode.image != nil - return { item, params, neighbors in + return { item, params, neighbors, headerAtTop in var updateArrowImage: UIImage? var updatedTheme: PresentationTheme? @@ -579,8 +796,11 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo insets.top = 0.0 insets.bottom = 0.0 } + if headerAtTop, let header = item.header { + insets.top += header.height + 18.0 + } - let titleSpacing: CGFloat = 1.0 + let titleSpacing: CGFloat = statusLayout.size.height == 0.0 ? 0.0 : 1.0 let minHeight: CGFloat = titleLayout.size.height + verticalInset * 2.0 let rawHeight: CGFloat = verticalInset * 2.0 + titleLayout.size.height + titleSpacing + statusLayout.size.height @@ -602,7 +822,7 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo return (layout, { [weak self] synchronousLoad, animated in if let strongSelf = self { - strongSelf.layoutParams = (item, params, neighbors) + strongSelf.layoutParams = (item, params, neighbors, headerAtTop) strongSelf.containerNode.frame = CGRect(origin: CGPoint(), size: layout.contentSize) strongSelf.containerNode.isGestureEnabled = item.contextAction != nil @@ -829,6 +1049,44 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo strongSelf.peerPresenceManager?.reset(presence: presence) } + if let shimmering = item.shimmering { + strongSelf.avatarNode.isHidden = true + strongSelf.titleNode.isHidden = true + + let shimmerNode: LoadingShimmerNode + if let current = strongSelf.shimmerNode { + shimmerNode = current + } else { + shimmerNode = LoadingShimmerNode() + strongSelf.shimmerNode = shimmerNode + strongSelf.insertSubnode(shimmerNode, aboveSubnode: strongSelf.backgroundNode) + } + shimmerNode.frame = CGRect(origin: CGPoint(), size: layout.contentSize) + if let (rect, size) = strongSelf.absoluteLocation { + shimmerNode.updateAbsoluteRect(rect, within: size) + } + var shapes: [LoadingShimmerNode.Shape] = [] + shapes.append(.circle(strongSelf.avatarNode.frame)) + let possibleLines: [[CGFloat]] = [ + [50.0, 40.0], + [70.0, 45.0] + ] + let titleFrame = strongSelf.titleNode.frame + let lineDiameter: CGFloat = 10.0 + var lineStart = titleFrame.minX + for lineWidth in possibleLines[shimmering.alternationIndex % possibleLines.count] { + shapes.append(.roundedRectLine(startPoint: CGPoint(x: lineStart, y: titleFrame.minY + floor((titleFrame.height - lineDiameter) / 2.0)), width: lineWidth, diameter: lineDiameter)) + lineStart += lineWidth + 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: layout.contentSize) + } else if let shimmerNode = strongSelf.shimmerNode { + strongSelf.avatarNode.isHidden = false + strongSelf.titleNode.isHidden = false + + strongSelf.shimmerNode = nil + shimmerNode.removeFromSupernode() + } + strongSelf.updateLayout(size: layout.contentSize, leftInset: params.leftInset, rightInset: params.rightInset) strongSelf.setRevealOptions((left: [], right: peerRevealOptions)) @@ -940,13 +1198,13 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo } override public func revealOptionsInteractivelyOpened() { - if let (item, _, _) = self.layoutParams { + if let (item, _, _, _) = self.layoutParams { item.setPeerIdWithRevealedOptions(item.peer.id, nil) } } override public func revealOptionsInteractivelyClosed() { - if let (item, _, _) = self.layoutParams { + if let (item, _, _, _) = self.layoutParams { item.setPeerIdWithRevealedOptions(nil, item.peer.id) } } @@ -955,7 +1213,7 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo self.setRevealOptionsOpened(false, animated: true) self.revealOptionsInteractivelyClosed() - if let (item, _, _) = self.layoutParams { + if let (item, _, _, _) = self.layoutParams { if let revealOptions = item.revealOptions { if option.key >= 0 && option.key < Int32(revealOptions.options.count) { revealOptions.options[Int(option.key)].action() @@ -967,8 +1225,205 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo } private func toggleUpdated(_ value: Bool) { - if let (item, _, _) = self.layoutParams { + if let (item, _, _, _) = self.layoutParams { item.toggleUpdated?(value) } } + + override public func header() -> ListViewItemHeader? { + return self.layoutParams?.0.header + } + + 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.shimmerNode { + shimmerNode.updateAbsoluteRect(rect, within: containerSize) + } + } +} + +public final class ItemListPeerItemHeader: ListViewItemHeader { + public let id: Int64 + public let text: String + public let additionalText: String + public let stickDirection: ListViewItemHeaderStickDirection = .topEdge + public let theme: PresentationTheme + public let strings: PresentationStrings + public let actionTitle: String? + public let action: (() -> Void)? + + public let height: CGFloat = 28.0 + + public init(theme: PresentationTheme, strings: PresentationStrings, text: String, additionalText: String, actionTitle: String? = nil, id: Int64, action: (() -> Void)? = nil) { + self.text = text + self.additionalText = additionalText + self.id = id + self.theme = theme + self.strings = strings + self.actionTitle = actionTitle + self.action = action + } + + public func node() -> ListViewItemHeaderNode { + return ItemListPeerItemHeaderNode(theme: self.theme, strings: self.strings, text: self.text, additionalText: self.additionalText, actionTitle: self.actionTitle, action: self.action) + } + + public func updateNode(_ node: ListViewItemHeaderNode, previous: ListViewItemHeader?, next: ListViewItemHeader?) { + (node as? ItemListPeerItemHeaderNode)?.update(text: self.text, additionalText: self.additionalText, actionTitle: self.actionTitle, action: self.action) + } +} + +public final class ItemListPeerItemHeaderNode: ListViewItemHeaderNode, ItemListHeaderItemNode { + private var theme: PresentationTheme + private var strings: PresentationStrings + private var actionTitle: String? + private var action: (() -> Void)? + + private var validLayout: (size: CGSize, leftInset: CGFloat, rightInset: CGFloat)? + + private let backgroundNode: ASDisplayNode + private let snappedBackgroundNode: ASDisplayNode + private let separatorNode: ASDisplayNode + private let textNode: ImmediateTextNode + private let additionalTextNode: ImmediateTextNode + private let actionTextNode: ImmediateTextNode + private let actionButton: HighlightableButtonNode + + private var stickDistanceFactor: CGFloat? + + public init(theme: PresentationTheme, strings: PresentationStrings, text: String, additionalText: String, actionTitle: String?, action: (() -> Void)?) { + self.theme = theme + self.strings = strings + self.actionTitle = actionTitle + self.action = action + + self.backgroundNode = ASDisplayNode() + self.backgroundNode.backgroundColor = theme.list.blocksBackgroundColor + + self.snappedBackgroundNode = ASDisplayNode() + self.snappedBackgroundNode.backgroundColor = theme.rootController.navigationBar.backgroundColor + self.snappedBackgroundNode.alpha = 0.0 + + self.separatorNode = ASDisplayNode() + self.separatorNode.backgroundColor = theme.list.itemBlocksSeparatorColor + self.separatorNode.alpha = 0.0 + + let titleFont = Font.regular(13.0) + + self.textNode = ImmediateTextNode() + self.textNode.displaysAsynchronously = false + self.textNode.maximumNumberOfLines = 1 + self.textNode.attributedText = NSAttributedString(string: text, font: titleFont, textColor: theme.list.sectionHeaderTextColor) + + self.additionalTextNode = ImmediateTextNode() + self.additionalTextNode.displaysAsynchronously = false + self.additionalTextNode.maximumNumberOfLines = 1 + self.additionalTextNode.attributedText = NSAttributedString(string: additionalText, font: titleFont, textColor: theme.list.sectionHeaderTextColor) + + self.actionTextNode = ImmediateTextNode() + self.actionTextNode.displaysAsynchronously = false + self.actionTextNode.maximumNumberOfLines = 1 + self.actionTextNode.attributedText = NSAttributedString(string: actionTitle ?? "", font: titleFont, textColor: action == nil ? theme.list.sectionHeaderTextColor : theme.list.itemAccentColor) + + self.actionButton = HighlightableButtonNode() + self.actionButton.isUserInteractionEnabled = self.action != nil + + super.init() + + self.addSubnode(self.backgroundNode) + self.addSubnode(self.snappedBackgroundNode) + self.addSubnode(self.separatorNode) + self.addSubnode(self.textNode) + self.addSubnode(self.additionalTextNode) + self.addSubnode(self.actionTextNode) + self.addSubnode(self.actionButton) + + self.actionButton.addTarget(self, action: #selector(self.actionButtonPressed), forControlEvents: .touchUpInside) + self.actionButton.highligthedChanged = { [weak self] highlighted in + if let strongSelf = self { + if highlighted { + strongSelf.actionTextNode.layer.removeAnimation(forKey: "opacity") + strongSelf.actionTextNode.alpha = 0.4 + } else { + strongSelf.actionTextNode.alpha = 1.0 + strongSelf.actionTextNode.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2) + } + } + } + } + + @objc private func actionButtonPressed() { + self.action?() + } + + public func updateTheme(theme: PresentationTheme) { + self.theme = theme + + self.backgroundNode.backgroundColor = theme.list.blocksBackgroundColor + self.snappedBackgroundNode.backgroundColor = theme.rootController.navigationBar.backgroundColor + self.separatorNode.backgroundColor = theme.list.itemBlocksSeparatorColor + + let titleFont = Font.regular(13.0) + + self.textNode.attributedText = NSAttributedString(string: self.textNode.attributedText?.string ?? "", font: titleFont, textColor: theme.list.sectionHeaderTextColor) + self.additionalTextNode.attributedText = NSAttributedString(string: self.additionalTextNode.attributedText?.string ?? "", font: titleFont, textColor: theme.list.sectionHeaderTextColor) + self.actionTextNode.attributedText = NSAttributedString(string: self.actionTextNode.attributedText?.string ?? "", font: titleFont, textColor: theme.list.sectionHeaderTextColor) + } + + public func update(text: String, additionalText: String, actionTitle: String?, action: (() -> Void)?) { + self.actionTitle = actionTitle + self.action = action + let titleFont = Font.regular(13.0) + self.textNode.attributedText = NSAttributedString(string: text, font: titleFont, textColor: theme.list.sectionHeaderTextColor) + self.additionalTextNode.attributedText = NSAttributedString(string: additionalText, font: titleFont, textColor: theme.list.sectionHeaderTextColor) + self.actionTextNode.attributedText = NSAttributedString(string: actionTitle ?? "", font: titleFont, textColor: action == nil ? theme.list.sectionHeaderTextColor : theme.list.itemAccentColor) + self.actionButton.isUserInteractionEnabled = self.action != nil + if let (size, leftInset, rightInset) = self.validLayout { + self.updateLayout(size: size, leftInset: leftInset, rightInset: rightInset) + } + } + + override public func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat) { + self.validLayout = (size, leftInset, rightInset) + self.backgroundNode.frame = CGRect(origin: CGPoint(), size: size) + self.snappedBackgroundNode.frame = CGRect(origin: CGPoint(), size: size) + self.separatorNode.frame = CGRect(origin: CGPoint(x: 0.0, y: size.height - UIScreenPixel), size: CGSize(width: size.width, height: UIScreenPixel)) + + let sideInset: CGFloat = 15.0 + leftInset + + let actionTextSize = self.actionTextNode.updateLayout(CGSize(width: size.width - sideInset * 2.0, height: size.height)) + let additionalTextSize = self.additionalTextNode.updateLayout(CGSize(width: size.width - sideInset * 2.0 - actionTextSize.width - 8.0, height: size.height)) + let textSize = self.textNode.updateLayout(CGSize(width: max(1.0, size.width - sideInset * 2.0 - actionTextSize.width - 8.0 - additionalTextSize.width), height: size.height)) + + let textFrame = CGRect(origin: CGPoint(x: sideInset, y: 7.0), size: textSize) + self.textNode.frame = textFrame + self.additionalTextNode.frame = CGRect(origin: CGPoint(x: textFrame.maxX, y: 7.0), size: additionalTextSize) + self.actionTextNode.frame = CGRect(origin: CGPoint(x: size.width - sideInset - actionTextSize.width, y: 7.0), size: actionTextSize) + self.actionButton.frame = CGRect(origin: CGPoint(x: size.width - sideInset - actionTextSize.width, y: 0.0), size: CGSize(width: actionTextSize.width, height: size.height)) + } + + override public func animateRemoved(duration: Double) { + self.alpha = 0.0 + self.layer.animateAlpha(from: 1.0, to: 0.0, duration: duration, removeOnCompletion: true) + } + + override public func updateStickDistanceFactor(_ factor: CGFloat, transition: ContainedViewLayoutTransition) { + if self.stickDistanceFactor == factor { + return + } + self.stickDistanceFactor = factor + if let (size, leftInset, _) = self.validLayout { + if leftInset.isZero { + transition.updateAlpha(node: self.separatorNode, alpha: 1.0) + transition.updateAlpha(node: self.snappedBackgroundNode, alpha: (1.0 - factor) * 0.0 + factor * 1.0) + } else { + let distance = factor * size.height + let alpha = abs(distance) / 16.0 + transition.updateAlpha(node: self.separatorNode, alpha: max(0.0, min(1.0, alpha))) + transition.updateAlpha(node: self.snappedBackgroundNode, alpha: 0.0) + } + } + } } diff --git a/submodules/ItemListStickerPackItem/Sources/ItemListStickerPackItem.swift b/submodules/ItemListStickerPackItem/Sources/ItemListStickerPackItem.swift index 23d9798148..0eb1793f6c 100644 --- a/submodules/ItemListStickerPackItem/Sources/ItemListStickerPackItem.swift +++ b/submodules/ItemListStickerPackItem/Sources/ItemListStickerPackItem.swift @@ -324,7 +324,7 @@ class ItemListStickerPackItemNode: ItemListRevealOptionsItemNode { let separatorHeight = UIScreenPixel var editableControlSizeAndApply: (CGFloat, (CGFloat) -> ItemListEditableControlNode)? - var reorderControlSizeAndApply: (CGFloat, (CGFloat, Bool) -> ItemListEditableReorderControlNode)? + var reorderControlSizeAndApply: (CGFloat, (CGFloat, Bool, ContainedViewLayoutTransition) -> ItemListEditableReorderControlNode)? var editingOffset: CGFloat = 0.0 var reorderInset: CGFloat = 0.0 @@ -485,7 +485,7 @@ class ItemListStickerPackItemNode: ItemListRevealOptionsItemNode { if let reorderControlSizeAndApply = reorderControlSizeAndApply { if strongSelf.reorderControlNode == nil { - let reorderControlNode = reorderControlSizeAndApply.1(layout.contentSize.height, false) + let reorderControlNode = reorderControlSizeAndApply.1(layout.contentSize.height, false, .immediate) strongSelf.reorderControlNode = reorderControlNode strongSelf.addSubnode(reorderControlNode) reorderControlNode.alpha = 0.0 diff --git a/submodules/ItemListUI/Sources/ItemListController.swift b/submodules/ItemListUI/Sources/ItemListController.swift index eb4ba6cd0d..70a717c81f 100644 --- a/submodules/ItemListUI/Sources/ItemListController.swift +++ b/submodules/ItemListUI/Sources/ItemListController.swift @@ -56,6 +56,7 @@ public struct ItemListBackButton: Equatable { public enum ItemListControllerTitle: Equatable { case text(String) + case textWithSubtitle(String, String) case sectionControl([String], Int) } @@ -197,12 +198,12 @@ open class ItemListController: ViewController, KeyShortcutResponder, Presentable public var willScrollToTop: (() -> Void)? - public func setReorderEntry(_ f: @escaping (Int, Int, [T]) -> Void) { + public func setReorderEntry(_ f: @escaping (Int, Int, [T]) -> Signal) { self.reorderEntry = { a, b, list in - f(a, b, list.map { $0 as! T }) + return f(a, b, list.map { $0 as! T }) } } - private var reorderEntry: ((Int, Int, [ItemListNodeAnyEntry]) -> Void)? { + private var reorderEntry: ((Int, Int, [ItemListNodeAnyEntry]) -> Signal)? { didSet { if self.isNodeLoaded { (self.displayNode as! ItemListControllerNode).reorderEntry = self.reorderEntry @@ -287,6 +288,10 @@ open class ItemListController: ViewController, KeyShortcutResponder, Presentable strongSelf.title = text strongSelf.navigationItem.titleView = nil strongSelf.segmentedTitleView = nil + case let .textWithSubtitle(title, subtitle): + strongSelf.title = "" + strongSelf.navigationItem.titleView = ItemListTextWithSubtitleTitleView(theme: controllerState.presentationData.theme, title: title, subtitle: subtitle) + strongSelf.segmentedTitleView = nil case let .sectionControl(sections, index): strongSelf.title = "" if let segmentedTitleView = strongSelf.segmentedTitleView, segmentedTitleView.segments == sections { @@ -417,6 +422,10 @@ open class ItemListController: ViewController, KeyShortcutResponder, Presentable strongSelf.segmentedTitleView?.theme = controllerState.presentationData.theme + if let titleView = strongSelf.navigationItem.titleView as? ItemListTextWithSubtitleTitleView { + titleView.updateTheme(theme: controllerState.presentationData.theme) + } + var items = strongSelf.navigationItem.rightBarButtonItems ?? [] for i in 0 ..< strongSelf.rightNavigationButtonTitleAndStyle.count { if case .activity = strongSelf.rightNavigationButtonTitleAndStyle[i].1 { @@ -517,6 +526,10 @@ open class ItemListController: ViewController, KeyShortcutResponder, Presentable self.didDisappear?(animated) } + public var listInsets: UIEdgeInsets { + return (self.displayNode as! ItemListControllerNode).listNode.insets + } + public func frameForItemNode(_ predicate: (ListViewItemNode) -> Bool) -> CGRect? { var result: CGRect? (self.displayNode as! ItemListControllerNode).listNode.forEachItemNode { itemNode in @@ -598,3 +611,68 @@ open class ItemListController: ViewController, KeyShortcutResponder, Presentable })] } } + +private final class ItemListTextWithSubtitleTitleView: UIView, NavigationBarTitleView { + private let titleNode: ImmediateTextNode + private let subtitleNode: ImmediateTextNode + + private var validLayout: (CGSize, CGRect)? + + init(theme: PresentationTheme, title: String, subtitle: String) { + self.titleNode = ImmediateTextNode() + self.titleNode.displaysAsynchronously = false + self.titleNode.maximumNumberOfLines = 1 + self.titleNode.isOpaque = false + + self.titleNode.attributedText = NSAttributedString(string: title, font: Font.medium(17.0), textColor: theme.rootController.navigationBar.primaryTextColor) + + self.subtitleNode = ImmediateTextNode() + self.subtitleNode.displaysAsynchronously = false + self.subtitleNode.maximumNumberOfLines = 1 + self.subtitleNode.isOpaque = false + + self.subtitleNode.attributedText = NSAttributedString(string: subtitle, font: Font.regular(13.0), textColor: theme.rootController.navigationBar.secondaryTextColor) + + super.init(frame: CGRect()) + + self.addSubnode(self.titleNode) + self.addSubnode(self.subtitleNode) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func updateTheme(theme: PresentationTheme) { + self.titleNode.attributedText = NSAttributedString(string: self.titleNode.attributedText?.string ?? "", font: Font.medium(17.0), textColor: theme.rootController.navigationBar.primaryTextColor) + self.subtitleNode.attributedText = NSAttributedString(string: self.subtitleNode.attributedText?.string ?? "", font: Font.regular(13.0), textColor: theme.rootController.navigationBar.secondaryTextColor) + if let (size, clearBounds) = self.validLayout { + self.updateLayout(size: size, clearBounds: clearBounds, transition: .immediate) + } + } + + override func layoutSubviews() { + super.layoutSubviews() + + if let (size, clearBounds) = self.validLayout { + self.updateLayout(size: size, clearBounds: clearBounds, transition: .immediate) + } + } + + func updateLayout(size: CGSize, clearBounds: CGRect, transition: ContainedViewLayoutTransition) { + self.validLayout = (size, clearBounds) + + let titleSize = self.titleNode.updateLayout(size) + let subtitleSize = self.subtitleNode.updateLayout(size) + let spacing: CGFloat = 0.0 + let contentHeight = titleSize.height + spacing + subtitleSize.height + let titleFrame = CGRect(origin: CGPoint(x: floor((size.width - titleSize.width) / 2.0), y: floor((size.height - contentHeight) / 2.0)), size: titleSize) + let subtitleFrame = CGRect(origin: CGPoint(x: floor((size.width - subtitleSize.width) / 2.0), y: titleFrame.maxY + spacing), size: subtitleSize) + + self.titleNode.frame = titleFrame + self.subtitleNode.frame = subtitleFrame + } + + func animateLayoutTransition() { + } +} diff --git a/submodules/ItemListUI/Sources/ItemListControllerNode.swift b/submodules/ItemListUI/Sources/ItemListControllerNode.swift index f8e3ab0236..253b2d14d2 100644 --- a/submodules/ItemListUI/Sources/ItemListControllerNode.swift +++ b/submodules/ItemListUI/Sources/ItemListControllerNode.swift @@ -8,6 +8,10 @@ import SyncCore import TelegramPresentationData import MergeLists +public protocol ItemListHeaderItemNode: class { + func updateTheme(theme: PresentationTheme) +} + public typealias ItemListSectionId = Int32 public protocol ItemListNodeAnyEntry { @@ -217,7 +221,7 @@ open class ItemListControllerNode: ASDisplayNode, UIScrollViewDelegate { public var contentOffsetChanged: ((ListViewVisibleContentOffset, Bool) -> Void)? public var contentScrollingEnded: ((ListView) -> Bool)? public var searchActivated: ((Bool) -> Void)? - public var reorderEntry: ((Int, Int, [ItemListNodeAnyEntry]) -> Void)? + public var reorderEntry: ((Int, Int, [ItemListNodeAnyEntry]) -> Signal)? public var reorderCompleted: (([ItemListNodeAnyEntry]) -> Void)? public var requestLayout: ((ContainedViewLayoutTransition) -> Void)? @@ -269,7 +273,7 @@ open class ItemListControllerNode: ASDisplayNode, UIScrollViewDelegate { self.listNode.reorderItem = { [weak self] fromIndex, toIndex, opaqueTransactionState in if let strongSelf = self, let reorderEntry = strongSelf.reorderEntry, let mergedEntries = (opaqueTransactionState as? ItemListNodeOpaqueState)?.mergedEntries { if fromIndex >= 0 && fromIndex < mergedEntries.count && toIndex >= 0 && toIndex < mergedEntries.count { - reorderEntry(fromIndex, toIndex, mergedEntries) + return reorderEntry(fromIndex, toIndex, mergedEntries) } } return .single(false) @@ -467,6 +471,12 @@ open class ItemListControllerNode: ASDisplayNode, UIScrollViewDelegate { self.rightOverlayNode.backgroundColor = transition.theme.list.blocksBackgroundColor } } + + self.listNode.forEachItemHeaderNode({ itemHeaderNode in + if let itemHeaderNode = itemHeaderNode as? ItemListHeaderItemNode { + itemHeaderNode.updateTheme(theme: transition.theme) + } + }) } if let updateStyle = transition.updateStyle { @@ -501,9 +511,12 @@ open class ItemListControllerNode: ASDisplayNode, UIScrollViewDelegate { options.insert(.PreferSynchronousDrawing) options.insert(.AnimateAlpha) } else if transition.crossfade { + options.insert(.PreferSynchronousResourceLoading) + options.insert(.PreferSynchronousDrawing) options.insert(.AnimateCrossfade) } else { options.insert(.Synchronous) + options.insert(.PreferSynchronousResourceLoading) options.insert(.PreferSynchronousDrawing) } if self.alwaysSynchronous { diff --git a/submodules/ItemListUI/Sources/ItemListEditableReorderControlNode.swift b/submodules/ItemListUI/Sources/ItemListEditableReorderControlNode.swift index ce53c7ca5c..e3c6028ec1 100644 --- a/submodules/ItemListUI/Sources/ItemListEditableReorderControlNode.swift +++ b/submodules/ItemListUI/Sources/ItemListEditableReorderControlNode.swift @@ -19,7 +19,7 @@ public final class ItemListEditableReorderControlNode: ASDisplayNode { self.addSubnode(self.iconNode) } - public static func asyncLayout(_ node: ItemListEditableReorderControlNode?) -> (_ theme: PresentationTheme) -> (CGFloat, (CGFloat, Bool) -> ItemListEditableReorderControlNode) { + public static func asyncLayout(_ node: ItemListEditableReorderControlNode?) -> (_ theme: PresentationTheme) -> (CGFloat, (CGFloat, Bool, ContainedViewLayoutTransition) -> ItemListEditableReorderControlNode) { return { theme in let image = PresentationResourcesItemList.itemListReorderIndicatorIcon(theme) @@ -31,9 +31,9 @@ public final class ItemListEditableReorderControlNode: ASDisplayNode { } resultNode.iconNode.image = image - return (40.0, { height, offsetForLabel in + return (40.0, { height, offsetForLabel, transition in if let image = image { - resultNode.iconNode.frame = CGRect(origin: CGPoint(x: 7.0, y: floor((height - image.size.height) / 2.0) - (offsetForLabel ? 6.0 : 0.0)), size: image.size) + transition.updateFrame(node: resultNode.iconNode, frame: CGRect(origin: CGPoint(x: 7.0, y: floor((height - image.size.height) / 2.0) - (offsetForLabel ? 6.0 : 0.0)), size: image.size)) } return resultNode }) diff --git a/submodules/ItemListUI/Sources/Items/ItemListEditableItem.swift b/submodules/ItemListUI/Sources/Items/ItemListEditableItem.swift index efd9e4e4c2..dc6f9a1a6f 100644 --- a/submodules/ItemListUI/Sources/Items/ItemListEditableItem.swift +++ b/submodules/ItemListUI/Sources/Items/ItemListEditableItem.swift @@ -82,6 +82,10 @@ open class ItemListRevealOptionsItemNode: ListViewItemNode, UIGestureRecognizerD super.init(layerBacked: layerBacked, dynamicBounce: dynamicBounce, rotated: rotated, seeThrough: seeThrough) } + open var controlsContainer: ASDisplayNode { + return self + } + override open func didLoad() { super.didLoad() @@ -310,7 +314,7 @@ open class ItemListRevealOptionsItemNode: ListViewItemNode, UIGestureRecognizerD revealNode.updateRevealOffset(offset: 0.0, sideInset: leftInset, transition: .immediate) } - self.addSubnode(revealNode) + self.controlsContainer.addSubnode(revealNode) } } @@ -332,7 +336,7 @@ open class ItemListRevealOptionsItemNode: ListViewItemNode, UIGestureRecognizerD revealNode.updateRevealOffset(offset: 0.0, sideInset: -rightInset, transition: .immediate) } - self.addSubnode(revealNode) + self.controlsContainer.addSubnode(revealNode) } } @@ -492,4 +496,8 @@ open class ItemListRevealOptionsItemNode: ListViewItemNode, UIGestureRecognizerD } self.hapticFeedback?.impact(.medium) } + + override open func animateFrameTransition(_ progress: CGFloat, _ currentValue: CGFloat) { + super.animateFrameTransition(progress, currentValue) + } } diff --git a/submodules/ItemListUI/Sources/Items/ItemListMultilineInputItem.swift b/submodules/ItemListUI/Sources/Items/ItemListMultilineInputItem.swift index b16081b935..8b26cbd59a 100644 --- a/submodules/ItemListUI/Sources/Items/ItemListMultilineInputItem.swift +++ b/submodules/ItemListUI/Sources/Items/ItemListMultilineInputItem.swift @@ -325,8 +325,6 @@ public class ItemListMultilineInputItemNode: ListViewItemNode, ASEditableTextNod 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: layoutSize.width, height: separatorHeight)) strongSelf.bottomStripeNode.frame = CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height), size: CGSize(width: layoutSize.width - bottomStripeInset, height: separatorHeight)) @@ -336,7 +334,11 @@ public class ItemListMultilineInputItemNode: ListViewItemNode, ASEditableTextNod strongSelf.textNode.keyboardAppearance = item.presentationData.theme.rootController.keyboardColor.keyboardAppearance - strongSelf.textClippingNode.frame = CGRect(origin: CGPoint(x: leftInset, y: textTopInset), size: CGSize(width: params.width - leftInset - params.rightInset, height: textLayout.size.height)) + if strongSelf.animationForKey("apparentHeight") == 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.textClippingNode.frame = CGRect(origin: CGPoint(x: leftInset, y: textTopInset), size: CGSize(width: params.width - leftInset - params.rightInset, height: textLayout.size.height)) + } strongSelf.textNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: params.width - leftInset - 16.0 - rightInset, height: textLayout.size.height + 1.0)) let _ = limitTextApply() @@ -394,6 +396,7 @@ public class ItemListMultilineInputItemNode: ListViewItemNode, ASEditableTextNod let textBottomInset: CGFloat = 11.0 self.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))) + self.maskNode.frame = self.backgroundNode.frame.insetBy(dx: params.leftInset, dy: 0.0) self.bottomStripeNode.frame = CGRect(origin: CGPoint(x: self.bottomStripeNode.frame.minX, y: contentSize.height), size: CGSize(width: self.bottomStripeNode.frame.size.width, height: separatorHeight)) self.textClippingNode.frame = CGRect(origin: CGPoint(x: leftInset, y: textTopInset), size: CGSize(width: max(0.0, params.width - leftInset - params.rightInset), height: max(0.0, contentSize.height - textTopInset - textBottomInset))) diff --git a/submodules/ItemListUI/Sources/Items/ItemListTextItem.swift b/submodules/ItemListUI/Sources/Items/ItemListTextItem.swift index 93e9f27da9..e8bf1de0d2 100644 --- a/submodules/ItemListUI/Sources/Items/ItemListTextItem.swift +++ b/submodules/ItemListUI/Sources/Items/ItemListTextItem.swift @@ -9,6 +9,7 @@ import Markdown public enum ItemListTextItemText { case plain(String) + case large(String) case markdown(String) } @@ -23,13 +24,15 @@ public class ItemListTextItem: ListViewItem, ItemListItem { let linkAction: ((ItemListTextItemLinkAction) -> Void)? let style: ItemListStyle public let isAlwaysPlain: Bool = true + public let tag: ItemListItemTag? - public init(presentationData: ItemListPresentationData, text: ItemListTextItemText, sectionId: ItemListSectionId, linkAction: ((ItemListTextItemLinkAction) -> Void)? = nil, style: ItemListStyle = .blocks) { + public init(presentationData: ItemListPresentationData, text: ItemListTextItemText, sectionId: ItemListSectionId, linkAction: ((ItemListTextItemLinkAction) -> Void)? = nil, style: ItemListStyle = .blocks, tag: ItemListItemTag? = nil) { self.presentationData = presentationData self.text = text self.sectionId = sectionId self.linkAction = linkAction self.style = style + self.tag = tag } public func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal?, (ListViewItemApply) -> Void)) -> Void) { @@ -69,12 +72,16 @@ public class ItemListTextItem: ListViewItem, ItemListItem { } } -public class ItemListTextItemNode: ListViewItemNode { +public class ItemListTextItemNode: ListViewItemNode, ItemListItemNode { private let titleNode: TextNode private let activateArea: AccessibilityAreaNode private var item: ItemListTextItem? + public var tag: ItemListItemTag? { + return self.item?.tag + } + public init() { self.titleNode = TextNode() self.titleNode.isUserInteractionEnabled = false @@ -105,15 +112,19 @@ public class ItemListTextItemNode: ListViewItemNode { return { item, params, neighbors in let leftInset: CGFloat = 15.0 + params.leftInset - let verticalInset: CGFloat = 7.0 + let topInset: CGFloat = 7.0 + var bottomInset: CGFloat = 7.0 let titleFont = Font.regular(item.presentationData.fontSize.itemListBaseHeaderFontSize) + let largeTitleFont = Font.semibold(floor(item.presentationData.fontSize.itemListBaseFontSize)) let titleBoldFont = Font.semibold(item.presentationData.fontSize.itemListBaseHeaderFontSize) let attributedText: NSAttributedString switch item.text { case let .plain(text): attributedText = NSAttributedString(string: text, font: titleFont, textColor: item.presentationData.theme.list.freeTextColor) + case let .large(text): + attributedText = NSAttributedString(string: text, font: largeTitleFont, textColor: item.presentationData.theme.list.itemPrimaryTextColor) case let .markdown(text): attributedText = parseMarkdownIntoAttributedString(text, attributes: MarkdownAttributes(body: MarkdownAttributeSet(font: titleFont, textColor: item.presentationData.theme.list.freeTextColor), bold: MarkdownAttributeSet(font: titleBoldFont, textColor: item.presentationData.theme.list.freeTextColor), link: MarkdownAttributeSet(font: titleFont, textColor: item.presentationData.theme.list.itemAccentColor), linkAttribute: { contents in return (TelegramTextAttributes.URL, contents) @@ -123,8 +134,12 @@ public class ItemListTextItemNode: ListViewItemNode { let contentSize: CGSize - contentSize = CGSize(width: params.width, height: titleLayout.size.height + verticalInset + verticalInset) - let insets = itemListNeighborsGroupedInsets(neighbors) + var insets = itemListNeighborsGroupedInsets(neighbors) + if case .large = item.text { + insets.top = 14.0 + bottomInset = -6.0 + } + contentSize = CGSize(width: params.width, height: titleLayout.size.height + topInset + bottomInset) let layout = ListViewItemNodeLayout(contentSize: contentSize, insets: insets) @@ -139,7 +154,7 @@ public class ItemListTextItemNode: ListViewItemNode { let _ = titleApply() - strongSelf.titleNode.frame = CGRect(origin: CGPoint(x: leftInset, y: verticalInset), size: titleLayout.size) + strongSelf.titleNode.frame = CGRect(origin: CGPoint(x: leftInset, y: topInset), size: titleLayout.size) } }) } diff --git a/submodules/LegacyComponents/LegacyComponents/TGCheckButtonView.m b/submodules/LegacyComponents/LegacyComponents/TGCheckButtonView.m index 8aa9440404..998363d54b 100644 --- a/submodules/LegacyComponents/LegacyComponents/TGCheckButtonView.m +++ b/submodules/LegacyComponents/LegacyComponents/TGCheckButtonView.m @@ -24,19 +24,14 @@ NSInteger _number; UIColor *_checkColor; + + CGAffineTransform TGCheckButtonDefaultTransform; } @end @implementation TGCheckButtonView -static NSMutableDictionary *backgroundImages; -static NSMutableDictionary *fillImages; -static CGAffineTransform TGCheckButtonDefaultTransform; - -+ (void)resetCache -{ - [backgroundImages removeAllObjects]; - [fillImages removeAllObjects]; ++ (void)resetCache { } - (instancetype)initWithStyle:(TGCheckButtonStyle)style { @@ -55,15 +50,12 @@ static CGAffineTransform TGCheckButtonDefaultTransform; self = [super initWithFrame:CGRectMake(0, 0, size.width, size.height)]; if (self != nil) { - static dispatch_once_t onceToken; - static CGFloat screenScale = 2.0f; - dispatch_once(&onceToken, ^ - { - TGCheckButtonDefaultTransform = CGAffineTransformMakeRotation(-M_PI_4); - backgroundImages = [[NSMutableDictionary alloc] init]; - fillImages = [[NSMutableDictionary alloc] init]; - screenScale = [UIScreen mainScreen].scale; - }); + CGFloat screenScale = 2.0f; + + TGCheckButtonDefaultTransform = CGAffineTransformMakeRotation(-M_PI_4); + NSMutableDictionary *backgroundImages = [[NSMutableDictionary alloc] init]; + NSMutableDictionary *fillImages = [[NSMutableDictionary alloc] init]; + screenScale = [UIScreen mainScreen].scale; int32_t hex = 0x29c519; UIColor *greenColor = [[UIColor alloc] initWithRed:(((hex >> 16) & 0xff) / 255.0f) green:(((hex >> 8) & 0xff) / 255.0f) blue:(((hex) & 0xff) / 255.0f) alpha:1.0f]; diff --git a/submodules/LegacyComponents/LegacyComponents/TGVideoCameraMovieRecorder.h b/submodules/LegacyComponents/LegacyComponents/TGVideoCameraMovieRecorder.h index a78b7baae3..19f9f1a4b5 100644 --- a/submodules/LegacyComponents/LegacyComponents/TGVideoCameraMovieRecorder.h +++ b/submodules/LegacyComponents/LegacyComponents/TGVideoCameraMovieRecorder.h @@ -18,7 +18,7 @@ - (void)appendVideoPixelBuffer:(CVPixelBufferRef)pixelBuffer withPresentationTime:(CMTime)presentationTime; - (void)appendAudioSampleBuffer:(CMSampleBufferRef)sampleBuffer; -- (void)finishRecording; +- (void)finishRecording:(void(^)())completed; - (NSTimeInterval)videoDuration; diff --git a/submodules/LegacyComponents/LegacyComponents/TGVideoCameraMovieRecorder.m b/submodules/LegacyComponents/LegacyComponents/TGVideoCameraMovieRecorder.m index 0be65e1b85..e236e9ae00 100644 --- a/submodules/LegacyComponents/LegacyComponents/TGVideoCameraMovieRecorder.m +++ b/submodules/LegacyComponents/LegacyComponents/TGVideoCameraMovieRecorder.m @@ -105,7 +105,7 @@ typedef enum { if (_status != TGMovieRecorderStatusIdle) return; - [self transitionToStatus:TGMovieRecorderStatusPreparingToRecord error:nil]; + [self transitionToStatus:TGMovieRecorderStatusPreparingToRecord error:nil completed:nil]; } dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^ @@ -138,9 +138,9 @@ typedef enum { @synchronized (self) { if (error || !succeed) - [self transitionToStatus:TGMovieRecorderStatusFailed error:error]; + [self transitionToStatus:TGMovieRecorderStatusFailed error:error completed:nil]; else - [self transitionToStatus:TGMovieRecorderStatusRecording error:nil]; + [self transitionToStatus:TGMovieRecorderStatusRecording error:nil completed:nil]; } } } ); @@ -169,8 +169,9 @@ typedef enum { [self appendSampleBuffer:sampleBuffer ofMediaType:AVMediaTypeAudio]; } -- (void)finishRecording +- (void)finishRecording:(void(^)())completed { + printf("finishRecording %d\n", _status); @synchronized (self) { bool shouldFinishRecording = false; @@ -190,9 +191,10 @@ typedef enum { } if (shouldFinishRecording) - [self transitionToStatus:TGMovieRecorderStatusFinishingWaiting error:nil]; - else + [self transitionToStatus:TGMovieRecorderStatusFinishingWaiting error:nil completed:completed]; + else { return; + } } dispatch_async(_writingQueue, ^ @@ -201,10 +203,14 @@ typedef enum { { @synchronized (self) { - if (_status != TGMovieRecorderStatusFinishingWaiting) + if (_status != TGMovieRecorderStatusFinishingWaiting) { + if (completed) { + completed(); + } return; + } - [self transitionToStatus:TGMovieRecorderStatusFinishingCommiting error:nil]; + [self transitionToStatus:TGMovieRecorderStatusFinishingCommiting error:nil completed:nil]; } [_assetWriter finishWritingWithCompletionHandler:^ @@ -213,9 +219,9 @@ typedef enum { { NSError *error = _assetWriter.error; if (error) - [self transitionToStatus:TGMovieRecorderStatusFailed error:error]; + [self transitionToStatus:TGMovieRecorderStatusFailed error:error completed:completed]; else - [self transitionToStatus:TGMovieRecorderStatusFinished error:nil]; + [self transitionToStatus:TGMovieRecorderStatusFinished error:nil completed:completed]; } }]; } @@ -340,7 +346,7 @@ typedef enum { NSError *error = _assetWriter.error; @synchronized (self) { - [self transitionToStatus:TGMovieRecorderStatusFailed error:error]; + [self transitionToStatus:TGMovieRecorderStatusFailed error:error completed:nil]; } } } @@ -349,8 +355,10 @@ typedef enum { }); } -- (void)transitionToStatus:(TGMovieRecorderStatus)newStatus error:(NSError *)error +- (void)transitionToStatus:(TGMovieRecorderStatus)newStatus error:(NSError *)error completed:(void(^)())completed { + printf("recorder transitionToStatus %d\n", newStatus); + bool shouldNotifyDelegate = false; if (newStatus != _status) @@ -389,6 +397,7 @@ typedef enum { break; case TGMovieRecorderStatusFinished: + printf("TGMovieRecorderStatusFinished _delegate == nil = %d\n", (int)(_delegate == nil)); [_delegate movieRecorderDidFinishRecording:self]; break; @@ -399,9 +408,16 @@ typedef enum { default: break; } + if (completed) { + completed(); + } } }); - } + } else { + if (completed) { + completed(); + } + } } - (bool)setupAssetWriterAudioInputWithSourceFormatDescription:(CMFormatDescriptionRef)audioFormatDescription settings:(NSDictionary *)audioSettings diff --git a/submodules/LegacyComponents/LegacyComponents/TGVideoCameraPipeline.h b/submodules/LegacyComponents/LegacyComponents/TGVideoCameraPipeline.h index 46cbde9207..6d79b12348 100644 --- a/submodules/LegacyComponents/LegacyComponents/TGVideoCameraPipeline.h +++ b/submodules/LegacyComponents/LegacyComponents/TGVideoCameraPipeline.h @@ -21,7 +21,7 @@ - (void)stopRunning; - (void)startRecording:(NSURL *)url preset:(TGMediaVideoConversionPreset)preset liveUpload:(bool)liveUpload; -- (void)stopRecording; +- (void)stopRecording:(void (^)())completed; - (CGAffineTransform)transformForOrientation:(AVCaptureVideoOrientation)orientation; diff --git a/submodules/LegacyComponents/LegacyComponents/TGVideoCameraPipeline.m b/submodules/LegacyComponents/LegacyComponents/TGVideoCameraPipeline.m index cf69baa75e..b788f7d53c 100644 --- a/submodules/LegacyComponents/LegacyComponents/TGVideoCameraPipeline.m +++ b/submodules/LegacyComponents/LegacyComponents/TGVideoCameraPipeline.m @@ -111,6 +111,7 @@ const NSInteger TGVideoCameraRetainedBufferCount = 16; - (void)dealloc { + printf("Camera pipeline dealloc\n"); [self destroyCaptureSession]; } @@ -134,7 +135,7 @@ const NSInteger TGVideoCameraRetainedBufferCount = 16; { _running = false; - [self stopRecording]; + [self stopRecording:^{}]; [_captureSession stopRunning]; [self captureSessionDidStopRunning]; @@ -285,7 +286,7 @@ const NSInteger TGVideoCameraRetainedBufferCount = 16; - (void)captureSessionDidStopRunning { - [self stopRecording]; + [self stopRecording:^{}]; [self destroyVideoPipeline]; } @@ -684,20 +685,29 @@ const NSInteger TGVideoCameraRetainedBufferCount = 16; [recorder prepareToRecord]; } -- (void)stopRecording +- (void)stopRecording:(void (^)())completed { [[TGVideoCameraPipeline cameraQueue] dispatch:^ { @synchronized (self) { - if (_recordingStatus != TGVideoCameraRecordingStatusRecording) + if (_recordingStatus != TGVideoCameraRecordingStatusRecording) { + if (completed) { + completed(); + } return; + } [self transitionToRecordingStatus:TGVideoCameraRecordingStatusStoppingRecording error:nil]; } _resultDuration = _recorder.videoDuration; - [_recorder finishRecording]; + [_recorder finishRecording:^{ + __unused __auto_type description = [self description]; + if (completed) { + completed(); + } + }]; }]; } @@ -734,6 +744,8 @@ const NSInteger TGVideoCameraRetainedBufferCount = 16; - (void)movieRecorderDidFinishRecording:(TGVideoCameraMovieRecorder *)__unused recorder { + printf("movieRecorderDidFinishRecording\n"); + @synchronized (self) { if (_recordingStatus != TGVideoCameraRecordingStatusStoppingRecording) @@ -750,6 +762,8 @@ const NSInteger TGVideoCameraRetainedBufferCount = 16; - (void)transitionToRecordingStatus:(TGVideoCameraRecordingStatus)newStatus error:(NSError *)error { + printf("transitionToRecordingStatus %d\n", newStatus); + TGVideoCameraRecordingStatus oldStatus = _recordingStatus; _recordingStatus = newStatus; @@ -763,12 +777,16 @@ const NSInteger TGVideoCameraRetainedBufferCount = 16; } else { + __strong id delegate = _delegate; if ((oldStatus == TGVideoCameraRecordingStatusStartingRecording) && (newStatus == TGVideoCameraRecordingStatusRecording)) - delegateCallbackBlock = ^{ [_delegate capturePipelineRecordingDidStart:self]; }; + delegateCallbackBlock = ^{ [delegate capturePipelineRecordingDidStart:self]; }; else if ((oldStatus == TGVideoCameraRecordingStatusRecording) && (newStatus == TGVideoCameraRecordingStatusStoppingRecording)) - delegateCallbackBlock = ^{ [_delegate capturePipelineRecordingWillStop:self]; }; + delegateCallbackBlock = ^{ [delegate capturePipelineRecordingWillStop:self]; }; else if ((oldStatus == TGVideoCameraRecordingStatusStoppingRecording) && (newStatus == TGVideoCameraRecordingStatusIdle)) - delegateCallbackBlock = ^{ [_delegate capturePipelineRecordingDidStop:self duration:_resultDuration liveUploadData:_liveUploadData thumbnailImage:_recordingThumbnail thumbnails:_thumbnails]; }; + delegateCallbackBlock = ^{ + printf("transitionToRecordingStatus delegateCallbackBlock _delegate == nil = %d\n", (int)(delegate == nil)); + [delegate capturePipelineRecordingDidStop:self duration:_resultDuration liveUploadData:_liveUploadData thumbnailImage:_recordingThumbnail thumbnails:_thumbnails]; + }; } if (delegateCallbackBlock != nil) diff --git a/submodules/LegacyComponents/LegacyComponents/TGVideoMessageCaptureController.m b/submodules/LegacyComponents/LegacyComponents/TGVideoMessageCaptureController.m index 045fbca31d..0fbca0c6f4 100644 --- a/submodules/LegacyComponents/LegacyComponents/TGVideoMessageCaptureController.m +++ b/submodules/LegacyComponents/LegacyComponents/TGVideoMessageCaptureController.m @@ -201,6 +201,7 @@ typedef enum - (void)dealloc { + printf("Video controller dealloc\n"); [_thumbnailsDisposable dispose]; [[NSNotificationCenter defaultCenter] removeObserver:_didEnterBackgroundObserver]; [_activityDisposable dispose]; @@ -649,9 +650,11 @@ typedef enum return; [_activityDisposable dispose]; - [self stopRecording]; - - [self dismiss:false]; + [self stopRecording:^{ + TGDispatchOnMainThread(^{ + [self dismiss:false]; + }); + }]; } - (void)buttonInteractionUpdate:(CGPoint)value @@ -684,7 +687,7 @@ typedef enum _switchButton.userInteractionEnabled = false; [_activityDisposable dispose]; - [self stopRecording]; + [self stopRecording:^{}]; return true; } @@ -939,9 +942,9 @@ typedef enum [self startRecordingTimer]; } -- (void)stopRecording +- (void)stopRecording:(void (^)())completed { - [_capturePipeline stopRecording]; + [_capturePipeline stopRecording:completed]; [_buttonHandler ignoreEventsFor:1.0f andDisable:true]; } diff --git a/submodules/LegacyMediaPickerUI/Sources/LegacyAttachmentMenu.swift b/submodules/LegacyMediaPickerUI/Sources/LegacyAttachmentMenu.swift index ea41559ece..f55097b4a2 100644 --- a/submodules/LegacyMediaPickerUI/Sources/LegacyAttachmentMenu.swift +++ b/submodules/LegacyMediaPickerUI/Sources/LegacyAttachmentMenu.swift @@ -296,7 +296,13 @@ public func legacyAttachmentMenu(context: AccountContext, peer: Peer, editMediaO })! itemViews.append(locationItem) - if (peer is TelegramGroup || peer is TelegramChannel) && canSendMessagesToPeer(peer) && canSendPolls { + var peerSupportsPolls = false + if peer is TelegramGroup || peer is TelegramChannel { + peerSupportsPolls = true + } else if let user = peer as? TelegramUser, let _ = user.botInfo { + peerSupportsPolls = true + } + if peerSupportsPolls && canSendMessagesToPeer(peer) && canSendPolls { let pollItem = TGMenuSheetButtonItemView(title: presentationData.strings.AttachmentMenu_Poll, type: TGMenuSheetButtonTypeDefault, fontSize: fontSize, action: { [weak controller] in controller?.dismiss(animated: true) openPoll() diff --git a/submodules/LegacyUI/Sources/LegacyController.swift b/submodules/LegacyUI/Sources/LegacyController.swift index c2c41c8c21..df58f2cbe3 100644 --- a/submodules/LegacyUI/Sources/LegacyController.swift +++ b/submodules/LegacyUI/Sources/LegacyController.swift @@ -173,6 +173,9 @@ public final class LegacyControllerContext: NSObject, LegacyComponentsContext { } public func currentlyInSplitView() -> Bool { + if let controller = self.controller as? LegacyController, let validLayout = controller.validLayout { + return validLayout.isNonExclusive + } return false } diff --git a/submodules/LegacyUI/Sources/TelegramInitializeLegacyComponents.swift b/submodules/LegacyUI/Sources/TelegramInitializeLegacyComponents.swift index 32df01e865..b77631410c 100644 --- a/submodules/LegacyUI/Sources/TelegramInitializeLegacyComponents.swift +++ b/submodules/LegacyUI/Sources/TelegramInitializeLegacyComponents.swift @@ -48,6 +48,13 @@ private final class LegacyComponentsAccessCheckerImpl: NSObject, LegacyComponent } public func checkPhotoAuthorizationStatus(for intent: TGPhotoAccessIntent, alertDismissCompletion: (() -> Void)!) -> Bool { + if let context = self.context { + DeviceAccess.authorizeAccess(to: .mediaLibrary(.send), presentationData: context.sharedContext.currentPresentationData.with { $0 }, present: context.sharedContext.presentGlobalController, openSettings: context.sharedContext.applicationBindings.openSettings, { value in + if !value { + alertDismissCompletion?() + } + }) + } return true } diff --git a/submodules/LocationUI/Sources/LocationMapHeaderNode.swift b/submodules/LocationUI/Sources/LocationMapHeaderNode.swift index 30bf055109..817b6fa7a7 100644 --- a/submodules/LocationUI/Sources/LocationMapHeaderNode.swift +++ b/submodules/LocationUI/Sources/LocationMapHeaderNode.swift @@ -84,7 +84,7 @@ final class LocationMapHeaderNode: ASDisplayNode { self.placesBackgroundNode.isUserInteractionEnabled = true self.placesButtonNode = HighlightableButtonNode() - self.placesButtonNode.setTitle("Places In This Area", with: Font.regular(17.0), with: presentationData.theme.rootController.navigationBar.buttonColor, for: .normal) + self.placesButtonNode.setTitle(presentationData.strings.Map_PlacesInThisArea, with: Font.regular(17.0), with: presentationData.theme.rootController.navigationBar.buttonColor, for: .normal) self.shadowNode = ASImageNode() self.shadowNode.contentMode = .scaleToFill diff --git a/submodules/LocationUI/Sources/LocationPickerControllerNode.swift b/submodules/LocationUI/Sources/LocationPickerControllerNode.swift index f13772ef3b..64a1d42783 100644 --- a/submodules/LocationUI/Sources/LocationPickerControllerNode.swift +++ b/submodules/LocationUI/Sources/LocationPickerControllerNode.swift @@ -548,7 +548,7 @@ final class LocationPickerControllerNode: ViewControllerTracingNode { if foundVenues == nil && !state.searchingVenuesAround { displayingPlacesButton = true } else if let previousLocation = foundVenuesLocation { - let currentLocation = CLLocation(latitude: coordinate.latitude, longitude: coordinate.longitude) + let currentLocation = CLLocation(latitude: coordinate.latitude, longitude: coordinate.longitude) if currentLocation.distance(from: previousLocation) > 300 { displayingPlacesButton = true } diff --git a/submodules/MergeLists/Sources/MergeLists.swift b/submodules/MergeLists/Sources/MergeLists.swift index 8b2780082e..5b366bba3a 100644 --- a/submodules/MergeLists/Sources/MergeLists.swift +++ b/submodules/MergeLists/Sources/MergeLists.swift @@ -181,7 +181,7 @@ public func mergeListsStableWithUpdates(leftList: [T], rightList: [T], allUpd return (removeIndices, insertItems, updatedIndices) } -@inlinable +//@inlinable public func mergeListsStableWithUpdates(leftList: [T], rightList: [T], isLess: (T, T) -> Bool, isEqual: (T, T) -> Bool, getId: (T) -> AnyHashable, allUpdated: Bool = false) -> ([Int], [(Int, T, Int?)], [(Int, T, Int)]) { var removeIndices: [Int] = [] var insertItems: [(Int, T, Int?)] = [] @@ -207,6 +207,25 @@ public func mergeListsStableWithUpdates(leftList: [T], rightList: [T], isLess } #endif + var leftStableIds: [AnyHashable] = [] + var rightStableIds: [AnyHashable] = [] + for item in leftList { + leftStableIds.append(getId(item)) + } + for item in rightList { + rightStableIds.append(getId(item)) + } + if Set(leftStableIds) == Set(rightStableIds) && leftStableIds != rightStableIds { + /*var i = 0 + var j = 0 + while true { + if getId(leftList[i]) != getId(rightList[i]) { + + } + }*/ + print("order changed") + } + var currentList = leftList var i = 0 diff --git a/submodules/MtProtoKit/MTProtoKit/MTContext.m b/submodules/MtProtoKit/MTProtoKit/MTContext.m index e38d403363..6abb56c6a0 100644 --- a/submodules/MtProtoKit/MTProtoKit/MTContext.m +++ b/submodules/MtProtoKit/MTProtoKit/MTContext.m @@ -1394,7 +1394,7 @@ static int32_t fixedTimeDifferenceValue = 0; NSArray *currentListeners = [[NSArray alloc] initWithArray:strongSelf->_changeListeners]; for (id listener in currentListeners) { if ([listener respondsToSelector:@selector(contextLoggedOut:)]) - [listener contextLoggedOut:self]; + [listener contextLoggedOut:strongSelf]; } } }]; diff --git a/submodules/MtProtoKit/MTProtoKit/MTProto.m b/submodules/MtProtoKit/MTProtoKit/MTProto.m index 67d8160f7d..6b4f2ce316 100644 --- a/submodules/MtProtoKit/MTProtoKit/MTProto.m +++ b/submodules/MtProtoKit/MTProtoKit/MTProto.m @@ -235,7 +235,7 @@ static const NSUInteger MTMaxUnacknowledgedMessageCount = 64; if ((_mtState & MTProtoStateStopped) == 0) { [self setMtState:_mtState | MTProtoStateStopped]; - + [_context removeChangeListener:self]; if (_transport != nil) { _transport.delegate = nil; @@ -2098,6 +2098,9 @@ static NSString *dumpHexString(NSData *data, int maxLength) { int64_t dataMessageId = 0; bool parseError = false; NSArray *parsedMessages = [self _parseIncomingMessages:decryptedData dataMessageId:&dataMessageId parseError:&parseError]; + + + for (MTIncomingMessage *message in parsedMessages) { if ([message.body isKindOfClass:[MTRpcResultMessage class]]) { MTRpcResultMessage *rpcResultMessage = message.body; @@ -2111,7 +2114,7 @@ static NSString *dumpHexString(NSData *data, int maxLength) { MTShortLog(@"[MTProto#%p@%p received AUTH_KEY_PERM_EMPTY]", self, _context); [self handleMissingKey:scheme.address]; [self requestSecureTransportReset]; - + return; } } diff --git a/submodules/PasswordSetupUI/Sources/SetupTwoStepVerificationContentNode.swift b/submodules/PasswordSetupUI/Sources/SetupTwoStepVerificationContentNode.swift index f2a564d5b7..80a45ac163 100644 --- a/submodules/PasswordSetupUI/Sources/SetupTwoStepVerificationContentNode.swift +++ b/submodules/PasswordSetupUI/Sources/SetupTwoStepVerificationContentNode.swift @@ -160,7 +160,7 @@ final class SetupTwoStepVerificationContentNode: ASDisplayNode, UITextFieldDeleg let minContentHeight = textHeight + inputHeight let contentHeight = min(215.0, max(size.height - insets.top - insets.bottom - 40.0, minContentHeight)) - let contentOrigin = insets.top + floor((size.height - insets.top - insets.bottom - contentHeight) / 2.0) + let contentOrigin = max(56.0, insets.top + floor((size.height - insets.top - insets.bottom - contentHeight) / 2.0)) let titleFrame = CGRect(origin: CGPoint(x: floor((size.width - titleSize.width) / 2.0), y: contentOrigin), size: titleSize) transition.updateFrame(node: self.titleNode, frame: titleFrame) transition.updateFrame(node: self.subtitleNode, frame: CGRect(origin: CGPoint(x: floor((size.width - subtitleSize.width) / 2.0), y: titleFrame.maxY + titleSubtitleSpacing), size: subtitleSize)) diff --git a/submodules/PasswordSetupUI/Sources/SetupTwoStepVerificationControllerNode.swift b/submodules/PasswordSetupUI/Sources/SetupTwoStepVerificationControllerNode.swift index b1eb0635ea..550b0f781c 100644 --- a/submodules/PasswordSetupUI/Sources/SetupTwoStepVerificationControllerNode.swift +++ b/submodules/PasswordSetupUI/Sources/SetupTwoStepVerificationControllerNode.swift @@ -698,7 +698,6 @@ final class SetupTwoStepVerificationControllerNode: ViewControllerTracingNode { }, transition: .animated(duration: 0.5, curve: .spring)) } if case let .enterEmail(enterEmail)? = self.innerState.data.state, case .create = enterEmail.state, enterEmail.email.isEmpty { - self.present(textAlertController(context: self.context, title: nil, text: self.presentationData.strings.TwoStepAuth_EmailSkipAlert, actions: [TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .destructiveAction, title: self.presentationData.strings.TwoStepAuth_EmailSkip, action: { continueImpl() })]), nil) diff --git a/submodules/Postbox/Sources/Coding.swift b/submodules/Postbox/Sources/Coding.swift index d1232ad1de..0e73649666 100644 --- a/submodules/Postbox/Sources/Coding.swift +++ b/submodules/Postbox/Sources/Coding.swift @@ -484,6 +484,22 @@ public final class PostboxEncoder { } } + public func encodeDataArray(_ value: [Data], forKey key: StaticString) { + self.encodeKey(key) + var type: Int8 = ValueType.BytesArray.rawValue + self.buffer.write(&type, offset: 0, length: 1) + var length: Int32 = Int32(value.count) + self.buffer.write(&length, offset: 0, length: 4) + + for object in value { + var length: Int32 = Int32(object.count) + self.buffer.write(&length, offset: 0, length: 4) + object.withUnsafeBytes { (bytes: UnsafePointer) -> Void in + self.buffer.write(bytes, offset: 0, length: Int(length)) + } + } + } + public func encodeObjectDictionary(_ value: [K : V], forKey key: StaticString) where K: PostboxCoding { self.encodeKey(key) var t: Int8 = ValueType.ObjectDictionary.rawValue @@ -1173,6 +1189,31 @@ public final class PostboxDecoder { } } + public func decodeOptionalDataArrayForKey(_ key: StaticString) -> [Data]? { + if PostboxDecoder.positionOnKey(self.buffer.memory, offset: &self.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: .BytesArray) { + var length: Int32 = 0 + memcpy(&length, self.buffer.memory + self.offset, 4) + self.offset += 4 + + var array: [Data] = [] + array.reserveCapacity(Int(length)) + + var i: Int32 = 0 + while i < length { + var length: Int32 = 0 + memcpy(&length, self.buffer.memory + self.offset, 4) + array.append(Data(bytes: self.buffer.memory.advanced(by: self.offset + 4), count: Int(length))) + self.offset += 4 + Int(length) + + i += 1 + } + + return array + } else { + return nil + } + } + public func decodeObjectArrayForKey(_ key: StaticString) -> [T] where T: PostboxCoding { if PostboxDecoder.positionOnKey(self.buffer.memory, offset: &self.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: .ObjectArray) { var length: Int32 = 0 diff --git a/submodules/Postbox/Sources/ItemCacheTable.swift b/submodules/Postbox/Sources/ItemCacheTable.swift index 60bfed7d1b..82a782081f 100644 --- a/submodules/Postbox/Sources/ItemCacheTable.swift +++ b/submodules/Postbox/Sources/ItemCacheTable.swift @@ -37,6 +37,17 @@ final class ItemCacheTable: Table { return key } + private func lowerBound(collectionId: ItemCacheCollectionId) -> ValueBoxKey { + let key = ValueBoxKey(length: 1 + 1) + key.setInt8(0, value: ItemCacheSection.items.rawValue) + key.setInt8(1, value: collectionId) + return key + } + + private func upperBound(collectionId: ItemCacheCollectionId) -> ValueBoxKey { + return self.lowerBound(collectionId: collectionId).successor + } + private func itemIdToAccessIndexKey(id: ItemCacheEntryId) -> ValueBoxKey { let key = ValueBoxKey(length: 1 + 1 + id.key.length) key.setInt8(0, value: ItemCacheSection.accessIndexToItemId.rawValue) @@ -72,6 +83,10 @@ final class ItemCacheTable: Table { self.valueBox.remove(self.table, key: self.itemKey(id: id), secure: false) } + func removeAll(collectionId: ItemCacheCollectionId) { + self.valueBox.removeRange(self.table, start: self.lowerBound(collectionId: collectionId), end: self.upperBound(collectionId: collectionId)) + } + override func clearMemoryCache() { } diff --git a/submodules/Postbox/Sources/Message.swift b/submodules/Postbox/Sources/Message.swift index 5960cc1bc4..3968afde45 100644 --- a/submodules/Postbox/Sources/Message.swift +++ b/submodules/Postbox/Sources/Message.swift @@ -525,6 +525,10 @@ public final class Message { return Message(stableId: self.stableId, stableVersion: self.stableVersion, id: self.id, globallyUniqueId: self.globallyUniqueId, groupingKey: self.groupingKey, groupInfo: self.groupInfo, timestamp: self.timestamp, flags: self.flags, tags: self.tags, globalTags: self.globalTags, localTags: self.localTags, forwardInfo: self.forwardInfo, author: self.author, text: self.text, attributes: self.attributes, media: media, peers: self.peers, associatedMessages: self.associatedMessages, associatedMessageIds: self.associatedMessageIds) } + public func withUpdatedPeers(_ peers: SimpleDictionary) -> Message { + return Message(stableId: self.stableId, stableVersion: self.stableVersion, id: self.id, globallyUniqueId: self.globallyUniqueId, groupingKey: self.groupingKey, groupInfo: self.groupInfo, timestamp: self.timestamp, flags: self.flags, tags: self.tags, globalTags: self.globalTags, localTags: self.localTags, forwardInfo: self.forwardInfo, author: self.author, text: self.text, attributes: self.attributes, media: self.media, peers: peers, associatedMessages: self.associatedMessages, associatedMessageIds: self.associatedMessageIds) + } + public func withUpdatedFlags(_ flags: MessageFlags) -> Message { return Message(stableId: self.stableId, stableVersion: self.stableVersion, id: self.id, globallyUniqueId: self.globallyUniqueId, groupingKey: self.groupingKey, groupInfo: self.groupInfo, timestamp: self.timestamp, flags: flags, tags: self.tags, globalTags: self.globalTags, localTags: self.localTags, forwardInfo: self.forwardInfo, author: self.author, text: self.text, attributes: self.attributes, media: self.media, peers: self.peers, associatedMessages: self.associatedMessages, associatedMessageIds: self.associatedMessageIds) } diff --git a/submodules/Postbox/Sources/MessageHistoryFailedTable.swift b/submodules/Postbox/Sources/MessageHistoryFailedTable.swift index 057e785ba4..eb9c7fa676 100644 --- a/submodules/Postbox/Sources/MessageHistoryFailedTable.swift +++ b/submodules/Postbox/Sources/MessageHistoryFailedTable.swift @@ -42,7 +42,7 @@ final class MessageHistoryFailedTable: Table { self.updatedMessageIds.remove(id) } - func get(peerId:PeerId) -> [MessageId] { + func get(peerId: PeerId) -> [MessageId] { var ids:[MessageId] = [] self.valueBox.range(self.table, start: self.lowerBound(peerId: peerId), end: self.upperBound(peerId: peerId), keys: { key in @@ -72,4 +72,3 @@ final class MessageHistoryFailedTable: Table { self.updatedPeerIds.removeAll() } } - diff --git a/submodules/Postbox/Sources/MessageHistoryTable.swift b/submodules/Postbox/Sources/MessageHistoryTable.swift index feac0da947..f0fb7ca809 100644 --- a/submodules/Postbox/Sources/MessageHistoryTable.swift +++ b/submodules/Postbox/Sources/MessageHistoryTable.swift @@ -2275,6 +2275,42 @@ final class MessageHistoryTable: Table { return Message(stableId: message.stableId, stableVersion: message.stableVersion, id: message.id, globallyUniqueId: message.globallyUniqueId, groupingKey: message.groupingKey, groupInfo: message.groupInfo, timestamp: message.timestamp, flags: message.flags, tags: message.tags, globalTags: message.globalTags, localTags: message.localTags, forwardInfo: forwardInfo, author: author, text: message.text, attributes: parsedAttributes, media: parsedMedia, peers: peers, associatedMessages: associatedMessages, associatedMessageIds: associatedMessageIds) } + func renderMessagePeers(_ message: Message, peerTable: PeerTable) -> Message { + var author: Peer? + var peers = SimpleDictionary() + if let authorId = message.author?.id { + author = peerTable.get(authorId) + } + + if let chatPeer = peerTable.get(message.id.peerId) { + peers[chatPeer.id] = chatPeer + + if let associatedPeerId = chatPeer.associatedPeerId { + if let peer = peerTable.get(associatedPeerId) { + peers[peer.id] = peer + } + } + } + + for media in message.media { + for peerId in media.peerIds { + if let peer = peerTable.get(peerId) { + peers[peer.id] = peer + } + } + } + + for attribute in message.attributes { + for peerId in attribute.associatedPeerIds { + if let peer = peerTable.get(peerId) { + peers[peer.id] = peer + } + } + } + + return message.withUpdatedPeers(peers) + } + func renderAssociatedMessages(associatedMessageIds: [MessageId], peerTable: PeerTable) -> SimpleDictionary { var associatedMessages = SimpleDictionary() for messageId in associatedMessageIds { diff --git a/submodules/Postbox/Sources/MessageHistoryView.swift b/submodules/Postbox/Sources/MessageHistoryView.swift index b5a0e719fb..615ef4082c 100644 --- a/submodules/Postbox/Sources/MessageHistoryView.swift +++ b/submodules/Postbox/Sources/MessageHistoryView.swift @@ -29,13 +29,13 @@ public struct MessageHistoryMessageEntry { enum MutableMessageHistoryEntry { case IntermediateMessageEntry(IntermediateMessage, MessageHistoryEntryLocation?, MessageHistoryEntryMonthLocation?) - case MessageEntry(MessageHistoryMessageEntry, reloadAssociatedMessages: Bool) + case MessageEntry(MessageHistoryMessageEntry, reloadAssociatedMessages: Bool, reloadPeers: Bool) var index: MessageIndex { switch self { case let .IntermediateMessageEntry(message, _, _): return message.index - case let .MessageEntry(message, _): + case let .MessageEntry(message, _, _): return message.message.index } } @@ -44,7 +44,7 @@ enum MutableMessageHistoryEntry { switch self { case let .IntermediateMessageEntry(message, _, _): return message.tags - case let .MessageEntry(message, _): + case let .MessageEntry(message, _, _): return message.message.tags } } @@ -53,8 +53,8 @@ enum MutableMessageHistoryEntry { switch self { case let .IntermediateMessageEntry(message, _, monthLocation): return .IntermediateMessageEntry(message, location, monthLocation) - case let .MessageEntry(message, reloadAssociatedMessages): - return .MessageEntry(MessageHistoryMessageEntry(message: message.message, location: location, monthLocation: message.monthLocation, attributes: message.attributes), reloadAssociatedMessages: reloadAssociatedMessages) + case let .MessageEntry(message, reloadAssociatedMessages, reloadPeers): + return .MessageEntry(MessageHistoryMessageEntry(message: message.message, location: location, monthLocation: message.monthLocation, attributes: message.attributes), reloadAssociatedMessages: reloadAssociatedMessages, reloadPeers: reloadPeers) } } @@ -62,8 +62,8 @@ enum MutableMessageHistoryEntry { switch self { case let .IntermediateMessageEntry(message, location, _): return .IntermediateMessageEntry(message, location, monthLocation) - case let .MessageEntry(message, reloadAssociatedMessages): - return .MessageEntry(MessageHistoryMessageEntry(message: message.message, location: message.location, monthLocation: monthLocation, attributes: message.attributes), reloadAssociatedMessages: reloadAssociatedMessages) + case let .MessageEntry(message, reloadAssociatedMessages, reloadPeers): + return .MessageEntry(MessageHistoryMessageEntry(message: message.message, location: message.location, monthLocation: monthLocation, attributes: message.attributes), reloadAssociatedMessages: reloadAssociatedMessages, reloadPeers: reloadPeers) } } @@ -79,12 +79,12 @@ enum MutableMessageHistoryEntry { } else { return self } - case let .MessageEntry(message, reloadAssociatedMessages): + case let .MessageEntry(message, reloadAssociatedMessages, reloadPeers): if let location = message.location { if message.message.index > index { - return .MessageEntry(MessageHistoryMessageEntry(message: message.message, location: MessageHistoryEntryLocation(index: location.index + 1, count: location.count + 1), monthLocation: message.monthLocation, attributes: message.attributes), reloadAssociatedMessages: reloadAssociatedMessages) + return .MessageEntry(MessageHistoryMessageEntry(message: message.message, location: MessageHistoryEntryLocation(index: location.index + 1, count: location.count + 1), monthLocation: message.monthLocation, attributes: message.attributes), reloadAssociatedMessages: reloadAssociatedMessages, reloadPeers: reloadPeers) } else { - return .MessageEntry(MessageHistoryMessageEntry(message: message.message, location: MessageHistoryEntryLocation(index: location.index, count: location.count + 1), monthLocation: message.monthLocation, attributes: message.attributes), reloadAssociatedMessages: reloadAssociatedMessages) + return .MessageEntry(MessageHistoryMessageEntry(message: message.message, location: MessageHistoryEntryLocation(index: location.index, count: location.count + 1), monthLocation: message.monthLocation, attributes: message.attributes), reloadAssociatedMessages: reloadAssociatedMessages, reloadPeers: reloadPeers) } } else { return self @@ -107,15 +107,15 @@ enum MutableMessageHistoryEntry { } else { return self } - case let .MessageEntry(message, reloadAssociatedMessages): + case let .MessageEntry(message, reloadAssociatedMessages, reloadPeers): if let location = message.location { if message.message.index > index { //assert(location.index > 0) //assert(location.count != 0) - return .MessageEntry(MessageHistoryMessageEntry(message: message.message, location: MessageHistoryEntryLocation(index: location.index - 1, count: location.count - 1), monthLocation: message.monthLocation, attributes: message.attributes), reloadAssociatedMessages: reloadAssociatedMessages) + return .MessageEntry(MessageHistoryMessageEntry(message: message.message, location: MessageHistoryEntryLocation(index: location.index - 1, count: location.count - 1), monthLocation: message.monthLocation, attributes: message.attributes), reloadAssociatedMessages: reloadAssociatedMessages, reloadPeers: reloadPeers) } else { //assert(location.count != 0) - return .MessageEntry(MessageHistoryMessageEntry(message: message.message, location: MessageHistoryEntryLocation(index: location.index, count: location.count - 1), monthLocation: message.monthLocation, attributes: message.attributes), reloadAssociatedMessages: reloadAssociatedMessages) + return .MessageEntry(MessageHistoryMessageEntry(message: message.message, location: MessageHistoryEntryLocation(index: location.index, count: location.count - 1), monthLocation: message.monthLocation, attributes: message.attributes), reloadAssociatedMessages: reloadAssociatedMessages, reloadPeers: reloadPeers) } } else { return self @@ -128,10 +128,10 @@ enum MutableMessageHistoryEntry { case let .IntermediateMessageEntry(message, location, monthLocation): let updatedMessage = IntermediateMessage(stableId: message.stableId, stableVersion: message.stableVersion, id: message.id, globallyUniqueId: message.globallyUniqueId, groupingKey: message.groupingKey, groupInfo: message.groupInfo, timestamp: timestamp, flags: message.flags, tags: message.tags, globalTags: message.globalTags, localTags: message.localTags, forwardInfo: message.forwardInfo, authorId: message.authorId, text: message.text, attributesData: message.attributesData, embeddedMediaData: message.embeddedMediaData, referencedMedia: message.referencedMedia) return .IntermediateMessageEntry(updatedMessage, location, monthLocation) - case let .MessageEntry(value, reloadAssociatedMessages): + case let .MessageEntry(value, reloadAssociatedMessages, reloadPeers): let message = value.message let updatedMessage = Message(stableId: message.stableId, stableVersion: message.stableVersion, id: message.id, globallyUniqueId: message.globallyUniqueId, groupingKey: message.groupingKey, groupInfo: message.groupInfo, timestamp: timestamp, flags: message.flags, tags: message.tags, globalTags: message.globalTags, localTags: message.localTags, forwardInfo: message.forwardInfo, author: message.author, text: message.text, attributes: message.attributes, media: message.media, peers: message.peers, associatedMessages: message.associatedMessages, associatedMessageIds: message.associatedMessageIds) - return .MessageEntry(MessageHistoryMessageEntry(message: updatedMessage, location: value.location, monthLocation: value.monthLocation, attributes: value.attributes), reloadAssociatedMessages: reloadAssociatedMessages) + return .MessageEntry(MessageHistoryMessageEntry(message: updatedMessage, location: value.location, monthLocation: value.monthLocation, attributes: value.attributes), reloadAssociatedMessages: reloadAssociatedMessages, reloadPeers: reloadPeers) } } @@ -139,7 +139,7 @@ enum MutableMessageHistoryEntry { switch self { case let .IntermediateMessageEntry(message, location, monthLocation): return [] - case let .MessageEntry(value, _): + case let .MessageEntry(value, _, _): return value.message.associatedMessageIds } } @@ -258,6 +258,7 @@ final class MutableMessageHistoryView { let tag: MessageTags? let namespaces: MessageIdNamespaces private let orderStatistics: MessageHistoryViewOrderStatistics + private let clipHoles: Bool private let anchor: HistoryViewInputAnchor fileprivate var combinedReadStates: MessageHistoryViewReadState? @@ -271,10 +272,11 @@ final class MutableMessageHistoryView { fileprivate(set) var sampledState: HistoryViewSample - init(postbox: Postbox, orderStatistics: MessageHistoryViewOrderStatistics, peerIds: MessageHistoryViewPeerIds, anchor inputAnchor: HistoryViewInputAnchor, combinedReadStates: MessageHistoryViewReadState?, transientReadStates: MessageHistoryViewReadState?, tag: MessageTags?, namespaces: MessageIdNamespaces, count: Int, topTaggedMessages: [MessageId.Namespace: MessageHistoryTopTaggedMessage?], additionalDatas: [AdditionalMessageHistoryViewDataEntry], getMessageCountInRange: (MessageIndex, MessageIndex) -> Int32) { + init(postbox: Postbox, orderStatistics: MessageHistoryViewOrderStatistics, clipHoles: Bool, peerIds: MessageHistoryViewPeerIds, anchor inputAnchor: HistoryViewInputAnchor, combinedReadStates: MessageHistoryViewReadState?, transientReadStates: MessageHistoryViewReadState?, tag: MessageTags?, namespaces: MessageIdNamespaces, count: Int, topTaggedMessages: [MessageId.Namespace: MessageHistoryTopTaggedMessage?], additionalDatas: [AdditionalMessageHistoryViewDataEntry], getMessageCountInRange: (MessageIndex, MessageIndex) -> Int32) { self.anchor = inputAnchor self.orderStatistics = orderStatistics + self.clipHoles = clipHoles self.peerIds = peerIds self.combinedReadStates = combinedReadStates self.transientReadStates = transientReadStates @@ -290,12 +292,12 @@ final class MutableMessageHistoryView { switch sampledState { case let .ready(anchor, holes): self.state = .loaded(HistoryViewLoadedState(anchor: anchor, tag: tag, namespaces: namespaces, statistics: self.orderStatistics, halfLimit: count + 1, locations: peerIds, postbox: postbox, holes: holes)) - self.sampledState = self.state.sample(postbox: postbox) + self.sampledState = self.state.sample(postbox: postbox, clipHoles: self.clipHoles) case .loadHole: break } } - self.sampledState = self.state.sample(postbox: postbox) + self.sampledState = self.state.sample(postbox: postbox, clipHoles: self.clipHoles) self.render(postbox: postbox) } @@ -320,7 +322,7 @@ final class MutableMessageHistoryView { break } } - self.sampledState = self.state.sample(postbox: postbox) + self.sampledState = self.state.sample(postbox: postbox, clipHoles: self.clipHoles) } func refreshDueToExternalTransaction(postbox: Postbox) -> Bool { @@ -509,7 +511,7 @@ final class MutableMessageHistoryView { break } } - self.sampledState = self.state.sample(postbox: postbox) + self.sampledState = self.state.sample(postbox: postbox, clipHoles: self.clipHoles) } for operationSet in operations { diff --git a/submodules/Postbox/Sources/MessageHistoryViewState.swift b/submodules/Postbox/Sources/MessageHistoryViewState.swift index 68995e3472..10dd22b484 100644 --- a/submodules/Postbox/Sources/MessageHistoryViewState.swift +++ b/submodules/Postbox/Sources/MessageHistoryViewState.swift @@ -874,10 +874,10 @@ final class HistoryViewLoadedState { let currentLocation = nextLocation nextLocation = nextLocation.successor switch entry { - case let .IntermediateMessageEntry(message, _, monthLocation): - return .IntermediateMessageEntry(message, currentLocation, monthLocation) - case let .MessageEntry(entry, reloadAssociatedMessages): - return .MessageEntry(MessageHistoryMessageEntry(message: entry.message, location: currentLocation, monthLocation: entry.monthLocation, attributes: entry.attributes), reloadAssociatedMessages: reloadAssociatedMessages) + case let .IntermediateMessageEntry(message, _, monthLocation): + return .IntermediateMessageEntry(message, currentLocation, monthLocation) + case let .MessageEntry(entry, reloadAssociatedMessages, reloadPeers): + return .MessageEntry(MessageHistoryMessageEntry(message: entry.message, location: currentLocation, monthLocation: entry.monthLocation, attributes: entry.attributes), reloadAssociatedMessages: reloadAssociatedMessages, reloadPeers: reloadPeers) } } } @@ -900,8 +900,8 @@ final class HistoryViewLoadedState { switch entry { case let .IntermediateMessageEntry(message, location, _): return .IntermediateMessageEntry(message, location, MessageHistoryEntryMonthLocation(indexInMonth: Int32(currentIndexInMonth))) - case let .MessageEntry(entry, reloadAssociatedMessages): - return .MessageEntry(MessageHistoryMessageEntry(message: entry.message, location: entry.location, monthLocation: MessageHistoryEntryMonthLocation(indexInMonth: Int32(currentIndexInMonth)), attributes: entry.attributes), reloadAssociatedMessages: reloadAssociatedMessages) + case let .MessageEntry(entry, reloadAssociatedMessages, reloadPeers): + return .MessageEntry(MessageHistoryMessageEntry(message: entry.message, location: entry.location, monthLocation: MessageHistoryEntryMonthLocation(indexInMonth: Int32(currentIndexInMonth)), attributes: entry.attributes), reloadAssociatedMessages: reloadAssociatedMessages, reloadPeers: reloadPeers) } } } @@ -960,8 +960,8 @@ final class HistoryViewLoadedState { switch entry { case let .IntermediateMessageEntry(message, location, monthLocation): return .IntermediateMessageEntry(message.withUpdatedGroupInfo(groupInfo), location, monthLocation) - case let .MessageEntry(messageEntry, reloadAssociatedMessages): - return .MessageEntry(MessageHistoryMessageEntry(message: messageEntry.message.withUpdatedGroupInfo(groupInfo), location: messageEntry.location, monthLocation: messageEntry.monthLocation, attributes: messageEntry.attributes), reloadAssociatedMessages: reloadAssociatedMessages) + case let .MessageEntry(messageEntry, reloadAssociatedMessages, reloadPeers): + return .MessageEntry(MessageHistoryMessageEntry(message: messageEntry.message.withUpdatedGroupInfo(groupInfo), location: messageEntry.location, monthLocation: messageEntry.monthLocation, attributes: messageEntry.attributes), reloadAssociatedMessages: reloadAssociatedMessages, reloadPeers: reloadPeers) } } return nil @@ -983,8 +983,8 @@ final class HistoryViewLoadedState { switch entry { case let .IntermediateMessageEntry(message, location, monthLocation): return .IntermediateMessageEntry(message.withUpdatedEmbeddedMedia(buffer), location, monthLocation) - case let .MessageEntry(messageEntry, reloadAssociatedMessages): - return .MessageEntry(MessageHistoryMessageEntry(message: messageEntry.message, location: messageEntry.location, monthLocation: messageEntry.monthLocation, attributes: messageEntry.attributes), reloadAssociatedMessages: reloadAssociatedMessages) + case let .MessageEntry(messageEntry, reloadAssociatedMessages, reloadPeers): + return .MessageEntry(MessageHistoryMessageEntry(message: messageEntry.message, location: messageEntry.location, monthLocation: messageEntry.monthLocation, attributes: messageEntry.attributes), reloadAssociatedMessages: reloadAssociatedMessages, reloadPeers: reloadPeers) } }) } @@ -994,8 +994,9 @@ final class HistoryViewLoadedState { for space in self.orderedEntriesBySpace.keys { let spaceUpdated = self.orderedEntriesBySpace[space]!.mutableScan({ entry in switch entry { - case let .MessageEntry(value, reloadAssociatedMessages): + case let .MessageEntry(value, reloadAssociatedMessages, reloadPeers): let message = value.message + var reloadPeers = reloadPeers var rebuild = false for media in message.media { @@ -1010,6 +1011,9 @@ final class HistoryViewLoadedState { for media in message.media { if let mediaId = media.id, let updated = updatedMedia[mediaId] { if let updated = updated { + if media.peerIds != updated.peerIds { + reloadPeers = true + } messageMedia.append(updated) } } else { @@ -1017,7 +1021,7 @@ final class HistoryViewLoadedState { } } let updatedMessage = Message(stableId: message.stableId, stableVersion: message.stableVersion, id: message.id, globallyUniqueId: message.globallyUniqueId, groupingKey: message.groupingKey, groupInfo: message.groupInfo, timestamp: message.timestamp, flags: message.flags, tags: message.tags, globalTags: message.globalTags, localTags: message.localTags, forwardInfo: message.forwardInfo, author: message.author, text: message.text, attributes: message.attributes, media: messageMedia, peers: message.peers, associatedMessages: message.associatedMessages, associatedMessageIds: message.associatedMessageIds) - return .MessageEntry(MessageHistoryMessageEntry(message: updatedMessage, location: value.location, monthLocation: value.monthLocation, attributes: value.attributes), reloadAssociatedMessages: reloadAssociatedMessages) + return .MessageEntry(MessageHistoryMessageEntry(message: updatedMessage, location: value.location, monthLocation: value.monthLocation, attributes: value.attributes), reloadAssociatedMessages: reloadAssociatedMessages, reloadPeers: reloadPeers) } case .IntermediateMessageEntry: break @@ -1046,9 +1050,9 @@ final class HistoryViewLoadedState { switch current { case .IntermediateMessageEntry: return current - case let .MessageEntry(messageEntry, _): + case let .MessageEntry(messageEntry, _, reloadPeers): updated = true - return .MessageEntry(messageEntry, reloadAssociatedMessages: true) + return .MessageEntry(messageEntry, reloadAssociatedMessages: true, reloadPeers: reloadPeers) } }) } @@ -1109,11 +1113,11 @@ final class HistoryViewLoadedState { switch current { case .IntermediateMessageEntry: return current - case let .MessageEntry(messageEntry, reloadAssociatedMessages): + case let .MessageEntry(messageEntry, reloadAssociatedMessages, reloadPeers): updated = true if let associatedMessages = messageEntry.message.associatedMessages.filteredOut(keysIn: [index.id]) { - return .MessageEntry(MessageHistoryMessageEntry(message: messageEntry.message.withUpdatedAssociatedMessages(associatedMessages), location: messageEntry.location, monthLocation: messageEntry.monthLocation, attributes: messageEntry.attributes), reloadAssociatedMessages: reloadAssociatedMessages) + return .MessageEntry(MessageHistoryMessageEntry(message: messageEntry.message.withUpdatedAssociatedMessages(associatedMessages), location: messageEntry.location, monthLocation: messageEntry.monthLocation, attributes: messageEntry.attributes), reloadAssociatedMessages: reloadAssociatedMessages, reloadPeers: reloadPeers) } else { return current } @@ -1130,7 +1134,7 @@ final class HistoryViewLoadedState { return updated } - func completeAndSample(postbox: Postbox) -> HistoryViewLoadedSample { + func completeAndSample(postbox: Postbox, clipHoles: Bool) -> HistoryViewLoadedSample { if !self.spacesWithRemovals.isEmpty { for space in self.spacesWithRemovals { self.fillSpace(space: space, postbox: postbox) @@ -1161,7 +1165,7 @@ final class HistoryViewLoadedState { entry = self.orderedEntriesBySpace[space]!.higherThanAnchor[index] } - if !clipRanges.isEmpty { + if clipHoles && !clipRanges.isEmpty { let entryIndex = entry.index for range in clipRanges { if range.contains(entryIndex) { @@ -1177,14 +1181,22 @@ final class HistoryViewLoadedState { } switch entry { - case let .MessageEntry(value, reloadAssociatedMessages): + case let .MessageEntry(value, reloadAssociatedMessages, reloadPeers): + var updatedMessage = value.message if reloadAssociatedMessages { let associatedMessages = postbox.messageHistoryTable.renderAssociatedMessages(associatedMessageIds: value.message.associatedMessageIds, peerTable: postbox.peerTable) - let updatedValue = MessageHistoryMessageEntry(message: value.message.withUpdatedAssociatedMessages(associatedMessages), location: value.location, monthLocation: value.monthLocation, attributes: value.attributes) + updatedMessage = value.message.withUpdatedAssociatedMessages(associatedMessages) + } + if reloadPeers { + updatedMessage = postbox.messageHistoryTable.renderMessagePeers(updatedMessage, peerTable: postbox.peerTable) + } + + if value.message !== updatedMessage { + let updatedValue = MessageHistoryMessageEntry(message: updatedMessage, location: value.location, monthLocation: value.monthLocation, attributes: value.attributes) if directionIndex == 0 { - self.orderedEntriesBySpace[space]!.setLowerOrAtAnchorAtArrayIndex(index, to: .MessageEntry(updatedValue, reloadAssociatedMessages: false)) + self.orderedEntriesBySpace[space]!.setLowerOrAtAnchorAtArrayIndex(index, to: .MessageEntry(updatedValue, reloadAssociatedMessages: false, reloadPeers: false)) } else { - self.orderedEntriesBySpace[space]!.setHigherThanAnchorAtArrayIndex(index, to: .MessageEntry(updatedValue, reloadAssociatedMessages: false)) + self.orderedEntriesBySpace[space]!.setHigherThanAnchorAtArrayIndex(index, to: .MessageEntry(updatedValue, reloadAssociatedMessages: false, reloadPeers: false)) } result.append(updatedValue) } else { @@ -1198,9 +1210,9 @@ final class HistoryViewLoadedState { } let entry = MessageHistoryMessageEntry(message: renderedMessage, location: location, monthLocation: monthLocation, attributes: MutableMessageHistoryEntryAttributes(authorIsContact: authorIsContact)) if directionIndex == 0 { - self.orderedEntriesBySpace[space]!.setLowerOrAtAnchorAtArrayIndex(index, to: .MessageEntry(entry, reloadAssociatedMessages: false)) + self.orderedEntriesBySpace[space]!.setLowerOrAtAnchorAtArrayIndex(index, to: .MessageEntry(entry, reloadAssociatedMessages: false, reloadPeers: false)) } else { - self.orderedEntriesBySpace[space]!.setHigherThanAnchorAtArrayIndex(index, to: .MessageEntry(entry, reloadAssociatedMessages: false)) + self.orderedEntriesBySpace[space]!.setHigherThanAnchorAtArrayIndex(index, to: .MessageEntry(entry, reloadAssociatedMessages: false, reloadPeers: false)) } result.append(entry) } @@ -1361,12 +1373,12 @@ enum HistoryViewState { } } - func sample(postbox: Postbox) -> HistoryViewSample { + func sample(postbox: Postbox, clipHoles: Bool) -> HistoryViewSample { switch self { - case let .loading(loadingState): - return .loading(loadingState.checkAndSample(postbox: postbox)) - case let .loaded(loadedState): - return .loaded(loadedState.completeAndSample(postbox: postbox)) + case let .loading(loadingState): + return .loading(loadingState.checkAndSample(postbox: postbox)) + case let .loaded(loadedState): + return .loaded(loadedState.completeAndSample(postbox: postbox, clipHoles: clipHoles)) } } } diff --git a/submodules/Postbox/Sources/MessageOfInterestHolesView.swift b/submodules/Postbox/Sources/MessageOfInterestHolesView.swift index efa86ca1ec..caa7f03362 100644 --- a/submodules/Postbox/Sources/MessageOfInterestHolesView.swift +++ b/submodules/Postbox/Sources/MessageOfInterestHolesView.swift @@ -60,7 +60,7 @@ final class MutableMessageOfInterestHolesView: MutablePostboxView { } } self.anchor = anchor - self.wrappedView = MutableMessageHistoryView(postbox: postbox, orderStatistics: [], peerIds: peerIds, anchor: self.anchor, combinedReadStates: nil, transientReadStates: nil, tag: nil, namespaces: .all, count: self.count, topTaggedMessages: [:], additionalDatas: [], getMessageCountInRange: { _, _ in return 0}) + self.wrappedView = MutableMessageHistoryView(postbox: postbox, orderStatistics: [], clipHoles: true, peerIds: peerIds, anchor: self.anchor, combinedReadStates: nil, transientReadStates: nil, tag: nil, namespaces: .all, count: self.count, topTaggedMessages: [:], additionalDatas: [], getMessageCountInRange: { _, _ in return 0}) let _ = self.updateFromView() } @@ -132,7 +132,7 @@ final class MutableMessageOfInterestHolesView: MutablePostboxView { case let .peer(id): peerIds = postbox.peerIdsForLocation(.peer(id), tagMask: nil) } - self.wrappedView = MutableMessageHistoryView(postbox: postbox, orderStatistics: [], peerIds: peerIds, anchor: self.anchor, combinedReadStates: nil, transientReadStates: nil, tag: nil, namespaces: .all, count: self.count, topTaggedMessages: [:], additionalDatas: [], getMessageCountInRange: { _, _ in return 0}) + self.wrappedView = MutableMessageHistoryView(postbox: postbox, orderStatistics: [], clipHoles: true, peerIds: peerIds, anchor: self.anchor, combinedReadStates: nil, transientReadStates: nil, tag: nil, namespaces: .all, count: self.count, topTaggedMessages: [:], additionalDatas: [], getMessageCountInRange: { _, _ in return 0}) return self.updateFromView() } else if self.wrappedView.replay(postbox: postbox, transaction: transaction) { var reloadView = false @@ -160,7 +160,7 @@ final class MutableMessageOfInterestHolesView: MutablePostboxView { case let .peer(id): peerIds = postbox.peerIdsForLocation(.peer(id), tagMask: nil) } - self.wrappedView = MutableMessageHistoryView(postbox: postbox, orderStatistics: [], peerIds: peerIds, anchor: self.anchor, combinedReadStates: nil, transientReadStates: nil, tag: nil, namespaces: .all, count: self.count, topTaggedMessages: [:], additionalDatas: [], getMessageCountInRange: { _, _ in return 0}) + self.wrappedView = MutableMessageHistoryView(postbox: postbox, orderStatistics: [], clipHoles: true, peerIds: peerIds, anchor: self.anchor, combinedReadStates: nil, transientReadStates: nil, tag: nil, namespaces: .all, count: self.count, topTaggedMessages: [:], additionalDatas: [], getMessageCountInRange: { _, _ in return 0}) } return self.updateFromView() diff --git a/submodules/Postbox/Sources/Postbox.swift b/submodules/Postbox/Sources/Postbox.swift index 9e333e6f9f..187939b9ab 100644 --- a/submodules/Postbox/Sources/Postbox.swift +++ b/submodules/Postbox/Sources/Postbox.swift @@ -655,6 +655,11 @@ public final class Transaction { return self.postbox?.retrieveItemCacheEntry(id: id) } + public func clearItemCacheCollection(collectionId: ItemCacheCollectionId) { + assert(!self.disposed) + self.postbox?.clearItemCacheCollection(collectionId: collectionId) + } + public func operationLogGetNextEntryLocalIndex(peerId: PeerId, tag: PeerOperationLogTag) -> Int32 { assert(!self.disposed) if let postbox = self.postbox { @@ -2065,6 +2070,10 @@ public final class Postbox { return self.itemCacheTable.retrieve(id: id, metaTable: self.itemCacheMetaTable) } + func clearItemCacheCollection(collectionId: ItemCacheCollectionId) { + return self.itemCacheTable.removeAll(collectionId: collectionId) + } + fileprivate func removeItemCacheEntry(id: ItemCacheEntryId) { self.itemCacheTable.remove(id: id, metaTable: self.itemCacheMetaTable) } @@ -2257,7 +2266,7 @@ public final class Postbox { return peerIds } - public func aroundMessageOfInterestHistoryViewForChatLocation(_ chatLocation: ChatLocation, count: Int, topTaggedMessageIdNamespaces: Set, tagMask: MessageTags?, namespaces: MessageIdNamespaces, orderStatistics: MessageHistoryViewOrderStatistics, additionalData: [AdditionalMessageHistoryViewData]) -> Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> { + public func aroundMessageOfInterestHistoryViewForChatLocation(_ chatLocation: ChatLocation, count: Int, clipHoles: Bool = true, topTaggedMessageIdNamespaces: Set, tagMask: MessageTags?, namespaces: MessageIdNamespaces, orderStatistics: MessageHistoryViewOrderStatistics, additionalData: [AdditionalMessageHistoryViewData]) -> Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> { return self.transactionSignal(userInteractive: true, { subscriber, transaction in let peerIds = self.peerIdsForLocation(chatLocation, tagMask: tagMask) @@ -2303,26 +2312,26 @@ public final class Postbox { } } } - return self.syncAroundMessageHistoryViewForPeerId(subscriber: subscriber, peerIds: peerIds, count: count, anchor: anchor, fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: topTaggedMessageIdNamespaces, tagMask: tagMask, namespaces: namespaces, orderStatistics: orderStatistics, additionalData: additionalData) + return self.syncAroundMessageHistoryViewForPeerId(subscriber: subscriber, peerIds: peerIds, count: count, clipHoles: clipHoles, anchor: anchor, fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: topTaggedMessageIdNamespaces, tagMask: tagMask, namespaces: namespaces, orderStatistics: orderStatistics, additionalData: additionalData) }) } - public func aroundIdMessageHistoryViewForLocation(_ chatLocation: ChatLocation, count: Int, messageId: MessageId, topTaggedMessageIdNamespaces: Set, tagMask: MessageTags?, namespaces: MessageIdNamespaces, orderStatistics: MessageHistoryViewOrderStatistics, additionalData: [AdditionalMessageHistoryViewData] = []) -> Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> { + public func aroundIdMessageHistoryViewForLocation(_ chatLocation: ChatLocation, count: Int, clipHoles: Bool = true, messageId: MessageId, topTaggedMessageIdNamespaces: Set, tagMask: MessageTags?, namespaces: MessageIdNamespaces, orderStatistics: MessageHistoryViewOrderStatistics, additionalData: [AdditionalMessageHistoryViewData] = []) -> Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> { return self.transactionSignal { subscriber, transaction in let peerIds = self.peerIdsForLocation(chatLocation, tagMask: tagMask) - return self.syncAroundMessageHistoryViewForPeerId(subscriber: subscriber, peerIds: peerIds, count: count, anchor: .message(messageId), fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: topTaggedMessageIdNamespaces, tagMask: tagMask, namespaces: namespaces, orderStatistics: orderStatistics, additionalData: additionalData) + return self.syncAroundMessageHistoryViewForPeerId(subscriber: subscriber, peerIds: peerIds, count: count, clipHoles: clipHoles, anchor: .message(messageId), fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: topTaggedMessageIdNamespaces, tagMask: tagMask, namespaces: namespaces, orderStatistics: orderStatistics, additionalData: additionalData) } } - public func aroundMessageHistoryViewForLocation(_ chatLocation: ChatLocation, anchor: HistoryViewInputAnchor, count: Int, fixedCombinedReadStates: MessageHistoryViewReadState?, topTaggedMessageIdNamespaces: Set, tagMask: MessageTags?, namespaces: MessageIdNamespaces, orderStatistics: MessageHistoryViewOrderStatistics, additionalData: [AdditionalMessageHistoryViewData] = []) -> Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> { + public func aroundMessageHistoryViewForLocation(_ chatLocation: ChatLocation, anchor: HistoryViewInputAnchor, count: Int, clipHoles: Bool = true, fixedCombinedReadStates: MessageHistoryViewReadState?, topTaggedMessageIdNamespaces: Set, tagMask: MessageTags?, namespaces: MessageIdNamespaces, orderStatistics: MessageHistoryViewOrderStatistics, additionalData: [AdditionalMessageHistoryViewData] = []) -> Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> { return self.transactionSignal { subscriber, transaction in let peerIds = self.peerIdsForLocation(chatLocation, tagMask: tagMask) - return self.syncAroundMessageHistoryViewForPeerId(subscriber: subscriber, peerIds: peerIds, count: count, anchor: anchor, fixedCombinedReadStates: fixedCombinedReadStates, topTaggedMessageIdNamespaces: topTaggedMessageIdNamespaces, tagMask: tagMask, namespaces: namespaces, orderStatistics: orderStatistics, additionalData: additionalData) + return self.syncAroundMessageHistoryViewForPeerId(subscriber: subscriber, peerIds: peerIds, count: count, clipHoles: clipHoles, anchor: anchor, fixedCombinedReadStates: fixedCombinedReadStates, topTaggedMessageIdNamespaces: topTaggedMessageIdNamespaces, tagMask: tagMask, namespaces: namespaces, orderStatistics: orderStatistics, additionalData: additionalData) } } - private func syncAroundMessageHistoryViewForPeerId(subscriber: Subscriber<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError>, peerIds: MessageHistoryViewPeerIds, count: Int, anchor: HistoryViewInputAnchor, fixedCombinedReadStates: MessageHistoryViewReadState?, topTaggedMessageIdNamespaces: Set, tagMask: MessageTags?, namespaces: MessageIdNamespaces, orderStatistics: MessageHistoryViewOrderStatistics, additionalData: [AdditionalMessageHistoryViewData]) -> Disposable { + private func syncAroundMessageHistoryViewForPeerId(subscriber: Subscriber<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError>, peerIds: MessageHistoryViewPeerIds, count: Int, clipHoles: Bool, anchor: HistoryViewInputAnchor, fixedCombinedReadStates: MessageHistoryViewReadState?, topTaggedMessageIdNamespaces: Set, tagMask: MessageTags?, namespaces: MessageIdNamespaces, orderStatistics: MessageHistoryViewOrderStatistics, additionalData: [AdditionalMessageHistoryViewData]) -> Disposable { var topTaggedMessages: [MessageId.Namespace: MessageHistoryTopTaggedMessage?] = [:] var mainPeerId: PeerId? switch peerIds { @@ -2411,7 +2420,7 @@ public final class Postbox { readStates = transientReadStates } - let mutableView = MutableMessageHistoryView(postbox: self, orderStatistics: orderStatistics, peerIds: peerIds, anchor: anchor, combinedReadStates: readStates, transientReadStates: transientReadStates, tag: tagMask, namespaces: namespaces, count: count, topTaggedMessages: topTaggedMessages, additionalDatas: additionalDataEntries, getMessageCountInRange: { lowerBound, upperBound in + let mutableView = MutableMessageHistoryView(postbox: self, orderStatistics: orderStatistics, clipHoles: clipHoles, peerIds: peerIds, anchor: anchor, combinedReadStates: readStates, transientReadStates: transientReadStates, tag: tagMask, namespaces: namespaces, count: count, topTaggedMessages: topTaggedMessages, additionalDatas: additionalDataEntries, getMessageCountInRange: { lowerBound, upperBound in if let tagMask = tagMask { return Int32(self.messageHistoryTable.getMessageCountInRange(peerId: lowerBound.id.peerId, namespace: lowerBound.id.namespace, tag: tagMask, lowerBound: lowerBound, upperBound: upperBound)) } else { diff --git a/submodules/Postbox/Sources/ValueBoxKey.swift b/submodules/Postbox/Sources/ValueBoxKey.swift index b309f6589b..9c2d75487a 100644 --- a/submodules/Postbox/Sources/ValueBoxKey.swift +++ b/submodules/Postbox/Sources/ValueBoxKey.swift @@ -38,6 +38,13 @@ public struct ValueBoxKey: Equatable, Hashable, CustomStringConvertible, Compara memcpy(self.memory, buffer.memory, buffer.length) } + public func setData(_ offset: Int, value: Data) { + let valueLength = value.count + value.withUnsafeBytes { (bytes: UnsafePointer) -> Void in + memcpy(self.memory + offset, bytes, valueLength) + } + } + public func setInt32(_ offset: Int, value: Int32) { var bigEndianValue = Int32(bigEndian: value) memcpy(self.memory + offset, &bigEndianValue, 4) diff --git a/submodules/ScreenCaptureDetection/Sources/ScreenCaptureDetection.swift b/submodules/ScreenCaptureDetection/Sources/ScreenCaptureDetection.swift index 0c35661804..26f6bfba5b 100644 --- a/submodules/ScreenCaptureDetection/Sources/ScreenCaptureDetection.swift +++ b/submodules/ScreenCaptureDetection/Sources/ScreenCaptureDetection.swift @@ -75,3 +75,50 @@ public func screenCaptureEvents() -> Signal { } |> runOn(Queue.mainQueue()) } + +public final class ScreenCaptureDetectionManager { + private var observer: NSObjectProtocol? + private var screenRecordingDisposable: Disposable? + private var screenRecordingCheckTimer: SwiftSignalKit.Timer? + + public init(check: @escaping () -> Bool) { + self.observer = NotificationCenter.default.addObserver(forName: UIApplication.userDidTakeScreenshotNotification, object: nil, queue: .main, using: { [weak self] _ in + guard let strongSelf = self else { + return + } + check() + }) + + self.screenRecordingDisposable = screenRecordingActive().start(next: { [weak self] value in + Queue.mainQueue().async { + guard let strongSelf = self else { + return + } + if value { + if strongSelf.screenRecordingCheckTimer == nil { + strongSelf.screenRecordingCheckTimer = SwiftSignalKit.Timer(timeout: 0.5, repeat: true, completion: { + guard let strongSelf = self else { + return + } + if check() { + strongSelf.screenRecordingCheckTimer?.invalidate() + strongSelf.screenRecordingCheckTimer = nil + } + }, queue: Queue.mainQueue()) + strongSelf.screenRecordingCheckTimer?.start() + } + } else if strongSelf.screenRecordingCheckTimer != nil { + strongSelf.screenRecordingCheckTimer?.invalidate() + strongSelf.screenRecordingCheckTimer = nil + } + } + }) + } + + deinit { + NotificationCenter.default.removeObserver(self.observer) + self.screenRecordingDisposable?.dispose() + self.screenRecordingCheckTimer?.invalidate() + self.screenRecordingCheckTimer = nil + } +} diff --git a/submodules/SearchBarNode/Sources/SearchBarNode.swift b/submodules/SearchBarNode/Sources/SearchBarNode.swift index 7c381d91a9..16b8215467 100644 --- a/submodules/SearchBarNode/Sources/SearchBarNode.swift +++ b/submodules/SearchBarNode/Sources/SearchBarNode.swift @@ -87,12 +87,13 @@ private class SearchBarTextField: UITextField { } var rect = bounds.insetBy(dx: 4.0, dy: 4.0) - let prefixSize = self.prefixLabel.measure(bounds.size) + let prefixSize = self.prefixLabel.measure(CGSize(width: floor(bounds.size.width * 0.7), height: bounds.size.height)) if !prefixSize.width.isZero { let prefixOffset = prefixSize.width rect.origin.x += prefixOffset rect.size.width -= prefixOffset } + rect.size.width = max(rect.size.width, 10.0) return rect } @@ -117,7 +118,7 @@ private class SearchBarTextField: UITextField { let labelSize = self.placeholderLabel.measure(textRect.size) self.placeholderLabel.frame = CGRect(origin: CGPoint(x: textRect.minX, y: textRect.minY + textOffset), size: labelSize) - let prefixSize = self.prefixLabel.measure(bounds.size) + let prefixSize = self.prefixLabel.measure(CGSize(width: floor(bounds.size.width * 0.7), height: bounds.size.height)) let prefixBounds = bounds.insetBy(dx: 4.0, dy: 4.0) self.prefixLabel.frame = CGRect(origin: CGPoint(x: prefixBounds.minX, y: prefixBounds.minY + textOffset), size: prefixSize) } diff --git a/submodules/SemanticStatusNode/Sources/SemanticStatusNode.swift b/submodules/SemanticStatusNode/Sources/SemanticStatusNode.swift index 6f7f587be3..483e62c5a5 100644 --- a/submodules/SemanticStatusNode/Sources/SemanticStatusNode.swift +++ b/submodules/SemanticStatusNode/Sources/SemanticStatusNode.swift @@ -334,14 +334,14 @@ private final class SemanticStatusNodeProgressContext: SemanticStatusNodeStateCo let resolvedValue: CGFloat? if let value = self.value { if let transition = self.transition { - transition.valueAt(timestamp: timestamp, actualValue: value) + resolvedValue = transition.valueAt(timestamp: timestamp, actualValue: value) } else { resolvedValue = value } } else { resolvedValue = nil } - return DrawingState(transitionFraction: transitionFraction, value: self.value, displayCancel: self.displayCancel, timestamp: timestamp) + return DrawingState(transitionFraction: transitionFraction, value: resolvedValue, displayCancel: self.displayCancel, timestamp: timestamp) } func updateValue(value: CGFloat?) { @@ -351,7 +351,7 @@ private final class SemanticStatusNodeProgressContext: SemanticStatusNodeStateCo let timestamp = CACurrentMediaTime() if let value = value, let previousValue = previousValue { if let transition = self.transition { - self.transition = SemanticStatusNodeProgressTransition(beginTime: timestamp, initialValue: transition.valueAt(timestamp: timestamp, actualValue: value)) + self.transition = SemanticStatusNodeProgressTransition(beginTime: timestamp, initialValue: transition.valueAt(timestamp: timestamp, actualValue: previousValue)) } else { self.transition = SemanticStatusNodeProgressTransition(beginTime: timestamp, initialValue: previousValue) } @@ -388,7 +388,7 @@ private extension SemanticStatusNodeState { } case let .progress(value, cancelEnabled): if let current = current as? SemanticStatusNodeProgressContext, current.displayCancel == cancelEnabled { - current.value = value + current.updateValue(value: value) return current } else { return SemanticStatusNodeProgressContext(value: value, displayCancel: cancelEnabled) diff --git a/submodules/SettingsUI/Sources/BubbleSettings/BubbleSettingsController.swift b/submodules/SettingsUI/Sources/BubbleSettings/BubbleSettingsController.swift new file mode 100644 index 0000000000..daa01db3bb --- /dev/null +++ b/submodules/SettingsUI/Sources/BubbleSettings/BubbleSettingsController.swift @@ -0,0 +1,542 @@ +import Foundation +import UIKit +import Display +import Postbox +import SwiftSignalKit +import AsyncDisplayKit +import TelegramCore +import SyncCore +import TelegramPresentationData +import TelegramUIPreferences +import AccountContext +import ChatListUI +import WallpaperResources +import LegacyComponents +import ItemListUI + +private func generateMaskImage(color: UIColor) -> UIImage? { + return generateImage(CGSize(width: 1.0, height: 80.0), opaque: false, rotatedContext: { size, context in + let bounds = CGRect(origin: CGPoint(), size: size) + context.clear(bounds) + + let gradientColors = [color.withAlphaComponent(0.0).cgColor, color.cgColor, color.cgColor] as CFArray + + var locations: [CGFloat] = [0.0, 0.75, 1.0] + let colorSpace = CGColorSpaceCreateDeviceRGB() + let gradient = CGGradient(colorsSpace: colorSpace, colors: gradientColors, locations: &locations)! + + context.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: 0.0), end: CGPoint(x: 0.0, y: 80.0), options: CGGradientDrawingOptions()) + }) +} + +private final class BubbleSettingsControllerNode: ASDisplayNode, UIScrollViewDelegate { + private let context: AccountContext + private var presentationThemeSettings: PresentationThemeSettings + private var presentationData: PresentationData + + private let referenceTimestamp: Int32 + + private let scrollNode: ASScrollNode + + private let maskNode: ASImageNode + private let chatBackgroundNode: WallpaperBackgroundNode + private let messagesContainerNode: ASDisplayNode + private var dateHeaderNode: ListViewItemHeaderNode? + private var messageNodes: [ListViewItemNode]? + private let toolbarNode: BubbleSettingsToolbarNode + + private var validLayout: (ContainerViewLayout, CGFloat)? + + init(context: AccountContext, presentationThemeSettings: PresentationThemeSettings, dismiss: @escaping () -> Void, apply: @escaping (PresentationChatBubbleSettings) -> Void) { + self.context = context + + self.presentationData = context.sharedContext.currentPresentationData.with { $0 } + self.presentationThemeSettings = presentationThemeSettings + + let calendar = Calendar(identifier: .gregorian) + var components = calendar.dateComponents(Set([.era, .year, .month, .day, .hour, .minute, .second]), from: Date()) + components.hour = 13 + components.minute = 0 + components.second = 0 + self.referenceTimestamp = Int32(calendar.date(from: components)?.timeIntervalSince1970 ?? 0.0) + + self.scrollNode = ASScrollNode() + + self.chatBackgroundNode = WallpaperBackgroundNode() + self.chatBackgroundNode.displaysAsynchronously = false + + self.messagesContainerNode = ASDisplayNode() + self.messagesContainerNode.clipsToBounds = true + self.messagesContainerNode.transform = CATransform3DMakeScale(1.0, -1.0, 1.0) + + self.chatBackgroundNode.image = chatControllerBackgroundImage(theme: self.presentationData.theme, wallpaper: self.presentationData.chatWallpaper, mediaBox: context.sharedContext.accountManager.mediaBox, knockoutMode: false) + self.chatBackgroundNode.motionEnabled = self.presentationData.chatWallpaper.settings?.motion ?? false + if case .gradient = self.presentationData.chatWallpaper { + self.chatBackgroundNode.imageContentMode = .scaleToFill + } + + self.toolbarNode = BubbleSettingsToolbarNode(presentationThemeSettings: self.presentationThemeSettings, presentationData: self.presentationData) + + self.maskNode = ASImageNode() + self.maskNode.displaysAsynchronously = false + self.maskNode.displayWithoutProcessing = true + self.maskNode.contentMode = .scaleToFill + + + super.init() + + self.setViewBlock({ + return UITracingLayerView() + }) + + self.backgroundColor = self.presentationData.theme.list.plainBackgroundColor + + self.maskNode.image = generateMaskImage(color: self.presentationData.theme.chatList.backgroundColor) + + self.addSubnode(self.scrollNode) + self.addSubnode(self.toolbarNode) + + self.scrollNode.addSubnode(self.chatBackgroundNode) + self.scrollNode.addSubnode(self.messagesContainerNode) + + self.toolbarNode.cancel = { + dismiss() + } + var dismissed = false + self.toolbarNode.done = { [weak self] in + guard let strongSelf = self else { + return + } + if !dismissed { + dismissed = true + apply(strongSelf.presentationThemeSettings.chatBubbleSettings) + } + } + self.toolbarNode.updateMergeBubbleCorners = { [weak self] value in + guard let strongSelf = self else { + return + } + strongSelf.presentationThemeSettings.chatBubbleSettings.mergeBubbleCorners = value + strongSelf.updatePresentationThemeSettings(strongSelf.presentationThemeSettings) + } + self.toolbarNode.updateCornerRadius = { [weak self] value in + guard let strongSelf = self else { + return + } + strongSelf.presentationThemeSettings.chatBubbleSettings.mainRadius = Int32(value) + strongSelf.presentationThemeSettings.chatBubbleSettings.auxiliaryRadius = Int32(value / 2) + strongSelf.updatePresentationThemeSettings(strongSelf.presentationThemeSettings) + } + } + + override func didLoad() { + super.didLoad() + + self.scrollNode.view.disablesInteractiveTransitionGestureRecognizer = true + self.scrollNode.view.showsHorizontalScrollIndicator = false + self.scrollNode.view.isPagingEnabled = true + self.scrollNode.view.delegate = self + self.scrollNode.view.alwaysBounceHorizontal = false + } + + func scrollViewDidScroll(_ scrollView: UIScrollView) { + } + + func animateIn(completion: (() -> Void)? = nil) { + if let (layout, _) = self.validLayout, case .compact = layout.metrics.widthClass { + self.layer.animatePosition(from: CGPoint(x: self.layer.position.x, y: self.layer.position.y + self.layer.bounds.size.height), to: self.layer.position, duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring) + } + } + + func animateOut(completion: (() -> Void)? = nil) { + if let (layout, _) = self.validLayout, case .compact = layout.metrics.widthClass { + self.layer.animatePosition(from: self.layer.position, to: CGPoint(x: self.layer.position.x, y: self.layer.position.y + self.layer.bounds.size.height), duration: 0.2, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, removeOnCompletion: false, completion: { _ in + completion?() + }) + } else { + completion?() + } + } + + private func updateMessagesLayout(layout: ContainerViewLayout, bottomInset: CGFloat, transition: ContainedViewLayoutTransition) { + let headerItem = self.context.sharedContext.makeChatMessageDateHeaderItem(context: self.context, timestamp: self.referenceTimestamp, theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder) + + var items: [ListViewItem] = [] + let peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: 1) + let otherPeerId = self.context.account.peerId + var peers = SimpleDictionary() + var messages = SimpleDictionary() + peers[peerId] = TelegramUser(id: peerId, accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_Chat_2_ReplyName, lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: []) + peers[otherPeerId] = TelegramUser(id: otherPeerId, accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_Chat_2_ReplyName, lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: []) + + let replyMessageId = MessageId(peerId: peerId, namespace: 0, id: 3) + messages[replyMessageId] = Message(stableId: 3, stableVersion: 0, id: replyMessageId, globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66000, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_1_Text, attributes: [], media: [], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: []) + + let message1 = Message(stableId: 4, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 4), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66003, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_3_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []) + items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: message1, theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil)) + + let message2 = Message(stableId: 3, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 3), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66002, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_2_Text, attributes: [ReplyMessageAttribute(messageId: replyMessageId)], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []) + items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: message2, theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil)) + + let waveformBase64 = "DAAOAAkACQAGAAwADwAMABAADQAPABsAGAALAA0AGAAfABoAHgATABgAGQAYABQADAAVABEAHwANAA0ACQAWABkACQAOAAwACQAfAAAAGQAVAAAAEwATAAAACAAfAAAAHAAAABwAHwAAABcAGQAAABQADgAAABQAHwAAAB8AHwAAAAwADwAAAB8AEwAAABoAFwAAAB8AFAAAAAAAHwAAAAAAHgAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAAAA=" + let voiceAttributes: [TelegramMediaFileAttribute] = [.Audio(isVoice: true, duration: 23, title: nil, performer: nil, waveform: MemoryBuffer(data: Data(base64Encoded: waveformBase64)!))] + let voiceMedia = TelegramMediaFile(fileId: MediaId(namespace: 0, id: 0), partialReference: nil, resource: LocalFileMediaResource(fileId: 0), previewRepresentations: [], immediateThumbnailData: nil, mimeType: "audio/ogg", size: 0, attributes: voiceAttributes) + + let message3 = Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66001, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [voiceMedia], peers: peers, associatedMessages: messages, associatedMessageIds: []) + items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: message3, theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: FileMediaResourceStatus(mediaStatus: .playbackStatus(.paused), fetchStatus: .Local), tapMessage: nil, clickThroughMessage: nil)) + + let message4 = Message(stableId: 2, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 2), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66001, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_1_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []) + items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: message4, theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil)) + + let width: CGFloat + if case .regular = layout.metrics.widthClass { + width = layout.size.width + } else { + width = layout.size.width + } + + let params = ListViewItemLayoutParams(width: width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, availableHeight: layout.size.height) + if let messageNodes = self.messageNodes { + for i in 0 ..< items.count { + let itemNode = messageNodes[i] + items[i].updateNode(async: { $0() }, node: { + return itemNode + }, params: params, previousItem: i == 0 ? nil : items[i - 1], nextItem: i == (items.count - 1) ? nil : items[i + 1], animation: .None, completion: { (layout, apply) in + let nodeFrame = CGRect(origin: itemNode.frame.origin, size: CGSize(width: width, height: layout.size.height)) + + itemNode.contentSize = layout.contentSize + itemNode.insets = layout.insets + itemNode.frame = nodeFrame + itemNode.isUserInteractionEnabled = false + + apply(ListViewItemApply(isOnScreen: true)) + }) + } + } else { + var messageNodes: [ListViewItemNode] = [] + for i in 0 ..< items.count { + var itemNode: ListViewItemNode? + items[i].nodeConfiguredForParams(async: { $0() }, params: params, synchronousLoads: false, previousItem: i == 0 ? nil : items[i - 1], nextItem: i == (items.count - 1) ? nil : items[i + 1], completion: { node, apply in + itemNode = node + apply().1(ListViewItemApply(isOnScreen: true)) + }) + itemNode!.subnodeTransform = CATransform3DMakeScale(-1.0, 1.0, 1.0) + itemNode!.isUserInteractionEnabled = false + messageNodes.append(itemNode!) + self.messagesContainerNode.addSubnode(itemNode!) + } + self.messageNodes = messageNodes + } + + var bottomOffset: CGFloat = 9.0 + bottomInset + if let messageNodes = self.messageNodes { + for itemNode in messageNodes { + transition.updateFrame(node: itemNode, frame: CGRect(origin: CGPoint(x: 0.0, y: bottomOffset), size: itemNode.frame.size)) + bottomOffset += itemNode.frame.height + itemNode.updateFrame(itemNode.frame, within: layout.size) + } + } + + let dateHeaderNode: ListViewItemHeaderNode + if let currentDateHeaderNode = self.dateHeaderNode { + dateHeaderNode = currentDateHeaderNode + headerItem.updateNode(dateHeaderNode, previous: nil, next: headerItem) + } else { + dateHeaderNode = headerItem.node() + dateHeaderNode.subnodeTransform = CATransform3DMakeScale(-1.0, 1.0, 1.0) + self.messagesContainerNode.addSubnode(dateHeaderNode) + self.dateHeaderNode = dateHeaderNode + } + + transition.updateFrame(node: dateHeaderNode, frame: CGRect(origin: CGPoint(x: 0.0, y: bottomOffset), size: CGSize(width: layout.size.width, height: headerItem.height))) + dateHeaderNode.updateLayout(size: self.messagesContainerNode.frame.size, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right) + } + + func updatePresentationThemeSettings(_ presentationThemeSettings: PresentationThemeSettings) { + let chatBubbleCorners = PresentationChatBubbleCorners(mainRadius: CGFloat(presentationThemeSettings.chatBubbleSettings.mainRadius), auxiliaryRadius: CGFloat(presentationThemeSettings.chatBubbleSettings.auxiliaryRadius), mergeBubbleCorners: presentationThemeSettings.chatBubbleSettings.mergeBubbleCorners) + + self.presentationData = self.presentationData.withChatBubbleCorners(chatBubbleCorners) + self.toolbarNode.updatePresentationData(presentationData: self.presentationData) + self.toolbarNode.updatePresentationThemeSettings(presentationThemeSettings: self.presentationThemeSettings) + if let (layout, navigationBarHeight) = self.validLayout { + self.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .immediate) + self.recursivelyEnsureDisplaySynchronously(true) + } + } + + func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) { + self.validLayout = (layout, navigationBarHeight) + + let bounds = CGRect(origin: CGPoint(), size: layout.size) + self.scrollNode.frame = bounds + + let toolbarHeight = self.toolbarNode.updateLayout(width: layout.size.width, bottomInset: layout.intrinsicInsets.bottom, layout: layout, transition: transition) + + var chatFrame = CGRect(x: 0.0, y: 0.0, width: bounds.width, height: bounds.height) + + let bottomInset: CGFloat + chatFrame = CGRect(x: 0.0, y: 0.0, width: bounds.width, height: bounds.height) + self.scrollNode.view.contentSize = CGSize(width: bounds.width, height: bounds.height) + + bottomInset = 37.0 + + self.chatBackgroundNode.frame = chatFrame + self.chatBackgroundNode.updateLayout(size: chatFrame.size, transition: transition) + self.messagesContainerNode.frame = chatFrame + + transition.updateFrame(node: self.toolbarNode, frame: CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - toolbarHeight), size: CGSize(width: layout.size.width, height: toolbarHeight + layout.intrinsicInsets.bottom))) + + self.updateMessagesLayout(layout: layout, bottomInset: toolbarHeight + bottomInset, transition: transition) + + transition.updateFrame(node: self.maskNode, frame: CGRect(x: 0.0, y: layout.size.height - toolbarHeight - 80.0, width: bounds.width, height: 80.0)) + } +} + +final class BubbleSettingsController: ViewController { + private let context: AccountContext + + private var controllerNode: BubbleSettingsControllerNode { + return self.displayNode as! BubbleSettingsControllerNode + } + + private var didPlayPresentationAnimation = false + + private var presentationData: PresentationData + private var presentationDataDisposable: Disposable? + + private var presentationThemeSettings: PresentationThemeSettings + private var presentationThemeSettingsDisposable: Disposable? + + private var disposable: Disposable? + private var applyDisposable = MetaDisposable() + + public init(context: AccountContext, presentationThemeSettings: PresentationThemeSettings) { + self.context = context + + self.presentationData = context.sharedContext.currentPresentationData.with { $0 } + self.presentationThemeSettings = presentationThemeSettings + + super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationTheme: self.presentationData.theme, presentationStrings: self.presentationData.strings)) + + self.blocksBackgroundWhenInOverlay = true + self.navigationPresentation = .modal + + self.navigationItem.title = self.presentationData.strings.Appearance_BubbleCorners_Title + self.navigationItem.leftBarButtonItem = UIBarButtonItem(customView: UIView()) + + self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style + self.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait) + + self.presentationDataDisposable = (context.sharedContext.presentationData + |> deliverOnMainQueue).start(next: { [weak self] presentationData in + if let strongSelf = self { + strongSelf.presentationData = presentationData + } + }) + } + + required public init(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + deinit { + self.presentationDataDisposable?.dispose() + self.presentationThemeSettingsDisposable?.dispose() + self.disposable?.dispose() + self.applyDisposable.dispose() + } + + override public func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + + if let presentationArguments = self.presentationArguments as? ViewControllerPresentationArguments, !self.didPlayPresentationAnimation { + self.didPlayPresentationAnimation = true + if case .modalSheet = presentationArguments.presentationAnimation { + self.controllerNode.animateIn() + } + } + } + + override public func loadDisplayNode() { + super.loadDisplayNode() + + self.displayNode = BubbleSettingsControllerNode(context: self.context, presentationThemeSettings: self.presentationThemeSettings, dismiss: { [weak self] in + if let strongSelf = self { + strongSelf.dismiss() + } + }, apply: { [weak self] chatBubbleSettings in + if let strongSelf = self { + strongSelf.apply(chatBubbleSettings: chatBubbleSettings) + } + }) + self.displayNodeDidLoad() + } + + private func apply(chatBubbleSettings: PresentationChatBubbleSettings) { + let _ = (updatePresentationThemeSettingsInteractively(accountManager: self.context.sharedContext.accountManager, { current in + var current = current + current.chatBubbleSettings = chatBubbleSettings + return current + }) + |> deliverOnMainQueue).start(completed: { [weak self] in + self?.dismiss() + }) + } + + override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) { + super.containerLayoutUpdated(layout, transition: transition) + + self.controllerNode.containerLayoutUpdated(layout, navigationBarHeight: self.navigationHeight, transition: transition) + } +} + +private enum TextSelectionCustomMode { + case list + case chat +} + +private final class BubbleSettingsToolbarNode: ASDisplayNode { + private var presentationThemeSettings: PresentationThemeSettings + private var presentationData: PresentationData + + private let cancelButton = HighlightableButtonNode() + private let doneButton = HighlightableButtonNode() + private let separatorNode = ASDisplayNode() + private let topSeparatorNode = ASDisplayNode() + + private var switchItemNode: ItemListSwitchItemNode + private var cornerRadiusItemNode: BubbleSettingsRadiusItemNode + + private(set) var customMode: TextSelectionCustomMode = .chat + + var cancel: (() -> Void)? + var done: (() -> Void)? + + var updateMergeBubbleCorners: ((Bool) -> Void)? + var updateCornerRadius: ((Int32) -> Void)? + + init(presentationThemeSettings: PresentationThemeSettings, presentationData: PresentationData) { + self.presentationThemeSettings = presentationThemeSettings + self.presentationData = presentationData + + self.switchItemNode = ItemListSwitchItemNode(type: .regular) + self.cornerRadiusItemNode = BubbleSettingsRadiusItemNode() + + super.init() + + self.addSubnode(self.switchItemNode) + self.addSubnode(self.cornerRadiusItemNode) + self.addSubnode(self.cancelButton) + self.addSubnode(self.doneButton) + self.addSubnode(self.separatorNode) + self.addSubnode(self.topSeparatorNode) + + self.updatePresentationData(presentationData: self.presentationData) + + self.cancelButton.highligthedChanged = { [weak self] highlighted in + if let strongSelf = self { + if highlighted { + strongSelf.cancelButton.backgroundColor = strongSelf.presentationData.theme.list.itemHighlightedBackgroundColor + } else { + UIView.animate(withDuration: 0.3, animations: { + strongSelf.cancelButton.backgroundColor = .clear + }) + } + } + } + + self.doneButton.highligthedChanged = { [weak self] highlighted in + if let strongSelf = self { + if highlighted { + strongSelf.doneButton.backgroundColor = strongSelf.presentationData.theme.list.itemHighlightedBackgroundColor + } else { + UIView.animate(withDuration: 0.3, animations: { + strongSelf.doneButton.backgroundColor = .clear + }) + } + } + } + + self.cancelButton.addTarget(self, action: #selector(self.cancelPressed), forControlEvents: .touchUpInside) + self.doneButton.addTarget(self, action: #selector(self.donePressed), forControlEvents: .touchUpInside) + } + + func setDoneEnabled(_ enabled: Bool) { + self.doneButton.alpha = enabled ? 1.0 : 0.4 + self.doneButton.isUserInteractionEnabled = enabled + } + + func setCustomMode(_ customMode: TextSelectionCustomMode) { + self.customMode = customMode + } + + func updatePresentationData(presentationData: PresentationData) { + self.backgroundColor = presentationData.theme.rootController.tabBar.backgroundColor + self.separatorNode.backgroundColor = presentationData.theme.rootController.tabBar.separatorColor + self.topSeparatorNode.backgroundColor = presentationData.theme.rootController.tabBar.separatorColor + + self.cancelButton.setTitle(presentationData.strings.Common_Cancel, with: Font.regular(17.0), with: presentationData.theme.list.itemPrimaryTextColor, for: []) + self.doneButton.setTitle(presentationData.strings.Wallpaper_Set, with: Font.regular(17.0), with: presentationData.theme.list.itemPrimaryTextColor, for: []) + } + + func updatePresentationThemeSettings(presentationThemeSettings: PresentationThemeSettings) { + self.presentationThemeSettings = presentationThemeSettings + } + + func updateLayout(width: CGFloat, bottomInset: CGFloat, layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) -> CGFloat { + var contentHeight: CGFloat = 0.0 + + let switchItem = ItemListSwitchItem(presentationData: ItemListPresentationData(self.presentationData), title: self.presentationData.strings.Appearance_BubbleCorners_AdjustAdjacent, value: self.presentationThemeSettings.chatBubbleSettings.mergeBubbleCorners, disableLeadingInset: true, sectionId: 0, style: .blocks, updated: { [weak self] value in + self?.updateMergeBubbleCorners?(value) + }) + let cornerRadiusItem = BubbleSettingsRadiusItem(theme: self.presentationData.theme, value: Int(self.presentationData.chatBubbleCorners.mainRadius), enabled: true, disableLeadingInset: false, displayIcons: false, force: false, sectionId: 0, updated: { [weak self] value in + self?.updateCornerRadius?(Int32(max(8, min(16, value)))) + }) + + /*switchItem.updateNode(async: { f in + f() + }, node: { + return self.switchItemNode + }, params: ListViewItemLayoutParams(width: width, leftInset: layout.intrinsicInsets.left, rightInset: layout.intrinsicInsets.right, availableHeight: 1000.0), previousItem: nil, nextItem: cornerRadiusItem, animation: .None, completion: { layout, apply in + self.switchItemNode.contentSize = layout.contentSize + self.switchItemNode.insets = layout.insets + transition.updateFrame(node: self.switchItemNode, frame: CGRect(origin: CGPoint(x: 0.0, y: contentHeight), size: layout.contentSize)) + contentHeight += layout.contentSize.height + apply(ListViewItemApply(isOnScreen: true)) + })*/ + + cornerRadiusItem.updateNode(async: { f in + f() + }, node: { + return self.cornerRadiusItemNode + }, params: ListViewItemLayoutParams(width: width, leftInset: layout.intrinsicInsets.left, rightInset: layout.intrinsicInsets.right, availableHeight: 1000.0), previousItem: switchItem, nextItem: nil, animation: .None, completion: { layout, apply in + self.cornerRadiusItemNode.contentSize = layout.contentSize + self.cornerRadiusItemNode.insets = layout.insets + transition.updateFrame(node: self.cornerRadiusItemNode, frame: CGRect(origin: CGPoint(x: 0.0, y: contentHeight), size: layout.contentSize)) + contentHeight += layout.contentSize.height + apply(ListViewItemApply(isOnScreen: true)) + }) + + self.cancelButton.frame = CGRect(origin: CGPoint(x: 0.0, y: contentHeight), size: CGSize(width: floor(width / 2.0), height: 49.0)) + self.doneButton.frame = CGRect(origin: CGPoint(x: floor(width / 2.0), y: contentHeight), size: CGSize(width: width - floor(width / 2.0), height: 49.0)) + + contentHeight += 49.0 + + self.topSeparatorNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: width, height: UIScreenPixel)) + + let resultHeight = contentHeight + bottomInset + + self.separatorNode.frame = CGRect(origin: CGPoint(x: floor(width / 2.0), y: self.cancelButton.frame.minY), size: CGSize(width: UIScreenPixel, height: resultHeight - self.cancelButton.frame.minY)) + + return resultHeight + } + + @objc func cancelPressed() { + self.cancel?() + } + + @objc func donePressed() { + self.doneButton.isUserInteractionEnabled = false + self.done?() + } +} diff --git a/submodules/SettingsUI/Sources/Data and Storage/IntentsSettingsController.swift b/submodules/SettingsUI/Sources/Data and Storage/IntentsSettingsController.swift index 7c36f94b57..42a06eeccb 100644 --- a/submodules/SettingsUI/Sources/Data and Storage/IntentsSettingsController.swift +++ b/submodules/SettingsUI/Sources/Data and Storage/IntentsSettingsController.swift @@ -187,7 +187,7 @@ private enum IntentsSettingsControllerEntry: ItemListNodeEntry { case let .accountHeader(theme, text): return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section) case let .account(theme, peer, selected, _): - return ItemListPeerItem(presentationData: presentationData, dateTimeFormat: PresentationDateTimeFormat(timeFormat: .regular, dateFormat: .dayFirst, dateSeparator: ".", decimalSeparator: ".", groupingSeparator: ""), nameDisplayOrder: .firstLast, context: arguments.context, peer: peer, height: .generic, aliasHandling: .standard, nameStyle: .plain, presence: nil, text: .none, label: .none, editing: ItemListPeerItemEditing(editable: true, editing: false, revealed: false), revealOptions: nil, switchValue: ItemListPeerItemSwitch(value: selected, style: .check), enabled: true, selectable: true, sectionId: self.section, action: { + return ItemListPeerItem(presentationData: presentationData, dateTimeFormat: PresentationDateTimeFormat(timeFormat: .regular, dateFormat: .dayFirst, dateSeparator: ".", decimalSeparator: ".", groupingSeparator: ""), nameDisplayOrder: .firstLast, context: arguments.context.sharedContext.makeTempAccountContext(account: arguments.context.account), peer: peer, height: .generic, aliasHandling: .standard, nameStyle: .plain, presence: nil, text: .none, label: .none, editing: ItemListPeerItemEditing(editable: true, editing: false, revealed: false), revealOptions: nil, switchValue: ItemListPeerItemSwitch(value: selected, style: .check), enabled: true, selectable: true, sectionId: self.section, action: { arguments.updateSettings { $0.withUpdatedAccount(peer.id) } }, setPeerIdWithRevealedOptions: { _, _ in}, removePeer: { _ in }) return ItemListTextItem(presentationData: presentationData, text: .plain(""), sectionId: self.section) diff --git a/submodules/SettingsUI/Sources/Data and Storage/ProxyListSettingsController.swift b/submodules/SettingsUI/Sources/Data and Storage/ProxyListSettingsController.swift index 317eedd81f..c0424de212 100644 --- a/submodules/SettingsUI/Sources/Data and Storage/ProxyListSettingsController.swift +++ b/submodules/SettingsUI/Sources/Data and Storage/ProxyListSettingsController.swift @@ -453,10 +453,10 @@ public func proxySettingsController(accountManager: AccountManager, context: Acc dismissImpl = { [weak controller] in controller?.dismiss() } - controller.setReorderEntry({ (fromIndex: Int, toIndex: Int, entries: [ProxySettingsControllerEntry]) -> Void in + controller.setReorderEntry({ (fromIndex: Int, toIndex: Int, entries: [ProxySettingsControllerEntry]) -> Signal in let fromEntry = entries[fromIndex] guard case let .server(_, _, _, fromServer, _, _, _, _) = fromEntry else { - return + return .single(false) } var referenceServer: ProxyServerSettings? var beforeAll = false @@ -476,7 +476,7 @@ public func proxySettingsController(accountManager: AccountManager, context: Acc afterAll = true } - let _ = updateProxySettingsInteractively(accountManager: accountManager, { current in + return updateProxySettingsInteractively(accountManager: accountManager, { current in var current = current if let index = current.servers.firstIndex(of: fromServer) { current.servers.remove(at: index) @@ -503,7 +503,7 @@ public func proxySettingsController(accountManager: AccountManager, context: Acc current.servers.append(fromServer) } return current - }).start() + }) }) shareProxyListImpl = { [weak controller] in diff --git a/submodules/SettingsUI/Sources/Data and Storage/ProxySettingsServerItem.swift b/submodules/SettingsUI/Sources/Data and Storage/ProxySettingsServerItem.swift index bde8b6ce21..5d7e756311 100644 --- a/submodules/SettingsUI/Sources/Data and Storage/ProxySettingsServerItem.swift +++ b/submodules/SettingsUI/Sources/Data and Storage/ProxySettingsServerItem.swift @@ -241,7 +241,7 @@ private final class ProxySettingsServerItemNode: ItemListRevealOptionsItemNode { let statusAttributedString = NSAttributedString(string: item.label, font: statusFont, textColor: item.labelAccent ? item.theme.list.itemAccentColor : item.theme.list.itemSecondaryTextColor) var editableControlSizeAndApply: (CGFloat, (CGFloat) -> ItemListEditableControlNode)? - var reorderControlSizeAndApply: (CGFloat, (CGFloat, Bool) -> ItemListEditableReorderControlNode)? + var reorderControlSizeAndApply: (CGFloat, (CGFloat, Bool, ContainedViewLayoutTransition) -> ItemListEditableReorderControlNode)? let editingOffset: CGFloat var reorderInset: CGFloat = 0.0 @@ -341,7 +341,7 @@ private final class ProxySettingsServerItemNode: ItemListRevealOptionsItemNode { if let reorderControlSizeAndApply = reorderControlSizeAndApply { if strongSelf.reorderControlNode == nil { - let reorderControlNode = reorderControlSizeAndApply.1(layout.contentSize.height, false) + let reorderControlNode = reorderControlSizeAndApply.1(layout.contentSize.height, false, .immediate) strongSelf.reorderControlNode = reorderControlNode strongSelf.addSubnode(reorderControlNode) reorderControlNode.alpha = 0.0 diff --git a/submodules/SettingsUI/Sources/DebugController.swift b/submodules/SettingsUI/Sources/DebugController.swift index 9d7e830613..be90bdccfa 100644 --- a/submodules/SettingsUI/Sources/DebugController.swift +++ b/submodules/SettingsUI/Sources/DebugController.swift @@ -393,6 +393,11 @@ private enum DebugControllerEntry: ItemListNodeEntry { let _ = (arguments.sharedContext.accountManager.transaction { transaction -> Void in transaction.clearNotices() }).start() + if let context = arguments.context { + let _ = (context.account.postbox.transaction { transaction -> Void in + transaction.clearItemCacheCollection(collectionId: Namespaces.CachedItemCollection.cachedPollResults) + }).start() + } }) case let .reimport(theme): return ItemListActionItem(presentationData: presentationData, title: "Reimport Application Data", kind: .generic, alignment: .natural, sectionId: self.section, style: .blocks, action: { diff --git a/submodules/SettingsUI/Sources/Privacy and Security/ForwardPrivacyChatPreviewItem.swift b/submodules/SettingsUI/Sources/Privacy and Security/ForwardPrivacyChatPreviewItem.swift index dfdba0d1ea..ccc324d7ef 100644 --- a/submodules/SettingsUI/Sources/Privacy and Security/ForwardPrivacyChatPreviewItem.swift +++ b/submodules/SettingsUI/Sources/Privacy and Security/ForwardPrivacyChatPreviewItem.swift @@ -18,6 +18,7 @@ class ForwardPrivacyChatPreviewItem: ListViewItem, ItemListItem { let strings: PresentationStrings let sectionId: ItemListSectionId let fontSize: PresentationFontSize + let chatBubbleCorners: PresentationChatBubbleCorners let wallpaper: TelegramWallpaper let dateTimeFormat: PresentationDateTimeFormat let nameDisplayOrder: PresentationPersonNameOrder @@ -25,12 +26,13 @@ class ForwardPrivacyChatPreviewItem: ListViewItem, ItemListItem { let linkEnabled: Bool let tooltipText: String - init(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, sectionId: ItemListSectionId, fontSize: PresentationFontSize, wallpaper: TelegramWallpaper, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, peerName: String, linkEnabled: Bool, tooltipText: String) { + init(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, sectionId: ItemListSectionId, fontSize: PresentationFontSize, chatBubbleCorners: PresentationChatBubbleCorners, wallpaper: TelegramWallpaper, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, peerName: String, linkEnabled: Bool, tooltipText: String) { self.context = context self.theme = theme self.strings = strings self.sectionId = sectionId self.fontSize = fontSize + self.chatBubbleCorners = chatBubbleCorners self.wallpaper = wallpaper self.dateTimeFormat = dateTimeFormat self.nameDisplayOrder = nameDisplayOrder @@ -157,7 +159,7 @@ class ForwardPrivacyChatPreviewItemNode: ListViewItemNode { let forwardInfo = MessageForwardInfo(author: item.linkEnabled ? peers[peerId] : nil, source: nil, sourceMessageId: nil, date: 0, authorSignature: item.linkEnabled ? nil : item.peerName) - let messageItem = item.context.sharedContext.makeChatMessagePreviewItem(context: item.context, message: Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66000, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: forwardInfo, author: nil, text: item.strings.Privacy_Forwards_PreviewMessageText, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), theme: item.theme, strings: item.strings, wallpaper: item.wallpaper, fontSize: item.fontSize, dateTimeFormat: item.dateTimeFormat, nameOrder: item.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil) + let messageItem = item.context.sharedContext.makeChatMessagePreviewItem(context: item.context, message: Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66000, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: forwardInfo, author: nil, text: item.strings.Privacy_Forwards_PreviewMessageText, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), theme: item.theme, strings: item.strings, wallpaper: item.wallpaper, fontSize: item.fontSize, chatBubbleCorners: item.chatBubbleCorners, dateTimeFormat: item.dateTimeFormat, nameOrder: item.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil) var node: ListViewItemNode? if let current = currentNode { diff --git a/submodules/SettingsUI/Sources/Privacy and Security/PrivacyAndSecurityController.swift b/submodules/SettingsUI/Sources/Privacy and Security/PrivacyAndSecurityController.swift index 99a2f3dbd2..6e741d6517 100644 --- a/submodules/SettingsUI/Sources/Privacy and Security/PrivacyAndSecurityController.swift +++ b/submodules/SettingsUI/Sources/Privacy and Security/PrivacyAndSecurityController.swift @@ -482,12 +482,19 @@ public func privacyAndSecurityController(context: AccountContext, initialSetting let updateHasTwoStepAuth: () -> Void = { let signal = twoStepVerificationConfiguration(account: context.account) |> map { value -> TwoStepVerificationAccessConfiguration? in - return TwoStepVerificationAccessConfiguration(configuration: value, password: nil) + return TwoStepVerificationAccessConfiguration(configuration: value, password: nil) } |> deliverOnMainQueue updateTwoStepAuthDisposable.set( signal.start(next: { value in twoStepAuthDataValue.set(.single(value)) + if let value = value { + if case .set = value { + updatedHasTwoStepAuth?(true) + } else { + updatedHasTwoStepAuth?(false) + } + } }) ) } diff --git a/submodules/SettingsUI/Sources/Privacy and Security/SelectivePrivacySettingsController.swift b/submodules/SettingsUI/Sources/Privacy and Security/SelectivePrivacySettingsController.swift index 1a89bcca3c..3c7774bcc4 100644 --- a/submodules/SettingsUI/Sources/Privacy and Security/SelectivePrivacySettingsController.swift +++ b/submodules/SettingsUI/Sources/Privacy and Security/SelectivePrivacySettingsController.swift @@ -87,7 +87,7 @@ private func stringForUserCount(_ peers: [PeerId: SelectivePrivacyPeer], strings private enum SelectivePrivacySettingsEntry: ItemListNodeEntry { case forwardsPreviewHeader(PresentationTheme, String) - case forwardsPreview(PresentationTheme, TelegramWallpaper, PresentationFontSize, PresentationStrings, PresentationDateTimeFormat, PresentationPersonNameOrder, String, Bool, String) + case forwardsPreview(PresentationTheme, TelegramWallpaper, PresentationFontSize, PresentationChatBubbleCorners, PresentationStrings, PresentationDateTimeFormat, PresentationPersonNameOrder, String, Bool, String) case settingHeader(PresentationTheme, String) case everybody(PresentationTheme, String, Bool) case contacts(PresentationTheme, String, Bool) @@ -194,8 +194,8 @@ private enum SelectivePrivacySettingsEntry: ItemListNodeEntry { } else { return false } - case let .forwardsPreview(lhsTheme, lhsWallpaper, lhsFontSize, lhsStrings, lhsTimeFormat, lhsNameOrder, lhsPeerName, lhsLinkEnabled, lhsTooltipText): - if case let .forwardsPreview(rhsTheme, rhsWallpaper, rhsFontSize, rhsStrings, rhsTimeFormat, rhsNameOrder, rhsPeerName, rhsLinkEnabled, rhsTooltipText) = rhs, lhsTheme === rhsTheme, lhsWallpaper == rhsWallpaper, lhsFontSize == rhsFontSize, lhsStrings === rhsStrings, lhsTimeFormat == rhsTimeFormat, lhsNameOrder == rhsNameOrder, lhsPeerName == rhsPeerName, lhsLinkEnabled == rhsLinkEnabled, lhsTooltipText == rhsTooltipText { + case let .forwardsPreview(lhsTheme, lhsWallpaper, lhsFontSize, lhsChatBubbleCorners, lhsStrings, lhsTimeFormat, lhsNameOrder, lhsPeerName, lhsLinkEnabled, lhsTooltipText): + if case let .forwardsPreview(rhsTheme, rhsWallpaper, rhsFontSize, rhsChatBubbleCorners, rhsStrings, rhsTimeFormat, rhsNameOrder, rhsPeerName, rhsLinkEnabled, rhsTooltipText) = rhs, lhsTheme === rhsTheme, lhsWallpaper == rhsWallpaper, lhsFontSize == rhsFontSize, lhsChatBubbleCorners == rhsChatBubbleCorners, lhsStrings === rhsStrings, lhsTimeFormat == rhsTimeFormat, lhsNameOrder == rhsNameOrder, lhsPeerName == rhsPeerName, lhsLinkEnabled == rhsLinkEnabled, lhsTooltipText == rhsTooltipText { return true } else { return false @@ -350,8 +350,8 @@ private enum SelectivePrivacySettingsEntry: ItemListNodeEntry { switch self { case let .forwardsPreviewHeader(theme, text): return ItemListSectionHeaderItem(presentationData: presentationData, text: text, multiline: true, sectionId: self.section) - case let .forwardsPreview(theme, wallpaper, fontSize, strings, dateTimeFormat, nameDisplayOrder, peerName, linkEnabled, tooltipText): - return ForwardPrivacyChatPreviewItem(context: arguments.context, theme: theme, strings: strings, sectionId: self.section, fontSize: fontSize, wallpaper: wallpaper, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, peerName: peerName, linkEnabled: linkEnabled, tooltipText: tooltipText) + case let .forwardsPreview(theme, wallpaper, fontSize, chatBubbleCorners, strings, dateTimeFormat, nameDisplayOrder, peerName, linkEnabled, tooltipText): + return ForwardPrivacyChatPreviewItem(context: arguments.context, theme: theme, strings: strings, sectionId: self.section, fontSize: fontSize, chatBubbleCorners: chatBubbleCorners, wallpaper: wallpaper, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, peerName: peerName, linkEnabled: linkEnabled, tooltipText: tooltipText) case let .settingHeader(theme, text): return ItemListSectionHeaderItem(presentationData: presentationData, text: text, multiline: true, sectionId: self.section) case let .everybody(theme, text, value): @@ -591,7 +591,7 @@ private func selectivePrivacySettingsControllerEntries(presentationData: Present linkEnabled = false } entries.append(.forwardsPreviewHeader(presentationData.theme, presentationData.strings.Privacy_Forwards_Preview)) - entries.append(.forwardsPreview(presentationData.theme, presentationData.chatWallpaper, presentationData.chatFontSize, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameDisplayOrder, peerName, linkEnabled, tootipText)) + entries.append(.forwardsPreview(presentationData.theme, presentationData.chatWallpaper, presentationData.chatFontSize, presentationData.chatBubbleCorners, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameDisplayOrder, peerName, linkEnabled, tootipText)) } entries.append(.settingHeader(presentationData.theme, settingTitle)) diff --git a/submodules/SettingsUI/Sources/SettingsController.swift b/submodules/SettingsUI/Sources/SettingsController.swift index 7bfc8531ea..280b6055f8 100644 --- a/submodules/SettingsUI/Sources/SettingsController.swift +++ b/submodules/SettingsUI/Sources/SettingsController.swift @@ -945,7 +945,7 @@ public func settingsController(context: AccountContext, accountManager: AccountM blockedPeers.set(.single(blockedPeersContext)) }, updatedHasTwoStepAuth: { hasTwoStepAuthValue in hasTwoStepAuthPromise.set(.single(hasTwoStepAuthValue)) - }, activeSessionsContext: activeSessionsContext, webSessionsContext: webSessionsContext, blockedPeersContext: blockedPeersContext)) + }, activeSessionsContext: activeSessionsContext, webSessionsContext: webSessionsContext, blockedPeersContext: blockedPeersContext, hasTwoStepAuth: hasTwoStepAuth)) }) }) }, openDataAndStorage: { @@ -1474,7 +1474,7 @@ public func settingsController(context: AccountContext, accountManager: AccountM if let primary = primary { let size = CGSize(width: 31.0, height: 31.0) let inset: CGFloat = 3.0 - if let signal = peerAvatarImage(account: primary.0, peer: primary.1, authorOfMessage: nil, representation: primary.1.profileImageRepresentations.first, displayDimensions: size, inset: 3.0, emptyColor: nil, synchronousLoad: false) { + if let signal = peerAvatarImage(account: primary.0, peerReference: PeerReference(primary.1), authorOfMessage: nil, representation: primary.1.profileImageRepresentations.first, displayDimensions: size, inset: 3.0, emptyColor: nil, synchronousLoad: false) { return signal |> map { image -> (UIImage, UIImage)? in if let image = image, let selectedImage = generateImage(size, rotatedContext: { size, context in @@ -1497,13 +1497,13 @@ public func settingsController(context: AccountContext, accountManager: AccountM let image = generateImage(size, rotatedContext: { size, context in context.clear(CGRect(origin: CGPoint(), size: size)) context.translateBy(x: inset, y: inset) - drawPeerAvatarLetters(context: context, size: CGSize(width: size.width - inset * 2.0, height: size.height - inset * 2.0), font: avatarFont, letters: primary.1.displayLetters, accountPeerId: primary.1.id, peerId: primary.1.id) + drawPeerAvatarLetters(context: context, size: CGSize(width: size.width - inset * 2.0, height: size.height - inset * 2.0), font: avatarFont, letters: primary.1.displayLetters, peerId: primary.1.id) })?.withRenderingMode(.alwaysOriginal) let selectedImage = generateImage(size, rotatedContext: { size, context in context.clear(CGRect(origin: CGPoint(), size: size)) context.translateBy(x: inset, y: inset) - drawPeerAvatarLetters(context: context, size: CGSize(width: size.width - inset * 2.0, height: size.height - inset * 2.0), font: avatarFont, letters: primary.1.displayLetters, accountPeerId: primary.1.id, peerId: primary.1.id) + drawPeerAvatarLetters(context: context, size: CGSize(width: size.width - inset * 2.0, height: size.height - inset * 2.0), font: avatarFont, letters: primary.1.displayLetters, peerId: primary.1.id) context.translateBy(x: -inset, y: -inset) context.setLineWidth(1.0) context.setStrokeColor(primary.2.rootController.tabBar.selectedIconColor.cgColor) diff --git a/submodules/SettingsUI/Sources/Stickers/ArchivedStickerPacksController.swift b/submodules/SettingsUI/Sources/Stickers/ArchivedStickerPacksController.swift index b262f583f3..e7d013025b 100644 --- a/submodules/SettingsUI/Sources/Stickers/ArchivedStickerPacksController.swift +++ b/submodules/SettingsUI/Sources/Stickers/ArchivedStickerPacksController.swift @@ -453,7 +453,7 @@ public func archivedStickerPacksController(context: AccountContext, mode: Archiv } presentStickerPackController = { [weak controller] info in let packReference: StickerPackReference = .id(id: info.id.id, accessHash: info.accessHash) - presentControllerImpl?(StickerPackScreen(context: context, mainStickerPack: packReference, stickerPacks: [packReference], parentNavigationController: controller?.navigationController as? NavigationController), nil) + presentControllerImpl?(StickerPackScreen(context: context, mode: .settings, mainStickerPack: packReference, stickerPacks: [packReference], parentNavigationController: controller?.navigationController as? NavigationController), nil) } return controller diff --git a/submodules/SettingsUI/Sources/Stickers/FeaturedStickerPacksController.swift b/submodules/SettingsUI/Sources/Stickers/FeaturedStickerPacksController.swift index 68743f7b34..febff7b0b1 100644 --- a/submodules/SettingsUI/Sources/Stickers/FeaturedStickerPacksController.swift +++ b/submodules/SettingsUI/Sources/Stickers/FeaturedStickerPacksController.swift @@ -265,7 +265,7 @@ public func featuredStickerPacksController(context: AccountContext) -> ViewContr presentStickerPackController = { [weak controller] info in let packReference: StickerPackReference = .id(id: info.id.id, accessHash: info.accessHash) - presentControllerImpl?(StickerPackScreen(context: context, mainStickerPack: packReference, stickerPacks: [packReference], parentNavigationController: controller?.navigationController as? NavigationController), nil) + presentControllerImpl?(StickerPackScreen(context: context, mode: .settings, mainStickerPack: packReference, stickerPacks: [packReference], parentNavigationController: controller?.navigationController as? NavigationController), nil) } return controller diff --git a/submodules/SettingsUI/Sources/Stickers/InstalledStickerPacksController.swift b/submodules/SettingsUI/Sources/Stickers/InstalledStickerPacksController.swift index a3ee302d57..5675e00417 100644 --- a/submodules/SettingsUI/Sources/Stickers/InstalledStickerPacksController.swift +++ b/submodules/SettingsUI/Sources/Stickers/InstalledStickerPacksController.swift @@ -698,10 +698,10 @@ public func installedStickerPacksController(context: AccountContext, mode: Insta if case .modal = mode { controller.navigationPresentation = .modal } - controller.setReorderEntry({ (fromIndex: Int, toIndex: Int, entries: [InstalledStickerPacksEntry]) -> Void in + controller.setReorderEntry({ (fromIndex: Int, toIndex: Int, entries: [InstalledStickerPacksEntry]) -> Signal in let fromEntry = entries[fromIndex] guard case let .pack(_, _, _, fromPackInfo, _, _, _, _, _) = fromEntry else { - return + return .single(false) } var referenceId: ItemCollectionId? var beforeAll = false @@ -731,20 +731,26 @@ public func installedStickerPacksController(context: AccountContext, mode: Insta } } + var previousIndex: Int? for i in 0 ..< currentIds.count { if currentIds[i] == fromPackInfo.id { + previousIndex = i currentIds.remove(at: i) break } } + var didReorder = false + if let referenceId = referenceId { var inserted = false for i in 0 ..< currentIds.count { if currentIds[i] == referenceId { if fromIndex < toIndex { + didReorder = previousIndex != i + 1 currentIds.insert(fromPackInfo.id, at: i + 1) } else { + didReorder = previousIndex != i currentIds.insert(fromPackInfo.id, at: i) } inserted = true @@ -752,15 +758,20 @@ public func installedStickerPacksController(context: AccountContext, mode: Insta } } if !inserted { + didReorder = previousIndex != currentIds.count currentIds.append(fromPackInfo.id) } } else if beforeAll { + didReorder = previousIndex != 0 currentIds.insert(fromPackInfo.id, at: 0) } else if afterAll { + didReorder = previousIndex != currentIds.count currentIds.append(fromPackInfo.id) } temporaryPackOrder.set(.single(currentIds)) + + return .single(didReorder) }) controller.setReorderCompleted({ (entries: [InstalledStickerPacksEntry]) -> Void in @@ -827,7 +838,7 @@ public func installedStickerPacksController(context: AccountContext, mode: Insta packs.insert(packReference, at: 0) } if let mainStickerPack = mainStickerPack { - presentControllerImpl?(StickerPackScreen(context: context, mainStickerPack: mainStickerPack, stickerPacks: packs, parentNavigationController: controller?.navigationController as? NavigationController, actionPerformed: { info, items, action in + presentControllerImpl?(StickerPackScreen(context: context, mode: .settings, mainStickerPack: mainStickerPack, stickerPacks: packs, parentNavigationController: controller?.navigationController as? NavigationController, actionPerformed: { info, items, action in let presentationData = context.sharedContext.currentPresentationData.with { $0 } var animateInAsReplacement = false if let navigationController = navigationControllerImpl?() { diff --git a/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift b/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift index 3aa34eab3d..675dc81df5 100644 --- a/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift +++ b/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift @@ -213,7 +213,7 @@ private final class TextSizeSelectionControllerNode: ASDisplayNode, UIScrollView private func updateChatsLayout(layout: ContainerViewLayout, topInset: CGFloat, transition: ContainedViewLayoutTransition) { var items: [ChatListItem] = [] - let interaction = ChatListNodeInteraction(activateSearch: {}, peerSelected: { _ in }, togglePeerSelected: { _ in }, messageSelected: { _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, deletePeer: { _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, activateChatPreview: { _, _, gesture in + let interaction = ChatListNodeInteraction(activateSearch: {}, peerSelected: { _ in }, disabledPeerSelected: { _ in }, togglePeerSelected: { _ in }, messageSelected: { _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, deletePeer: { _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, activateChatPreview: { _, _, gesture in gesture?.cancel() }) let chatListPresentationData = ChatListPresentationData(theme: self.presentationData.theme, fontSize: self.presentationData.listsFontSize, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameSortOrder: self.presentationData.nameSortOrder, nameDisplayOrder: self.presentationData.nameDisplayOrder, disableAnimations: true) @@ -303,7 +303,7 @@ private final class TextSizeSelectionControllerNode: ASDisplayNode, UIScrollView } private func updateMessagesLayout(layout: ContainerViewLayout, bottomInset: CGFloat, transition: ContainedViewLayoutTransition) { - let headerItem = self.context.sharedContext.makeChatMessageDateHeaderItem(context: self.context, timestamp: self.referenceTimestamp, theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder) + let headerItem = self.context.sharedContext.makeChatMessageDateHeaderItem(context: self.context, timestamp: self.referenceTimestamp, theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder) var items: [ListViewItem] = [] let peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: 1) @@ -317,20 +317,20 @@ private final class TextSizeSelectionControllerNode: ASDisplayNode, UIScrollView messages[replyMessageId] = Message(stableId: 3, stableVersion: 0, id: replyMessageId, globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66000, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_1_Text, attributes: [], media: [], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: []) let message1 = Message(stableId: 4, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 4), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66003, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_3_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []) - items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: message1, theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil)) + items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: message1, theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil)) let message2 = Message(stableId: 3, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 3), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66002, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_2_Text, attributes: [ReplyMessageAttribute(messageId: replyMessageId)], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []) - items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: message2, theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil)) + items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: message2, theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil)) let waveformBase64 = "DAAOAAkACQAGAAwADwAMABAADQAPABsAGAALAA0AGAAfABoAHgATABgAGQAYABQADAAVABEAHwANAA0ACQAWABkACQAOAAwACQAfAAAAGQAVAAAAEwATAAAACAAfAAAAHAAAABwAHwAAABcAGQAAABQADgAAABQAHwAAAB8AHwAAAAwADwAAAB8AEwAAABoAFwAAAB8AFAAAAAAAHwAAAAAAHgAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAAAA=" let voiceAttributes: [TelegramMediaFileAttribute] = [.Audio(isVoice: true, duration: 23, title: nil, performer: nil, waveform: MemoryBuffer(data: Data(base64Encoded: waveformBase64)!))] let voiceMedia = TelegramMediaFile(fileId: MediaId(namespace: 0, id: 0), partialReference: nil, resource: LocalFileMediaResource(fileId: 0), previewRepresentations: [], immediateThumbnailData: nil, mimeType: "audio/ogg", size: 0, attributes: voiceAttributes) let message3 = Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66001, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [voiceMedia], peers: peers, associatedMessages: messages, associatedMessageIds: []) - items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: message3, theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: FileMediaResourceStatus(mediaStatus: .playbackStatus(.paused), fetchStatus: .Local), tapMessage: nil, clickThroughMessage: nil)) + items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: message3, theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: FileMediaResourceStatus(mediaStatus: .playbackStatus(.paused), fetchStatus: .Local), tapMessage: nil, clickThroughMessage: nil)) let message4 = Message(stableId: 2, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 2), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66001, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_1_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []) - items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: message4, theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil)) + items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: message4, theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil)) let width: CGFloat if case .regular = layout.metrics.widthClass { diff --git a/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionItem.swift b/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionItem.swift new file mode 100644 index 0000000000..d714a7a6bc --- /dev/null +++ b/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionItem.swift @@ -0,0 +1,302 @@ +import Foundation +import UIKit +import Display +import AsyncDisplayKit +import SwiftSignalKit +import TelegramCore +import SyncCore +import TelegramPresentationData +import TelegramUIPreferences +import LegacyComponents +import ItemListUI +import PresentationDataUtils +import AppBundle + +class BubbleSettingsRadiusItem: ListViewItem, ItemListItem { + let theme: PresentationTheme + let value: Int + let disableLeadingInset: Bool + let displayIcons: Bool + let force: Bool + let enabled: Bool + let sectionId: ItemListSectionId + let updated: (Int) -> Void + let tag: ItemListItemTag? + + init(theme: PresentationTheme, value: Int, enabled: Bool = true, disableLeadingInset: Bool = false, displayIcons: Bool = true, force: Bool = false, sectionId: ItemListSectionId, updated: @escaping (Int) -> Void, tag: ItemListItemTag? = nil) { + self.theme = theme + self.value = value + self.enabled = enabled + self.disableLeadingInset = disableLeadingInset + self.displayIcons = displayIcons + self.force = force + self.sectionId = sectionId + self.updated = updated + self.tag = tag + } + + func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal?, (ListViewItemApply) -> Void)) -> Void) { + async { + let node = BubbleSettingsRadiusItemNode() + 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() }) + }) + } + } + } + + 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? BubbleSettingsRadiusItemNode { + 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() + }) + } + } + } + } + } +} + +private func generateKnobImage() -> UIImage? { + return generateImage(CGSize(width: 40.0, height: 40.0), rotatedContext: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + context.setShadow(offset: CGSize(width: 0.0, height: -1.0), blur: 3.5, color: UIColor(white: 0.0, alpha: 0.25).cgColor) + context.setFillColor(UIColor.white.cgColor) + context.fillEllipse(in: CGRect(origin: CGPoint(x: 6.0, y: 6.0), size: CGSize(width: 28.0, height: 28.0))) + }) +} + +class BubbleSettingsRadiusItemNode: ListViewItemNode, ItemListItemNode { + private let backgroundNode: ASDisplayNode + private let topStripeNode: ASDisplayNode + private let bottomStripeNode: ASDisplayNode + private let maskNode: ASImageNode + + private var sliderView: TGPhotoEditorSliderView? + private let leftIconNode: ASImageNode + private let rightIconNode: ASImageNode + private let disabledOverlayNode: ASDisplayNode + + private var item: BubbleSettingsRadiusItem? + private var layoutParams: ListViewItemLayoutParams? + + var tag: ItemListItemTag? { + return self.item?.tag + } + + init() { + self.backgroundNode = ASDisplayNode() + self.backgroundNode.isLayerBacked = true + + self.topStripeNode = ASDisplayNode() + self.topStripeNode.isLayerBacked = true + + self.bottomStripeNode = ASDisplayNode() + self.bottomStripeNode.isLayerBacked = true + + self.maskNode = ASImageNode() + + self.leftIconNode = ASImageNode() + self.leftIconNode.displaysAsynchronously = false + self.leftIconNode.displayWithoutProcessing = true + + self.rightIconNode = ASImageNode() + self.rightIconNode.displaysAsynchronously = false + self.rightIconNode.displayWithoutProcessing = true + + self.disabledOverlayNode = ASDisplayNode() + + super.init(layerBacked: false, dynamicBounce: false) + + self.addSubnode(self.leftIconNode) + self.addSubnode(self.rightIconNode) + + self.addSubnode(self.disabledOverlayNode) + } + + override func didLoad() { + super.didLoad() + + let sliderView = TGPhotoEditorSliderView() + sliderView.enablePanHandling = true + sliderView.enablePanHandling = true + sliderView.trackCornerRadius = 1.0 + sliderView.lineSize = 2.0 + sliderView.dotSize = 5.0 + sliderView.minimumValue = 0.0 + sliderView.maximumValue = 4.0 + sliderView.startValue = 0.0 + sliderView.positionsCount = 5 + sliderView.disablesInteractiveTransitionGestureRecognizer = true + if let item = self.item, let params = self.layoutParams { + sliderView.isUserInteractionEnabled = item.enabled + + sliderView.value = CGFloat((item.value - 8) / 2) + sliderView.backgroundColor = item.theme.list.itemBlocksBackgroundColor + sliderView.backColor = item.theme.list.disclosureArrowColor + sliderView.trackColor = item.enabled ? item.theme.list.itemAccentColor : item.theme.list.itemDisabledTextColor + sliderView.knobImage = generateKnobImage() + + let sliderInset: CGFloat = item.displayIcons ? 38.0 : 16.0 + + sliderView.frame = CGRect(origin: CGPoint(x: params.leftInset + sliderInset, y: 8.0), size: CGSize(width: params.width - params.leftInset - params.rightInset - sliderInset * 2.0, height: 44.0)) + } + self.view.insertSubview(sliderView, belowSubview: self.disabledOverlayNode.view) + sliderView.addTarget(self, action: #selector(self.sliderValueChanged), for: .valueChanged) + self.sliderView = sliderView + } + + func asyncLayout() -> (_ item: BubbleSettingsRadiusItem, _ params: ListViewItemLayoutParams, _ neighbors: ItemListNeighbors) -> (ListViewItemNodeLayout, () -> Void) { + let currentItem = self.item + + return { item, params, neighbors in + var updatedLeftIcon: UIImage? + var updatedRightIcon: UIImage? + + var themeUpdated = false + if currentItem?.theme !== item.theme { + themeUpdated = true + + updatedLeftIcon = generateTintedImage(image: UIImage(bundleImageName: "Instant View/SettingsFontMinIcon"), color: item.theme.list.itemPrimaryTextColor) + updatedRightIcon = generateTintedImage(image: UIImage(bundleImageName: "Instant View/SettingsFontMaxIcon"), color: item.theme.list.itemPrimaryTextColor) + } + + let contentSize: CGSize + var insets: UIEdgeInsets + let separatorHeight = UIScreenPixel + + contentSize = CGSize(width: params.width, height: 60.0) + insets = itemListNeighborsGroupedInsets(neighbors) + + if item.disableLeadingInset { + insets.top = 0.0 + insets.bottom = 0.0 + } + + let layout = ListViewItemNodeLayout(contentSize: contentSize, insets: insets) + let layoutSize = layout.size + + return (layout, { [weak self] in + if let strongSelf = self { + let firstTime = strongSelf.item == nil || item.force + strongSelf.item = item + strongSelf.layoutParams = params + + strongSelf.backgroundNode.backgroundColor = item.theme.list.itemBlocksBackgroundColor + strongSelf.topStripeNode.backgroundColor = item.theme.list.itemBlocksSeparatorColor + strongSelf.bottomStripeNode.backgroundColor = item.theme.list.itemBlocksSeparatorColor + + strongSelf.disabledOverlayNode.backgroundColor = item.theme.list.itemBlocksBackgroundColor.withAlphaComponent(0.4) + strongSelf.disabledOverlayNode.isHidden = item.enabled + strongSelf.disabledOverlayNode.frame = CGRect(origin: CGPoint(x: params.leftInset, y: 8.0), size: CGSize(width: params.width - params.leftInset - params.rightInset, height: 44.0)) + + 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 + let bottomStripeOffset: CGFloat + switch neighbors.bottom { + case .sameSection(false): + bottomStripeInset = params.leftInset + 16.0 + bottomStripeOffset = -separatorHeight + default: + bottomStripeInset = 0.0 + bottomStripeOffset = 0.0 + hasBottomCorners = true + strongSelf.bottomStripeNode.isHidden = hasCorners + } + + strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.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: layoutSize.width, height: separatorHeight)) + strongSelf.bottomStripeNode.frame = CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height + bottomStripeOffset), size: CGSize(width: layoutSize.width - bottomStripeInset, height: separatorHeight)) + + if let updatedLeftIcon = updatedLeftIcon { + strongSelf.leftIconNode.image = updatedLeftIcon + } + if let image = strongSelf.leftIconNode.image { + strongSelf.leftIconNode.frame = CGRect(origin: CGPoint(x: params.leftInset + 18.0, y: 25.0), size: CGSize(width: image.size.width, height: image.size.height)) + } + if let updatedRightIcon = updatedRightIcon { + strongSelf.rightIconNode.image = updatedRightIcon + } + if let image = strongSelf.rightIconNode.image { + strongSelf.rightIconNode.frame = CGRect(origin: CGPoint(x: params.width - params.rightInset - 14.0 - image.size.width, y: 21.0), size: CGSize(width: image.size.width, height: image.size.height)) + } + + strongSelf.leftIconNode.isHidden = !item.displayIcons + strongSelf.rightIconNode.isHidden = !item.displayIcons + + if let sliderView = strongSelf.sliderView { + sliderView.isUserInteractionEnabled = item.enabled + sliderView.trackColor = item.enabled ? item.theme.list.itemAccentColor : item.theme.list.itemDisabledTextColor + + if themeUpdated { + sliderView.backgroundColor = item.theme.list.itemBlocksBackgroundColor + sliderView.backColor = item.theme.list.disclosureArrowColor + sliderView.knobImage = generateKnobImage() + } + + let value: CGFloat = CGFloat((item.value - 8) / 2) + if firstTime { + sliderView.value = value + } + + let sliderInset: CGFloat = item.displayIcons ? 38.0 : 16.0 + sliderView.frame = CGRect(origin: CGPoint(x: params.leftInset + sliderInset, y: 8.0), size: CGSize(width: params.width - params.leftInset - params.rightInset - sliderInset * 2.0, height: 44.0)) + } + } + }) + } + } + + override func animateInsertion(_ currentTimestamp: Double, duration: Double, short: Bool) { + self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.4) + } + + override func animateRemoved(_ currentTimestamp: Double, duration: Double) { + self.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false) + } + + @objc func sliderValueChanged() { + guard let sliderView = self.sliderView else { + return + } + let value = Int(sliderView.value) * 2 + 8 + self.item?.updated(value) + } +} diff --git a/submodules/SettingsUI/Sources/Themes/EditThemeController.swift b/submodules/SettingsUI/Sources/Themes/EditThemeController.swift index d0933c5a1c..f85e96e5ce 100644 --- a/submodules/SettingsUI/Sources/Themes/EditThemeController.swift +++ b/submodules/SettingsUI/Sources/Themes/EditThemeController.swift @@ -53,7 +53,7 @@ private enum EditThemeControllerEntry: ItemListNodeEntry { case slug(PresentationTheme, PresentationStrings, String, String, Bool) case slugInfo(PresentationTheme, String) case chatPreviewHeader(PresentationTheme, String) - case chatPreview(PresentationTheme, PresentationTheme, TelegramWallpaper, PresentationFontSize, PresentationStrings, PresentationDateTimeFormat, PresentationPersonNameOrder, [ChatPreviewMessageItem]) + case chatPreview(PresentationTheme, PresentationTheme, TelegramWallpaper, PresentationFontSize, PresentationChatBubbleCorners, PresentationStrings, PresentationDateTimeFormat, PresentationPersonNameOrder, [ChatPreviewMessageItem]) case changeColors(PresentationTheme, String) case uploadTheme(PresentationTheme, String) case uploadInfo(PresentationTheme, String) @@ -114,8 +114,8 @@ private enum EditThemeControllerEntry: ItemListNodeEntry { } else { return false } - case let .chatPreview(lhsTheme, lhsComponentTheme, lhsWallpaper, lhsFontSize, lhsStrings, lhsTimeFormat, lhsNameOrder, lhsItems): - if case let .chatPreview(rhsTheme, rhsComponentTheme, rhsWallpaper, rhsFontSize, rhsStrings, rhsTimeFormat, rhsNameOrder, rhsItems) = rhs, lhsComponentTheme === rhsComponentTheme, lhsTheme === rhsTheme, lhsWallpaper == rhsWallpaper, lhsFontSize == rhsFontSize, lhsStrings === rhsStrings, lhsTimeFormat == rhsTimeFormat, lhsNameOrder == rhsNameOrder, lhsItems == rhsItems { + case let .chatPreview(lhsTheme, lhsComponentTheme, lhsWallpaper, lhsFontSize, lhsChatBubbleCorners, lhsStrings, lhsTimeFormat, lhsNameOrder, lhsItems): + if case let .chatPreview(rhsTheme, rhsComponentTheme, rhsWallpaper, rhsFontSize, rhsChatBubbleCorners, rhsStrings, rhsTimeFormat, rhsNameOrder, rhsItems) = rhs, lhsComponentTheme === rhsComponentTheme, lhsTheme === rhsTheme, lhsWallpaper == rhsWallpaper, lhsFontSize == rhsFontSize, lhsChatBubbleCorners == rhsChatBubbleCorners, lhsStrings === rhsStrings, lhsTimeFormat == rhsTimeFormat, lhsNameOrder == rhsNameOrder, lhsItems == rhsItems { return true } else { return false @@ -172,8 +172,8 @@ private enum EditThemeControllerEntry: ItemListNodeEntry { return ItemListTextItem(presentationData: presentationData, text: .markdown(text), sectionId: self.section) case let .chatPreviewHeader(theme, text): return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section) - case let .chatPreview(theme, componentTheme, wallpaper, fontSize, strings, dateTimeFormat, nameDisplayOrder, items): - return ThemeSettingsChatPreviewItem(context: arguments.context, theme: theme, componentTheme: componentTheme, strings: strings, sectionId: self.section, fontSize: fontSize, wallpaper: wallpaper, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, messageItems: items) + case let .chatPreview(theme, componentTheme, wallpaper, fontSize, chatBubbleCorners, strings, dateTimeFormat, nameDisplayOrder, items): + return ThemeSettingsChatPreviewItem(context: arguments.context, theme: theme, componentTheme: componentTheme, strings: strings, sectionId: self.section, fontSize: fontSize, chatBubbleCorners: chatBubbleCorners, wallpaper: wallpaper, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, messageItems: items) case let .changeColors(theme, text): return ItemListActionItem(presentationData: presentationData, title: text, kind: .generic, alignment: .natural, sectionId: self.section, style: .blocks, action: { arguments.openColors() @@ -253,7 +253,7 @@ private func editThemeControllerEntries(presentationData: PresentationData, stat entries.append(.slugInfo(presentationData.theme, infoText)) entries.append(.chatPreviewHeader(presentationData.theme, presentationData.strings.EditTheme_Preview.uppercased())) - entries.append(.chatPreview(presentationData.theme, previewTheme, previewTheme.chat.defaultWallpaper, presentationData.chatFontSize, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameDisplayOrder, [ChatPreviewMessageItem(outgoing: false, reply: (previewIncomingReplyName, previewIncomingReplyText), text: previewIncomingText), ChatPreviewMessageItem(outgoing: true, reply: nil, text: previewOutgoingText)])) + entries.append(.chatPreview(presentationData.theme, previewTheme, previewTheme.chat.defaultWallpaper, presentationData.chatFontSize, presentationData.chatBubbleCorners, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameDisplayOrder, [ChatPreviewMessageItem(outgoing: false, reply: (previewIncomingReplyName, previewIncomingReplyText), text: previewIncomingText), ChatPreviewMessageItem(outgoing: true, reply: nil, text: previewOutgoingText)])) entries.append(.changeColors(presentationData.theme, presentationData.strings.EditTheme_ChangeColors)) if !hasSettings { @@ -551,12 +551,12 @@ public func editThemeController(context: AccountContext, mode: EditThemeControll context.sharedContext.accountManager.mediaBox.storeResourceData(resource.id, data: data, synchronous: true) } - let themeReference: PresentationThemeReference = .cloud(PresentationCloudTheme(theme: resultTheme, resolvedWallpaper: resolvedWallpaper)) + let themeReference: PresentationThemeReference = .cloud(PresentationCloudTheme(theme: resultTheme, resolvedWallpaper: resolvedWallpaper, creatorAccountId: context.account.id)) var themeSpecificChatWallpapers = current.themeSpecificChatWallpapers themeSpecificChatWallpapers[themeReference.index] = nil - return PresentationThemeSettings(theme: themeReference, themeSpecificAccentColors: current.themeSpecificAccentColors, themeSpecificChatWallpapers: themeSpecificChatWallpapers, useSystemFont: current.useSystemFont, fontSize: current.fontSize, listsFontSize: current.listsFontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations) + return PresentationThemeSettings(theme: themeReference, themeSpecificAccentColors: current.themeSpecificAccentColors, themeSpecificChatWallpapers: themeSpecificChatWallpapers, useSystemFont: current.useSystemFont, fontSize: current.fontSize, listsFontSize: current.listsFontSize, chatBubbleSettings: current.chatBubbleSettings, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations) }) |> deliverOnMainQueue).start(completed: { if !hasCustomFile { saveThemeTemplateFile(state.title, themeResource, { @@ -585,12 +585,12 @@ public func editThemeController(context: AccountContext, mode: EditThemeControll context.sharedContext.accountManager.mediaBox.storeResourceData(resource.id, data: data, synchronous: true) } - let themeReference: PresentationThemeReference = .cloud(PresentationCloudTheme(theme: resultTheme, resolvedWallpaper: resolvedWallpaper)) + let themeReference: PresentationThemeReference = .cloud(PresentationCloudTheme(theme: resultTheme, resolvedWallpaper: resolvedWallpaper, creatorAccountId: context.account.id)) var themeSpecificChatWallpapers = current.themeSpecificChatWallpapers themeSpecificChatWallpapers[themeReference.index] = nil - return PresentationThemeSettings(theme: themeReference, themeSpecificAccentColors: current.themeSpecificAccentColors, themeSpecificChatWallpapers: themeSpecificChatWallpapers, useSystemFont: current.useSystemFont, fontSize: current.fontSize, listsFontSize: current.listsFontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations) + return PresentationThemeSettings(theme: themeReference, themeSpecificAccentColors: current.themeSpecificAccentColors, themeSpecificChatWallpapers: themeSpecificChatWallpapers, useSystemFont: current.useSystemFont, fontSize: current.fontSize, listsFontSize: current.listsFontSize, chatBubbleSettings: current.chatBubbleSettings, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations) }) |> deliverOnMainQueue).start(completed: { if let themeResource = themeResource, !hasCustomFile { saveThemeTemplateFile(state.title, themeResource, { diff --git a/submodules/SettingsUI/Sources/Themes/ThemeAccentColorController.swift b/submodules/SettingsUI/Sources/Themes/ThemeAccentColorController.swift index 11f2677bcc..21c9950124 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeAccentColorController.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeAccentColorController.swift @@ -260,7 +260,7 @@ final class ThemeAccentColorController: ViewController { if case let .result(resultTheme) = next { let _ = applyTheme(accountManager: context.sharedContext.accountManager, account: context.account, theme: resultTheme).start() return updatePresentationThemeSettingsInteractively(accountManager: context.sharedContext.accountManager, { current in - let themeReference: PresentationThemeReference = .cloud(PresentationCloudTheme(theme: resultTheme, resolvedWallpaper: wallpaper)) + let themeReference: PresentationThemeReference = .cloud(PresentationCloudTheme(theme: resultTheme, resolvedWallpaper: wallpaper, creatorAccountId: context.account.id)) var updatedTheme = current.theme var updatedAutomaticThemeSwitchSetting = current.automaticThemeSwitchSetting @@ -276,7 +276,7 @@ final class ThemeAccentColorController: ViewController { var themeSpecificAccentColors = current.themeSpecificAccentColors themeSpecificAccentColors[baseThemeReference.index] = PresentationThemeAccentColor(themeIndex: themeReference.index) - return PresentationThemeSettings(theme: updatedTheme, themeSpecificAccentColors: themeSpecificAccentColors, themeSpecificChatWallpapers: themeSpecificChatWallpapers, useSystemFont: current.useSystemFont, fontSize: current.fontSize, listsFontSize: current.listsFontSize, automaticThemeSwitchSetting: updatedAutomaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations) + return PresentationThemeSettings(theme: updatedTheme, themeSpecificAccentColors: themeSpecificAccentColors, themeSpecificChatWallpapers: themeSpecificChatWallpapers, useSystemFont: current.useSystemFont, fontSize: current.fontSize, listsFontSize: current.listsFontSize, chatBubbleSettings: current.chatBubbleSettings, automaticThemeSwitchSetting: updatedAutomaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations) }) |> castError(CreateThemeError.self) } else { @@ -289,7 +289,7 @@ final class ThemeAccentColorController: ViewController { if case let .result(resultTheme) = next { let _ = applyTheme(accountManager: context.sharedContext.accountManager, account: context.account, theme: resultTheme).start() return updatePresentationThemeSettingsInteractively(accountManager: context.sharedContext.accountManager, { current in - let themeReference: PresentationThemeReference = .cloud(PresentationCloudTheme(theme: resultTheme, resolvedWallpaper: wallpaper)) + let themeReference: PresentationThemeReference = .cloud(PresentationCloudTheme(theme: resultTheme, resolvedWallpaper: wallpaper, creatorAccountId: context.account.id)) var updatedTheme = current.theme var updatedAutomaticThemeSwitchSetting = current.automaticThemeSwitchSetting @@ -305,7 +305,7 @@ final class ThemeAccentColorController: ViewController { var themeSpecificAccentColors = current.themeSpecificAccentColors themeSpecificAccentColors[baseThemeReference.index] = PresentationThemeAccentColor(themeIndex: themeReference.index) - return PresentationThemeSettings(theme: updatedTheme, themeSpecificAccentColors: themeSpecificAccentColors, themeSpecificChatWallpapers: themeSpecificChatWallpapers, useSystemFont: current.useSystemFont, fontSize: current.fontSize, listsFontSize: current.listsFontSize, automaticThemeSwitchSetting: updatedAutomaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations) + return PresentationThemeSettings(theme: updatedTheme, themeSpecificAccentColors: themeSpecificAccentColors, themeSpecificChatWallpapers: themeSpecificChatWallpapers, useSystemFont: current.useSystemFont, fontSize: current.fontSize, listsFontSize: current.listsFontSize, chatBubbleSettings: current.chatBubbleSettings, automaticThemeSwitchSetting: updatedAutomaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations) }) |> castError(CreateThemeError.self) } else { @@ -443,8 +443,7 @@ final class ThemeAccentColorController: ViewController { var defaultPatternWallpaper: TelegramWallpaper? for wallpaper in wallpapers { - //JqSUrO0-mFIBAAAAWwTvLzoWGQI, 25 - if case let .file(file) = wallpaper, file.slug == "-Xc-np9y2VMCAAAARKr0yNNPYW0" { + if case let .file(file) = wallpaper, file.slug == "JqSUrO0-mFIBAAAAWwTvLzoWGQI" { defaultPatternWallpaper = wallpaper break } diff --git a/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift b/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift index 5a7eb80052..57916acc46 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift @@ -226,6 +226,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate self.presentationData = context.sharedContext.currentPresentationData.with { $0 } self.theme = theme self.wallpaper = self.presentationData.chatWallpaper + let bubbleCorners = self.presentationData.chatBubbleCorners self.ready = ready @@ -498,7 +499,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate updatedTheme = theme } - let _ = PresentationResourcesChat.principalGraphics(mediaBox: context.account.postbox.mediaBox, knockoutWallpaper: context.sharedContext.immediateExperimentalUISettings.knockoutWallpaper, theme: updatedTheme!, wallpaper: wallpaper) + let _ = PresentationResourcesChat.principalGraphics(mediaBox: context.account.postbox.mediaBox, knockoutWallpaper: context.sharedContext.immediateExperimentalUISettings.knockoutWallpaper, theme: updatedTheme!, wallpaper: wallpaper, bubbleCorners: bubbleCorners) } else { updatedTheme = nil } @@ -765,7 +766,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate private func updateChatsLayout(layout: ContainerViewLayout, topInset: CGFloat, transition: ContainedViewLayoutTransition) { var items: [ChatListItem] = [] - let interaction = ChatListNodeInteraction(activateSearch: {}, peerSelected: { _ in }, togglePeerSelected: { _ in }, messageSelected: { _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, deletePeer: { _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, activateChatPreview: { _, _, gesture in + let interaction = ChatListNodeInteraction(activateSearch: {}, peerSelected: { _ in }, disabledPeerSelected: { _ in }, togglePeerSelected: { _ in }, messageSelected: { _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, deletePeer: { _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, activateChatPreview: { _, _, gesture in gesture?.cancel() }) let chatListPresentationData = ChatListPresentationData(theme: self.theme, fontSize: self.presentationData.listsFontSize, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameSortOrder: self.presentationData.nameSortOrder, nameDisplayOrder: self.presentationData.nameDisplayOrder, disableAnimations: true) @@ -836,7 +837,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate } private func updateMessagesLayout(layout: ContainerViewLayout, bottomInset: CGFloat, transition: ContainedViewLayoutTransition) { - let headerItem = self.context.sharedContext.makeChatMessageDateHeaderItem(context: self.context, timestamp: self.referenceTimestamp, theme: self.theme, strings: self.presentationData.strings, wallpaper: self.wallpaper, fontSize: self.presentationData.chatFontSize, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder) + let headerItem = self.context.sharedContext.makeChatMessageDateHeaderItem(context: self.context, timestamp: self.referenceTimestamp, theme: self.theme, strings: self.presentationData.strings, wallpaper: self.wallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder) var items: [ListViewItem] = [] let peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: 1) @@ -878,7 +879,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate sampleMessages.append(message8) items = sampleMessages.reversed().map { message in - let item = self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: message, theme: self.theme, strings: self.presentationData.strings, wallpaper: self.wallpaper, fontSize: self.presentationData.chatFontSize, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: !message.media.isEmpty ? FileMediaResourceStatus(mediaStatus: .playbackStatus(.paused), fetchStatus: .Local) : nil, tapMessage: { [weak self] message in + let item = self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: message, theme: self.theme, strings: self.presentationData.strings, wallpaper: self.wallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: !message.media.isEmpty ? FileMediaResourceStatus(mediaStatus: .playbackStatus(.paused), fetchStatus: .Local) : nil, tapMessage: { [weak self] message in if message.flags.contains(.Incoming) { self?.updateSection(.accent) self?.requestSectionUpdate?(.accent) diff --git a/submodules/SettingsUI/Sources/Themes/ThemeAutoNightSettingsController.swift b/submodules/SettingsUI/Sources/Themes/ThemeAutoNightSettingsController.swift index 34b81ce429..13136e0a15 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeAutoNightSettingsController.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeAutoNightSettingsController.swift @@ -550,7 +550,7 @@ public func themeAutoNightSettingsController(context: AccountContext) -> ViewCon |> mapToSignal { resolvedWallpaper -> Signal in var updatedTheme = theme if case let .cloud(info) = theme { - updatedTheme = .cloud(PresentationCloudTheme(theme: info.theme, resolvedWallpaper: resolvedWallpaper)) + updatedTheme = .cloud(PresentationCloudTheme(theme: info.theme, resolvedWallpaper: resolvedWallpaper, creatorAccountId: info.theme.isCreator ? context.account.id : nil)) } updateSettings { settings in @@ -572,7 +572,7 @@ public func themeAutoNightSettingsController(context: AccountContext) -> ViewCon let settings = (sharedData.entries[ApplicationSpecificSharedDataKeys.presentationThemeSettings] as? PresentationThemeSettings) ?? PresentationThemeSettings.defaultSettings let defaultThemes: [PresentationThemeReference] = [.builtin(.night), .builtin(.nightAccent)] - let cloudThemes: [PresentationThemeReference] = cloudThemes.map { .cloud(PresentationCloudTheme(theme: $0, resolvedWallpaper: nil)) } + let cloudThemes: [PresentationThemeReference] = cloudThemes.map { .cloud(PresentationCloudTheme(theme: $0, resolvedWallpaper: nil, creatorAccountId: $0.isCreator ? context.account.id : nil)) } var availableThemes = defaultThemes availableThemes.append(contentsOf: cloudThemes) @@ -587,6 +587,7 @@ public func themeAutoNightSettingsController(context: AccountContext) -> ViewCon } let controller = ItemListController(context: context, state: signal) + controller.alwaysSynchronous = true presentControllerImpl = { [weak controller] c in controller?.present(c, in: .window(.root)) } diff --git a/submodules/SettingsUI/Sources/Themes/ThemePreviewController.swift b/submodules/SettingsUI/Sources/Themes/ThemePreviewController.swift index 6fd9305c14..6ea9fd97cd 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemePreviewController.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemePreviewController.swift @@ -234,9 +234,9 @@ public final class ThemePreviewController: ViewController { |> mapToSignal { theme, wallpaper -> Signal in if let theme = theme { if case let .file(file) = wallpaper, file.id != 0 { - return .single(.cloud(PresentationCloudTheme(theme: theme, resolvedWallpaper: wallpaper))) + return .single(.cloud(PresentationCloudTheme(theme: theme, resolvedWallpaper: wallpaper, creatorAccountId: theme.isCreator ? context.account.id : nil))) } else { - return .single(.cloud(PresentationCloudTheme(theme: theme, resolvedWallpaper: nil))) + return .single(.cloud(PresentationCloudTheme(theme: theme, resolvedWallpaper: nil, creatorAccountId: theme.isCreator ? context.account.id : nil))) } } else { return .complete() @@ -313,7 +313,7 @@ public final class ThemePreviewController: ViewController { } if case let .result(theme) = result, let file = theme.file { context.sharedContext.accountManager.mediaBox.moveResourceData(from: info.resource.id, to: file.resource.id) - return .single((.cloud(PresentationCloudTheme(theme: theme, resolvedWallpaper: resolvedWallpaper)), true)) + return .single((.cloud(PresentationCloudTheme(theme: theme, resolvedWallpaper: resolvedWallpaper, creatorAccountId: theme.isCreator ? context.account.id : nil)), true)) } else { return .complete() } @@ -332,7 +332,7 @@ public final class ThemePreviewController: ViewController { } if case let .result(updatedTheme) = result, let file = updatedTheme.file { context.sharedContext.accountManager.mediaBox.moveResourceData(from: info.resource.id, to: file.resource.id) - return .single((.cloud(PresentationCloudTheme(theme: updatedTheme, resolvedWallpaper: resolvedWallpaper)), true)) + return .single((.cloud(PresentationCloudTheme(theme: updatedTheme, resolvedWallpaper: resolvedWallpaper, creatorAccountId: updatedTheme.isCreator ? context.account.id : nil)), true)) } else { return .complete() } diff --git a/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift b/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift index fa3397d30e..469477a0d6 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift @@ -350,7 +350,7 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate { private func updateChatsLayout(layout: ContainerViewLayout, topInset: CGFloat, transition: ContainedViewLayoutTransition) { var items: [ChatListItem] = [] - let interaction = ChatListNodeInteraction(activateSearch: {}, peerSelected: { _ in }, togglePeerSelected: { _ in }, messageSelected: { _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, deletePeer: { _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, activateChatPreview: { _, _, gesture in + let interaction = ChatListNodeInteraction(activateSearch: {}, peerSelected: { _ in }, disabledPeerSelected: { _ in }, togglePeerSelected: { _ in }, messageSelected: { _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, deletePeer: { _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, activateChatPreview: { _, _, gesture in gesture?.cancel() }) let chatListPresentationData = ChatListPresentationData(theme: self.previewTheme, fontSize: self.presentationData.listsFontSize, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameSortOrder: self.presentationData.nameSortOrder, nameDisplayOrder: self.presentationData.nameDisplayOrder, disableAnimations: true) @@ -442,7 +442,7 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate { } private func updateMessagesLayout(layout: ContainerViewLayout, bottomInset: CGFloat, transition: ContainedViewLayoutTransition) { - let headerItem = self.context.sharedContext.makeChatMessageDateHeaderItem(context: self.context, timestamp: self.referenceTimestamp, theme: self.previewTheme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder) + let headerItem = self.context.sharedContext.makeChatMessageDateHeaderItem(context: self.context, timestamp: self.referenceTimestamp, theme: self.previewTheme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder) var items: [ListViewItem] = [] let peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: 1) @@ -484,7 +484,7 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate { sampleMessages.append(message8) items = sampleMessages.reversed().map { message in - self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: message, theme: self.previewTheme, strings: self.presentationData.strings, wallpaper: self.previewTheme.chat.defaultWallpaper, fontSize: self.presentationData.chatFontSize, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: !message.media.isEmpty ? FileMediaResourceStatus(mediaStatus: .playbackStatus(.paused), fetchStatus: .Local) : nil, tapMessage: nil, clickThroughMessage: nil) + self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: message, theme: self.previewTheme, strings: self.presentationData.strings, wallpaper: self.previewTheme.chat.defaultWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: !message.media.isEmpty ? FileMediaResourceStatus(mediaStatus: .playbackStatus(.paused), fetchStatus: .Local) : nil, tapMessage: nil, clickThroughMessage: nil) } let width: CGFloat diff --git a/submodules/SettingsUI/Sources/Themes/ThemeSettingsChatPreviewItem.swift b/submodules/SettingsUI/Sources/Themes/ThemeSettingsChatPreviewItem.swift index 4a2a1361ad..47b4bbda37 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeSettingsChatPreviewItem.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeSettingsChatPreviewItem.swift @@ -40,18 +40,20 @@ class ThemeSettingsChatPreviewItem: ListViewItem, ItemListItem { let strings: PresentationStrings let sectionId: ItemListSectionId let fontSize: PresentationFontSize + let chatBubbleCorners: PresentationChatBubbleCorners let wallpaper: TelegramWallpaper let dateTimeFormat: PresentationDateTimeFormat let nameDisplayOrder: PresentationPersonNameOrder let messageItems: [ChatPreviewMessageItem] - init(context: AccountContext, theme: PresentationTheme, componentTheme: PresentationTheme, strings: PresentationStrings, sectionId: ItemListSectionId, fontSize: PresentationFontSize, wallpaper: TelegramWallpaper, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, messageItems: [ChatPreviewMessageItem]) { + init(context: AccountContext, theme: PresentationTheme, componentTheme: PresentationTheme, strings: PresentationStrings, sectionId: ItemListSectionId, fontSize: PresentationFontSize, chatBubbleCorners: PresentationChatBubbleCorners, wallpaper: TelegramWallpaper, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, messageItems: [ChatPreviewMessageItem]) { self.context = context self.theme = theme self.componentTheme = componentTheme self.strings = strings self.sectionId = sectionId self.fontSize = fontSize + self.chatBubbleCorners = chatBubbleCorners self.wallpaper = wallpaper self.dateTimeFormat = dateTimeFormat self.nameDisplayOrder = nameDisplayOrder @@ -163,7 +165,7 @@ class ThemeSettingsChatPreviewItemNode: ListViewItemNode { } let message = Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: messageItem.outgoing ? otherPeerId : peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66000, flags: messageItem.outgoing ? [] : [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: messageItem.outgoing ? TelegramUser(id: otherPeerId, accessHash: nil, firstName: "", lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: []) : nil, text: messageItem.text, attributes: messageItem.reply != nil ? [ReplyMessageAttribute(messageId: replyMessageId)] : [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []) - items.append(item.context.sharedContext.makeChatMessagePreviewItem(context: item.context, message: message, theme: item.componentTheme, strings: item.strings, wallpaper: item.wallpaper, fontSize: item.fontSize, dateTimeFormat: item.dateTimeFormat, nameOrder: item.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil)) + items.append(item.context.sharedContext.makeChatMessagePreviewItem(context: item.context, message: message, theme: item.componentTheme, strings: item.strings, wallpaper: item.wallpaper, fontSize: item.fontSize, chatBubbleCorners: item.chatBubbleCorners, dateTimeFormat: item.dateTimeFormat, nameOrder: item.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil)) } var nodes: [ListViewItemNode] = [] diff --git a/submodules/SettingsUI/Sources/Themes/ThemeSettingsController.swift b/submodules/SettingsUI/Sources/Themes/ThemeSettingsController.swift index c47addabc2..6f8357dfba 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeSettingsController.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeSettingsController.swift @@ -76,6 +76,7 @@ private final class ThemeSettingsControllerArguments { let openAccentColorPicker: (PresentationThemeReference, Bool) -> Void let openAutoNightTheme: () -> Void let openTextSize: () -> Void + let openBubbleSettings: () -> Void let toggleLargeEmoji: (Bool) -> Void let disableAnimations: (Bool) -> Void let selectAppIcon: (String) -> Void @@ -83,7 +84,7 @@ private final class ThemeSettingsControllerArguments { let themeContextAction: (Bool, PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void let colorContextAction: (Bool, PresentationThemeReference, ThemeSettingsColorOption?, ASDisplayNode, ContextGesture?) -> Void - init(context: AccountContext, selectTheme: @escaping (PresentationThemeReference) -> Void, selectFontSize: @escaping (PresentationFontSize) -> Void, openWallpaperSettings: @escaping () -> Void, selectAccentColor: @escaping (PresentationThemeAccentColor?) -> Void, openAccentColorPicker: @escaping (PresentationThemeReference, Bool) -> Void, openAutoNightTheme: @escaping () -> Void, openTextSize: @escaping () -> Void, toggleLargeEmoji: @escaping (Bool) -> Void, disableAnimations: @escaping (Bool) -> Void, selectAppIcon: @escaping (String) -> Void, editTheme: @escaping (PresentationCloudTheme) -> Void, themeContextAction: @escaping (Bool, PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void, colorContextAction: @escaping (Bool, PresentationThemeReference, ThemeSettingsColorOption?, ASDisplayNode, ContextGesture?) -> Void) { + init(context: AccountContext, selectTheme: @escaping (PresentationThemeReference) -> Void, selectFontSize: @escaping (PresentationFontSize) -> Void, openWallpaperSettings: @escaping () -> Void, selectAccentColor: @escaping (PresentationThemeAccentColor?) -> Void, openAccentColorPicker: @escaping (PresentationThemeReference, Bool) -> Void, openAutoNightTheme: @escaping () -> Void, openTextSize: @escaping () -> Void, openBubbleSettings: @escaping () -> Void, toggleLargeEmoji: @escaping (Bool) -> Void, disableAnimations: @escaping (Bool) -> Void, selectAppIcon: @escaping (String) -> Void, editTheme: @escaping (PresentationCloudTheme) -> Void, themeContextAction: @escaping (Bool, PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void, colorContextAction: @escaping (Bool, PresentationThemeReference, ThemeSettingsColorOption?, ASDisplayNode, ContextGesture?) -> Void) { self.context = context self.selectTheme = selectTheme self.selectFontSize = selectFontSize @@ -92,6 +93,7 @@ private final class ThemeSettingsControllerArguments { self.openAccentColorPicker = openAccentColorPicker self.openAutoNightTheme = openAutoNightTheme self.openTextSize = openTextSize + self.openBubbleSettings = openBubbleSettings self.toggleLargeEmoji = toggleLargeEmoji self.disableAnimations = disableAnimations self.selectAppIcon = selectAppIcon @@ -131,11 +133,12 @@ private enum ThemeSettingsControllerEntry: ItemListNodeEntry { case themeListHeader(PresentationTheme, String) case fontSizeHeader(PresentationTheme, String) case fontSize(PresentationTheme, PresentationFontSize) - case chatPreview(PresentationTheme, TelegramWallpaper, PresentationFontSize, PresentationStrings, PresentationDateTimeFormat, PresentationPersonNameOrder, [ChatPreviewMessageItem]) + case chatPreview(PresentationTheme, TelegramWallpaper, PresentationFontSize, PresentationChatBubbleCorners, PresentationStrings, PresentationDateTimeFormat, PresentationPersonNameOrder, [ChatPreviewMessageItem]) case wallpaper(PresentationTheme, String) case accentColor(PresentationTheme, PresentationThemeReference, PresentationThemeReference, [PresentationThemeReference], ThemeSettingsColorOption?) case autoNightTheme(PresentationTheme, String, String) case textSize(PresentationTheme, String, String) + case bubbleSettings(PresentationTheme, String, String) case themeItem(PresentationTheme, PresentationStrings, [PresentationThemeReference], [PresentationThemeReference], PresentationThemeReference, [Int64: PresentationThemeAccentColor], [Int64: TelegramWallpaper], PresentationThemeAccentColor?) case iconHeader(PresentationTheme, String) case iconItem(PresentationTheme, PresentationStrings, [PresentationAppIcon], String?) @@ -150,7 +153,7 @@ private enum ThemeSettingsControllerEntry: ItemListNodeEntry { return ThemeSettingsControllerSection.chatPreview.rawValue case .fontSizeHeader, .fontSize: return ThemeSettingsControllerSection.fontSize.rawValue - case .wallpaper, .autoNightTheme, .textSize: + case .wallpaper, .autoNightTheme, .textSize, .bubbleSettings: return ThemeSettingsControllerSection.background.rawValue case .iconHeader, .iconItem: return ThemeSettingsControllerSection.icon.rawValue @@ -175,29 +178,31 @@ private enum ThemeSettingsControllerEntry: ItemListNodeEntry { return 6 case .textSize: return 7 - case .fontSizeHeader: + case .bubbleSettings: return 8 - case .fontSize: + case .fontSizeHeader: return 9 - case .iconHeader: + case .fontSize: return 10 - case .iconItem: + case .iconHeader: return 11 - case .otherHeader: + case .iconItem: return 12 - case .largeEmoji: + case .otherHeader: return 13 - case .animations: + case .largeEmoji: return 14 - case .animationsInfo: + case .animations: return 15 + case .animationsInfo: + return 16 } } static func ==(lhs: ThemeSettingsControllerEntry, rhs: ThemeSettingsControllerEntry) -> Bool { switch lhs { - case let .chatPreview(lhsTheme, lhsWallpaper, lhsFontSize, lhsStrings, lhsTimeFormat, lhsNameOrder, lhsItems): - if case let .chatPreview(rhsTheme, rhsWallpaper, rhsFontSize, rhsStrings, rhsTimeFormat, rhsNameOrder, rhsItems) = rhs, lhsTheme === rhsTheme, lhsWallpaper == rhsWallpaper, lhsFontSize == rhsFontSize, lhsStrings === rhsStrings, lhsTimeFormat == rhsTimeFormat, lhsNameOrder == rhsNameOrder, lhsItems == rhsItems { + case let .chatPreview(lhsTheme, lhsWallpaper, lhsFontSize, lhsChatBubbleCorners, lhsStrings, lhsTimeFormat, lhsNameOrder, lhsItems): + if case let .chatPreview(rhsTheme, rhsWallpaper, rhsFontSize, rhsChatBubbleCorners, rhsStrings, rhsTimeFormat, rhsNameOrder, rhsItems) = rhs, lhsTheme === rhsTheme, lhsWallpaper == rhsWallpaper, lhsFontSize == rhsFontSize, lhsChatBubbleCorners == rhsChatBubbleCorners, lhsStrings === rhsStrings, lhsTimeFormat == rhsTimeFormat, lhsNameOrder == rhsNameOrder, lhsItems == rhsItems { return true } else { return false @@ -226,6 +231,12 @@ private enum ThemeSettingsControllerEntry: ItemListNodeEntry { } else { return false } + case let .bubbleSettings(lhsTheme, lhsText, lhsValue): + if case let .bubbleSettings(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue { + return true + } else { + return false + } case let .themeListHeader(lhsTheme, lhsText): if case let .themeListHeader(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText { return true @@ -302,8 +313,8 @@ private enum ThemeSettingsControllerEntry: ItemListNodeEntry { return ThemeSettingsFontSizeItem(theme: theme, fontSize: fontSize, sectionId: self.section, updated: { value in arguments.selectFontSize(value) }, tag: ThemeSettingsEntryTag.fontSize) - case let .chatPreview(theme, wallpaper, fontSize, strings, dateTimeFormat, nameDisplayOrder, items): - return ThemeSettingsChatPreviewItem(context: arguments.context, theme: theme, componentTheme: theme, strings: strings, sectionId: self.section, fontSize: fontSize, wallpaper: wallpaper, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, messageItems: items) + case let .chatPreview(theme, wallpaper, fontSize, chatBubbleCorners, strings, dateTimeFormat, nameDisplayOrder, items): + return ThemeSettingsChatPreviewItem(context: arguments.context, theme: theme, componentTheme: theme, strings: strings, sectionId: self.section, fontSize: fontSize, chatBubbleCorners: chatBubbleCorners, wallpaper: wallpaper, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, messageItems: items) case let .wallpaper(theme, text): return ItemListDisclosureItem(presentationData: presentationData, title: text, label: "", sectionId: self.section, style: .blocks, action: { arguments.openWallpaperSettings() @@ -387,6 +398,10 @@ private enum ThemeSettingsControllerEntry: ItemListNodeEntry { return ItemListDisclosureItem(presentationData: presentationData, icon: nil, title: text, label: value, labelStyle: .text, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: { arguments.openTextSize() }) + case let .bubbleSettings(theme, text, value): + return ItemListDisclosureItem(presentationData: presentationData, icon: nil, title: text, label: value, labelStyle: .text, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: { + arguments.openBubbleSettings() + }) case let .themeListHeader(theme, text): return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section) case let .themeItem(theme, strings, themes, allThemes, currentTheme, themeSpecificAccentColors, themeSpecificChatWallpapers, _): @@ -429,7 +444,7 @@ private func themeSettingsControllerEntries(presentationData: PresentationData, let strings = presentationData.strings let title = presentationData.autoNightModeTriggered ? strings.Appearance_ColorThemeNight.uppercased() : strings.Appearance_ColorTheme.uppercased() entries.append(.themeListHeader(presentationData.theme, title)) - entries.append(.chatPreview(presentationData.theme, presentationData.chatWallpaper, presentationData.chatFontSize, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameDisplayOrder, [ChatPreviewMessageItem(outgoing: false, reply: (presentationData.strings.Appearance_PreviewReplyAuthor, presentationData.strings.Appearance_PreviewReplyText), text: presentationData.strings.Appearance_PreviewIncomingText), ChatPreviewMessageItem(outgoing: true, reply: nil, text: presentationData.strings.Appearance_PreviewOutgoingText)])) + entries.append(.chatPreview(presentationData.theme, presentationData.chatWallpaper, presentationData.chatFontSize, presentationData.chatBubbleCorners, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameDisplayOrder, [ChatPreviewMessageItem(outgoing: false, reply: (presentationData.strings.Appearance_PreviewReplyAuthor, presentationData.strings.Appearance_PreviewReplyText), text: presentationData.strings.Appearance_PreviewIncomingText), ChatPreviewMessageItem(outgoing: true, reply: nil, text: presentationData.strings.Appearance_PreviewOutgoingText)])) let generalThemes: [PresentationThemeReference] = availableThemes.filter { reference in if case let .cloud(theme) = reference { @@ -497,6 +512,7 @@ private func themeSettingsControllerEntries(presentationData: PresentationData, } } entries.append(.textSize(presentationData.theme, strings.Appearance_TextSizeSetting, textSizeValue)) + entries.append(.bubbleSettings(presentationData.theme, strings.Appearance_BubbleCornersSetting, "")) if !availableAppIcons.isEmpty { entries.append(.iconHeader(presentationData.theme, strings.Appearance_AppIcon.uppercased())) @@ -570,6 +586,13 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The let settings = (view.entries[ApplicationSpecificSharedDataKeys.presentationThemeSettings] as? PresentationThemeSettings) ?? PresentationThemeSettings.defaultSettings pushControllerImpl?(TextSizeSelectionController(context: context, presentationThemeSettings: settings)) }) + }, openBubbleSettings: { + let _ = (context.sharedContext.accountManager.sharedData(keys: Set([ApplicationSpecificSharedDataKeys.presentationThemeSettings])) + |> take(1) + |> deliverOnMainQueue).start(next: { view in + let settings = (view.entries[ApplicationSpecificSharedDataKeys.presentationThemeSettings] as? PresentationThemeSettings) ?? PresentationThemeSettings.defaultSettings + pushControllerImpl?(BubbleSettingsController(context: context, presentationThemeSettings: settings)) + }) }, toggleLargeEmoji: { largeEmoji in let _ = updatePresentationThemeSettingsInteractively(accountManager: context.sharedContext.accountManager, { current in return current.withUpdatedLargeEmoji(largeEmoji) @@ -729,7 +752,8 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The let previousThemeIndex = themes.prefix(upTo: currentThemeIndex).reversed().firstIndex(where: { $0.file != nil }) let newTheme: PresentationThemeReference if let previousThemeIndex = previousThemeIndex { - newTheme = .cloud(PresentationCloudTheme(theme: themes[themes.index(before: previousThemeIndex.base)], resolvedWallpaper: nil)) + let theme = themes[themes.index(before: previousThemeIndex.base)] + newTheme = .cloud(PresentationCloudTheme(theme: theme, resolvedWallpaper: nil, creatorAccountId: theme.isCreator ? context.account.id : nil)) } else { newTheme = .builtin(.nightAccent) } @@ -953,7 +977,8 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The let previousThemeIndex = themes.prefix(upTo: currentThemeIndex).reversed().firstIndex(where: { $0.file != nil }) let newTheme: PresentationThemeReference if let previousThemeIndex = previousThemeIndex { - selectThemeImpl?(.cloud(PresentationCloudTheme(theme: themes[themes.index(before: previousThemeIndex.base)], resolvedWallpaper: nil))) + let theme = themes[themes.index(before: previousThemeIndex.base)] + selectThemeImpl?(.cloud(PresentationCloudTheme(theme: theme, resolvedWallpaper: nil, creatorAccountId: theme.isCreator ? context.account.id : nil))) } else { if settings.baseTheme == .night { selectAccentColorImpl?(PresentationThemeAccentColor(baseColor: .blue)) @@ -1014,7 +1039,7 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The } defaultThemes.append(contentsOf: [.builtin(.night), .builtin(.nightAccent)]) - let cloudThemes: [PresentationThemeReference] = cloudThemes.map { .cloud(PresentationCloudTheme(theme: $0, resolvedWallpaper: nil)) }.filter { !removedThemeIndexes.contains($0.index) } + let cloudThemes: [PresentationThemeReference] = cloudThemes.map { .cloud(PresentationCloudTheme(theme: $0, resolvedWallpaper: nil, creatorAccountId: $0.isCreator ? context.account.id : nil)) }.filter { !removedThemeIndexes.contains($0.index) } var availableThemes = defaultThemes if defaultThemes.first(where: { $0.index == themeReference.index }) == nil && cloudThemes.first(where: { $0.index == themeReference.index }) == nil { @@ -1158,7 +1183,7 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The var baseThemeIndex: Int64? var updatedThemeBaseIndex: Int64? if case let .cloud(info) = theme { - updatedTheme = .cloud(PresentationCloudTheme(theme: info.theme, resolvedWallpaper: resolvedWallpaper)) + updatedTheme = .cloud(PresentationCloudTheme(theme: info.theme, resolvedWallpaper: resolvedWallpaper, creatorAccountId: info.theme.isCreator ? context.account.id : nil)) if let settings = info.theme.settings { baseThemeIndex = PresentationThemeReference.builtin(PresentationBuiltinThemeReference(baseTheme: settings.baseTheme)).index updatedThemeBaseIndex = baseThemeIndex @@ -1270,7 +1295,7 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The } } - return PresentationThemeSettings(theme: updatedTheme, themeSpecificAccentColors: themeSpecificAccentColors, themeSpecificChatWallpapers: themeSpecificChatWallpapers, useSystemFont: current.useSystemFont, fontSize: current.fontSize, listsFontSize: current.listsFontSize, automaticThemeSwitchSetting: updatedAutomaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations) + return PresentationThemeSettings(theme: updatedTheme, themeSpecificAccentColors: themeSpecificAccentColors, themeSpecificChatWallpapers: themeSpecificChatWallpapers, useSystemFont: current.useSystemFont, fontSize: current.fontSize, listsFontSize: current.listsFontSize, chatBubbleSettings: current.chatBubbleSettings, automaticThemeSwitchSetting: updatedAutomaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations) }).start() presentCrossfadeControllerImpl?(true) diff --git a/submodules/SettingsUI/Sources/Themes/ThemeSettingsFontSizeItem.swift b/submodules/SettingsUI/Sources/Themes/ThemeSettingsFontSizeItem.swift index b409c93e4b..61b07eabff 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeSettingsFontSizeItem.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeSettingsFontSizeItem.swift @@ -16,17 +16,19 @@ class ThemeSettingsFontSizeItem: ListViewItem, ItemListItem { let theme: PresentationTheme let fontSize: PresentationFontSize let disableLeadingInset: Bool + let displayIcons: Bool let force: Bool let enabled: Bool let sectionId: ItemListSectionId let updated: (PresentationFontSize) -> Void let tag: ItemListItemTag? - init(theme: PresentationTheme, fontSize: PresentationFontSize, enabled: Bool = true, disableLeadingInset: Bool = false, force: Bool = false, sectionId: ItemListSectionId, updated: @escaping (PresentationFontSize) -> Void, tag: ItemListItemTag? = nil) { + init(theme: PresentationTheme, fontSize: PresentationFontSize, enabled: Bool = true, disableLeadingInset: Bool = false, displayIcons: Bool = true, force: Bool = false, sectionId: ItemListSectionId, updated: @escaping (PresentationFontSize) -> Void, tag: ItemListItemTag? = nil) { self.theme = theme self.fontSize = fontSize self.enabled = enabled self.disableLeadingInset = disableLeadingInset + self.displayIcons = displayIcons self.force = force self.sectionId = sectionId self.updated = updated @@ -164,7 +166,9 @@ class ThemeSettingsFontSizeItemNode: ListViewItemNode, ItemListItemNode { sliderView.trackColor = item.enabled ? item.theme.list.itemAccentColor : item.theme.list.itemDisabledTextColor sliderView.knobImage = generateKnobImage() - sliderView.frame = CGRect(origin: CGPoint(x: params.leftInset + 38.0, y: 8.0), size: CGSize(width: params.width - params.leftInset - params.rightInset - 38.0 * 2.0, height: 44.0)) + let sliderInset: CGFloat = item.displayIcons ? 38.0 : 16.0 + + sliderView.frame = CGRect(origin: CGPoint(x: params.leftInset + sliderInset, y: 8.0), size: CGSize(width: params.width - params.leftInset - params.rightInset - sliderInset * 2.0, height: 44.0)) } self.view.insertSubview(sliderView, belowSubview: self.disabledOverlayNode.view) sliderView.addTarget(self, action: #selector(self.sliderValueChanged), for: .valueChanged) @@ -271,6 +275,9 @@ class ThemeSettingsFontSizeItemNode: ListViewItemNode, ItemListItemNode { strongSelf.rightIconNode.frame = CGRect(origin: CGPoint(x: params.width - params.rightInset - 14.0 - image.size.width, y: 21.0), size: CGSize(width: image.size.width, height: image.size.height)) } + strongSelf.leftIconNode.isHidden = !item.displayIcons + strongSelf.rightIconNode.isHidden = !item.displayIcons + if let sliderView = strongSelf.sliderView { sliderView.isUserInteractionEnabled = item.enabled sliderView.trackColor = item.enabled ? item.theme.list.itemAccentColor : item.theme.list.itemDisabledTextColor @@ -302,7 +309,8 @@ class ThemeSettingsFontSizeItemNode: ListViewItemNode, ItemListItemNode { sliderView.value = value } - sliderView.frame = CGRect(origin: CGPoint(x: params.leftInset + 38.0, y: 8.0), size: CGSize(width: params.width - params.leftInset - params.rightInset - 38.0 * 2.0, height: 44.0)) + let sliderInset: CGFloat = item.displayIcons ? 38.0 : 16.0 + sliderView.frame = CGRect(origin: CGPoint(x: params.leftInset + sliderInset, y: 8.0), size: CGSize(width: params.width - params.leftInset - params.rightInset - sliderInset * 2.0, height: 44.0)) } } }) diff --git a/submodules/SettingsUI/Sources/Themes/ThemeSettingsThemeItem.swift b/submodules/SettingsUI/Sources/Themes/ThemeSettingsThemeItem.swift index cde23ef767..70366675ee 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeSettingsThemeItem.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeSettingsThemeItem.swift @@ -415,6 +415,7 @@ private struct ThemeSettingsThemeItemNodeTransition { let insertions: [ListViewInsertItem] let updates: [ListViewUpdateItem] let crossfade: Bool + let entries: [ThemeSettingsThemeEntry] } private func preparedTransition(context: AccountContext, action: @escaping (PresentationThemeReference) -> Void, contextAction: ((PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void)?, from fromEntries: [ThemeSettingsThemeEntry], to toEntries: [ThemeSettingsThemeEntry], crossfade: Bool) -> ThemeSettingsThemeItemNodeTransition { @@ -424,7 +425,7 @@ private func preparedTransition(context: AccountContext, action: @escaping (Pres let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, action: action, contextAction: contextAction), directionHint: .Down) } let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, action: action, contextAction: contextAction), directionHint: nil) } - return ThemeSettingsThemeItemNodeTransition(deletions: deletions, insertions: insertions, updates: updates, crossfade: crossfade) + return ThemeSettingsThemeItemNodeTransition(deletions: deletions, insertions: insertions, updates: updates, crossfade: crossfade, entries: toEntries) } private func ensureThemeVisible(listNode: ListView, themeReference: PresentationThemeReference, animated: Bool) -> Bool { @@ -512,10 +513,13 @@ class ThemeSettingsThemeItemNode: ListViewItemNode, ItemListItemNode { if self.initialized && transition.crossfade { options.insert(.AnimateCrossfade) } + options.insert(.Synchronous) var scrollToItem: ListViewScrollToItem? if !self.initialized { - if let index = item.themes.firstIndex(where: { $0.index == item.currentTheme.index }) { + if let index = transition.entries.firstIndex(where: { entry in + return entry.theme.index == item.currentTheme.index + }) { scrollToItem = ListViewScrollToItem(index: index, position: .bottom(-57.0), animated: false, curve: .Default(duration: 0.0), directionHint: .Down) self.initialized = true } diff --git a/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift b/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift index 9453bc392c..a159690102 100644 --- a/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift +++ b/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift @@ -832,10 +832,10 @@ final class WallpaperGalleryItemNode: GalleryItemNode { let theme = self.presentationData.theme.withUpdated(preview: true) let message1 = Message(stableId: 2, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 2), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66001, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: bottomMessageText, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []) - items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: message1, theme: theme, strings: self.presentationData.strings, wallpaper: currentWallpaper, fontSize: self.presentationData.chatFontSize, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil)) + items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: message1, theme: theme, strings: self.presentationData.strings, wallpaper: currentWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil)) let message2 = Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66000, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: topMessageText, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []) - items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: message2, theme: theme, strings: self.presentationData.strings, wallpaper: currentWallpaper, fontSize: self.presentationData.chatFontSize, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil)) + items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: message2, theme: theme, strings: self.presentationData.strings, wallpaper: currentWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil)) let params = ListViewItemLayoutParams(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, availableHeight: layout.size.height) if let messageNodes = self.messageNodes { diff --git a/submodules/ShareController/Sources/ShareController.swift b/submodules/ShareController/Sources/ShareController.swift index 2c93585f78..80e42a7db5 100644 --- a/submodules/ShareController/Sources/ShareController.swift +++ b/submodules/ShareController/Sources/ShareController.swift @@ -172,10 +172,19 @@ private func collectExternalShareItems(strings: PresentationStrings, dateTimeFor text.append("\n— \(option.text)") } let totalVoters = poll.results.totalVoters ?? 0 - if totalVoters == 0 { - text.append("\n\(strings.MessagePoll_NoVotes)") - } else { - text.append("\n\(strings.MessagePoll_VotedCount(totalVoters))") + switch poll.kind { + case .poll: + if totalVoters == 0 { + text.append("\n\(strings.MessagePoll_NoVotes)") + } else { + text.append("\n\(strings.MessagePoll_VotedCount(totalVoters))") + } + case .quiz: + if totalVoters == 0 { + text.append("\n\(strings.MessagePoll_QuizNoUsers)") + } else { + text.append("\n\(strings.MessagePoll_QuizCount(totalVoters))") + } } signals.append(.single(.done(.text(text)))) } else if let mediaReference = item.mediaReference, let contact = mediaReference.media as? TelegramMediaContact { diff --git a/submodules/StickerPackPreviewUI/BUCK b/submodules/StickerPackPreviewUI/BUCK index d9156bceac..4b0f121d0a 100644 --- a/submodules/StickerPackPreviewUI/BUCK +++ b/submodules/StickerPackPreviewUI/BUCK @@ -24,6 +24,7 @@ static_library( "//submodules/ActivityIndicator:ActivityIndicator", "//submodules/AnimatedStickerNode:AnimatedStickerNode", "//submodules/TelegramAnimatedStickerNode:TelegramAnimatedStickerNode", + "//submodules/ArchivedStickerPacksNotice:ArchivedStickerPacksNotice", ], frameworks = [ "$SDKROOT/System/Library/Frameworks/Foundation.framework", diff --git a/submodules/StickerPackPreviewUI/Sources/StickerPackScreen.swift b/submodules/StickerPackPreviewUI/Sources/StickerPackScreen.swift index a9db7dd57b..38e9a5ce70 100644 --- a/submodules/StickerPackPreviewUI/Sources/StickerPackScreen.swift +++ b/submodules/StickerPackPreviewUI/Sources/StickerPackScreen.swift @@ -847,8 +847,8 @@ public enum StickerPackScreenPerformedAction { case remove(positionInList: Int) } -public func StickerPackScreen(context: AccountContext, mainStickerPack: StickerPackReference, stickerPacks: [StickerPackReference], parentNavigationController: NavigationController? = nil, sendSticker: ((FileMediaReference, ASDisplayNode, CGRect) -> Bool)? = nil, actionPerformed: ((StickerPackCollectionInfo, [ItemCollectionItem], StickerPackScreenPerformedAction) -> Void)? = nil) -> ViewController { - let controller = StickerPackPreviewController(context: context, stickerPack: mainStickerPack, mode: .default, parentNavigationController: parentNavigationController, actionPerformed: actionPerformed) +public func StickerPackScreen(context: AccountContext, mode: StickerPackPreviewControllerMode = .default, mainStickerPack: StickerPackReference, stickerPacks: [StickerPackReference], parentNavigationController: NavigationController? = nil, sendSticker: ((FileMediaReference, ASDisplayNode, CGRect) -> Bool)? = nil, actionPerformed: ((StickerPackCollectionInfo, [ItemCollectionItem], StickerPackScreenPerformedAction) -> Void)? = nil) -> ViewController { + let controller = StickerPackPreviewController(context: context, stickerPack: mainStickerPack, mode: mode, parentNavigationController: parentNavigationController, actionPerformed: actionPerformed) controller.sendSticker = sendSticker return controller } diff --git a/submodules/SyncCore/Sources/Namespaces.swift b/submodules/SyncCore/Sources/Namespaces.swift index 52ae19ac3a..96db1f9af2 100644 --- a/submodules/SyncCore/Sources/Namespaces.swift +++ b/submodules/SyncCore/Sources/Namespaces.swift @@ -67,7 +67,8 @@ public struct Namespaces { public static let cachedStickerQueryResults: Int8 = 5 public static let cachedSecureIdConfiguration: Int8 = 6 public static let cachedWallpapersConfiguration: Int8 = 7 - public static let cachedThemesConfiguration: Int8 = 7 + public static let cachedThemesConfiguration: Int8 = 8 + public static let cachedPollResults: Int8 = 9 } public struct UnorderedItemList { diff --git a/submodules/SyncCore/Sources/ReplyMarkupMessageAttribute.swift b/submodules/SyncCore/Sources/ReplyMarkupMessageAttribute.swift index 6cebd304fc..c72fdced40 100644 --- a/submodules/SyncCore/Sources/ReplyMarkupMessageAttribute.swift +++ b/submodules/SyncCore/Sources/ReplyMarkupMessageAttribute.swift @@ -10,6 +10,7 @@ public enum ReplyMarkupButtonAction: PostboxCoding, Equatable { case openWebApp case payment case urlAuth(url: String, buttonId: Int32) + case setupPoll(isQuiz: Bool?) public init(decoder: PostboxDecoder) { switch decoder.decodeInt32ForKey("v", orElse: 0) { @@ -31,6 +32,8 @@ public enum ReplyMarkupButtonAction: PostboxCoding, Equatable { self = .payment case 8: self = .urlAuth(url: decoder.decodeStringForKey("u", orElse: ""), buttonId: decoder.decodeInt32ForKey("b", orElse: 0)) + case 9: + self = .setupPoll(isQuiz: decoder.decodeOptionalInt32ForKey("isq").flatMap { $0 != 0 }) default: self = .text } @@ -38,30 +41,37 @@ public enum ReplyMarkupButtonAction: PostboxCoding, Equatable { public func encode(_ encoder: PostboxEncoder) { switch self { - case .text: - encoder.encodeInt32(0, forKey: "v") - case let .url(url): - encoder.encodeInt32(1, forKey: "v") - encoder.encodeString(url, forKey: "u") - case let .callback(data): - encoder.encodeInt32(2, forKey: "v") - encoder.encodeBytes(data, forKey: "d") - case .requestPhone: - encoder.encodeInt32(3, forKey: "v") - case .requestMap: - encoder.encodeInt32(4, forKey: "v") - case let .switchInline(samePeer, query): - encoder.encodeInt32(5, forKey: "v") - encoder.encodeInt32(samePeer ? 1 : 0, forKey: "s") - encoder.encodeString(query, forKey: "q") - case .openWebApp: - encoder.encodeInt32(6, forKey: "v") - case .payment: - encoder.encodeInt32(7, forKey: "v") - case let .urlAuth(url, buttonId): - encoder.encodeInt32(8, forKey: "v") - encoder.encodeString(url, forKey: "u") - encoder.encodeInt32(buttonId, forKey: "b") + case .text: + encoder.encodeInt32(0, forKey: "v") + case let .url(url): + encoder.encodeInt32(1, forKey: "v") + encoder.encodeString(url, forKey: "u") + case let .callback(data): + encoder.encodeInt32(2, forKey: "v") + encoder.encodeBytes(data, forKey: "d") + case .requestPhone: + encoder.encodeInt32(3, forKey: "v") + case .requestMap: + encoder.encodeInt32(4, forKey: "v") + case let .switchInline(samePeer, query): + encoder.encodeInt32(5, forKey: "v") + encoder.encodeInt32(samePeer ? 1 : 0, forKey: "s") + encoder.encodeString(query, forKey: "q") + case .openWebApp: + encoder.encodeInt32(6, forKey: "v") + case .payment: + encoder.encodeInt32(7, forKey: "v") + case let .urlAuth(url, buttonId): + encoder.encodeInt32(8, forKey: "v") + encoder.encodeString(url, forKey: "u") + encoder.encodeInt32(buttonId, forKey: "b") + case let .setupPoll(isQuiz): + encoder.encodeInt32(9, forKey: "v") + if let isQuiz = isQuiz { + encoder.encodeInt32(isQuiz ? 1 : 0, forKey: "isq") + } else { + encoder.encodeNil(forKey: "isq") + } } } } diff --git a/submodules/SyncCore/Sources/TelegramMediaPoll.swift b/submodules/SyncCore/Sources/TelegramMediaPoll.swift index 91bcc68457..f01ba73c87 100644 --- a/submodules/SyncCore/Sources/TelegramMediaPoll.swift +++ b/submodules/SyncCore/Sources/TelegramMediaPoll.swift @@ -24,38 +24,45 @@ public struct TelegramMediaPollOptionVoters: Equatable, PostboxCoding { public let selected: Bool public let opaqueIdentifier: Data public let count: Int32 + public let isCorrect: Bool - public init(selected: Bool, opaqueIdentifier: Data, count: Int32) { + public init(selected: Bool, opaqueIdentifier: Data, count: Int32, isCorrect: Bool) { self.selected = selected self.opaqueIdentifier = opaqueIdentifier self.count = count + self.isCorrect = isCorrect } public init(decoder: PostboxDecoder) { self.selected = decoder.decodeInt32ForKey("s", orElse: 0) != 0 self.opaqueIdentifier = decoder.decodeDataForKey("i") ?? Data() self.count = decoder.decodeInt32ForKey("c", orElse: 0) + self.isCorrect = decoder.decodeInt32ForKey("cr", orElse: 0) != 0 } public func encode(_ encoder: PostboxEncoder) { encoder.encodeInt32(self.selected ? 1 : 0, forKey: "s") encoder.encodeData(self.opaqueIdentifier, forKey: "i") encoder.encodeInt32(self.count, forKey: "c") + encoder.encodeInt32(self.isCorrect ? 1 : 0, forKey: "cr") } } public struct TelegramMediaPollResults: Equatable, PostboxCoding { public let voters: [TelegramMediaPollOptionVoters]? public let totalVoters: Int32? + public let recentVoters: [PeerId] - public init(voters: [TelegramMediaPollOptionVoters]?, totalVoters: Int32?) { + public init(voters: [TelegramMediaPollOptionVoters]?, totalVoters: Int32?, recentVoters: [PeerId]) { self.voters = voters self.totalVoters = totalVoters + self.recentVoters = recentVoters } public init(decoder: PostboxDecoder) { self.voters = decoder.decodeOptionalObjectArrayWithDecoderForKey("v") self.totalVoters = decoder.decodeOptionalInt32ForKey("t") + self.recentVoters = decoder.decodeInt64ArrayForKey("rv").map(PeerId.init) } public func encode(_ encoder: PostboxEncoder) { @@ -69,6 +76,39 @@ public struct TelegramMediaPollResults: Equatable, PostboxCoding { } else { encoder.encodeNil(forKey: "t") } + encoder.encodeInt64Array(self.recentVoters.map { $0.toInt64() }, forKey: "rv") + } +} + +public enum TelegramMediaPollPublicity: Int32 { + case anonymous + case `public` +} + +public enum TelegramMediaPollKind: Equatable, PostboxCoding { + case poll(multipleAnswers: Bool) + case quiz + + public init(decoder: PostboxDecoder) { + switch decoder.decodeInt32ForKey("_v", orElse: 0) { + case 0: + self = .poll(multipleAnswers: decoder.decodeInt32ForKey("m", orElse: 0) != 0) + case 1: + self = .quiz + default: + assertionFailure() + self = .poll(multipleAnswers: false) + } + } + + public func encode(_ encoder: PostboxEncoder) { + switch self { + case let .poll(multipleAnswers): + encoder.encodeInt32(0, forKey: "_v") + encoder.encodeInt32(multipleAnswers ? 1 : 0, forKey: "m") + case .quiz: + encoder.encodeInt32(1, forKey: "_v") + } } } @@ -77,17 +117,26 @@ public final class TelegramMediaPoll: Media, Equatable { return self.pollId } public let pollId: MediaId - public let peerIds: [PeerId] = [] + public var peerIds: [PeerId] { + return results.recentVoters + } + + public let publicity: TelegramMediaPollPublicity + public let kind: TelegramMediaPollKind public let text: String public let options: [TelegramMediaPollOption] + public let correctAnswers: [Data]? public let results: TelegramMediaPollResults public let isClosed: Bool - public init(pollId: MediaId, text: String, options: [TelegramMediaPollOption], results: TelegramMediaPollResults, isClosed: Bool) { + public init(pollId: MediaId, publicity: TelegramMediaPollPublicity, kind: TelegramMediaPollKind, text: String, options: [TelegramMediaPollOption], correctAnswers: [Data]?, results: TelegramMediaPollResults, isClosed: Bool) { self.pollId = pollId + self.publicity = publicity + self.kind = kind self.text = text self.options = options + self.correctAnswers = correctAnswers self.results = results self.isClosed = isClosed } @@ -98,18 +147,28 @@ public final class TelegramMediaPoll: Media, Equatable { } else { self.pollId = MediaId(namespace: Namespaces.Media.LocalPoll, id: 0) } + self.publicity = TelegramMediaPollPublicity(rawValue: decoder.decodeInt32ForKey("pb", orElse: 0)) ?? TelegramMediaPollPublicity.anonymous + self.kind = decoder.decodeObjectForKey("kn", decoder: { TelegramMediaPollKind(decoder: $0) }) as? TelegramMediaPollKind ?? TelegramMediaPollKind.poll(multipleAnswers: false) self.text = decoder.decodeStringForKey("t", orElse: "") self.options = decoder.decodeObjectArrayWithDecoderForKey("os") - self.results = decoder.decodeObjectForKey("rs", decoder: { TelegramMediaPollResults(decoder: $0) }) as? TelegramMediaPollResults ?? TelegramMediaPollResults(voters: nil, totalVoters: nil) + self.correctAnswers = decoder.decodeOptionalDataArrayForKey("ca") + self.results = decoder.decodeObjectForKey("rs", decoder: { TelegramMediaPollResults(decoder: $0) }) as? TelegramMediaPollResults ?? TelegramMediaPollResults(voters: nil, totalVoters: nil, recentVoters: []) self.isClosed = decoder.decodeInt32ForKey("ic", orElse: 0) != 0 } public func encode(_ encoder: PostboxEncoder) { let buffer = WriteBuffer() self.pollId.encodeToBuffer(buffer) + encoder.encodeInt32(self.publicity.rawValue, forKey: "pb") + encoder.encodeObject(self.kind, forKey: "kn") encoder.encodeBytes(buffer, forKey: "i") encoder.encodeString(self.text, forKey: "t") encoder.encodeObjectArray(self.options, forKey: "os") + if let correctAnswers = self.correctAnswers { + encoder.encodeDataArray(correctAnswers, forKey: "ca") + } else { + encoder.encodeNil(forKey: "ca") + } encoder.encodeObject(results, forKey: "rs") encoder.encodeInt32(self.isClosed ? 1 : 0, forKey: "ic") } @@ -129,12 +188,21 @@ public final class TelegramMediaPoll: Media, Equatable { if lhs.pollId != rhs.pollId { return false } + if lhs.publicity != rhs.publicity { + return false + } + if lhs.kind != rhs.kind { + return false + } if lhs.text != rhs.text { return false } if lhs.options != rhs.options { return false } + if lhs.correctAnswers != rhs.correctAnswers { + return false + } if lhs.results != rhs.results { return false } @@ -149,20 +217,26 @@ public final class TelegramMediaPoll: Media, Equatable { if min { if let currentVoters = self.results.voters, let updatedVoters = results.voters { var selectedOpaqueIdentifiers = Set() + var correctOpaqueIdentifiers = Set() for voters in currentVoters { if voters.selected { selectedOpaqueIdentifiers.insert(voters.opaqueIdentifier) } + if voters.isCorrect { + correctOpaqueIdentifiers.insert(voters.opaqueIdentifier) + } } updatedResults = TelegramMediaPollResults(voters: updatedVoters.map({ voters in - return TelegramMediaPollOptionVoters(selected: selectedOpaqueIdentifiers.contains(voters.opaqueIdentifier), opaqueIdentifier: voters.opaqueIdentifier, count: voters.count) - }), totalVoters: results.totalVoters) + return TelegramMediaPollOptionVoters(selected: selectedOpaqueIdentifiers.contains(voters.opaqueIdentifier), opaqueIdentifier: voters.opaqueIdentifier, count: voters.count, isCorrect: correctOpaqueIdentifiers.contains(voters.opaqueIdentifier)) + }), totalVoters: results.totalVoters, recentVoters: results.recentVoters) + } else if let updatedVoters = results.voters { + updatedResults = TelegramMediaPollResults(voters: updatedVoters, totalVoters: results.totalVoters, recentVoters: results.recentVoters) } else { - updatedResults = TelegramMediaPollResults(voters: self.results.voters, totalVoters: results.totalVoters) + updatedResults = TelegramMediaPollResults(voters: self.results.voters, totalVoters: results.totalVoters, recentVoters: results.recentVoters) } } else { updatedResults = results } - return TelegramMediaPoll(pollId: self.pollId, text: self.text, options: self.options, results: updatedResults, isClosed: self.isClosed) + return TelegramMediaPoll(pollId: self.pollId, publicity: self.publicity, kind: self.kind, text: self.text, options: self.options, correctAnswers: self.correctAnswers, results: updatedResults, isClosed: self.isClosed) } } diff --git a/submodules/TelegramApi/Sources/Api0.swift b/submodules/TelegramApi/Sources/Api0.swift index ba15174d5d..ab2f4a7b7b 100644 --- a/submodules/TelegramApi/Sources/Api0.swift +++ b/submodules/TelegramApi/Sources/Api0.swift @@ -11,7 +11,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-784000893] = { return Api.payments.ValidatedRequestedInfo.parse_validatedRequestedInfo($0) } dict[461151667] = { return Api.ChatFull.parse_chatFull($0) } dict[763976820] = { return Api.ChatFull.parse_channelFull($0) } - dict[1465219162] = { return Api.PollResults.parse_pollResults($0) } + dict[-932174686] = { return Api.PollResults.parse_pollResults($0) } dict[-925415106] = { return Api.ChatParticipant.parse_chatParticipant($0) } dict[-636267638] = { return Api.ChatParticipant.parse_chatParticipantCreator($0) } dict[-489233354] = { return Api.ChatParticipant.parse_chatParticipantAdmin($0) } @@ -72,7 +72,6 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[1516793212] = { return Api.ChatInvite.parse_chatInviteAlready($0) } dict[-540871282] = { return Api.ChatInvite.parse_chatInvite($0) } dict[-532532493] = { return Api.AutoDownloadSettings.parse_autoDownloadSettings($0) } - dict[-767099577] = { return Api.AutoDownloadSettings.parse_autoDownloadSettingsLegacy($0) } dict[1678812626] = { return Api.StickerSetCovered.parse_stickerSetCovered($0) } dict[872932635] = { return Api.StickerSetCovered.parse_stickerSetMultiCovered($0) } dict[1189204285] = { return Api.RecentMeUrl.parse_recentMeUrlUnknown($0) } @@ -247,6 +246,8 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-2112423005] = { return Api.Update.parse_updateTheme($0) } dict[-2027964103] = { return Api.Update.parse_updateGeoLiveViewed($0) } dict[1448076945] = { return Api.Update.parse_updateLoginToken($0) } + dict[1123585836] = { return Api.Update.parse_updateMessagePollVote($0) } + dict[136574537] = { return Api.messages.VotesList.parse_votesList($0) } dict[1558266229] = { return Api.PopularContact.parse_popularContact($0) } dict[-373643672] = { return Api.FolderPeer.parse_folderPeer($0) } dict[367766557] = { return Api.ChannelParticipant.parse_channelParticipant($0) } @@ -254,6 +255,9 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[470789295] = { return Api.ChannelParticipant.parse_channelParticipantBanned($0) } dict[-859915345] = { return Api.ChannelParticipant.parse_channelParticipantAdmin($0) } dict[-2138237532] = { return Api.ChannelParticipant.parse_channelParticipantCreator($0) } + dict[-1567730343] = { return Api.MessageUserVote.parse_messageUserVote($0) } + dict[909603888] = { return Api.MessageUserVote.parse_messageUserVoteInputOption($0) } + dict[244310238] = { return Api.MessageUserVote.parse_messageUserVoteMultiple($0) } dict[471043349] = { return Api.contacts.Blocked.parse_blocked($0) } dict[-1878523231] = { return Api.contacts.Blocked.parse_blockedSlice($0) } dict[-55902537] = { return Api.InputDialogPeer.parse_inputDialogPeer($0) } @@ -269,6 +273,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-1344716869] = { return Api.KeyboardButton.parse_keyboardButtonBuy($0) } dict[280464681] = { return Api.KeyboardButton.parse_keyboardButtonUrlAuth($0) } dict[-802258988] = { return Api.KeyboardButton.parse_inputKeyboardButtonUrlAuth($0) } + dict[-1144565411] = { return Api.KeyboardButton.parse_keyboardButtonRequestPoll($0) } dict[-748155807] = { return Api.ContactStatus.parse_contactStatus($0) } dict[1679398724] = { return Api.SecureFile.parse_secureFileEmpty($0) } dict[-534283678] = { return Api.SecureFile.parse_secureFile($0) } @@ -350,8 +355,8 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-440664550] = { return Api.InputMedia.parse_inputMediaPhotoExternal($0) } dict[-78455655] = { return Api.InputMedia.parse_inputMediaDocumentExternal($0) } dict[-122978821] = { return Api.InputMedia.parse_inputMediaContact($0) } - dict[112424539] = { return Api.InputMedia.parse_inputMediaPoll($0) } dict[-833715459] = { return Api.InputMedia.parse_inputMediaGeoLive($0) } + dict[-1410741723] = { return Api.InputMedia.parse_inputMediaPoll($0) } dict[2134579434] = { return Api.InputPeer.parse_inputPeerEmpty($0) } dict[2107670217] = { return Api.InputPeer.parse_inputPeerSelf($0) } dict[396093539] = { return Api.InputPeer.parse_inputPeerChat($0) } @@ -603,7 +608,6 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[1421174295] = { return Api.WebPageAttribute.parse_webPageAttributeTheme($0) } dict[82699215] = { return Api.messages.FeaturedStickers.parse_featuredStickersNotModified($0) } dict[-123893531] = { return Api.messages.FeaturedStickers.parse_featuredStickers($0) } - dict[1375940666] = { return Api.auth.LoginTokenInfo.parse_loginTokenInfo($0) } dict[-2048646399] = { return Api.PhoneCallDiscardReason.parse_phoneCallDiscardReasonMissed($0) } dict[-527056480] = { return Api.PhoneCallDiscardReason.parse_phoneCallDiscardReasonDisconnect($0) } dict[1471006352] = { return Api.PhoneCallDiscardReason.parse_phoneCallDiscardReasonHangup($0) } @@ -674,6 +678,8 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-395967805] = { return Api.messages.AllStickers.parse_allStickersNotModified($0) } dict[-302170017] = { return Api.messages.AllStickers.parse_allStickers($0) } dict[-1655957568] = { return Api.PhoneConnection.parse_phoneConnection($0) } + dict[-206688531] = { return Api.help.UserInfo.parse_userInfoEmpty($0) } + dict[32192344] = { return Api.help.UserInfo.parse_userInfo($0) } dict[-1194283041] = { return Api.AccountDaysTTL.parse_accountDaysTTL($0) } dict[-1658158621] = { return Api.SecureValueType.parse_secureValueTypePersonalDetails($0) } dict[1034709504] = { return Api.SecureValueType.parse_secureValueTypePassport($0) } @@ -740,7 +746,6 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[1363483106] = { return Api.DialogPeer.parse_dialogPeerFolder($0) } dict[-104284986] = { return Api.WebDocument.parse_webDocumentNoProxy($0) } dict[475467473] = { return Api.WebDocument.parse_webDocument($0) } - dict[1211967244] = { return Api.Theme.parse_themeDocumentNotModified($0) } dict[42930452] = { return Api.Theme.parse_theme($0) } dict[-1290580579] = { return Api.contacts.Found.parse_found($0) } dict[-368018716] = { return Api.ChannelAdminLogEventsFilter.parse_channelAdminLogEventsFilter($0) } @@ -960,12 +965,16 @@ public struct Api { _1.serialize(buffer, boxed) case let _1 as Api.Update: _1.serialize(buffer, boxed) + case let _1 as Api.messages.VotesList: + _1.serialize(buffer, boxed) case let _1 as Api.PopularContact: _1.serialize(buffer, boxed) case let _1 as Api.FolderPeer: _1.serialize(buffer, boxed) case let _1 as Api.ChannelParticipant: _1.serialize(buffer, boxed) + case let _1 as Api.MessageUserVote: + _1.serialize(buffer, boxed) case let _1 as Api.contacts.Blocked: _1.serialize(buffer, boxed) case let _1 as Api.InputDialogPeer: @@ -1264,8 +1273,6 @@ public struct Api { _1.serialize(buffer, boxed) case let _1 as Api.messages.FeaturedStickers: _1.serialize(buffer, boxed) - case let _1 as Api.auth.LoginTokenInfo: - _1.serialize(buffer, boxed) case let _1 as Api.PhoneCallDiscardReason: _1.serialize(buffer, boxed) case let _1 as Api.NearestDc: @@ -1324,6 +1331,8 @@ public struct Api { _1.serialize(buffer, boxed) case let _1 as Api.PhoneConnection: _1.serialize(buffer, boxed) + case let _1 as Api.help.UserInfo: + _1.serialize(buffer, boxed) case let _1 as Api.AccountDaysTTL: _1.serialize(buffer, boxed) case let _1 as Api.SecureValueType: diff --git a/submodules/TelegramApi/Sources/Api1.swift b/submodules/TelegramApi/Sources/Api1.swift index dfdba7d654..9e6d38f6d1 100644 --- a/submodules/TelegramApi/Sources/Api1.swift +++ b/submodules/TelegramApi/Sources/Api1.swift @@ -219,6 +219,68 @@ public struct messages { } } + } + public enum VotesList: TypeConstructorDescription { + case votesList(flags: Int32, count: Int32, votes: [Api.MessageUserVote], users: [Api.User], nextOffset: String?) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .votesList(let flags, let count, let votes, let users, let nextOffset): + if boxed { + buffer.appendInt32(136574537) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt32(count, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(votes.count)) + for item in votes { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) + } + if Int(flags) & Int(1 << 0) != 0 {serializeString(nextOffset!, buffer: buffer, boxed: false)} + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .votesList(let flags, let count, let votes, let users, let nextOffset): + return ("votesList", [("flags", flags), ("count", count), ("votes", votes), ("users", users), ("nextOffset", nextOffset)]) + } + } + + public static func parse_votesList(_ reader: BufferReader) -> VotesList? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + _2 = reader.readInt32() + var _3: [Api.MessageUserVote]? + if let _ = reader.readInt32() { + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageUserVote.self) + } + var _4: [Api.User]? + if let _ = reader.readInt32() { + _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + } + var _5: String? + if Int(_1!) & Int(1 << 0) != 0 {_5 = parseString(reader) } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + let _c5 = (Int(_1!) & Int(1 << 0) == 0) || _5 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 { + return Api.messages.VotesList.votesList(flags: _1!, count: _2!, votes: _3!, users: _4!, nextOffset: _5) + } + else { + return nil + } + } + } public enum Stickers: TypeConstructorDescription { case stickersNotModified @@ -1962,13 +2024,13 @@ public extension Api { } public enum PollResults: TypeConstructorDescription { - case pollResults(flags: Int32, results: [Api.PollAnswerVoters]?, totalVoters: Int32?) + case pollResults(flags: Int32, results: [Api.PollAnswerVoters]?, totalVoters: Int32?, recentVoters: [Int32]?) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .pollResults(let flags, let results, let totalVoters): + case .pollResults(let flags, let results, let totalVoters, let recentVoters): if boxed { - buffer.appendInt32(1465219162) + buffer.appendInt32(-932174686) } serializeInt32(flags, buffer: buffer, boxed: false) if Int(flags) & Int(1 << 1) != 0 {buffer.appendInt32(481674261) @@ -1977,14 +2039,19 @@ public extension Api { item.serialize(buffer, true) }} if Int(flags) & Int(1 << 2) != 0 {serializeInt32(totalVoters!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 3) != 0 {buffer.appendInt32(481674261) + buffer.appendInt32(Int32(recentVoters!.count)) + for item in recentVoters! { + serializeInt32(item, buffer: buffer, boxed: false) + }} break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .pollResults(let flags, let results, let totalVoters): - return ("pollResults", [("flags", flags), ("results", results), ("totalVoters", totalVoters)]) + case .pollResults(let flags, let results, let totalVoters, let recentVoters): + return ("pollResults", [("flags", flags), ("results", results), ("totalVoters", totalVoters), ("recentVoters", recentVoters)]) } } @@ -1997,11 +2064,16 @@ public extension Api { } } var _3: Int32? if Int(_1!) & Int(1 << 2) != 0 {_3 = reader.readInt32() } + var _4: [Int32]? + if Int(_1!) & Int(1 << 3) != 0 {if let _ = reader.readInt32() { + _4 = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self) + } } let _c1 = _1 != nil let _c2 = (Int(_1!) & Int(1 << 1) == 0) || _2 != nil let _c3 = (Int(_1!) & Int(1 << 2) == 0) || _3 != nil - if _c1 && _c2 && _c3 { - return Api.PollResults.pollResults(flags: _1!, results: _2, totalVoters: _3) + let _c4 = (Int(_1!) & Int(1 << 3) == 0) || _4 != nil + if _c1 && _c2 && _c3 && _c4 { + return Api.PollResults.pollResults(flags: _1!, results: _2, totalVoters: _3, recentVoters: _4) } else { return nil @@ -3601,7 +3673,6 @@ public extension Api { } public enum AutoDownloadSettings: TypeConstructorDescription { case autoDownloadSettings(flags: Int32, photoSizeMax: Int32, videoSizeMax: Int32, fileSizeMax: Int32, videoUploadMaxbitrate: Int32) - case autoDownloadSettingsLegacy(flags: Int32, photoSizeMax: Int32, videoSizeMax: Int32, fileSizeMax: Int32) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { @@ -3615,15 +3686,6 @@ public extension Api { serializeInt32(fileSizeMax, buffer: buffer, boxed: false) serializeInt32(videoUploadMaxbitrate, buffer: buffer, boxed: false) break - case .autoDownloadSettingsLegacy(let flags, let photoSizeMax, let videoSizeMax, let fileSizeMax): - if boxed { - buffer.appendInt32(-767099577) - } - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt32(photoSizeMax, buffer: buffer, boxed: false) - serializeInt32(videoSizeMax, buffer: buffer, boxed: false) - serializeInt32(fileSizeMax, buffer: buffer, boxed: false) - break } } @@ -3631,8 +3693,6 @@ public extension Api { switch self { case .autoDownloadSettings(let flags, let photoSizeMax, let videoSizeMax, let fileSizeMax, let videoUploadMaxbitrate): return ("autoDownloadSettings", [("flags", flags), ("photoSizeMax", photoSizeMax), ("videoSizeMax", videoSizeMax), ("fileSizeMax", fileSizeMax), ("videoUploadMaxbitrate", videoUploadMaxbitrate)]) - case .autoDownloadSettingsLegacy(let flags, let photoSizeMax, let videoSizeMax, let fileSizeMax): - return ("autoDownloadSettingsLegacy", [("flags", flags), ("photoSizeMax", photoSizeMax), ("videoSizeMax", videoSizeMax), ("fileSizeMax", fileSizeMax)]) } } @@ -3659,26 +3719,6 @@ public extension Api { return nil } } - public static func parse_autoDownloadSettingsLegacy(_ reader: BufferReader) -> AutoDownloadSettings? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Int32? - _2 = reader.readInt32() - var _3: Int32? - _3 = reader.readInt32() - var _4: Int32? - _4 = reader.readInt32() - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.AutoDownloadSettings.autoDownloadSettingsLegacy(flags: _1!, photoSizeMax: _2!, videoSizeMax: _3!, fileSizeMax: _4!) - } - else { - return nil - } - } } public enum StickerSetCovered: TypeConstructorDescription { @@ -5820,6 +5860,7 @@ public extension Api { case updateTheme(theme: Api.Theme) case updateGeoLiveViewed(peer: Api.Peer, msgId: Int32) case updateLoginToken + case updateMessagePollVote(pollId: Int64, userId: Int32, options: [Buffer]) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { @@ -6456,6 +6497,18 @@ public extension Api { buffer.appendInt32(1448076945) } + break + case .updateMessagePollVote(let pollId, let userId, let options): + if boxed { + buffer.appendInt32(1123585836) + } + serializeInt64(pollId, buffer: buffer, boxed: false) + serializeInt32(userId, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(options.count)) + for item in options { + serializeBytes(item, buffer: buffer, boxed: false) + } break } } @@ -6614,6 +6667,8 @@ public extension Api { return ("updateGeoLiveViewed", [("peer", peer), ("msgId", msgId)]) case .updateLoginToken: return ("updateLoginToken", []) + case .updateMessagePollVote(let pollId, let userId, let options): + return ("updateMessagePollVote", [("pollId", pollId), ("userId", userId), ("options", options)]) } } @@ -7891,6 +7946,25 @@ public extension Api { public static func parse_updateLoginToken(_ reader: BufferReader) -> Update? { return Api.Update.updateLoginToken } + public static func parse_updateMessagePollVote(_ reader: BufferReader) -> Update? { + var _1: Int64? + _1 = reader.readInt64() + var _2: Int32? + _2 = reader.readInt32() + var _3: [Buffer]? + if let _ = reader.readInt32() { + _3 = Api.parseVector(reader, elementSignature: -1255641564, elementType: Buffer.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.Update.updateMessagePollVote(pollId: _1!, userId: _2!, options: _3!) + } + else { + return nil + } + } } public enum PopularContact: TypeConstructorDescription { @@ -8148,6 +8222,106 @@ public extension Api { } } + } + public enum MessageUserVote: TypeConstructorDescription { + case messageUserVote(userId: Int32, option: Buffer, date: Int32) + case messageUserVoteInputOption(userId: Int32, date: Int32) + case messageUserVoteMultiple(userId: Int32, options: [Buffer], date: Int32) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .messageUserVote(let userId, let option, let date): + if boxed { + buffer.appendInt32(-1567730343) + } + serializeInt32(userId, buffer: buffer, boxed: false) + serializeBytes(option, buffer: buffer, boxed: false) + serializeInt32(date, buffer: buffer, boxed: false) + break + case .messageUserVoteInputOption(let userId, let date): + if boxed { + buffer.appendInt32(909603888) + } + serializeInt32(userId, buffer: buffer, boxed: false) + serializeInt32(date, buffer: buffer, boxed: false) + break + case .messageUserVoteMultiple(let userId, let options, let date): + if boxed { + buffer.appendInt32(244310238) + } + serializeInt32(userId, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(options.count)) + for item in options { + serializeBytes(item, buffer: buffer, boxed: false) + } + serializeInt32(date, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .messageUserVote(let userId, let option, let date): + return ("messageUserVote", [("userId", userId), ("option", option), ("date", date)]) + case .messageUserVoteInputOption(let userId, let date): + return ("messageUserVoteInputOption", [("userId", userId), ("date", date)]) + case .messageUserVoteMultiple(let userId, let options, let date): + return ("messageUserVoteMultiple", [("userId", userId), ("options", options), ("date", date)]) + } + } + + public static func parse_messageUserVote(_ reader: BufferReader) -> MessageUserVote? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Buffer? + _2 = parseBytes(reader) + var _3: Int32? + _3 = reader.readInt32() + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.MessageUserVote.messageUserVote(userId: _1!, option: _2!, date: _3!) + } + else { + return nil + } + } + public static func parse_messageUserVoteInputOption(_ reader: BufferReader) -> MessageUserVote? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + _2 = reader.readInt32() + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.MessageUserVote.messageUserVoteInputOption(userId: _1!, date: _2!) + } + else { + return nil + } + } + public static func parse_messageUserVoteMultiple(_ reader: BufferReader) -> MessageUserVote? { + var _1: Int32? + _1 = reader.readInt32() + var _2: [Buffer]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: -1255641564, elementType: Buffer.self) + } + var _3: Int32? + _3 = reader.readInt32() + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.MessageUserVote.messageUserVoteMultiple(userId: _1!, options: _2!, date: _3!) + } + else { + return nil + } + } + } public enum InputDialogPeer: TypeConstructorDescription { case inputDialogPeer(peer: Api.InputPeer) @@ -8254,6 +8428,7 @@ public extension Api { case keyboardButtonBuy(text: String) case keyboardButtonUrlAuth(flags: Int32, text: String, fwdText: String?, url: String, buttonId: Int32) case inputKeyboardButtonUrlAuth(flags: Int32, text: String, fwdText: String?, url: String, bot: Api.InputUser) + case keyboardButtonRequestPoll(flags: Int32, quiz: Api.Bool?, text: String) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { @@ -8329,6 +8504,14 @@ public extension Api { serializeString(url, buffer: buffer, boxed: false) bot.serialize(buffer, true) break + case .keyboardButtonRequestPoll(let flags, let quiz, let text): + if boxed { + buffer.appendInt32(-1144565411) + } + serializeInt32(flags, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {quiz!.serialize(buffer, true)} + serializeString(text, buffer: buffer, boxed: false) + break } } @@ -8354,6 +8537,8 @@ public extension Api { return ("keyboardButtonUrlAuth", [("flags", flags), ("text", text), ("fwdText", fwdText), ("url", url), ("buttonId", buttonId)]) case .inputKeyboardButtonUrlAuth(let flags, let text, let fwdText, let url, let bot): return ("inputKeyboardButtonUrlAuth", [("flags", flags), ("text", text), ("fwdText", fwdText), ("url", url), ("bot", bot)]) + case .keyboardButtonRequestPoll(let flags, let quiz, let text): + return ("keyboardButtonRequestPoll", [("flags", flags), ("quiz", quiz), ("text", text)]) } } @@ -8505,6 +8690,25 @@ public extension Api { return nil } } + public static func parse_keyboardButtonRequestPoll(_ reader: BufferReader) -> KeyboardButton? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Api.Bool? + if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.Bool + } } + var _3: String? + _3 = parseString(reader) + let _c1 = _1 != nil + let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.KeyboardButton.keyboardButtonRequestPoll(flags: _1!, quiz: _2, text: _3!) + } + else { + return nil + } + } } public enum ContactStatus: TypeConstructorDescription { @@ -10159,8 +10363,8 @@ public extension Api { case inputMediaPhotoExternal(flags: Int32, url: String, ttlSeconds: Int32?) case inputMediaDocumentExternal(flags: Int32, url: String, ttlSeconds: Int32?) case inputMediaContact(phoneNumber: String, firstName: String, lastName: String, vcard: String) - case inputMediaPoll(poll: Api.Poll) case inputMediaGeoLive(flags: Int32, geoPoint: Api.InputGeoPoint, period: Int32?) + case inputMediaPoll(flags: Int32, poll: Api.Poll, correctAnswers: [Buffer]?) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { @@ -10288,12 +10492,6 @@ public extension Api { serializeString(lastName, buffer: buffer, boxed: false) serializeString(vcard, buffer: buffer, boxed: false) break - case .inputMediaPoll(let poll): - if boxed { - buffer.appendInt32(112424539) - } - poll.serialize(buffer, true) - break case .inputMediaGeoLive(let flags, let geoPoint, let period): if boxed { buffer.appendInt32(-833715459) @@ -10302,6 +10500,18 @@ public extension Api { geoPoint.serialize(buffer, true) if Int(flags) & Int(1 << 1) != 0 {serializeInt32(period!, buffer: buffer, boxed: false)} break + case .inputMediaPoll(let flags, let poll, let correctAnswers): + if boxed { + buffer.appendInt32(-1410741723) + } + serializeInt32(flags, buffer: buffer, boxed: false) + poll.serialize(buffer, true) + if Int(flags) & Int(1 << 0) != 0 {buffer.appendInt32(481674261) + buffer.appendInt32(Int32(correctAnswers!.count)) + for item in correctAnswers! { + serializeBytes(item, buffer: buffer, boxed: false) + }} + break } } @@ -10333,10 +10543,10 @@ public extension Api { return ("inputMediaDocumentExternal", [("flags", flags), ("url", url), ("ttlSeconds", ttlSeconds)]) case .inputMediaContact(let phoneNumber, let firstName, let lastName, let vcard): return ("inputMediaContact", [("phoneNumber", phoneNumber), ("firstName", firstName), ("lastName", lastName), ("vcard", vcard)]) - case .inputMediaPoll(let poll): - return ("inputMediaPoll", [("poll", poll)]) case .inputMediaGeoLive(let flags, let geoPoint, let period): return ("inputMediaGeoLive", [("flags", flags), ("geoPoint", geoPoint), ("period", period)]) + case .inputMediaPoll(let flags, let poll, let correctAnswers): + return ("inputMediaPoll", [("flags", flags), ("poll", poll), ("correctAnswers", correctAnswers)]) } } @@ -10605,19 +10815,6 @@ public extension Api { return nil } } - public static func parse_inputMediaPoll(_ reader: BufferReader) -> InputMedia? { - var _1: Api.Poll? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.Poll - } - let _c1 = _1 != nil - if _c1 { - return Api.InputMedia.inputMediaPoll(poll: _1!) - } - else { - return nil - } - } public static func parse_inputMediaGeoLive(_ reader: BufferReader) -> InputMedia? { var _1: Int32? _1 = reader.readInt32() @@ -10637,6 +10834,27 @@ public extension Api { return nil } } + public static func parse_inputMediaPoll(_ reader: BufferReader) -> InputMedia? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Api.Poll? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.Poll + } + var _3: [Buffer]? + if Int(_1!) & Int(1 << 0) != 0 {if let _ = reader.readInt32() { + _3 = Api.parseVector(reader, elementSignature: -1255641564, elementType: Buffer.self) + } } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil + if _c1 && _c2 && _c3 { + return Api.InputMedia.inputMediaPoll(flags: _1!, poll: _2!, correctAnswers: _3) + } + else { + return nil + } + } } public enum InputPeer: TypeConstructorDescription { @@ -20230,17 +20448,10 @@ public extension Api { } public enum Theme: TypeConstructorDescription { - case themeDocumentNotModified case theme(flags: Int32, id: Int64, accessHash: Int64, slug: String, title: String, document: Api.Document?, settings: Api.ThemeSettings?, installsCount: Int32) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .themeDocumentNotModified: - if boxed { - buffer.appendInt32(1211967244) - } - - break case .theme(let flags, let id, let accessHash, let slug, let title, let document, let settings, let installsCount): if boxed { buffer.appendInt32(42930452) @@ -20259,16 +20470,11 @@ public extension Api { public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .themeDocumentNotModified: - return ("themeDocumentNotModified", []) case .theme(let flags, let id, let accessHash, let slug, let title, let document, let settings, let installsCount): return ("theme", [("flags", flags), ("id", id), ("accessHash", accessHash), ("slug", slug), ("title", title), ("document", document), ("settings", settings), ("installsCount", installsCount)]) } } - public static func parse_themeDocumentNotModified(_ reader: BufferReader) -> Theme? { - return Api.Theme.themeDocumentNotModified - } public static func parse_theme(_ reader: BufferReader) -> Theme? { var _1: Int32? _1 = reader.readInt32() diff --git a/submodules/TelegramApi/Sources/Api2.swift b/submodules/TelegramApi/Sources/Api2.swift index 0eacd270a2..f0cb6195e9 100644 --- a/submodules/TelegramApi/Sources/Api2.swift +++ b/submodules/TelegramApi/Sources/Api2.swift @@ -859,76 +859,6 @@ public struct auth { return Api.auth.CodeType.codeTypeFlashCall } - } - public enum LoginTokenInfo: TypeConstructorDescription { - case loginTokenInfo(dcId: Int32, authKeyId: Int64, deviceModel: String, platform: String, systemVersion: String, apiId: Int32, appName: String, appVersion: String, ip: String, region: String) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .loginTokenInfo(let dcId, let authKeyId, let deviceModel, let platform, let systemVersion, let apiId, let appName, let appVersion, let ip, let region): - if boxed { - buffer.appendInt32(1375940666) - } - serializeInt32(dcId, buffer: buffer, boxed: false) - serializeInt64(authKeyId, buffer: buffer, boxed: false) - serializeString(deviceModel, buffer: buffer, boxed: false) - serializeString(platform, buffer: buffer, boxed: false) - serializeString(systemVersion, buffer: buffer, boxed: false) - serializeInt32(apiId, buffer: buffer, boxed: false) - serializeString(appName, buffer: buffer, boxed: false) - serializeString(appVersion, buffer: buffer, boxed: false) - serializeString(ip, buffer: buffer, boxed: false) - serializeString(region, buffer: buffer, boxed: false) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .loginTokenInfo(let dcId, let authKeyId, let deviceModel, let platform, let systemVersion, let apiId, let appName, let appVersion, let ip, let region): - return ("loginTokenInfo", [("dcId", dcId), ("authKeyId", authKeyId), ("deviceModel", deviceModel), ("platform", platform), ("systemVersion", systemVersion), ("apiId", apiId), ("appName", appName), ("appVersion", appVersion), ("ip", ip), ("region", region)]) - } - } - - public static func parse_loginTokenInfo(_ reader: BufferReader) -> LoginTokenInfo? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Int64? - _2 = reader.readInt64() - var _3: String? - _3 = parseString(reader) - var _4: String? - _4 = parseString(reader) - var _5: String? - _5 = parseString(reader) - var _6: Int32? - _6 = reader.readInt32() - var _7: String? - _7 = parseString(reader) - var _8: String? - _8 = parseString(reader) - var _9: String? - _9 = parseString(reader) - var _10: String? - _10 = parseString(reader) - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - let _c4 = _4 != nil - let _c5 = _5 != nil - let _c6 = _6 != nil - let _c7 = _7 != nil - let _c8 = _8 != nil - let _c9 = _9 != nil - let _c10 = _10 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 { - return Api.auth.LoginTokenInfo.loginTokenInfo(dcId: _1!, authKeyId: _2!, deviceModel: _3!, platform: _4!, systemVersion: _5!, apiId: _6!, appName: _7!, appVersion: _8!, ip: _9!, region: _10!) - } - else { - return nil - } - } - } public enum SentCodeType: TypeConstructorDescription { case sentCodeTypeApp(length: Int32) @@ -1929,6 +1859,70 @@ public struct help { } } + } + public enum UserInfo: TypeConstructorDescription { + case userInfoEmpty + case userInfo(message: String, entities: [Api.MessageEntity], author: String, date: Int32) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .userInfoEmpty: + if boxed { + buffer.appendInt32(-206688531) + } + + break + case .userInfo(let message, let entities, let author, let date): + if boxed { + buffer.appendInt32(32192344) + } + serializeString(message, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(entities.count)) + for item in entities { + item.serialize(buffer, true) + } + serializeString(author, buffer: buffer, boxed: false) + serializeInt32(date, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .userInfoEmpty: + return ("userInfoEmpty", []) + case .userInfo(let message, let entities, let author, let date): + return ("userInfo", [("message", message), ("entities", entities), ("author", author), ("date", date)]) + } + } + + public static func parse_userInfoEmpty(_ reader: BufferReader) -> UserInfo? { + return Api.help.UserInfo.userInfoEmpty + } + public static func parse_userInfo(_ reader: BufferReader) -> UserInfo? { + var _1: String? + _1 = parseString(reader) + var _2: [Api.MessageEntity]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageEntity.self) + } + var _3: String? + _3 = parseString(reader) + var _4: Int32? + _4 = reader.readInt32() + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + if _c1 && _c2 && _c3 && _c4 { + return Api.help.UserInfo.userInfo(message: _1!, entities: _2!, author: _3!, date: _4!) + } + else { + return nil + } + } + } public enum TermsOfServiceUpdate: TypeConstructorDescription { case termsOfServiceUpdateEmpty(expires: Int32) diff --git a/submodules/TelegramApi/Sources/Api3.swift b/submodules/TelegramApi/Sources/Api3.swift index 4d4168b2e0..74e4884bf6 100644 --- a/submodules/TelegramApi/Sources/Api3.swift +++ b/submodules/TelegramApi/Sources/Api3.swift @@ -3195,6 +3195,25 @@ public extension Api { return result }) } + + public static func getPollVotes(flags: Int32, peer: Api.InputPeer, id: Int32, option: Buffer?, offset: String?, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1200736242) + serializeInt32(flags, buffer: buffer, boxed: false) + peer.serialize(buffer, true) + serializeInt32(id, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {serializeBytes(option!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 1) != 0 {serializeString(offset!, buffer: buffer, boxed: false)} + serializeInt32(limit, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.getPollVotes", parameters: [("flags", flags), ("peer", peer), ("id", id), ("option", option), ("offset", offset), ("limit", limit)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.VotesList? in + let reader = BufferReader(buffer) + var result: Api.messages.VotesList? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.VotesList + } + return result + }) + } } public struct channels { public static func readHistory(channel: Api.InputChannel, maxId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { @@ -4796,6 +4815,40 @@ public extension Api { return result }) } + + public static func editUserInfo(userId: Api.InputUser, message: String, entities: [Api.MessageEntity]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1723407216) + userId.serialize(buffer, true) + serializeString(message, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(entities.count)) + for item in entities { + item.serialize(buffer, true) + } + return (FunctionDescription(name: "help.editUserInfo", parameters: [("userId", userId), ("message", message), ("entities", entities)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.help.UserInfo? in + let reader = BufferReader(buffer) + var result: Api.help.UserInfo? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.help.UserInfo + } + return result + }) + } + + public static func getUserInfo(userId: Api.InputUser) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(59377875) + userId.serialize(buffer, true) + return (FunctionDescription(name: "help.getUserInfo", parameters: [("userId", userId)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.help.UserInfo? in + let reader = BufferReader(buffer) + var result: Api.help.UserInfo? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.help.UserInfo + } + return result + }) + } } public struct updates { public static func getState() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { diff --git a/submodules/TelegramBaseController/Sources/LocationBroadcastNavigationAccessoryPanel.swift b/submodules/TelegramBaseController/Sources/LocationBroadcastNavigationAccessoryPanel.swift index d75b0ee04f..59430f3883 100644 --- a/submodules/TelegramBaseController/Sources/LocationBroadcastNavigationAccessoryPanel.swift +++ b/submodules/TelegramBaseController/Sources/LocationBroadcastNavigationAccessoryPanel.swift @@ -135,13 +135,13 @@ final class LocationBroadcastNavigationAccessoryPanel: ASDisplayNode { } else { let otherString: String if filteredPeers.count == 1 { - otherString = peers[0].compactDisplayTitle + otherString = peers[0].compactDisplayTitle.replacingOccurrences(of: "*", with: "") } else { otherString = self.strings.Conversation_LiveLocationMembersCount(Int32(peers.count)) } let rawText: String if filteredPeers.count != peers.count { - rawText = self.strings.Conversation_LiveLocationYouAnd(otherString).0.replacingOccurrences(of: "*", with: "**") + rawText = self.strings.Conversation_LiveLocationYouAndOther(otherString).0 } else { rawText = otherString } diff --git a/submodules/TelegramCallsUI/Sources/CallController.swift b/submodules/TelegramCallsUI/Sources/CallController.swift index 3644129160..1181b67ea5 100644 --- a/submodules/TelegramCallsUI/Sources/CallController.swift +++ b/submodules/TelegramCallsUI/Sources/CallController.swift @@ -216,11 +216,13 @@ public final class CallController: ViewController { } |> deliverOnMainQueue).start(next: { [weak self] callsTabTip in if let strongSelf = self { if callsTabTip == 2 { - let controller = callSuggestTabController(sharedContext: strongSelf.sharedContext) - strongSelf.present(controller, in: .window(.root)) + Queue.mainQueue().after(1.0) { + let controller = callSuggestTabController(sharedContext: strongSelf.sharedContext) + strongSelf.present(controller, in: .window(.root)) + } } if callsTabTip < 3 { - let _ = ApplicationSpecificNotice.incrementCallsTabTips(accountManager: strongSelf.sharedContext.accountManager, count: 4).start() + let _ = ApplicationSpecificNotice.incrementCallsTabTips(accountManager: strongSelf.sharedContext.accountManager).start() } } }) diff --git a/submodules/TelegramCore/Sources/AccountManager.swift b/submodules/TelegramCore/Sources/AccountManager.swift index 05576c23b2..f4e0043af9 100644 --- a/submodules/TelegramCore/Sources/AccountManager.swift +++ b/submodules/TelegramCore/Sources/AccountManager.swift @@ -151,6 +151,7 @@ private var declaredEncodables: Void = { declareEncodable(WalletCollection.self, f: { WalletCollection(decoder: $0) }) declareEncodable(EmbeddedMediaStickersMessageAttribute.self, f: { EmbeddedMediaStickersMessageAttribute(decoder: $0) }) declareEncodable(TelegramMediaWebpageAttribute.self, f: { TelegramMediaWebpageAttribute(decoder: $0) }) + declareEncodable(CachedPollOptionResult.self, f: { CachedPollOptionResult(decoder: $0) }) return }() diff --git a/submodules/TelegramCore/Sources/AccountStateManagementUtils.swift b/submodules/TelegramCore/Sources/AccountStateManagementUtils.swift index b627fdd6a8..5cc2c88aa7 100644 --- a/submodules/TelegramCore/Sources/AccountStateManagementUtils.swift +++ b/submodules/TelegramCore/Sources/AccountStateManagementUtils.swift @@ -2344,17 +2344,28 @@ func replayFinalState(accountManager: AccountManager, postbox: Postbox, accountP case let .UpdateMessagePoll(pollId, apiPoll, results): if let poll = transaction.getMedia(pollId) as? TelegramMediaPoll { var updatedPoll = poll - if let apiPoll = apiPoll { - switch apiPoll { - case let .poll(id, flags, question, answers): - updatedPoll = TelegramMediaPoll(pollId: MediaId(namespace: Namespaces.Media.CloudPoll, id: id), text: question, options: answers.map(TelegramMediaPollOption.init(apiOption:)), results: TelegramMediaPollResults(apiResults: results), isClosed: (flags & (1 << 0)) != 0) - } - } - let resultsMin: Bool switch results { - case let .pollResults(pollResults): - resultsMin = (pollResults.flags & (1 << 0)) != 0 + case let .pollResults(pollResults): + resultsMin = (pollResults.flags & (1 << 0)) != 0 + } + if let apiPoll = apiPoll { + switch apiPoll { + case let .poll(id, flags, question, answers): + let publicity: TelegramMediaPollPublicity + if (flags & (1 << 1)) != 0 { + publicity = .public + } else { + publicity = .anonymous + } + let kind: TelegramMediaPollKind + if (flags & (1 << 3)) != 0 { + kind = .quiz + } else { + kind = .poll(multipleAnswers: (flags & (1 << 2)) != 0) + } + updatedPoll = TelegramMediaPoll(pollId: MediaId(namespace: Namespaces.Media.CloudPoll, id: id), publicity: publicity, kind: kind, text: question, options: answers.map(TelegramMediaPollOption.init(apiOption:)), correctAnswers: nil, results: poll.results, isClosed: (flags & (1 << 0)) != 0) + } } updatedPoll = updatedPoll.withUpdatedResults(TelegramMediaPollResults(apiResults: results), min: resultsMin) updateMessageMedia(transaction: transaction, id: pollId, media: updatedPoll) diff --git a/submodules/TelegramCore/Sources/ApiUtils.swift b/submodules/TelegramCore/Sources/ApiUtils.swift index 8b4b19cb23..f7beff73f4 100644 --- a/submodules/TelegramCore/Sources/ApiUtils.swift +++ b/submodules/TelegramCore/Sources/ApiUtils.swift @@ -4,7 +4,7 @@ import TelegramApi import SyncCore -extension PeerReference { +public extension PeerReference { var id: PeerId { switch self { case let .user(id, _): @@ -15,7 +15,9 @@ extension PeerReference { return PeerId(namespace: Namespaces.Peer.CloudChannel, id: id) } } - +} + +extension PeerReference { var inputPeer: Api.InputPeer { switch self { case let .user(id, accessHash): diff --git a/submodules/TelegramCore/Sources/AutodownloadSettings.swift b/submodules/TelegramCore/Sources/AutodownloadSettings.swift index b86130c742..ff23b1d15c 100644 --- a/submodules/TelegramCore/Sources/AutodownloadSettings.swift +++ b/submodules/TelegramCore/Sources/AutodownloadSettings.swift @@ -24,8 +24,6 @@ extension AutodownloadPresetSettings { switch apiAutodownloadSettings { case let .autoDownloadSettings(flags, photoSizeMax, videoSizeMax, fileSizeMax, videoUploadMaxbitrate): self.init(disabled: (flags & (1 << 0)) != 0, photoSizeMax: photoSizeMax, videoSizeMax: videoSizeMax, fileSizeMax: fileSizeMax, preloadLargeVideo: (flags & (1 << 1)) != 0, lessDataForPhoneCalls: (flags & (1 << 3)) != 0, videoUploadMaxbitrate: videoUploadMaxbitrate) - case let .autoDownloadSettingsLegacy(flags, photoSizeMax, videoSizeMax, fileSizeMax): - self.init(disabled: (flags & (1 << 0)) != 0, photoSizeMax: photoSizeMax, videoSizeMax: videoSizeMax, fileSizeMax: fileSizeMax, preloadLargeVideo: (flags & (1 << 1)) != 0, lessDataForPhoneCalls: (flags & (1 << 3)) != 0, videoUploadMaxbitrate: 0) } } } diff --git a/submodules/TelegramCore/Sources/FetchedMediaResource.swift b/submodules/TelegramCore/Sources/FetchedMediaResource.swift index 7adb6c9e2a..8ab6ae7d41 100644 --- a/submodules/TelegramCore/Sources/FetchedMediaResource.swift +++ b/submodules/TelegramCore/Sources/FetchedMediaResource.swift @@ -445,12 +445,12 @@ struct RevalidatedMediaResource { func revalidateMediaResourceReference(postbox: Postbox, network: Network, revalidationContext: MediaReferenceRevalidationContext, info: TelegramCloudMediaResourceFetchInfo, resource: MediaResource) -> Signal { var updatedReference = info.reference if case let .media(media, resource) = updatedReference { - if case let .message(_, mediaValue) = media { + if case let .message(messageReference, mediaValue) = media { if let file = mediaValue as? TelegramMediaFile { if let partialReference = file.partialReference { updatedReference = partialReference.mediaReference(media.media).resourceReference(resource) } - if file.isSticker { + if file.isSticker, messageReference.isSecret == true { var stickerPackReference: StickerPackReference? for attribute in file.attributes { if case let .Sticker(sticker) = attribute { diff --git a/submodules/TelegramCore/Sources/JoinLink.swift b/submodules/TelegramCore/Sources/JoinLink.swift index 76711093d6..b037b017c5 100644 --- a/submodules/TelegramCore/Sources/JoinLink.swift +++ b/submodules/TelegramCore/Sources/JoinLink.swift @@ -28,9 +28,6 @@ public enum ExternalJoiningChatState { } public func joinChatInteractively(with hash: String, account: Account) -> Signal { - #if DEBUG - return .fail(.tooMuchJoined) - #endif return account.network.request(Api.functions.messages.importChatInvite(hash: hash)) |> mapError { error -> JoinLinkError in if error.errorDescription == "CHANNELS_TOO_MUCH" { diff --git a/submodules/TelegramCore/Sources/PendingMessageUploadedContent.swift b/submodules/TelegramCore/Sources/PendingMessageUploadedContent.swift index 82550c9813..6715783a15 100644 --- a/submodules/TelegramCore/Sources/PendingMessageUploadedContent.swift +++ b/submodules/TelegramCore/Sources/PendingMessageUploadedContent.swift @@ -140,7 +140,28 @@ func mediaContentToUpload(network: Network, postbox: Postbox, auxiliaryMethods: if peerId.namespace == Namespaces.Peer.SecretChat { return .fail(.generic) } - let inputPoll = Api.InputMedia.inputMediaPoll(poll: Api.Poll.poll(id: 0, flags: 0, question: poll.text, answers: poll.options.map({ $0.apiOption }))) + var pollFlags: Int32 = 0 + switch poll.kind { + case let .poll(multipleAnswers): + if multipleAnswers { + pollFlags |= 1 << 2 + } + case .quiz: + pollFlags |= 1 << 3 + } + switch poll.publicity { + case .anonymous: + break + case .public: + pollFlags |= 1 << 1 + } + var pollMediaFlags: Int32 = 0 + var correctAnswers: [Buffer]? + if let correctAnswersValue = poll.correctAnswers { + pollMediaFlags |= 1 << 0 + correctAnswers = correctAnswersValue.map { Buffer(data: $0) } + } + let inputPoll = Api.InputMedia.inputMediaPoll(flags: pollMediaFlags, poll: Api.Poll.poll(id: 0, flags: pollFlags, question: poll.text, answers: poll.options.map({ $0.apiOption })), correctAnswers: correctAnswers) return .single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .media(inputPoll, text), reuploadInfo: nil))) } else { return nil diff --git a/submodules/TelegramCore/Sources/Polls.swift b/submodules/TelegramCore/Sources/Polls.swift index 207cd11cfb..803323a618 100644 --- a/submodules/TelegramCore/Sources/Polls.swift +++ b/submodules/TelegramCore/Sources/Polls.swift @@ -10,22 +10,72 @@ public enum RequestMessageSelectPollOptionError { case generic } -public func requestMessageSelectPollOption(account: Account, messageId: MessageId, opaqueIdentifier: Data?) -> Signal { +public func requestMessageSelectPollOption(account: Account, messageId: MessageId, opaqueIdentifiers: [Data]) -> Signal { return account.postbox.loadedPeerWithId(messageId.peerId) |> take(1) |> castError(RequestMessageSelectPollOptionError.self) |> mapToSignal { peer in if let inputPeer = apiInputPeer(peer) { - return account.network.request(Api.functions.messages.sendVote(peer: inputPeer, msgId: messageId.id, options: opaqueIdentifier.flatMap({ [Buffer(data: $0)] }) ?? [])) + return account.network.request(Api.functions.messages.sendVote(peer: inputPeer, msgId: messageId.id, options: opaqueIdentifiers.map { Buffer(data: $0) })) |> mapError { _ -> RequestMessageSelectPollOptionError in return .generic } - |> mapToSignal { result -> Signal in - account.stateManager.addUpdates(result) - return .complete() + |> mapToSignal { result -> Signal in + return account.postbox.transaction { transaction -> TelegramMediaPoll? in + var resultPoll: TelegramMediaPoll? + switch result { + case let .updates(updates, _, _, _, _): + for update in updates { + switch update { + case let .updateMessagePoll(_, id, poll, results): + let pollId = MediaId(namespace: Namespaces.Media.CloudPoll, id: id) + resultPoll = transaction.getMedia(pollId) as? TelegramMediaPoll + if let poll = poll { + switch poll { + case let .poll(id, flags, question, answers): + let publicity: TelegramMediaPollPublicity + if (flags & (1 << 1)) != 0 { + publicity = .public + } else { + publicity = .anonymous + } + let kind: TelegramMediaPollKind + if (flags & (1 << 3)) != 0 { + kind = .quiz + } else { + kind = .poll(multipleAnswers: (flags & (1 << 2)) != 0) + } + resultPoll = TelegramMediaPoll(pollId: pollId, publicity: publicity, kind: kind, text: question, options: answers.map(TelegramMediaPollOption.init(apiOption:)), correctAnswers: nil, results: TelegramMediaPollResults(apiResults: results), isClosed: (flags & (1 << 0)) != 0) + default: + break + } + } + + let resultsMin: Bool + switch results { + case let .pollResults(pollResults): + resultsMin = (pollResults.flags & (1 << 0)) != 0 + } + resultPoll = resultPoll?.withUpdatedResults(TelegramMediaPollResults(apiResults: results), min: resultsMin) + + if let resultPoll = resultPoll { + updateMessageMedia(transaction: transaction, id: pollId, media: resultPoll) + } + default: + break + } + } + break + default: + break + } + account.stateManager.addUpdates(result) + return resultPoll + } + |> castError(RequestMessageSelectPollOptionError.self) } } else { - return .complete() + return .single(nil) } } } @@ -51,7 +101,32 @@ public func requestClosePoll(postbox: Postbox, network: Network, stateManager: A } var flags: Int32 = 0 flags |= 1 << 14 - return network.request(Api.functions.messages.editMessage(flags: flags, peer: inputPeer, id: messageId.id, message: nil, media: .inputMediaPoll(poll: .poll(id: poll.pollId.id, flags: 1 << 0, question: poll.text, answers: poll.options.map({ $0.apiOption }))), replyMarkup: nil, entities: nil, scheduleDate: nil)) + + var pollFlags: Int32 = 0 + switch poll.kind { + case let .poll(multipleAnswers): + if multipleAnswers { + pollFlags |= 1 << 2 + } + case .quiz: + pollFlags |= 1 << 3 + } + switch poll.publicity { + case .anonymous: + break + case .public: + pollFlags |= 1 << 1 + } + var pollMediaFlags: Int32 = 0 + var correctAnswers: [Buffer]? + if let correctAnswersValue = poll.correctAnswers { + pollMediaFlags |= 1 << 0 + correctAnswers = correctAnswersValue.map { Buffer(data: $0) } + } + + pollFlags |= 1 << 0 + + return network.request(Api.functions.messages.editMessage(flags: flags, peer: inputPeer, id: messageId.id, message: nil, media: .inputMediaPoll(flags: pollMediaFlags, poll: .poll(id: poll.pollId.id, flags: pollFlags, question: poll.text, answers: poll.options.map({ $0.apiOption })), correctAnswers: correctAnswers), replyMarkup: nil, entities: nil, scheduleDate: nil)) |> map(Optional.init) |> `catch` { _ -> Signal in return .single(nil) @@ -64,3 +139,280 @@ public func requestClosePoll(postbox: Postbox, network: Network, stateManager: A } } } + +private let cachedPollResultsCollectionSpec = ItemCacheCollectionSpec(lowWaterItemCount: 20, highWaterItemCount: 40) + +final class CachedPollOptionResult: PostboxCoding { + let peerIds: [PeerId] + let count: Int32 + + public static func key(pollId: MediaId, optionOpaqueIdentifier: Data) -> ValueBoxKey { + let key = ValueBoxKey(length: 4 + 8 + optionOpaqueIdentifier.count) + key.setInt32(0, value: pollId.namespace) + key.setInt64(4, value: pollId.id) + key.setData(4 + 8, value: optionOpaqueIdentifier) + return key + } + + public init(peerIds: [PeerId], count: Int32) { + self.peerIds = peerIds + self.count = count + } + + public init(decoder: PostboxDecoder) { + self.peerIds = decoder.decodeInt64ArrayForKey("peerIds").map(PeerId.init) + self.count = decoder.decodeInt32ForKey("count", orElse: 0) + } + + public func encode(_ encoder: PostboxEncoder) { + encoder.encodeInt64Array(self.peerIds.map { $0.toInt64() }, forKey: "peerIds") + encoder.encodeInt32(self.count, forKey: "count") + } +} + +private final class PollResultsOptionContext { + private let queue: Queue + private let account: Account + private let pollId: MediaId + private let messageId: MessageId + private let opaqueIdentifier: Data + private let disposable = MetaDisposable() + private var isLoadingMore: Bool = false + private var hasLoadedOnce: Bool = false + private var canLoadMore: Bool = true + private var nextOffset: String? + private var results: [RenderedPeer] = [] + private var count: Int + private var populateCache: Bool = true + + let state = Promise() + + init(queue: Queue, account: Account, pollId: MediaId, messageId: MessageId, opaqueIdentifier: Data, count: Int) { + self.queue = queue + self.account = account + self.pollId = pollId + self.messageId = messageId + self.opaqueIdentifier = opaqueIdentifier + self.count = count + + self.isLoadingMore = true + self.disposable.set((account.postbox.transaction { transaction -> (peers: [RenderedPeer], canLoadMore: Bool)? in + let cachedResult = transaction.retrieveItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedPollResults, key: CachedPollOptionResult.key(pollId: pollId, optionOpaqueIdentifier: opaqueIdentifier))) as? CachedPollOptionResult + if let cachedResult = cachedResult, Int(cachedResult.count) == count { + var result: [RenderedPeer] = [] + for peerId in cachedResult.peerIds { + if let peer = transaction.getPeer(peerId) { + result.append(RenderedPeer(peer: peer)) + } else { + return nil + } + } + return (result, Int(cachedResult.count) > result.count) + } else { + return nil + } + } + |> deliverOn(self.queue)).start(next: { [weak self] cachedPeersAndCanLoadMore in + guard let strongSelf = self else { + return + } + strongSelf.isLoadingMore = false + if let (cachedPeers, canLoadMore) = cachedPeersAndCanLoadMore { + strongSelf.results = cachedPeers + strongSelf.hasLoadedOnce = true + strongSelf.canLoadMore = canLoadMore + } + strongSelf.loadMore() + })) + } + + deinit { + self.disposable.dispose() + } + + func loadMore() { + if self.isLoadingMore { + return + } + self.isLoadingMore = true + let pollId = self.pollId + let messageId = self.messageId + let opaqueIdentifier = self.opaqueIdentifier + let account = self.account + let nextOffset = self.nextOffset + let populateCache = self.populateCache + self.disposable.set((self.account.postbox.transaction { transaction -> Api.InputPeer? in + return transaction.getPeer(messageId.peerId).flatMap(apiInputPeer) + } + |> mapToSignal { inputPeer -> Signal<([RenderedPeer], Int, String?), NoError> in + if let inputPeer = inputPeer { + let signal = account.network.request(Api.functions.messages.getPollVotes(flags: 1 << 0, peer: inputPeer, id: messageId.id, option: Buffer(data: opaqueIdentifier), offset: nextOffset, limit: nextOffset == nil ? 15 : 50)) + |> map(Optional.init) + |> `catch` { _ -> Signal in + return .single(nil) + } + |> mapToSignal { result -> Signal<([RenderedPeer], Int, String?), NoError> in + return account.postbox.transaction { transaction -> ([RenderedPeer], Int, String?) in + guard let result = result else { + return ([], 0, nil) + } + switch result { + case let .votesList(_, count, votes, users, nextOffset): + var peers: [Peer] = [] + for apiUser in users { + peers.append(TelegramUser(user: apiUser)) + } + updatePeers(transaction: transaction, peers: peers, update: { _, updated in + return updated + }) + var resultPeers: [RenderedPeer] = [] + for vote in votes { + let peerId: PeerId + switch vote { + case let .messageUserVote(userId, _, _): + peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: userId) + case let .messageUserVoteInputOption(userId, _): + peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: userId) + case let .messageUserVoteMultiple(userId, _, _): + peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: userId) + } + if let peer = transaction.getPeer(peerId) { + resultPeers.append(RenderedPeer(peer: peer)) + } + } + if populateCache { + transaction.putItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedPollResults, key: CachedPollOptionResult.key(pollId: pollId, optionOpaqueIdentifier: opaqueIdentifier)), entry: CachedPollOptionResult(peerIds: resultPeers.map { $0.peerId }, count: count), collectionSpec: cachedPollResultsCollectionSpec) + } + return (resultPeers, Int(count), nextOffset) + } + } + } + #if DEBUG + //return signal |> delay(4.0, queue: .concurrentDefaultQueue()) + #endif + return signal + } else { + return .single(([], 0, nil)) + } + } + |> deliverOn(self.queue)).start(next: { [weak self] peers, updatedCount, nextOffset in + guard let strongSelf = self else { + return + } + if strongSelf.populateCache { + strongSelf.populateCache = false + strongSelf.results.removeAll() + } + var existingIds = Set(strongSelf.results.map { $0.peerId }) + for peer in peers { + if !existingIds.contains(peer.peerId) { + strongSelf.results.append(peer) + existingIds.insert(peer.peerId) + } + } + strongSelf.isLoadingMore = false + strongSelf.hasLoadedOnce = true + strongSelf.canLoadMore = nextOffset != nil + strongSelf.nextOffset = nextOffset + if strongSelf.canLoadMore { + strongSelf.count = max(updatedCount, strongSelf.results.count) + } else { + strongSelf.count = strongSelf.results.count + } + strongSelf.updateState() + })) + self.updateState() + } + + func updateState() { + self.state.set(.single(PollResultsOptionState(peers: self.results, isLoadingMore: self.isLoadingMore, hasLoadedOnce: self.hasLoadedOnce, canLoadMore: self.canLoadMore, count: self.count))) + } +} + +public struct PollResultsOptionState: Equatable { + public var peers: [RenderedPeer] + public var isLoadingMore: Bool + public var hasLoadedOnce: Bool + public var canLoadMore: Bool + public var count: Int +} + +public struct PollResultsState: Equatable { + public var options: [Data: PollResultsOptionState] +} + +private final class PollResultsContextImpl { + private let queue: Queue + + private var optionContexts: [Data: PollResultsOptionContext] = [:] + + let state = Promise() + + init(queue: Queue, account: Account, messageId: MessageId, poll: TelegramMediaPoll) { + self.queue = queue + + for option in poll.options { + var count = 0 + if let voters = poll.results.voters { + for voter in voters { + if voter.opaqueIdentifier == option.opaqueIdentifier { + count = Int(voter.count) + } + } + } + self.optionContexts[option.opaqueIdentifier] = PollResultsOptionContext(queue: self.queue, account: account, pollId: poll.pollId, messageId: messageId, opaqueIdentifier: option.opaqueIdentifier, count: count) + } + + self.state.set(combineLatest(queue: self.queue, self.optionContexts.map { (opaqueIdentifier, context) -> Signal<(Data, PollResultsOptionState), NoError> in + return context.state.get() + |> map { state -> (Data, PollResultsOptionState) in + return (opaqueIdentifier, state) + } + }) + |> map { states -> PollResultsState in + var options: [Data: PollResultsOptionState] = [:] + for (opaqueIdentifier, state) in states { + options[opaqueIdentifier] = state + } + return PollResultsState(options: options) + }) + + for (_, context) in self.optionContexts { + context.loadMore() + } + } + + func loadMore(optionOpaqueIdentifier: Data) { + self.optionContexts[optionOpaqueIdentifier]?.loadMore() + } +} + +public final class PollResultsContext { + private let queue: Queue = Queue() + private let impl: QueueLocalObject + + public var state: Signal { + return Signal { subscriber in + let disposable = MetaDisposable() + self.impl.with { impl in + disposable.set(impl.state.get().start(next: { value in + subscriber.putNext(value) + })) + } + return disposable + } + } + + public init(account: Account, messageId: MessageId, poll: TelegramMediaPoll) { + let queue = self.queue + self.impl = QueueLocalObject(queue: queue, generate: { + return PollResultsContextImpl(queue: queue, account: account, messageId: messageId, poll: poll) + }) + } + + public func loadMore(optionOpaqueIdentifier: Data) { + self.impl.with { impl in + impl.loadMore(optionOpaqueIdentifier: optionOpaqueIdentifier) + } + } +} diff --git a/submodules/TelegramCore/Sources/ProxySettings.swift b/submodules/TelegramCore/Sources/ProxySettings.swift index 82253e7fb4..933f79dc14 100644 --- a/submodules/TelegramCore/Sources/ProxySettings.swift +++ b/submodules/TelegramCore/Sources/ProxySettings.swift @@ -4,9 +4,9 @@ import SwiftSignalKit import MtProtoKit import SyncCore -public func updateProxySettingsInteractively(accountManager: AccountManager, _ f: @escaping (ProxySettings) -> ProxySettings) -> Signal { - return accountManager.transaction { transaction -> Void in - updateProxySettingsInteractively(transaction: transaction, f) +public func updateProxySettingsInteractively(accountManager: AccountManager, _ f: @escaping (ProxySettings) -> ProxySettings) -> Signal { + return accountManager.transaction { transaction -> Bool in + return updateProxySettingsInteractively(transaction: transaction, f) } } @@ -21,10 +21,13 @@ extension ProxyServerSettings { } } -public func updateProxySettingsInteractively(transaction: AccountManagerModifier, _ f: @escaping (ProxySettings) -> ProxySettings) { +public func updateProxySettingsInteractively(transaction: AccountManagerModifier, _ f: @escaping (ProxySettings) -> ProxySettings) -> Bool { + var hasChanges = false transaction.updateSharedData(SharedDataKeys.proxySettings, { current in let previous = (current as? ProxySettings) ?? ProxySettings.defaultSettings let updated = f(previous) + hasChanges = previous != updated return updated }) + return hasChanges } diff --git a/submodules/TelegramCore/Sources/RecentPeers.swift b/submodules/TelegramCore/Sources/RecentPeers.swift index eb44d97c28..fd4d853cb6 100644 --- a/submodules/TelegramCore/Sources/RecentPeers.swift +++ b/submodules/TelegramCore/Sources/RecentPeers.swift @@ -231,3 +231,20 @@ public func recentlyUsedInlineBots(postbox: Postbox) -> Signal<[(Peer, Double)], } } +public func removeRecentlyUsedInlineBot(account: Account, peerId: PeerId) -> Signal { + return account.postbox.transaction { transaction -> Signal in + transaction.removeOrderedItemListItem(collectionId: Namespaces.OrderedItemList.CloudRecentInlineBots, itemId: RecentPeerItemId(peerId).rawValue) + + if let peer = transaction.getPeer(peerId), let apiPeer = apiInputPeer(peer) { + return account.network.request(Api.functions.contacts.resetTopPeerRating(category: .topPeerCategoryBotsInline, peer: apiPeer)) + |> `catch` { _ -> Signal in + return .single(.boolFalse) + } + |> mapToSignal { _ -> Signal in + return .complete() + } + } else { + return .complete() + } + } |> switchToLatest +} diff --git a/submodules/TelegramCore/Sources/ReplyMarkupMessageAttribute.swift b/submodules/TelegramCore/Sources/ReplyMarkupMessageAttribute.swift index cf364c8429..587ee1be42 100644 --- a/submodules/TelegramCore/Sources/ReplyMarkupMessageAttribute.swift +++ b/submodules/TelegramCore/Sources/ReplyMarkupMessageAttribute.swift @@ -30,6 +30,15 @@ extension ReplyMarkupButton { self.init(title: text, titleWhenForwarded: fwdText, action: .urlAuth(url: url, buttonId: buttonId)) case let .inputKeyboardButtonUrlAuth(_, text, fwdText, url, _): self.init(title: text, titleWhenForwarded: fwdText, action: .urlAuth(url: url, buttonId: 0)) + case let .keyboardButtonRequestPoll(_, quiz, text): + var isQuiz: Bool? = quiz.flatMap { quiz in + if case .boolTrue = quiz { + return true + } else { + return false + } + } + self.init(title: text, titleWhenForwarded: nil, action: .setupPoll(isQuiz: isQuiz)) } } } diff --git a/submodules/TelegramCore/Sources/Serialization.swift b/submodules/TelegramCore/Sources/Serialization.swift index f0a22a6eb7..02470ccbac 100644 --- a/submodules/TelegramCore/Sources/Serialization.swift +++ b/submodules/TelegramCore/Sources/Serialization.swift @@ -210,7 +210,7 @@ public class BoxedMessage: NSObject { public class Serialization: NSObject, MTSerialization { public func currentLayer() -> UInt { - return 108 + return 109 } public func parseMessage(_ data: Data!) -> Any! { diff --git a/submodules/TelegramCore/Sources/StoreMessage_Telegram.swift b/submodules/TelegramCore/Sources/StoreMessage_Telegram.swift index c4f24f6dec..d5c0b31029 100644 --- a/submodules/TelegramCore/Sources/StoreMessage_Telegram.swift +++ b/submodules/TelegramCore/Sources/StoreMessage_Telegram.swift @@ -328,7 +328,19 @@ func textMediaAndExpirationTimerFromApiMedia(_ media: Api.MessageMedia?, _ peerI case let .messageMediaPoll(poll, results): switch poll { case let .poll(id, flags, question, answers): - return (TelegramMediaPoll(pollId: MediaId(namespace: Namespaces.Media.CloudPoll, id: id), text: question, options: answers.map(TelegramMediaPollOption.init(apiOption:)), results: TelegramMediaPollResults(apiResults: results), isClosed: (flags & (1 << 0)) != 0), nil) + let publicity: TelegramMediaPollPublicity + if (flags & (1 << 1)) != 0 { + publicity = .public + } else { + publicity = .anonymous + } + let kind: TelegramMediaPollKind + if (flags & (1 << 3)) != 0 { + kind = .quiz + } else { + kind = .poll(multipleAnswers: (flags & (1 << 2)) != 0) + } + return (TelegramMediaPoll(pollId: MediaId(namespace: Namespaces.Media.CloudPoll, id: id), publicity: publicity, kind: kind, text: question, options: answers.map(TelegramMediaPollOption.init(apiOption:)), correctAnswers: nil, results: TelegramMediaPollResults(apiResults: results), isClosed: (flags & (1 << 0)) != 0), nil) } } } diff --git a/submodules/TelegramCore/Sources/TelegramMediaPoll.swift b/submodules/TelegramCore/Sources/TelegramMediaPoll.swift index ab4e9fc689..0a80671435 100644 --- a/submodules/TelegramCore/Sources/TelegramMediaPoll.swift +++ b/submodules/TelegramCore/Sources/TelegramMediaPoll.swift @@ -21,7 +21,7 @@ extension TelegramMediaPollOptionVoters { init(apiVoters: Api.PollAnswerVoters) { switch apiVoters { case let .pollAnswerVoters(flags, option, voters): - self.init(selected: (flags & (1 << 0)) != 0, opaqueIdentifier: option.makeData(), count: voters) + self.init(selected: (flags & (1 << 0)) != 0, opaqueIdentifier: option.makeData(), count: voters, isCorrect: (flags & (1 << 1)) != 0) } } } @@ -29,8 +29,10 @@ extension TelegramMediaPollOptionVoters { extension TelegramMediaPollResults { init(apiResults: Api.PollResults) { switch apiResults { - case let .pollResults(_, results, totalVoters): - self.init(voters: results.flatMap({ $0.map(TelegramMediaPollOptionVoters.init(apiVoters:)) }), totalVoters: totalVoters) + case let .pollResults(_, results, totalVoters, recentVoters): + self.init(voters: results.flatMap({ $0.map(TelegramMediaPollOptionVoters.init(apiVoters:)) }), totalVoters: totalVoters, recentVoters: recentVoters.flatMap { recentVoters in + return recentVoters.map { PeerId(namespace: Namespaces.Peer.CloudUser, id: $0) } + } ?? []) } } } diff --git a/submodules/TelegramIntents/Sources/TelegramIntents.swift b/submodules/TelegramIntents/Sources/TelegramIntents.swift index 094e34a705..3cf79eb5d8 100644 --- a/submodules/TelegramIntents/Sources/TelegramIntents.swift +++ b/submodules/TelegramIntents/Sources/TelegramIntents.swift @@ -134,7 +134,7 @@ public func donateSendMessageIntent(account: Account, sharedContext: SharedAccou if peer.id == account.peerId { signals.append(.single((peer, subject, savedMessagesAvatar))) } else { - let peerAndAvatar = (peerAvatarImage(account: account, peer: peer, authorOfMessage: nil, representation: peer.smallProfileImage, round: false) ?? .single(nil)) + let peerAndAvatar = (peerAvatarImage(account: account, peerReference: PeerReference(peer), authorOfMessage: nil, representation: peer.smallProfileImage, round: false) ?? .single(nil)) |> map { avatarImage in return (peer, subject, avatarImage) } @@ -151,7 +151,7 @@ public func donateSendMessageIntent(account: Account, sharedContext: SharedAccou let presentationData = sharedContext.currentPresentationData.with { $0 } for (peer, subject, avatarImage) in peers { - let recipientHandle = INPersonHandle(value: "tg\(peer.id.id)", type: .unknown) + let recipientHandle = INPersonHandle(value: "tg\(peer.id.toInt64())", type: .unknown) let displayTitle: String var nameComponents = PersonNameComponents() @@ -173,9 +173,9 @@ public func donateSendMessageIntent(account: Account, sharedContext: SharedAccou nameComponents.givenName = displayTitle } - let recipient = INPerson(personHandle: recipientHandle, nameComponents: nameComponents, displayName: displayTitle, image: nil, contactIdentifier: nil, customIdentifier: "tg\(peer.id.id)") + let recipient = INPerson(personHandle: recipientHandle, nameComponents: nameComponents, displayName: displayTitle, image: nil, contactIdentifier: nil, customIdentifier: "tg\(peer.id.toInt64())") - let intent = INSendMessageIntent(recipients: [recipient], content: nil, speakableGroupName: INSpeakableString(spokenPhrase: displayTitle), conversationIdentifier: "tg\(peer.id.id)", serviceName: nil, sender: nil) + let intent = INSendMessageIntent(recipients: [recipient], content: nil, speakableGroupName: INSpeakableString(spokenPhrase: displayTitle), conversationIdentifier: "tg\(peer.id.toInt64())", serviceName: nil, sender: nil) if let avatarImage = avatarImage, let avatarImageData = avatarImage.jpegData(compressionQuality: 0.8) { intent.setImage(INImage(imageData: avatarImageData), forParameterNamed: \.groupName) } diff --git a/submodules/TelegramNotices/Sources/Notices.swift b/submodules/TelegramNotices/Sources/Notices.swift index 01c3c17b33..f74f864b5c 100644 --- a/submodules/TelegramNotices/Sources/Notices.swift +++ b/submodules/TelegramNotices/Sources/Notices.swift @@ -129,12 +129,12 @@ private enum ApplicationSpecificGlobalNotice: Int32 { case volumeButtonToUnmuteTip = 9 case archiveChatTips = 10 case archiveIntroDismissed = 11 - case callsTabTip = 12 case cellularDataPermissionWarning = 13 case chatMessageSearchResultsTip = 14 case chatMessageOptionsTip = 15 case chatTextSelectionTip = 16 case themeChangeTip = 17 + case callsTabTip = 18 var key: ValueBoxKey { let v = ValueBoxKey(length: 4) diff --git a/submodules/TelegramPresentationData/Sources/ChatMessageBubbleImages.swift b/submodules/TelegramPresentationData/Sources/ChatMessageBubbleImages.swift index 4ae705e92f..d09c06ccc4 100644 --- a/submodules/TelegramPresentationData/Sources/ChatMessageBubbleImages.swift +++ b/submodules/TelegramPresentationData/Sources/ChatMessageBubbleImages.swift @@ -26,14 +26,264 @@ public func messageSingleBubbleLikeImage(fillColor: UIColor, strokeColor: UIColo })!.stretchableImage(withLeftCapWidth: Int(diameter / 2.0), topCapHeight: Int(diameter / 2.0)) } -public func messageBubbleImage(incoming: Bool, fillColor: UIColor, strokeColor: UIColor, neighbors: MessageBubbleImageNeighbors, theme: PresentationThemeChat, wallpaper: TelegramWallpaper, knockout knockoutValue: Bool, mask: Bool = false, extendedEdges: Bool = false, onlyOutline: Bool = false) -> UIImage { - let diameter: CGFloat = 36.0 - let corner: CGFloat = 7.0 +private let minRadiusForFullTailCorner: CGFloat = 14.0 + +func mediaBubbleCornerImage(incoming: Bool, radius: CGFloat, inset: CGFloat) -> UIImage { + let imageSize = CGSize(width: radius + 7.0, height: 8.0) + let fixedMainDiameter: CGFloat = 33.0 + + let formContext = DrawingContext(size: imageSize) + formContext.withFlippedContext { context in + context.clear(CGRect(origin: CGPoint(), size: imageSize)) + context.translateBy(x: imageSize.width / 2.0, y: imageSize.height / 2.0) + context.scaleBy(x: incoming ? -1.0 : 1.0, y: -1.0) + context.translateBy(x: -imageSize.width / 2.0, y: -imageSize.height / 2.0) + + context.setFillColor(UIColor.black.cgColor) + + let bottomEllipse = CGRect(origin: CGPoint(x: 24.0, y: 16.0), size: CGSize(width: 27.0, height: 17.0)).insetBy(dx: inset, dy: inset).offsetBy(dx: inset, dy: inset) + let topEllipse = CGRect(origin: CGPoint(x: 33.0, y: 14.0), size: CGSize(width: 23.0, height: 21.0)).insetBy(dx: -inset, dy: -inset).offsetBy(dx: inset, dy: inset) + + context.translateBy(x: -fixedMainDiameter + imageSize.width - 6.0, y: -fixedMainDiameter + imageSize.height) + + let topLeftRadius: CGFloat = 2.0 + let topRightRadius: CGFloat = 2.0 + let bottomLeftRadius: CGFloat = 2.0 + let bottomRightRadius: CGFloat = radius + + context.move(to: CGPoint(x: 0.0, y: topLeftRadius)) + context.addArc(tangent1End: CGPoint(x: 0.0, y: 0.0), tangent2End: CGPoint(x: topLeftRadius, y: 0.0), radius: topLeftRadius) + context.addLine(to: CGPoint(x: fixedMainDiameter - topRightRadius, y: 0.0)) + context.addArc(tangent1End: CGPoint(x: fixedMainDiameter, y: 0.0), tangent2End: CGPoint(x: fixedMainDiameter, y: topRightRadius), radius: topRightRadius) + context.addLine(to: CGPoint(x: fixedMainDiameter, y: fixedMainDiameter - bottomRightRadius)) + context.addArc(tangent1End: CGPoint(x: fixedMainDiameter, y: fixedMainDiameter), tangent2End: CGPoint(x: fixedMainDiameter - bottomRightRadius, y: fixedMainDiameter), radius: bottomRightRadius) + context.addLine(to: CGPoint(x: bottomLeftRadius, y: fixedMainDiameter)) + context.addArc(tangent1End: CGPoint(x: 0.0, y: fixedMainDiameter), tangent2End: CGPoint(x: 0.0, y: fixedMainDiameter - bottomLeftRadius), radius: bottomLeftRadius) + context.addLine(to: CGPoint(x: 0.0, y: topLeftRadius)) + context.fillPath() + + if radius >= minRadiusForFullTailCorner { + context.move(to: CGPoint(x: bottomEllipse.minX, y: bottomEllipse.midY)) + context.addQuadCurve(to: CGPoint(x: bottomEllipse.midX, y: bottomEllipse.maxY), control: CGPoint(x: bottomEllipse.minX, y: bottomEllipse.maxY)) + context.addQuadCurve(to: CGPoint(x: bottomEllipse.maxX, y: bottomEllipse.midY), control: CGPoint(x: bottomEllipse.maxX, y: bottomEllipse.maxY)) + context.fillPath() + } else { + context.fill(CGRect(origin: CGPoint(x: bottomEllipse.minX - 5.0, y: bottomEllipse.midY), size: CGSize(width: bottomEllipse.width + 5.0, height: bottomEllipse.height / 2.0))) + } + context.fill(CGRect(origin: CGPoint(x: fixedMainDiameter / 2.0, y: floor(fixedMainDiameter / 2.0)), size: CGSize(width: fixedMainDiameter / 2.0, height: ceil(bottomEllipse.midY) - floor(fixedMainDiameter / 2.0)))) + context.setFillColor(UIColor.clear.cgColor) + context.setBlendMode(.copy) + context.fillEllipse(in: topEllipse) + } + + return formContext.generateImage()! +} + +public func messageBubbleImage(maxCornerRadius: CGFloat, minCornerRadius: CGFloat, incoming: Bool, fillColor: UIColor, strokeColor: UIColor, neighbors: MessageBubbleImageNeighbors, theme: PresentationThemeChat, wallpaper: TelegramWallpaper, knockout knockoutValue: Bool, mask: Bool = false, extendedEdges: Bool = false, onlyOutline: Bool = false) -> UIImage { + let topLeftRadius: CGFloat + let topRightRadius: CGFloat + let bottomLeftRadius: CGFloat + let bottomRightRadius: CGFloat + let drawTail: Bool + + switch neighbors { + case .none: + topLeftRadius = maxCornerRadius + topRightRadius = maxCornerRadius + bottomLeftRadius = maxCornerRadius + bottomRightRadius = maxCornerRadius + drawTail = true + case .both: + topLeftRadius = maxCornerRadius + topRightRadius = minCornerRadius + bottomLeftRadius = maxCornerRadius + bottomRightRadius = minCornerRadius + drawTail = false + case .bottom: + topLeftRadius = maxCornerRadius + topRightRadius = minCornerRadius + bottomLeftRadius = maxCornerRadius + bottomRightRadius = maxCornerRadius + drawTail = true + case .side: + topLeftRadius = maxCornerRadius + topRightRadius = maxCornerRadius + bottomLeftRadius = minCornerRadius + bottomRightRadius = minCornerRadius + drawTail = false + case let .top(side): + topLeftRadius = maxCornerRadius + topRightRadius = maxCornerRadius + bottomLeftRadius = side ? minCornerRadius : maxCornerRadius + bottomRightRadius = minCornerRadius + drawTail = false + } + + let fixedMainDiameter: CGFloat = 33.0 + let innerSize = CGSize(width: fixedMainDiameter + 6.0, height: fixedMainDiameter) + let strokeInset: CGFloat = 1.0 + let sourceRawSize = CGSize(width: innerSize.width + strokeInset * 2.0, height: innerSize.height + strokeInset * 2.0) + let additionalInset: CGFloat = 1.0 + let imageSize = CGSize(width: sourceRawSize.width + additionalInset * 2.0, height: sourceRawSize.height + additionalInset * 2.0) + let outgoingStretchPoint: (x: Int, y: Int) = (Int(additionalInset + strokeInset + round(fixedMainDiameter / 2.0)) - 1, Int(additionalInset + strokeInset + round(fixedMainDiameter / 2.0))) + let incomingStretchPoint: (x: Int, y: Int) = (Int(sourceRawSize.width) - outgoingStretchPoint.x + 1, outgoingStretchPoint.y) + let knockout = knockoutValue && !mask - let inset: CGFloat = 1.0 + let rawSize = imageSize - return generateImage(CGSize(width: 42.0 + inset * 2.0, height: diameter + inset * 2.0), contextGenerator: { rawSize, context in + let bottomEllipse = CGRect(origin: CGPoint(x: 24.0, y: 16.0), size: CGSize(width: 27.0, height: 17.0)) + let topEllipse = CGRect(origin: CGPoint(x: 33.0, y: 14.0), size: CGSize(width: 23.0, height: 21.0)) + + let formContext = DrawingContext(size: imageSize) + formContext.withFlippedContext { context in + context.clear(CGRect(origin: CGPoint(), size: rawSize)) + context.translateBy(x: additionalInset + strokeInset, y: additionalInset + strokeInset) + + context.setFillColor(UIColor.black.cgColor) + + context.move(to: CGPoint(x: 0.0, y: topLeftRadius)) + context.addArc(tangent1End: CGPoint(x: 0.0, y: 0.0), tangent2End: CGPoint(x: topLeftRadius, y: 0.0), radius: topLeftRadius) + context.addLine(to: CGPoint(x: fixedMainDiameter - topRightRadius, y: 0.0)) + context.addArc(tangent1End: CGPoint(x: fixedMainDiameter, y: 0.0), tangent2End: CGPoint(x: fixedMainDiameter, y: topRightRadius), radius: topRightRadius) + context.addLine(to: CGPoint(x: fixedMainDiameter, y: fixedMainDiameter - bottomRightRadius)) + context.addArc(tangent1End: CGPoint(x: fixedMainDiameter, y: fixedMainDiameter), tangent2End: CGPoint(x: fixedMainDiameter - bottomRightRadius, y: fixedMainDiameter), radius: bottomRightRadius) + context.addLine(to: CGPoint(x: bottomLeftRadius, y: fixedMainDiameter)) + context.addArc(tangent1End: CGPoint(x: 0.0, y: fixedMainDiameter), tangent2End: CGPoint(x: 0.0, y: fixedMainDiameter - bottomLeftRadius), radius: bottomLeftRadius) + context.addLine(to: CGPoint(x: 0.0, y: topLeftRadius)) + context.fillPath() + + if drawTail { + if maxCornerRadius >= minRadiusForFullTailCorner { + context.move(to: CGPoint(x: bottomEllipse.minX, y: bottomEllipse.midY)) + context.addQuadCurve(to: CGPoint(x: bottomEllipse.midX, y: bottomEllipse.maxY), control: CGPoint(x: bottomEllipse.minX, y: bottomEllipse.maxY)) + context.addQuadCurve(to: CGPoint(x: bottomEllipse.maxX, y: bottomEllipse.midY), control: CGPoint(x: bottomEllipse.maxX, y: bottomEllipse.maxY)) + context.fillPath() + } else { + context.fill(CGRect(origin: CGPoint(x: bottomEllipse.minX - 2.0, y: bottomEllipse.midY), size: CGSize(width: bottomEllipse.width + 2.0, height: bottomEllipse.height / 2.0))) + } + context.fill(CGRect(origin: CGPoint(x: fixedMainDiameter / 2.0, y: floor(fixedMainDiameter / 2.0)), size: CGSize(width: fixedMainDiameter / 2.0, height: ceil(bottomEllipse.midY) - floor(fixedMainDiameter / 2.0)))) + context.setFillColor(UIColor.clear.cgColor) + context.setBlendMode(.copy) + context.fillEllipse(in: topEllipse) + } + } + let formImage = formContext.generateImage()! + + let outlineContext = DrawingContext(size: imageSize) + outlineContext.withFlippedContext { context in + context.clear(CGRect(origin: CGPoint(), size: rawSize)) + context.translateBy(x: additionalInset + strokeInset, y: additionalInset + strokeInset) + + context.setStrokeColor(UIColor.black.cgColor) + let borderWidth: CGFloat + if abs(UIScreenPixel - 0.5) < CGFloat.ulpOfOne { + borderWidth = UIScreenPixel + } else { + borderWidth = UIScreenPixel * 2.0 + } + context.setLineWidth(borderWidth) + + let borderOffset: CGFloat = borderWidth / 2.0 + + context.move(to: CGPoint(x: -borderOffset, y: topLeftRadius + borderOffset)) + context.addArc(tangent1End: CGPoint(x: -borderOffset, y: -borderOffset), tangent2End: CGPoint(x: topLeftRadius + borderOffset, y: -borderOffset), radius: topLeftRadius + borderOffset * 2.0) + context.addLine(to: CGPoint(x: fixedMainDiameter - topRightRadius - borderOffset, y: -borderOffset)) + context.addArc(tangent1End: CGPoint(x: fixedMainDiameter + borderOffset, y: -borderOffset), tangent2End: CGPoint(x: fixedMainDiameter + borderOffset, y: topRightRadius + borderOffset), radius: topRightRadius + borderOffset * 2.0) + context.addLine(to: CGPoint(x: fixedMainDiameter + borderOffset, y: fixedMainDiameter - bottomRightRadius - borderOffset)) + context.addArc(tangent1End: CGPoint(x: fixedMainDiameter + borderOffset, y: fixedMainDiameter + borderOffset), tangent2End: CGPoint(x: fixedMainDiameter - bottomRightRadius - borderOffset, y: fixedMainDiameter + borderOffset), radius: bottomRightRadius + borderOffset * 2.0) + context.addLine(to: CGPoint(x: bottomLeftRadius + borderOffset, y: fixedMainDiameter + borderOffset)) + context.addArc(tangent1End: CGPoint(x: -borderOffset, y: fixedMainDiameter + borderOffset), tangent2End: CGPoint(x: -borderOffset, y: fixedMainDiameter - bottomLeftRadius - borderOffset), radius: bottomLeftRadius + borderOffset * 2.0) + context.closePath() + context.strokePath() + + if drawTail { + let outlineBottomEllipse = bottomEllipse.insetBy(dx: -borderOffset, dy: -borderOffset) + let outlineInnerTopEllipse = topEllipse.insetBy(dx: borderOffset, dy: borderOffset) + let outlineTopEllipse = topEllipse.insetBy(dx: -borderOffset, dy: -borderOffset) + + context.setBlendMode(.copy) + context.setFillColor(UIColor.clear.cgColor) + + if maxCornerRadius >= minRadiusForFullTailCorner { + context.move(to: CGPoint(x: bottomEllipse.minX, y: bottomEllipse.midY)) + context.addQuadCurve(to: CGPoint(x: bottomEllipse.midX, y: bottomEllipse.maxY), control: CGPoint(x: bottomEllipse.minX, y: bottomEllipse.maxY)) + context.addQuadCurve(to: CGPoint(x: bottomEllipse.maxX, y: bottomEllipse.midY), control: CGPoint(x: bottomEllipse.maxX, y: bottomEllipse.maxY)) + context.fillPath() + } else { + context.fill(CGRect(origin: CGPoint(x: bottomEllipse.minX - 2.0, y: floor(bottomEllipse.midY)), size: CGSize(width: bottomEllipse.width + 2.0, height: ceil(bottomEllipse.height / 2.0)))) + } + context.fill(CGRect(origin: CGPoint(x: floor(fixedMainDiameter / 2.0), y: fixedMainDiameter / 2.0), size: CGSize(width: fixedMainDiameter / 2.0 + borderWidth, height: ceil(bottomEllipse.midY) - floor(fixedMainDiameter / 2.0)))) + + context.setBlendMode(.normal) + context.move(to: CGPoint(x: fixedMainDiameter + borderOffset, y: fixedMainDiameter / 2.0)) + context.addLine(to: CGPoint(x: fixedMainDiameter + borderOffset, y: outlineBottomEllipse.midY)) + context.strokePath() + + let bubbleTailContext = DrawingContext(size: imageSize) + bubbleTailContext.withFlippedContext { context in + context.clear(CGRect(origin: CGPoint(), size: rawSize)) + context.translateBy(x: additionalInset + strokeInset, y: additionalInset + strokeInset) + + context.setStrokeColor(UIColor.black.cgColor) + context.setLineWidth(borderWidth) + + if maxCornerRadius >= minRadiusForFullTailCorner { + context.move(to: CGPoint(x: outlineBottomEllipse.minX, y: outlineBottomEllipse.midY)) + context.addQuadCurve(to: CGPoint(x: outlineBottomEllipse.midX, y: outlineBottomEllipse.maxY), control: CGPoint(x: outlineBottomEllipse.minX, y: outlineBottomEllipse.maxY)) + context.addQuadCurve(to: CGPoint(x: outlineBottomEllipse.maxX, y: outlineBottomEllipse.midY), control: CGPoint(x: outlineBottomEllipse.maxX, y: outlineBottomEllipse.maxY)) + } else { + context.move(to: CGPoint(x: outlineBottomEllipse.minX - 2.0, y: outlineBottomEllipse.maxY)) + context.addLine(to: CGPoint(x: outlineBottomEllipse.minX, y: outlineBottomEllipse.maxY)) + context.addLine(to: CGPoint(x: outlineBottomEllipse.maxX, y: outlineBottomEllipse.maxY)) + } + context.strokePath() + context.setFillColor(UIColor.clear.cgColor) + context.setBlendMode(.copy) + context.fillEllipse(in: outlineInnerTopEllipse) + + context.move(to: CGPoint(x: 0.0, y: topLeftRadius)) + context.addArc(tangent1End: CGPoint(x: 0.0, y: 0.0), tangent2End: CGPoint(x: topLeftRadius, y: 0.0), radius: topLeftRadius) + context.addLine(to: CGPoint(x: fixedMainDiameter - topRightRadius, y: 0.0)) + context.addArc(tangent1End: CGPoint(x: fixedMainDiameter, y: 0.0), tangent2End: CGPoint(x: fixedMainDiameter, y: topRightRadius), radius: topRightRadius) + context.addLine(to: CGPoint(x: fixedMainDiameter, y: fixedMainDiameter - bottomRightRadius)) + context.addArc(tangent1End: CGPoint(x: fixedMainDiameter, y: fixedMainDiameter), tangent2End: CGPoint(x: fixedMainDiameter - bottomRightRadius, y: fixedMainDiameter), radius: bottomRightRadius) + context.addLine(to: CGPoint(x: bottomLeftRadius, y: fixedMainDiameter)) + context.addArc(tangent1End: CGPoint(x: 0.0, y: fixedMainDiameter), tangent2End: CGPoint(x: 0.0, y: fixedMainDiameter - bottomLeftRadius), radius: bottomLeftRadius) + context.addLine(to: CGPoint(x: 0.0, y: topLeftRadius)) + context.fillPath() + + let bottomEllipseMask = generateImage(bottomEllipse.insetBy(dx: -1.0, dy: -1.0).size, contextGenerator: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + context.setFillColor(UIColor.black.cgColor) + if maxCornerRadius >= minRadiusForFullTailCorner { + context.fillEllipse(in: CGRect(origin: CGPoint(x: 1.0 - borderOffset, y: 1.0 - borderOffset), size: CGSize(width: outlineBottomEllipse.width, height: outlineBottomEllipse.height))) + } else { + context.fill(CGRect(origin: CGPoint(x: 1.0 - borderOffset, y: 1.0 - borderOffset), size: CGSize(width: outlineBottomEllipse.width, height: outlineBottomEllipse.height))) + } + context.clear(CGRect(origin: CGPoint(), size: CGSize(width: size.width, height: size.height / 2.0))) + })! + + context.clip(to: bottomEllipse.insetBy(dx: -1.0, dy: -1.0), mask: bottomEllipseMask.cgImage!) + context.strokeEllipse(in: outlineInnerTopEllipse) + context.resetClip() + } + + context.translateBy(x: -(additionalInset + strokeInset), y: -(additionalInset + strokeInset)) + context.draw(bubbleTailContext.generateImage()!.cgImage!, in: CGRect(origin: CGPoint(), size: rawSize)) + context.translateBy(x: additionalInset + strokeInset, y: additionalInset + strokeInset) + } + } + let outlineImage = generateImage(outlineContext.size, contextGenerator: { size, context in + context.setBlendMode(.copy) + let image = outlineContext.generateImage()! + context.draw(image.cgImage!, in: CGRect(origin: CGPoint(), size: size)) + context.setBlendMode(.normal) + context.draw(image.cgImage!, in: CGRect(origin: CGPoint(), size: size)) + })! + + let drawingContext = DrawingContext(size: imageSize) + drawingContext.withFlippedContext { context in var drawWithClearColor = false if knockout { @@ -48,94 +298,33 @@ public func messageBubbleImage(incoming: Bool, fillColor: UIColor, strokeColor: context.clear(CGRect(origin: CGPoint(), size: rawSize)) } - let additionalOffset: CGFloat - switch neighbors { - case .none, .bottom: - additionalOffset = 0.0 - case .both, .side, .top: - additionalOffset = 6.0 - } - - context.translateBy(x: rawSize.width / 2.0, y: rawSize.height / 2.0) - context.scaleBy(x: incoming ? 1.0 : -1.0, y: -1.0) - context.translateBy(x: -rawSize.width / 2.0, y: -rawSize.height / 2.0) - - context.translateBy(x: additionalOffset + 0.5, y: 0.5) - - let size = CGSize(width: rawSize.width - inset * 2.0, height: rawSize.height - inset * 2.0) - context.translateBy(x: inset, y: inset) - - var lineWidth: CGFloat = 1.0 - if drawWithClearColor { context.setBlendMode(.copy) context.setFillColor(UIColor.clear.cgColor) - context.setStrokeColor(UIColor.clear.cgColor) } else { + context.setBlendMode(.normal) context.setFillColor(fillColor.cgColor) - context.setLineWidth(lineWidth) - context.setStrokeColor(strokeColor.cgColor) } - if onlyOutline { - if knockout { - lineWidth = max(UIScreenPixel, 1.0 - 0.5) - } - context.setLineWidth(lineWidth) - context.setStrokeColor(strokeColor.cgColor) + context.saveGState() + + context.translateBy(x: rawSize.width / 2.0, y: rawSize.height / 2.0) + context.scaleBy(x: incoming ? -1.0 : 1.0, y: -1.0) + context.translateBy(x: -rawSize.width / 2.0, y: -rawSize.height / 2.0) + + if !onlyOutline { + context.clip(to: CGRect(origin: CGPoint(), size: rawSize), mask: formImage.cgImage!) + context.fill(CGRect(origin: CGPoint(), size: rawSize)) + } else { + context.setFillColor(strokeColor.cgColor) + context.clip(to: CGRect(origin: CGPoint(), size: rawSize), mask: outlineImage.cgImage!) + context.fill(CGRect(origin: CGPoint(), size: rawSize)) } - switch neighbors { - case .none: - if onlyOutline { - let _ = try? drawSvgPath(context, path: "M6,17.5 C6,7.83289181 13.8350169,0 23.5,0 C33.1671082,0 41,7.83501688 41,17.5 C41,27.1671082 33.1649831,35 23.5,35 C19.2941198,35 15.4354328,33.5169337 12.4179496,31.0453367 C9.05531719,34.9894816 -2.41102995e-08,35 0,35 C5.972003,31.5499861 6,26.8616169 6,26.8616169 L6,17.5 L6,17.5 ") - context.strokePath() - } else { - let _ = try? drawSvgPath(context, path: "M6,17.5 C6,7.83289181 13.8350169,0 23.5,0 C33.1671082,0 41,7.83501688 41,17.5 C41,27.1671082 33.1649831,35 23.5,35 C19.2941198,35 15.4354328,33.5169337 12.4179496,31.0453367 C9.05531719,34.9894816 -2.41102995e-08,35 0,35 C5.972003,31.5499861 6,26.8616169 6,26.8616169 L6,17.5 L6,17.5 ") - context.fillPath() - } - case .side: - if onlyOutline { - context.strokeEllipse(in: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: 35.0, height: 35.0))) - } else { - context.fillEllipse(in: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: 35.0, height: 35.0))) - } - case let .top(side): - if side { - if onlyOutline { - let _ = try? drawSvgPath(context, path: "M17.5,0 L17.5,0 C27.1649831,-1.7754286e-15 35,7.83501688 35,17.5 L35,29 C35,32.3137085 32.3137085,35 29,35 L6,35 C2.6862915,35 4.05812251e-16,32.3137085 0,29 L0,17.5 C-1.18361906e-15,7.83501688 7.83501688,1.7754286e-15 17.5,0 ") - context.strokePath() - } else { - let _ = try? drawSvgPath(context, path: "M17.5,0 L17.5,0 C27.1649831,-1.7754286e-15 35,7.83501688 35,17.5 L35,29 C35,32.3137085 32.3137085,35 29,35 L6,35 C2.6862915,35 4.05812251e-16,32.3137085 0,29 L0,17.5 C-1.18361906e-15,7.83501688 7.83501688,1.7754286e-15 17.5,0 ") - context.fillPath() - } - } else { - if onlyOutline { - let _ = try? drawSvgPath(context, path: "M35,17.5 C35,7.83501688 27.1671082,0 17.5,0 L17.5,0 C7.83501688,0 0,7.83289181 0,17.5 L0,29.0031815 C0,32.3151329 2.6882755,35 5.99681848,35 L17.5,35 C27.1649831,35 35,27.1671082 35,17.5 L35,17.5 L35,17.5 ") - context.strokePath() - } else { - let _ = try? drawSvgPath(context, path: "M35,17.5 C35,7.83501688 27.1671082,0 17.5,0 L17.5,0 C7.83501688,0 0,7.83289181 0,17.5 L0,29.0031815 C0,32.3151329 2.6882755,35 5.99681848,35 L17.5,35 C27.1649831,35 35,27.1671082 35,17.5 L35,17.5 L35,17.5 ") - context.fillPath() - } - } - case .bottom: - if onlyOutline { - let _ = try? drawSvgPath(context, path: "M6,17.5 L6,5.99681848 C6,2.6882755 8.68486709,0 11.9968185,0 L23.5,0 C33.1671082,0 41,7.83501688 41,17.5 C41,27.1671082 33.1649831,35 23.5,35 C19.2941198,35 15.4354328,33.5169337 12.4179496,31.0453367 C9.05531719,34.9894816 -2.41103066e-08,35 0,35 C5.972003,31.5499861 6,26.8616169 6,26.8616169 L6,17.5 L6,17.5 ") - context.strokePath() - } else { - let _ = try? drawSvgPath(context, path: "M6,17.5 L6,5.99681848 C6,2.6882755 8.68486709,0 11.9968185,0 L23.5,0 C33.1671082,0 41,7.83501688 41,17.5 C41,27.1671082 33.1649831,35 23.5,35 C19.2941198,35 15.4354328,33.5169337 12.4179496,31.0453367 C9.05531719,34.9894816 -2.41103066e-08,35 0,35 C5.972003,31.5499861 6,26.8616169 6,26.8616169 L6,17.5 L6,17.5 ") - context.fillPath() - } - case .both: - if onlyOutline { - let _ = try? drawSvgPath(context, path: "M35,17.5 C35,7.83501688 27.1671082,0 17.5,0 L5.99681848,0 C2.68486709,0 0,2.6882755 0,5.99681848 L0,29.0031815 C0,32.3151329 2.6882755,35 5.99681848,35 L17.5,35 C27.1649831,35 35,27.1671082 35,17.5 L35,17.5 L35,17.5 ") - context.strokePath() - } else { - let _ = try? drawSvgPath(context, path: "M35,17.5 C35,7.83501688 27.1671082,0 17.5,0 L5.99681848,0 C2.68486709,0 0,2.6882755 0,5.99681848 L0,29.0031815 C0,32.3151329 2.6882755,35 5.99681848,35 L17.5,35 C27.1649831,35 35,27.1671082 35,17.5 L35,17.5 L35,17.5 ") - context.fillPath() - } - } - })!.stretchableImage(withLeftCapWidth: incoming ? Int(inset + corner + diameter / 2.0 - 1.0) : Int(inset + diameter / 2.0), topCapHeight: Int(inset + diameter / 2.0)) + context.restoreGState() + } + + return drawingContext.generateImage()!.stretchableImage(withLeftCapWidth: incoming ? incomingStretchPoint.x : outgoingStretchPoint.x, topCapHeight: incoming ? incomingStretchPoint.y : outgoingStretchPoint.y) } public enum MessageBubbleActionButtonPosition { @@ -145,14 +334,14 @@ public enum MessageBubbleActionButtonPosition { case bottomSingle } -public func messageBubbleActionButtonImage(color: UIColor, strokeColor: UIColor, position: MessageBubbleActionButtonPosition) -> UIImage { - let largeRadius: CGFloat = 17.0 - let smallRadius: CGFloat = 6.0 +public func messageBubbleActionButtonImage(color: UIColor, strokeColor: UIColor, position: MessageBubbleActionButtonPosition, bubbleCorners: PresentationChatBubbleCorners) -> UIImage { + let largeRadius: CGFloat = bubbleCorners.mainRadius + let smallRadius: CGFloat = (bubbleCorners.mergeBubbleCorners && largeRadius >= 10.0) ? bubbleCorners.auxiliaryRadius : bubbleCorners.mainRadius let size: CGSize if case .middle = position { size = CGSize(width: smallRadius + smallRadius, height: smallRadius + smallRadius) } else { - size = CGSize(width: 35.0, height: 35.0) + size = CGSize(width: largeRadius + largeRadius, height: largeRadius + largeRadius) } return generateImage(size, contextGenerator: { size, context in context.clear(CGRect(origin: CGPoint(), size: size)) diff --git a/submodules/TelegramPresentationData/Sources/DefaultDarkPresentationTheme.swift b/submodules/TelegramPresentationData/Sources/DefaultDarkPresentationTheme.swift index d71494e8cd..e1be4c62bf 100644 --- a/submodules/TelegramPresentationData/Sources/DefaultDarkPresentationTheme.swift +++ b/submodules/TelegramPresentationData/Sources/DefaultDarkPresentationTheme.swift @@ -343,7 +343,7 @@ public func makeDefaultDarkPresentationTheme(extendingThemeReference: Presentati primaryColor: UIColor(rgb: 0xffffff), controlColor: UIColor(rgb: 0x4d4d4d) ), - mediaPlaceholderColor: UIColor(rgb: 0x1c1c1d), + mediaPlaceholderColor: UIColor(rgb: 0xffffff).mixedWith(UIColor(rgb: 0x1c1c1d), alpha: 0.9), scrollIndicatorColor: UIColor(rgb: 0xffffff, alpha: 0.3), pageIndicatorInactiveColor: UIColor(white: 1.0, alpha: 0.3), inputClearButtonColor: UIColor(rgb: 0x8b9197), @@ -387,8 +387,8 @@ public func makeDefaultDarkPresentationTheme(extendingThemeReference: Presentati ) let message = PresentationThemeChatMessage( - incoming: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0x262628), highlightedFill: UIColor(rgb: 0x353539), stroke: UIColor(rgb: 0x262628)), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0x262628), highlightedFill: UIColor(rgb: 0x353539), stroke: UIColor(rgb: 0x262628))), primaryTextColor: UIColor(rgb: 0xffffff), secondaryTextColor: UIColor(rgb: 0xffffff, alpha: 0.5), linkTextColor: UIColor(rgb: 0xffffff), linkHighlightColor: UIColor(rgb: 0xffffff, alpha: 0.5), scamColor: UIColor(rgb: 0xeb5545), textHighlightColor: UIColor(rgb: 0xf5c038), accentTextColor: UIColor(rgb: 0xffffff), accentControlColor: UIColor(rgb: 0xffffff), mediaActiveControlColor: UIColor(rgb: 0xffffff), mediaInactiveControlColor: UIColor(rgb: 0xffffff, alpha: 0.4), mediaControlInnerBackgroundColor: UIColor(rgb: 0x262628), pendingActivityColor: UIColor(rgb: 0xffffff, alpha: 0.5), fileTitleColor: UIColor(rgb: 0xffffff), fileDescriptionColor: UIColor(rgb: 0xffffff, alpha: 0.5), fileDurationColor: UIColor(rgb: 0xffffff, alpha: 0.5), mediaPlaceholderColor: UIColor(rgb: 0x1f1f1f).mixedWith(UIColor(rgb: 0xffffff), alpha: 0.05), polls: PresentationThemeChatBubblePolls(radioButton: UIColor(rgb: 0x737373), radioProgress: UIColor(rgb: 0xffffff), highlight: UIColor(rgb: 0xffffff, alpha: 0.12), separator: UIColor(rgb: 0x000000), bar: UIColor(rgb: 0xffffff)), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: UIColor(rgb: 0x000000, alpha: 0.5), withoutWallpaper: UIColor(rgb: 0x000000, alpha: 0.5)), actionButtonsStrokeColor: PresentationThemeVariableColor(color: UIColor(rgb: 0xb2b2b2, alpha: 0.18)), actionButtonsTextColor: PresentationThemeVariableColor(color: UIColor(rgb: 0xffffff)), textSelectionColor: UIColor(rgb: 0xffffff, alpha: 0.2), textSelectionKnobColor: UIColor(rgb: 0xffffff)), - outgoing: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0x313131), gradientFill: UIColor(rgb: 0x313131), highlightedFill: UIColor(rgb: 0x464646), stroke: UIColor(rgb: 0x313131)), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0x313131), gradientFill: UIColor(rgb: 0x313131), highlightedFill: UIColor(rgb: 0x464646), stroke: UIColor(rgb: 0x313131))), primaryTextColor: UIColor(rgb: 0xffffff), secondaryTextColor: UIColor(rgb: 0xffffff, alpha: 0.5), linkTextColor: UIColor(rgb: 0xffffff), linkHighlightColor: UIColor(rgb: 0xffffff, alpha: 0.5), scamColor: UIColor(rgb: 0xeb5545), textHighlightColor: UIColor(rgb: 0xf5c038), accentTextColor: UIColor(rgb: 0xffffff), accentControlColor: UIColor(rgb: 0xffffff), mediaActiveControlColor: UIColor(rgb: 0xffffff), mediaInactiveControlColor: UIColor(rgb: 0xffffff, alpha: 0.5), mediaControlInnerBackgroundColor: UIColor(rgb: 0x313131), pendingActivityColor: UIColor(rgb: 0xffffff, alpha: 0.5), fileTitleColor: UIColor(rgb: 0xffffff), fileDescriptionColor: UIColor(rgb: 0xffffff, alpha: 0.5), fileDurationColor: UIColor(rgb: 0xffffff, alpha: 0.5), mediaPlaceholderColor: UIColor(rgb: 0x313131).mixedWith(UIColor(rgb: 0xffffff), alpha: 0.05), polls: PresentationThemeChatBubblePolls(radioButton: UIColor(rgb: 0xffffff), radioProgress: UIColor(rgb: 0xffffff), highlight: UIColor(rgb: 0xffffff).withAlphaComponent(0.12), separator: UIColor(rgb: 0xffffff, alpha: 0.5), bar: UIColor(rgb: 0xffffff)), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: UIColor(rgb: 0x000000, alpha: 0.5), withoutWallpaper: UIColor(rgb: 0x000000, alpha: 0.5)), actionButtonsStrokeColor: PresentationThemeVariableColor(color: UIColor(rgb: 0xb2b2b2, alpha: 0.18)), actionButtonsTextColor: PresentationThemeVariableColor(color: UIColor(rgb: 0xffffff)), textSelectionColor: UIColor(rgb: 0xffffff, alpha: 0.2), textSelectionKnobColor: UIColor(rgb: 0xffffff)), + incoming: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0x262628), highlightedFill: UIColor(rgb: 0x353539), stroke: UIColor(rgb: 0x262628)), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0x262628), highlightedFill: UIColor(rgb: 0x353539), stroke: UIColor(rgb: 0x262628))), primaryTextColor: UIColor(rgb: 0xffffff), secondaryTextColor: UIColor(rgb: 0xffffff, alpha: 0.5), linkTextColor: UIColor(rgb: 0xffffff), linkHighlightColor: UIColor(rgb: 0xffffff, alpha: 0.5), scamColor: UIColor(rgb: 0xeb5545), textHighlightColor: UIColor(rgb: 0xf5c038), accentTextColor: UIColor(rgb: 0xffffff), accentControlColor: UIColor(rgb: 0xffffff), accentControlDisabledColor: UIColor(rgb: 0xffffff, alpha: 0.5), mediaActiveControlColor: UIColor(rgb: 0xffffff), mediaInactiveControlColor: UIColor(rgb: 0xffffff, alpha: 0.4), mediaControlInnerBackgroundColor: UIColor(rgb: 0x262628), pendingActivityColor: UIColor(rgb: 0xffffff, alpha: 0.5), fileTitleColor: UIColor(rgb: 0xffffff), fileDescriptionColor: UIColor(rgb: 0xffffff, alpha: 0.5), fileDurationColor: UIColor(rgb: 0xffffff, alpha: 0.5), mediaPlaceholderColor: UIColor(rgb: 0x1f1f1f).mixedWith(UIColor(rgb: 0xffffff), alpha: 0.05), polls: PresentationThemeChatBubblePolls(radioButton: UIColor(rgb: 0x737373), radioProgress: UIColor(rgb: 0xffffff), highlight: UIColor(rgb: 0xffffff, alpha: 0.12), separator: UIColor(rgb: 0x000000), bar: UIColor(rgb: 0xffffff), barIconForeground: .white, barPositive: UIColor(rgb: 0x00A700), barNegative: UIColor(rgb: 0xFE3824)), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: UIColor(rgb: 0x000000, alpha: 0.5), withoutWallpaper: UIColor(rgb: 0x000000, alpha: 0.5)), actionButtonsStrokeColor: PresentationThemeVariableColor(color: UIColor(rgb: 0xb2b2b2, alpha: 0.18)), actionButtonsTextColor: PresentationThemeVariableColor(color: UIColor(rgb: 0xffffff)), textSelectionColor: UIColor(rgb: 0xffffff, alpha: 0.2), textSelectionKnobColor: UIColor(rgb: 0xffffff)), + outgoing: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0x313131), gradientFill: UIColor(rgb: 0x313131), highlightedFill: UIColor(rgb: 0x464646), stroke: UIColor(rgb: 0x313131)), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0x313131), gradientFill: UIColor(rgb: 0x313131), highlightedFill: UIColor(rgb: 0x464646), stroke: UIColor(rgb: 0x313131))), primaryTextColor: UIColor(rgb: 0xffffff), secondaryTextColor: UIColor(rgb: 0xffffff, alpha: 0.5), linkTextColor: UIColor(rgb: 0xffffff), linkHighlightColor: UIColor(rgb: 0xffffff, alpha: 0.5), scamColor: UIColor(rgb: 0xeb5545), textHighlightColor: UIColor(rgb: 0xf5c038), accentTextColor: UIColor(rgb: 0xffffff), accentControlColor: UIColor(rgb: 0xffffff), accentControlDisabledColor: UIColor(rgb: 0xffffff, alpha: 0.5), mediaActiveControlColor: UIColor(rgb: 0xffffff), mediaInactiveControlColor: UIColor(rgb: 0xffffff, alpha: 0.5), mediaControlInnerBackgroundColor: UIColor(rgb: 0x313131), pendingActivityColor: UIColor(rgb: 0xffffff, alpha: 0.5), fileTitleColor: UIColor(rgb: 0xffffff), fileDescriptionColor: UIColor(rgb: 0xffffff, alpha: 0.5), fileDurationColor: UIColor(rgb: 0xffffff, alpha: 0.5), mediaPlaceholderColor: UIColor(rgb: 0x313131).mixedWith(UIColor(rgb: 0xffffff), alpha: 0.05), polls: PresentationThemeChatBubblePolls(radioButton: UIColor(rgb: 0xffffff), radioProgress: UIColor(rgb: 0xffffff), highlight: UIColor(rgb: 0xffffff).withAlphaComponent(0.12), separator: UIColor(rgb: 0xffffff, alpha: 0.5), bar: UIColor(rgb: 0xffffff), barIconForeground: .clear, barPositive: UIColor(rgb: 0xffffff), barNegative: UIColor(rgb: 0xffffff)), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: UIColor(rgb: 0x000000, alpha: 0.5), withoutWallpaper: UIColor(rgb: 0x000000, alpha: 0.5)), actionButtonsStrokeColor: PresentationThemeVariableColor(color: UIColor(rgb: 0xb2b2b2, alpha: 0.18)), actionButtonsTextColor: PresentationThemeVariableColor(color: UIColor(rgb: 0xffffff)), textSelectionColor: UIColor(rgb: 0xffffff, alpha: 0.2), textSelectionKnobColor: UIColor(rgb: 0xffffff)), freeform: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0x1f1f1f), highlightedFill: UIColor(rgb: 0x2a2a2a), stroke: UIColor(rgb: 0x1f1f1f)), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0x1f1f1f), highlightedFill: UIColor(rgb: 0x2a2a2a), stroke: UIColor(rgb: 0x1f1f1f))), infoPrimaryTextColor: UIColor(rgb: 0xffffff), infoLinkTextColor: UIColor(rgb: 0xffffff), diff --git a/submodules/TelegramPresentationData/Sources/DefaultDarkTintedPresentationTheme.swift b/submodules/TelegramPresentationData/Sources/DefaultDarkTintedPresentationTheme.swift index 1261eb0bd6..850da9400b 100644 --- a/submodules/TelegramPresentationData/Sources/DefaultDarkTintedPresentationTheme.swift +++ b/submodules/TelegramPresentationData/Sources/DefaultDarkTintedPresentationTheme.swift @@ -154,7 +154,7 @@ public func customizeDefaultDarkTintedPresentationTheme(theme: PresentationTheme backgroundColor: mainSecondaryTextColor?.withAlphaComponent(0.5), strokeColor: mainSecondaryTextColor?.withAlphaComponent(0.5) ), - mediaPlaceholderColor: accentColor?.withMultiplied(hue: 1.019, saturation: 0.585, brightness: 0.23), + mediaPlaceholderColor: UIColor(rgb: 0xffffff).mixedWith(mainBackgroundColor ?? list.itemBlocksBackgroundColor, alpha: 0.9), pageIndicatorInactiveColor: mainSecondaryTextColor?.withAlphaComponent(0.4), inputClearButtonColor: mainSecondaryColor, itemBarChart: list.itemBarChart.withUpdated( @@ -597,7 +597,7 @@ public func makeDefaultDarkTintedPresentationTheme(extendingThemeReference: Pres primaryColor: .white, controlColor: UIColor(rgb: 0x4d4d4d) ), - mediaPlaceholderColor: accentColor.withMultiplied(hue: 1.019, saturation: 0.585, brightness: 0.23), + mediaPlaceholderColor: UIColor(rgb: 0xffffff).mixedWith(mainBackgroundColor, alpha: 0.9), scrollIndicatorColor: UIColor(white: 1.0, alpha: 0.3), pageIndicatorInactiveColor: mainSecondaryTextColor.withAlphaComponent(0.4), inputClearButtonColor: mainSecondaryColor, @@ -643,8 +643,8 @@ public func makeDefaultDarkTintedPresentationTheme(extendingThemeReference: Pres let buttonStrokeColor = accentColor.withMultiplied(hue: 1.014, saturation: 0.56, brightness: 0.64).withAlphaComponent(0.15) let message = PresentationThemeChatMessage( - incoming: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: mainBackgroundColor, highlightedFill: highlightedIncomingBubbleColor, stroke: mainBackgroundColor), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: mainBackgroundColor, highlightedFill: highlightedIncomingBubbleColor, stroke: mainBackgroundColor)), primaryTextColor: .white, secondaryTextColor: mainSecondaryTextColor.withAlphaComponent(0.5), linkTextColor: accentColor, linkHighlightColor: accentColor.withAlphaComponent(0.5), scamColor: UIColor(rgb: 0xff6767), textHighlightColor: UIColor(rgb: 0xf5c038), accentTextColor: accentColor, accentControlColor: accentColor, mediaActiveControlColor: accentColor, mediaInactiveControlColor: accentColor.withAlphaComponent(0.5), mediaControlInnerBackgroundColor: mainBackgroundColor, pendingActivityColor: mainSecondaryTextColor.withAlphaComponent(0.5), fileTitleColor: accentColor, fileDescriptionColor: mainSecondaryTextColor.withAlphaComponent(0.5), fileDurationColor: mainSecondaryTextColor.withAlphaComponent(0.5), mediaPlaceholderColor: accentColor.withMultiplied(hue: 1.019, saturation: 0.585, brightness: 0.23), polls: PresentationThemeChatBubblePolls(radioButton: accentColor.withMultiplied(hue: 0.995, saturation: 0.317, brightness: 0.51), radioProgress: accentColor, highlight: accentColor.withAlphaComponent(0.12), separator: mainSeparatorColor, bar: accentColor), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: additionalBackgroundColor.withAlphaComponent(0.5), withoutWallpaper: additionalBackgroundColor.withAlphaComponent(0.5)), actionButtonsStrokeColor: PresentationThemeVariableColor(color: buttonStrokeColor), actionButtonsTextColor: PresentationThemeVariableColor(color: .white), textSelectionColor: accentColor.withAlphaComponent(0.2), textSelectionKnobColor: accentColor), - outgoing: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: outgoingBubbleFillColor, gradientFill: outgoingBubbleFillGradientColor, highlightedFill: highlightedOutgoingBubbleColor, stroke: outgoingBubbleFillColor), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: outgoingBubbleFillColor, gradientFill: outgoingBubbleFillGradientColor, highlightedFill: highlightedOutgoingBubbleColor, stroke: outgoingBubbleFillColor)), primaryTextColor: outgoingPrimaryTextColor, secondaryTextColor: outgoingSecondaryTextColor, linkTextColor: outgoingLinkTextColor, linkHighlightColor: UIColor.white.withAlphaComponent(0.5), scamColor: outgoingScamColor, textHighlightColor: UIColor(rgb: 0xf5c038), accentTextColor: outgoingPrimaryTextColor, accentControlColor: outgoingPrimaryTextColor, mediaActiveControlColor: outgoingPrimaryTextColor, mediaInactiveControlColor: outgoingSecondaryTextColor, mediaControlInnerBackgroundColor: outgoingBubbleFillColor, pendingActivityColor: outgoingSecondaryTextColor, fileTitleColor: outgoingPrimaryTextColor, fileDescriptionColor: outgoingSecondaryTextColor, fileDurationColor: outgoingSecondaryTextColor, mediaPlaceholderColor: accentColor.withMultiplied(hue: 1.019, saturation: 0.804, brightness: 0.51), polls: PresentationThemeChatBubblePolls(radioButton: outgoingPrimaryTextColor, radioProgress: accentColor.withMultiplied(hue: 0.99, saturation: 0.56, brightness: 1.0), highlight: accentColor.withMultiplied(hue: 0.99, saturation: 0.56, brightness: 1.0).withAlphaComponent(0.12), separator: mainSeparatorColor, bar: outgoingPrimaryTextColor), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: additionalBackgroundColor.withAlphaComponent(0.5), withoutWallpaper: additionalBackgroundColor.withAlphaComponent(0.5)), actionButtonsStrokeColor: PresentationThemeVariableColor(color: buttonStrokeColor), actionButtonsTextColor: PresentationThemeVariableColor(color: .white), textSelectionColor: UIColor.white.withAlphaComponent(0.2), textSelectionKnobColor: UIColor.white), + incoming: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: mainBackgroundColor, highlightedFill: highlightedIncomingBubbleColor, stroke: mainBackgroundColor), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: mainBackgroundColor, highlightedFill: highlightedIncomingBubbleColor, stroke: mainBackgroundColor)), primaryTextColor: .white, secondaryTextColor: mainSecondaryTextColor.withAlphaComponent(0.5), linkTextColor: accentColor, linkHighlightColor: accentColor.withAlphaComponent(0.5), scamColor: UIColor(rgb: 0xff6767), textHighlightColor: UIColor(rgb: 0xf5c038), accentTextColor: accentColor, accentControlColor: accentColor, accentControlDisabledColor: mainSecondaryTextColor.withAlphaComponent(0.5), mediaActiveControlColor: accentColor, mediaInactiveControlColor: accentColor.withAlphaComponent(0.5), mediaControlInnerBackgroundColor: mainBackgroundColor, pendingActivityColor: mainSecondaryTextColor.withAlphaComponent(0.5), fileTitleColor: accentColor, fileDescriptionColor: mainSecondaryTextColor.withAlphaComponent(0.5), fileDurationColor: mainSecondaryTextColor.withAlphaComponent(0.5), mediaPlaceholderColor: accentColor.withMultiplied(hue: 1.019, saturation: 0.585, brightness: 0.23), polls: PresentationThemeChatBubblePolls(radioButton: accentColor.withMultiplied(hue: 0.995, saturation: 0.317, brightness: 0.51), radioProgress: accentColor, highlight: accentColor.withAlphaComponent(0.12), separator: mainSeparatorColor, bar: accentColor, barIconForeground: .white, barPositive: UIColor(rgb: 0x00A700), barNegative: UIColor(rgb: 0xFE3824)), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: additionalBackgroundColor.withAlphaComponent(0.5), withoutWallpaper: additionalBackgroundColor.withAlphaComponent(0.5)), actionButtonsStrokeColor: PresentationThemeVariableColor(color: buttonStrokeColor), actionButtonsTextColor: PresentationThemeVariableColor(color: .white), textSelectionColor: accentColor.withAlphaComponent(0.2), textSelectionKnobColor: accentColor), + outgoing: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: outgoingBubbleFillColor, gradientFill: outgoingBubbleFillGradientColor, highlightedFill: highlightedOutgoingBubbleColor, stroke: outgoingBubbleFillColor), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: outgoingBubbleFillColor, gradientFill: outgoingBubbleFillGradientColor, highlightedFill: highlightedOutgoingBubbleColor, stroke: outgoingBubbleFillColor)), primaryTextColor: outgoingPrimaryTextColor, secondaryTextColor: outgoingSecondaryTextColor, linkTextColor: outgoingLinkTextColor, linkHighlightColor: UIColor.white.withAlphaComponent(0.5), scamColor: outgoingScamColor, textHighlightColor: UIColor(rgb: 0xf5c038), accentTextColor: outgoingPrimaryTextColor, accentControlColor: outgoingPrimaryTextColor, accentControlDisabledColor: mainSecondaryTextColor.withAlphaComponent(0.5), mediaActiveControlColor: outgoingPrimaryTextColor, mediaInactiveControlColor: outgoingSecondaryTextColor, mediaControlInnerBackgroundColor: outgoingBubbleFillColor, pendingActivityColor: outgoingSecondaryTextColor, fileTitleColor: outgoingPrimaryTextColor, fileDescriptionColor: outgoingSecondaryTextColor, fileDurationColor: outgoingSecondaryTextColor, mediaPlaceholderColor: accentColor.withMultiplied(hue: 1.019, saturation: 0.804, brightness: 0.51), polls: PresentationThemeChatBubblePolls(radioButton: outgoingPrimaryTextColor, radioProgress: accentColor.withMultiplied(hue: 0.99, saturation: 0.56, brightness: 1.0), highlight: accentColor.withMultiplied(hue: 0.99, saturation: 0.56, brightness: 1.0).withAlphaComponent(0.12), separator: mainSeparatorColor, bar: outgoingPrimaryTextColor, barIconForeground: .clear, barPositive: outgoingPrimaryTextColor, barNegative: outgoingPrimaryTextColor), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: additionalBackgroundColor.withAlphaComponent(0.5), withoutWallpaper: additionalBackgroundColor.withAlphaComponent(0.5)), actionButtonsStrokeColor: PresentationThemeVariableColor(color: buttonStrokeColor), actionButtonsTextColor: PresentationThemeVariableColor(color: .white), textSelectionColor: UIColor.white.withAlphaComponent(0.2), textSelectionKnobColor: UIColor.white), freeform: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: mainBackgroundColor, highlightedFill: highlightedIncomingBubbleColor, stroke: mainBackgroundColor), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: mainBackgroundColor, highlightedFill: highlightedIncomingBubbleColor, stroke: mainBackgroundColor)), infoPrimaryTextColor: UIColor(rgb: 0xffffff), infoLinkTextColor: accentColor, diff --git a/submodules/TelegramPresentationData/Sources/DefaultDayPresentationTheme.swift b/submodules/TelegramPresentationData/Sources/DefaultDayPresentationTheme.swift index f3dc735c7c..ce28633511 100644 --- a/submodules/TelegramPresentationData/Sources/DefaultDayPresentationTheme.swift +++ b/submodules/TelegramPresentationData/Sources/DefaultDayPresentationTheme.swift @@ -226,6 +226,7 @@ public func customizeDefaultDayTheme(theme: PresentationTheme, editing: Bool, ti linkHighlightColor: accentColor?.withAlphaComponent(0.3), accentTextColor: accentColor, accentControlColor: accentColor, + accentControlDisabledColor: accentColor?.withAlphaComponent(0.7), mediaActiveControlColor: accentColor, fileTitleColor: accentColor, polls: chat.message.incoming.polls.withUpdated( @@ -261,6 +262,7 @@ public func customizeDefaultDayTheme(theme: PresentationTheme, editing: Bool, ti scamColor: outgoingScamColor, accentTextColor: outgoingAccentTextColor, accentControlColor: outgoingControlColor, + accentControlDisabledColor: outgoingControlColor?.withAlphaComponent(0.7), mediaActiveControlColor: outgoingControlColor, mediaInactiveControlColor: outgoingInactiveControlColor, mediaControlInnerBackgroundColor: .clear, @@ -269,7 +271,7 @@ public func customizeDefaultDayTheme(theme: PresentationTheme, editing: Bool, ti fileDescriptionColor: outgoingFileDescriptionColor, fileDurationColor: outgoingFileDurationColor, mediaPlaceholderColor: day ? accentColor?.withMultipliedBrightnessBy(0.95) : outgoingMediaPlaceholderColor, - polls: chat.message.outgoing.polls.withUpdated(radioButton: outgoingPollsButtonColor, radioProgress: outgoingPollsProgressColor, highlight: outgoingPollsProgressColor?.withAlphaComponent(0.12), separator: outgoingPollsButtonColor, bar: outgoingPollsProgressColor), + polls: chat.message.outgoing.polls.withUpdated(radioButton: outgoingPollsButtonColor, radioProgress: outgoingPollsProgressColor, highlight: outgoingPollsProgressColor?.withAlphaComponent(0.12), separator: outgoingPollsButtonColor, bar: outgoingPollsProgressColor, barIconForeground: .clear, barPositive: outgoingPollsProgressColor, barNegative: outgoingPollsProgressColor), actionButtonsFillColor: chat.message.outgoing.actionButtonsFillColor.withUpdated(withWallpaper: serviceBackgroundColor), actionButtonsStrokeColor: day ? chat.message.outgoing.actionButtonsStrokeColor.withUpdated(withoutWallpaper: accentColor) : nil, actionButtonsTextColor: day ? chat.message.outgoing.actionButtonsTextColor.withUpdated(withoutWallpaper: accentColor) : nil, @@ -438,7 +440,7 @@ public func makeDefaultDayPresentationTheme(extendingThemeReference: Presentatio primaryColor: UIColor(rgb: 0x000000), controlColor: UIColor(rgb: 0xbcbcc0) ), - mediaPlaceholderColor: UIColor(rgb: 0xe4e4e4), + mediaPlaceholderColor: UIColor(rgb: 0xEFEFF4), scrollIndicatorColor: UIColor(white: 0.0, alpha: 0.3), pageIndicatorInactiveColor: UIColor(rgb: 0xe3e3e7), inputClearButtonColor: UIColor(rgb: 0xcccccc), @@ -494,6 +496,7 @@ public func makeDefaultDayPresentationTheme(extendingThemeReference: Presentatio textHighlightColor: UIColor(rgb: 0xffe438), accentTextColor: UIColor(rgb: 0x007ee5), accentControlColor: UIColor(rgb: 0x007ee5), + accentControlDisabledColor: UIColor(rgb: 0x525252, alpha: 0.6), mediaActiveControlColor: UIColor(rgb: 0x007ee5), mediaInactiveControlColor: UIColor(rgb: 0xcacaca), mediaControlInnerBackgroundColor: UIColor(rgb: 0xffffff), @@ -502,7 +505,7 @@ public func makeDefaultDayPresentationTheme(extendingThemeReference: Presentatio fileDescriptionColor: UIColor(rgb: 0x999999), fileDurationColor: UIColor(rgb: 0x525252, alpha: 0.6), mediaPlaceholderColor: UIColor(rgb: 0xe8ecf0), - polls: PresentationThemeChatBubblePolls(radioButton: UIColor(rgb: 0xc8c7cc), radioProgress: UIColor(rgb: 0x007ee5), highlight: UIColor(rgb: 0x007ee5, alpha: 0.08), separator: UIColor(rgb: 0xc8c7cc), bar: UIColor(rgb: 0x007ee5)), + polls: PresentationThemeChatBubblePolls(radioButton: UIColor(rgb: 0xc8c7cc), radioProgress: UIColor(rgb: 0x007ee5), highlight: UIColor(rgb: 0x007ee5, alpha: 0.08), separator: UIColor(rgb: 0xc8c7cc), bar: UIColor(rgb: 0x007ee5), barIconForeground: .white, barPositive: UIColor(rgb: 0x2dba45), barNegative: UIColor(rgb: 0xFE3824)), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: serviceBackgroundColor, withoutWallpaper: UIColor(rgb: 0x596e89, alpha: 0.35)), actionButtonsStrokeColor: PresentationThemeVariableColor(color: .clear), actionButtonsTextColor: PresentationThemeVariableColor(color: UIColor(rgb: 0xffffff)), textSelectionColor: UIColor(rgb: 0x007ee5, alpha: 0.2), textSelectionKnobColor: UIColor(rgb: 0x007ee5)), outgoing: PresentationThemePartedColors( @@ -515,6 +518,7 @@ public func makeDefaultDayPresentationTheme(extendingThemeReference: Presentatio textHighlightColor: UIColor(rgb: 0xffe438), accentTextColor: UIColor(rgb: 0x00a700), accentControlColor: UIColor(rgb: 0x3fc33b), + accentControlDisabledColor: UIColor(rgb: 0x3fc33b).withAlphaComponent(0.7), mediaActiveControlColor: UIColor(rgb: 0x3fc33b), mediaInactiveControlColor: UIColor(rgb: 0x93d987), mediaControlInnerBackgroundColor: UIColor(rgb: 0xe1ffc7), @@ -523,7 +527,7 @@ public func makeDefaultDayPresentationTheme(extendingThemeReference: Presentatio fileDescriptionColor: UIColor(rgb: 0x6fb26a), fileDurationColor: UIColor(rgb: 0x008c09, alpha: 0.8), mediaPlaceholderColor: UIColor(rgb: 0xd2f2b6), - polls: PresentationThemeChatBubblePolls(radioButton: UIColor(rgb: 0x93d987), radioProgress: UIColor(rgb: 0x3fc33b), highlight: UIColor(rgb: 0x3fc33b).withAlphaComponent(0.08), separator: UIColor(rgb: 0x93d987), bar: UIColor(rgb: 0x3fc33b)), + polls: PresentationThemeChatBubblePolls(radioButton: UIColor(rgb: 0x93d987), radioProgress: UIColor(rgb: 0x3fc33b), highlight: UIColor(rgb: 0x3fc33b).withAlphaComponent(0.08), separator: UIColor(rgb: 0x93d987), bar: UIColor(rgb: 0x00A700), barIconForeground: .white, barPositive: UIColor(rgb: 0x00A700), barNegative: UIColor(rgb: 0x00A700)), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: serviceBackgroundColor, withoutWallpaper: UIColor(rgb: 0x596e89, alpha: 0.35)), actionButtonsStrokeColor: PresentationThemeVariableColor(color: .clear), actionButtonsTextColor: PresentationThemeVariableColor(color: UIColor(rgb: 0xffffff)), @@ -555,6 +559,7 @@ public func makeDefaultDayPresentationTheme(extendingThemeReference: Presentatio textHighlightColor: UIColor(rgb: 0xffc738), accentTextColor: UIColor(rgb: 0x007ee5), accentControlColor: UIColor(rgb: 0x007ee5), + accentControlDisabledColor: UIColor(rgb: 0x525252, alpha: 0.6), mediaActiveControlColor: UIColor(rgb: 0x007ee5), mediaInactiveControlColor: UIColor(rgb: 0xcacaca), mediaControlInnerBackgroundColor: UIColor(rgb: 0xffffff), @@ -563,7 +568,7 @@ public func makeDefaultDayPresentationTheme(extendingThemeReference: Presentatio fileDescriptionColor: UIColor(rgb: 0x999999), fileDurationColor: UIColor(rgb: 0x525252, alpha: 0.6), mediaPlaceholderColor: UIColor(rgb: 0xffffff).withMultipliedBrightnessBy(0.95), - polls: PresentationThemeChatBubblePolls(radioButton: UIColor(rgb: 0xc8c7cc), radioProgress: UIColor(rgb: 0x007ee5), highlight: UIColor(rgb: 0x007ee5, alpha: 0.12), separator: UIColor(rgb: 0xc8c7cc), bar: UIColor(rgb: 0x007ee5)), + polls: PresentationThemeChatBubblePolls(radioButton: UIColor(rgb: 0xc8c7cc), radioProgress: UIColor(rgb: 0x007ee5), highlight: UIColor(rgb: 0x007ee5, alpha: 0.12), separator: UIColor(rgb: 0xc8c7cc), bar: UIColor(rgb: 0x007ee5), barIconForeground: .white, barPositive: UIColor(rgb: 0x00A700), barNegative: UIColor(rgb: 0xFE3824)), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: serviceBackgroundColor, withoutWallpaper: UIColor(rgb: 0xffffff, alpha: 0.8)), actionButtonsStrokeColor: PresentationThemeVariableColor(withWallpaper: .clear, withoutWallpaper: UIColor(rgb: 0x007ee5)), actionButtonsTextColor: PresentationThemeVariableColor(withWallpaper: UIColor(rgb: 0xffffff), withoutWallpaper: UIColor(rgb: 0x007ee5)), @@ -579,6 +584,7 @@ public func makeDefaultDayPresentationTheme(extendingThemeReference: Presentatio textHighlightColor: UIColor(rgb: 0xffc738), accentTextColor: UIColor(rgb: 0xffffff), accentControlColor: UIColor(rgb: 0xffffff), + accentControlDisabledColor: UIColor(rgb: 0xffffff).withAlphaComponent(0.5), mediaActiveControlColor: UIColor(rgb: 0xffffff), mediaInactiveControlColor: UIColor(rgb: 0xffffff, alpha: 0.65), mediaControlInnerBackgroundColor: .clear, @@ -587,7 +593,7 @@ public func makeDefaultDayPresentationTheme(extendingThemeReference: Presentatio fileDescriptionColor: UIColor(rgb: 0xffffff, alpha: 0.65), fileDurationColor: UIColor(rgb: 0xffffff, alpha: 0.65), mediaPlaceholderColor: UIColor(rgb: 0x0077d9), - polls: PresentationThemeChatBubblePolls(radioButton: UIColor(rgb: 0xffffff, alpha: 0.65), radioProgress: UIColor(rgb: 0xffffff), highlight: UIColor(rgb: 0xffffff, alpha: 0.12), separator: UIColor(rgb: 0xffffff, alpha: 0.65), bar: UIColor(rgb: 0xffffff)), + polls: PresentationThemeChatBubblePolls(radioButton: UIColor(rgb: 0xffffff, alpha: 0.65), radioProgress: UIColor(rgb: 0xffffff), highlight: UIColor(rgb: 0xffffff, alpha: 0.12), separator: UIColor(rgb: 0xffffff, alpha: 0.65), bar: UIColor(rgb: 0xffffff), barIconForeground: .clear, barPositive: UIColor(rgb: 0xffffff), barNegative: UIColor(rgb: 0xffffff)), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: serviceBackgroundColor, withoutWallpaper: UIColor(rgb: 0xffffff, alpha: 0.8)), actionButtonsStrokeColor: PresentationThemeVariableColor(withWallpaper: .clear, withoutWallpaper: UIColor(rgb: 0x007ee5)), actionButtonsTextColor: PresentationThemeVariableColor(withWallpaper: UIColor(rgb: 0xffffff), withoutWallpaper: UIColor(rgb: 0x007ee5)), diff --git a/submodules/TelegramPresentationData/Sources/PresentationData.swift b/submodules/TelegramPresentationData/Sources/PresentationData.swift index c8e271e5cd..f33079da79 100644 --- a/submodules/TelegramPresentationData/Sources/PresentationData.swift +++ b/submodules/TelegramPresentationData/Sources/PresentationData.swift @@ -48,12 +48,25 @@ public enum PresentationDateFormat { case dayFirst } +public struct PresentationChatBubbleCorners: Equatable, Hashable { + public var mainRadius: CGFloat + public var auxiliaryRadius: CGFloat + public var mergeBubbleCorners: Bool + + public init(mainRadius: CGFloat, auxiliaryRadius: CGFloat, mergeBubbleCorners: Bool) { + self.mainRadius = mainRadius + self.auxiliaryRadius = auxiliaryRadius + self.mergeBubbleCorners = mergeBubbleCorners + } +} + public final class PresentationData: Equatable { public let strings: PresentationStrings public let theme: PresentationTheme public let autoNightModeTriggered: Bool public let chatWallpaper: TelegramWallpaper public let chatFontSize: PresentationFontSize + public let chatBubbleCorners: PresentationChatBubbleCorners public let listsFontSize: PresentationFontSize public let dateTimeFormat: PresentationDateTimeFormat public let nameDisplayOrder: PresentationPersonNameOrder @@ -61,12 +74,13 @@ public final class PresentationData: Equatable { public let disableAnimations: Bool public let largeEmoji: Bool - public init(strings: PresentationStrings, theme: PresentationTheme, autoNightModeTriggered: Bool, chatWallpaper: TelegramWallpaper, chatFontSize: PresentationFontSize, listsFontSize: PresentationFontSize, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, nameSortOrder: PresentationPersonNameOrder, disableAnimations: Bool, largeEmoji: Bool) { + public init(strings: PresentationStrings, theme: PresentationTheme, autoNightModeTriggered: Bool, chatWallpaper: TelegramWallpaper, chatFontSize: PresentationFontSize, chatBubbleCorners: PresentationChatBubbleCorners, listsFontSize: PresentationFontSize, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, nameSortOrder: PresentationPersonNameOrder, disableAnimations: Bool, largeEmoji: Bool) { self.strings = strings self.theme = theme self.autoNightModeTriggered = autoNightModeTriggered self.chatWallpaper = chatWallpaper self.chatFontSize = chatFontSize + self.chatBubbleCorners = chatBubbleCorners self.listsFontSize = listsFontSize self.dateTimeFormat = dateTimeFormat self.nameDisplayOrder = nameDisplayOrder @@ -76,7 +90,7 @@ public final class PresentationData: Equatable { } public static func ==(lhs: PresentationData, rhs: PresentationData) -> Bool { - return lhs.strings === rhs.strings && lhs.theme === rhs.theme && lhs.autoNightModeTriggered == rhs.autoNightModeTriggered && lhs.chatWallpaper == rhs.chatWallpaper && lhs.chatFontSize == rhs.chatFontSize && lhs.listsFontSize == rhs.listsFontSize && lhs.dateTimeFormat == rhs.dateTimeFormat && lhs.disableAnimations == rhs.disableAnimations && lhs.largeEmoji == rhs.largeEmoji + return lhs.strings === rhs.strings && lhs.theme === rhs.theme && lhs.autoNightModeTriggered == rhs.autoNightModeTriggered && lhs.chatWallpaper == rhs.chatWallpaper && lhs.chatFontSize == rhs.chatFontSize && lhs.chatBubbleCorners == rhs.chatBubbleCorners && lhs.listsFontSize == rhs.listsFontSize && lhs.dateTimeFormat == rhs.dateTimeFormat && lhs.disableAnimations == rhs.disableAnimations && lhs.largeEmoji == rhs.largeEmoji } } @@ -268,7 +282,9 @@ public func currentPresentationDataAndSettings(accountManager: AccountManager, s let (chatFontSize, listsFontSize) = resolveFontSize(settings: themeSettings) - return InitialPresentationDataAndSettings(presentationData: PresentationData(strings: stringsValue, theme: theme, autoNightModeTriggered: autoNightModeTriggered, chatWallpaper: effectiveChatWallpaper, chatFontSize: chatFontSize, listsFontSize: listsFontSize, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, nameSortOrder: nameSortOrder, disableAnimations: themeSettings.disableAnimations, largeEmoji: themeSettings.largeEmoji), automaticMediaDownloadSettings: automaticMediaDownloadSettings, autodownloadSettings: autodownloadSettings, callListSettings: callListSettings, inAppNotificationSettings: inAppNotificationSettings, mediaInputSettings: mediaInputSettings, experimentalUISettings: experimentalUISettings) + let chatBubbleCorners = PresentationChatBubbleCorners(mainRadius: CGFloat(themeSettings.chatBubbleSettings.mainRadius), auxiliaryRadius: CGFloat(themeSettings.chatBubbleSettings.auxiliaryRadius), mergeBubbleCorners: themeSettings.chatBubbleSettings.mergeBubbleCorners) + + return InitialPresentationDataAndSettings(presentationData: PresentationData(strings: stringsValue, theme: theme, autoNightModeTriggered: autoNightModeTriggered, chatWallpaper: effectiveChatWallpaper, chatFontSize: chatFontSize, chatBubbleCorners: chatBubbleCorners, listsFontSize: listsFontSize, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, nameSortOrder: nameSortOrder, disableAnimations: themeSettings.disableAnimations, largeEmoji: themeSettings.largeEmoji), automaticMediaDownloadSettings: automaticMediaDownloadSettings, autodownloadSettings: autodownloadSettings, callListSettings: callListSettings, inAppNotificationSettings: inAppNotificationSettings, mediaInputSettings: mediaInputSettings, experimentalUISettings: experimentalUISettings) } } @@ -601,7 +617,9 @@ public func updatedPresentationData(accountManager: AccountManager, applicationI let (chatFontSize, listsFontSize) = resolveFontSize(settings: themeSettings) - return PresentationData(strings: stringsValue, theme: themeValue, autoNightModeTriggered: autoNightModeTriggered, chatWallpaper: effectiveChatWallpaper, chatFontSize: chatFontSize, listsFontSize: listsFontSize, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, nameSortOrder: nameSortOrder, disableAnimations: themeSettings.disableAnimations, largeEmoji: themeSettings.largeEmoji) + let chatBubbleCorners = PresentationChatBubbleCorners(mainRadius: CGFloat(themeSettings.chatBubbleSettings.mainRadius), auxiliaryRadius: CGFloat(themeSettings.chatBubbleSettings.auxiliaryRadius), mergeBubbleCorners: themeSettings.chatBubbleSettings.mergeBubbleCorners) + + return PresentationData(strings: stringsValue, theme: themeValue, autoNightModeTriggered: autoNightModeTriggered, chatWallpaper: effectiveChatWallpaper, chatFontSize: chatFontSize, chatBubbleCorners: chatBubbleCorners, listsFontSize: listsFontSize, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, nameSortOrder: nameSortOrder, disableAnimations: themeSettings.disableAnimations, largeEmoji: themeSettings.largeEmoji) } } else { return .complete() @@ -634,15 +652,21 @@ public func defaultPresentationData() -> PresentationData { let (chatFontSize, listsFontSize) = resolveFontSize(settings: themeSettings) - return PresentationData(strings: defaultPresentationStrings, theme: defaultPresentationTheme, autoNightModeTriggered: false, chatWallpaper: .builtin(WallpaperSettings()), chatFontSize: chatFontSize, listsFontSize: listsFontSize, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, nameSortOrder: nameSortOrder, disableAnimations: themeSettings.disableAnimations, largeEmoji: themeSettings.largeEmoji) + let chatBubbleCorners = PresentationChatBubbleCorners(mainRadius: CGFloat(themeSettings.chatBubbleSettings.mainRadius), auxiliaryRadius: CGFloat(themeSettings.chatBubbleSettings.auxiliaryRadius), mergeBubbleCorners: themeSettings.chatBubbleSettings.mergeBubbleCorners) + + return PresentationData(strings: defaultPresentationStrings, theme: defaultPresentationTheme, autoNightModeTriggered: false, chatWallpaper: .builtin(WallpaperSettings()), chatFontSize: chatFontSize, chatBubbleCorners: chatBubbleCorners, listsFontSize: listsFontSize, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, nameSortOrder: nameSortOrder, disableAnimations: themeSettings.disableAnimations, largeEmoji: themeSettings.largeEmoji) } public extension PresentationData { func withFontSizes(chatFontSize: PresentationFontSize, listsFontSize: PresentationFontSize) -> PresentationData { - return PresentationData(strings: self.strings, theme: self.theme, autoNightModeTriggered: self.autoNightModeTriggered, chatWallpaper: self.chatWallpaper, chatFontSize: chatFontSize, listsFontSize: listsFontSize, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, nameSortOrder: self.nameSortOrder, disableAnimations: self.disableAnimations, largeEmoji: self.largeEmoji) + return PresentationData(strings: self.strings, theme: self.theme, autoNightModeTriggered: self.autoNightModeTriggered, chatWallpaper: self.chatWallpaper, chatFontSize: chatFontSize, chatBubbleCorners: self.chatBubbleCorners, listsFontSize: listsFontSize, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, nameSortOrder: self.nameSortOrder, disableAnimations: self.disableAnimations, largeEmoji: self.largeEmoji) + } + + func withChatBubbleCorners(_ chatBubbleCorners: PresentationChatBubbleCorners) -> PresentationData { + return PresentationData(strings: self.strings, theme: self.theme, autoNightModeTriggered: self.autoNightModeTriggered, chatWallpaper: self.chatWallpaper, chatFontSize: self.chatFontSize, chatBubbleCorners: chatBubbleCorners, listsFontSize: self.listsFontSize, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, nameSortOrder: self.nameSortOrder, disableAnimations: self.disableAnimations, largeEmoji: self.largeEmoji) } func withStrings(_ strings: PresentationStrings) -> PresentationData { - return PresentationData(strings: strings, theme: self.theme, autoNightModeTriggered: self.autoNightModeTriggered, chatWallpaper: self.chatWallpaper, chatFontSize: self.chatFontSize, listsFontSize: self.listsFontSize, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, nameSortOrder: self.nameSortOrder, disableAnimations: self.disableAnimations, largeEmoji: self.largeEmoji) + return PresentationData(strings: strings, theme: self.theme, autoNightModeTriggered: self.autoNightModeTriggered, chatWallpaper: self.chatWallpaper, chatFontSize: self.chatFontSize, chatBubbleCorners: chatBubbleCorners, listsFontSize: self.listsFontSize, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, nameSortOrder: self.nameSortOrder, disableAnimations: self.disableAnimations, largeEmoji: self.largeEmoji) } } diff --git a/submodules/TelegramPresentationData/Sources/PresentationStrings.swift b/submodules/TelegramPresentationData/Sources/PresentationStrings.swift index bf9ee62939..05eee7ac5d 100644 --- a/submodules/TelegramPresentationData/Sources/PresentationStrings.swift +++ b/submodules/TelegramPresentationData/Sources/PresentationStrings.swift @@ -309,4417 +309,4464 @@ public final class PresentationStrings: Equatable { public func Wallet_Info_TransactionBlockchainFee(_ _0: String) -> (String, [(Int, NSRange)]) { return formatWithArgumentRanges(self._s[115]!, self._r[115]!, [_0]) } - public var Conversation_StopLiveLocation: String { return self._s[117]! } - public var Channel_AdminLogFilter_EventsAll: String { return self._s[118]! } - public var GroupInfo_InviteLink_CopyAlert_Success: String { return self._s[120]! } - public var Username_LinkCopied: String { return self._s[122]! } - public var GroupRemoved_Title: String { return self._s[123]! } - public var SecretVideo_Title: String { return self._s[124]! } + public var Conversation_StopLiveLocation: String { return self._s[118]! } + public var Channel_AdminLogFilter_EventsAll: String { return self._s[119]! } + public var GroupInfo_InviteLink_CopyAlert_Success: String { return self._s[121]! } + public var Username_LinkCopied: String { return self._s[123]! } + public var GroupRemoved_Title: String { return self._s[124]! } + public var SecretVideo_Title: String { return self._s[125]! } public func PUSH_PINNED_VIDEO(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[125]!, self._r[125]!, [_1]) + return formatWithArgumentRanges(self._s[126]!, self._r[126]!, [_1]) } - public var AccessDenied_PhotosAndVideos: String { return self._s[126]! } - public var Appearance_ThemePreview_Chat_1_Text: String { return self._s[127]! } + public var AccessDenied_PhotosAndVideos: String { return self._s[127]! } + public var Appearance_ThemePreview_Chat_1_Text: String { return self._s[128]! } public func PUSH_CHANNEL_MESSAGE_GEOLIVE(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[129]!, self._r[129]!, [_1]) + return formatWithArgumentRanges(self._s[130]!, self._r[130]!, [_1]) } - public var Map_OpenInGoogleMaps: String { return self._s[131]! } + public var Map_OpenInGoogleMaps: String { return self._s[132]! } public func Time_PreciseDate_m12(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[132]!, self._r[132]!, [_1, _2, _3]) + return formatWithArgumentRanges(self._s[133]!, self._r[133]!, [_1, _2, _3]) } public func Channel_AdminLog_MessageKickedNameUsername(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[133]!, self._r[133]!, [_1, _2]) + return formatWithArgumentRanges(self._s[134]!, self._r[134]!, [_1, _2]) } - public var Call_StatusRinging: String { return self._s[134]! } - public var SettingsSearch_Synonyms_EditProfile_Username: String { return self._s[135]! } - public var Group_Username_InvalidStartsWithNumber: String { return self._s[136]! } - public var UserInfo_NotificationsEnabled: String { return self._s[137]! } - public var Map_Search: String { return self._s[138]! } - public var ClearCache_StorageFree: String { return self._s[140]! } - public var Login_TermsOfServiceHeader: String { return self._s[141]! } + public var Call_StatusRinging: String { return self._s[135]! } + public var SettingsSearch_Synonyms_EditProfile_Username: String { return self._s[136]! } + public var Group_Username_InvalidStartsWithNumber: String { return self._s[137]! } + public var UserInfo_NotificationsEnabled: String { return self._s[138]! } + public var Map_Search: String { return self._s[139]! } + public var ClearCache_StorageFree: String { return self._s[141]! } + public var Login_TermsOfServiceHeader: String { return self._s[142]! } public func Notification_PinnedVideoMessage(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[142]!, self._r[142]!, [_0]) - } - public func Channel_AdminLog_MessageToggleSignaturesOn(_ _0: String) -> (String, [(Int, NSRange)]) { return formatWithArgumentRanges(self._s[143]!, self._r[143]!, [_0]) } - public var Wallet_Sent_Title: String { return self._s[144]! } - public var TwoStepAuth_SetupPasswordConfirmPassword: String { return self._s[145]! } - public var Weekday_Today: String { return self._s[146]! } + public func Channel_AdminLog_MessageToggleSignaturesOn(_ _0: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[145]!, self._r[145]!, [_0]) + } + public var Wallet_Sent_Title: String { return self._s[146]! } + public var TwoStepAuth_SetupPasswordConfirmPassword: String { return self._s[147]! } + public var Weekday_Today: String { return self._s[148]! } public func InstantPage_AuthorAndDateTitle(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[148]!, self._r[148]!, [_1, _2]) + return formatWithArgumentRanges(self._s[150]!, self._r[150]!, [_1, _2]) } public func Conversation_MessageDialogRetryAll(_ _1: Int) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[149]!, self._r[149]!, ["\(_1)"]) + return formatWithArgumentRanges(self._s[151]!, self._r[151]!, ["\(_1)"]) } - public var Notification_PassportValuePersonalDetails: String { return self._s[151]! } - public var Channel_AdminLog_MessagePreviousLink: String { return self._s[152]! } - public var ChangePhoneNumberNumber_NewNumber: String { return self._s[153]! } - public var ApplyLanguage_LanguageNotSupportedError: String { return self._s[154]! } - public var TwoStepAuth_ChangePasswordDescription: String { return self._s[155]! } - public var PhotoEditor_BlurToolLinear: String { return self._s[156]! } - public var Contacts_PermissionsAllowInSettings: String { return self._s[157]! } - public var Weekday_ShortMonday: String { return self._s[158]! } - public var Cache_KeepMedia: String { return self._s[159]! } - public var Passport_FieldIdentitySelfieHelp: String { return self._s[160]! } + public var Notification_PassportValuePersonalDetails: String { return self._s[153]! } + public var Channel_AdminLog_MessagePreviousLink: String { return self._s[154]! } + public var ChangePhoneNumberNumber_NewNumber: String { return self._s[155]! } + public var ApplyLanguage_LanguageNotSupportedError: String { return self._s[156]! } + public var TwoStepAuth_ChangePasswordDescription: String { return self._s[157]! } + public var PhotoEditor_BlurToolLinear: String { return self._s[158]! } + public var Contacts_PermissionsAllowInSettings: String { return self._s[159]! } + public var Weekday_ShortMonday: String { return self._s[160]! } + public var Cache_KeepMedia: String { return self._s[161]! } + public var Passport_FieldIdentitySelfieHelp: String { return self._s[162]! } public func PUSH_PINNED_STICKER(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[161]!, self._r[161]!, [_1, _2]) + return formatWithArgumentRanges(self._s[163]!, self._r[163]!, [_1, _2]) } public func Chat_SlowmodeTooltip(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[162]!, self._r[162]!, [_0]) + return formatWithArgumentRanges(self._s[164]!, self._r[164]!, [_0]) } - public var Wallet_Receive_ShareUrlInfo: String { return self._s[163]! } - public var Conversation_ClousStorageInfo_Description4: String { return self._s[164]! } - public var Wallet_RestoreFailed_Title: String { return self._s[165]! } - public var Passport_Language_ru: String { return self._s[166]! } + public var Wallet_Receive_ShareUrlInfo: String { return self._s[165]! } + public var Conversation_ClousStorageInfo_Description4: String { return self._s[166]! } + public var Wallet_RestoreFailed_Title: String { return self._s[167]! } + public var Passport_Language_ru: String { return self._s[168]! } public func Notification_CreatedChatWithTitle(_ _0: String, _ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[167]!, self._r[167]!, [_0, _1]) + return formatWithArgumentRanges(self._s[169]!, self._r[169]!, [_0, _1]) } - public var WallpaperPreview_PatternIntensity: String { return self._s[168]! } - public var WebBrowser_InAppSafari: String { return self._s[171]! } - public var TwoStepAuth_RecoveryUnavailable: String { return self._s[172]! } - public var EnterPasscode_TouchId: String { return self._s[173]! } - public var PhotoEditor_QualityVeryHigh: String { return self._s[176]! } - public var Checkout_NewCard_SaveInfo: String { return self._s[178]! } - public var Gif_NoGifsPlaceholder: String { return self._s[180]! } + public var WallpaperPreview_PatternIntensity: String { return self._s[170]! } + public var WebBrowser_InAppSafari: String { return self._s[173]! } + public var TwoStepAuth_RecoveryUnavailable: String { return self._s[174]! } + public var EnterPasscode_TouchId: String { return self._s[175]! } + public var PhotoEditor_QualityVeryHigh: String { return self._s[178]! } + public var Checkout_NewCard_SaveInfo: String { return self._s[180]! } + public var Gif_NoGifsPlaceholder: String { return self._s[182]! } public func Notification_InvitedMultiple(_ _0: String, _ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[182]!, self._r[182]!, [_0, _1]) + return formatWithArgumentRanges(self._s[184]!, self._r[184]!, [_0, _1]) } - public var ChatSettings_AutoDownloadEnabled: String { return self._s[183]! } - public var NetworkUsageSettings_BytesSent: String { return self._s[184]! } - public var Checkout_PasswordEntry_Pay: String { return self._s[185]! } - public var AuthSessions_TerminateSession: String { return self._s[186]! } - public var Message_File: String { return self._s[187]! } - public var MediaPicker_VideoMuteDescription: String { return self._s[188]! } - public var SocksProxySetup_ProxyStatusConnected: String { return self._s[189]! } - public var TwoStepAuth_RecoveryCode: String { return self._s[190]! } - public var EnterPasscode_EnterCurrentPasscode: String { return self._s[191]! } + public var ChatSettings_AutoDownloadEnabled: String { return self._s[185]! } + public var NetworkUsageSettings_BytesSent: String { return self._s[186]! } + public var Checkout_PasswordEntry_Pay: String { return self._s[187]! } + public var AuthSessions_TerminateSession: String { return self._s[188]! } + public var Message_File: String { return self._s[189]! } + public var MediaPicker_VideoMuteDescription: String { return self._s[190]! } + public var SocksProxySetup_ProxyStatusConnected: String { return self._s[191]! } + public var TwoStepAuth_RecoveryCode: String { return self._s[192]! } + public var EnterPasscode_EnterCurrentPasscode: String { return self._s[193]! } public func TwoStepAuth_EnterPasswordHint(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[192]!, self._r[192]!, [_0]) + return formatWithArgumentRanges(self._s[194]!, self._r[194]!, [_0]) } - public var Conversation_Moderate_Report: String { return self._s[194]! } - public var TwoStepAuth_EmailInvalid: String { return self._s[195]! } - public var Passport_Language_ms: String { return self._s[196]! } - public var Channel_Edit_AboutItem: String { return self._s[198]! } - public var DialogList_SearchSectionGlobal: String { return self._s[202]! } - public var AttachmentMenu_WebSearch: String { return self._s[203]! } - public var PasscodeSettings_TurnPasscodeOn: String { return self._s[204]! } - public var Channel_BanUser_Title: String { return self._s[205]! } - public var WallpaperPreview_SwipeTopText: String { return self._s[206]! } - public var ChatList_DeleteSavedMessagesConfirmationText: String { return self._s[207]! } - public var ArchivedChats_IntroText2: String { return self._s[208]! } - public var Conversation_OpenBotLinkTitle: String { return self._s[209]! } - public var ChatSearch_SearchPlaceholder: String { return self._s[211]! } - public var Notification_Exceptions_DeleteAll: String { return self._s[212]! } - public var Passport_FieldAddressTranslationHelp: String { return self._s[213]! } - public var NotificationsSound_Aurora: String { return self._s[214]! } + public var Conversation_Moderate_Report: String { return self._s[196]! } + public var TwoStepAuth_EmailInvalid: String { return self._s[197]! } + public var Passport_Language_ms: String { return self._s[198]! } + public var Channel_Edit_AboutItem: String { return self._s[200]! } + public var DialogList_SearchSectionGlobal: String { return self._s[204]! } + public var AttachmentMenu_WebSearch: String { return self._s[205]! } + public var PasscodeSettings_TurnPasscodeOn: String { return self._s[206]! } + public var Channel_BanUser_Title: String { return self._s[207]! } + public var WallpaperPreview_SwipeTopText: String { return self._s[208]! } + public var ChatList_DeleteSavedMessagesConfirmationText: String { return self._s[209]! } + public var ArchivedChats_IntroText2: String { return self._s[210]! } + public var Conversation_OpenBotLinkTitle: String { return self._s[211]! } + public var ChatSearch_SearchPlaceholder: String { return self._s[213]! } + public var Notification_Exceptions_DeleteAll: String { return self._s[214]! } + public var Passport_FieldAddressTranslationHelp: String { return self._s[215]! } + public var NotificationsSound_Aurora: String { return self._s[216]! } public func Channel_AdminLog_MessageTransferedNameUsername(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[215]!, self._r[215]!, [_1, _2]) + return formatWithArgumentRanges(self._s[217]!, self._r[217]!, [_1, _2]) } public func FileSize_GB(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[216]!, self._r[216]!, [_0]) + return formatWithArgumentRanges(self._s[218]!, self._r[218]!, [_0]) } - public var AuthSessions_LoggedInWithTelegram: String { return self._s[219]! } + public var AuthSessions_LoggedInWithTelegram: String { return self._s[221]! } public func Privacy_GroupsAndChannels_InviteToGroupError(_ _0: String, _ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[220]!, self._r[220]!, [_0, _1]) + return formatWithArgumentRanges(self._s[222]!, self._r[222]!, [_0, _1]) } - public var Passport_PasswordNext: String { return self._s[221]! } - public var Bot_GroupStatusReadsHistory: String { return self._s[222]! } - public var EmptyGroupInfo_Line2: String { return self._s[223]! } - public var VoiceOver_Chat_SeenByRecipients: String { return self._s[224]! } - public var Settings_FAQ_Intro: String { return self._s[227]! } - public var PrivacySettings_PasscodeAndTouchId: String { return self._s[229]! } - public var FeaturedStickerPacks_Title: String { return self._s[230]! } - public var TwoStepAuth_PasswordRemoveConfirmation: String { return self._s[232]! } - public var Username_Title: String { return self._s[233]! } + public var Passport_PasswordNext: String { return self._s[223]! } + public var Bot_GroupStatusReadsHistory: String { return self._s[224]! } + public var EmptyGroupInfo_Line2: String { return self._s[225]! } + public var VoiceOver_Chat_SeenByRecipients: String { return self._s[226]! } + public var Settings_FAQ_Intro: String { return self._s[229]! } + public var PrivacySettings_PasscodeAndTouchId: String { return self._s[231]! } + public var FeaturedStickerPacks_Title: String { return self._s[232]! } + public var TwoStepAuth_PasswordRemoveConfirmation: String { return self._s[234]! } + public var Username_Title: String { return self._s[235]! } public func Message_StickerText(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[234]!, self._r[234]!, [_0]) + return formatWithArgumentRanges(self._s[236]!, self._r[236]!, [_0]) } - public var PasscodeSettings_AlphanumericCode: String { return self._s[235]! } - public var Localization_LanguageOther: String { return self._s[236]! } - public var Stickers_SuggestStickers: String { return self._s[237]! } + public var PasscodeSettings_AlphanumericCode: String { return self._s[237]! } + public var Localization_LanguageOther: String { return self._s[238]! } + public var Stickers_SuggestStickers: String { return self._s[239]! } public func Channel_AdminLog_MessageRemovedGroupUsername(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[238]!, self._r[238]!, [_0]) + return formatWithArgumentRanges(self._s[240]!, self._r[240]!, [_0]) } - public var NotificationSettings_ShowNotificationsFromAccountsSection: String { return self._s[239]! } - public var Channel_AdminLogFilter_EventsAdmins: String { return self._s[240]! } - public var Conversation_DefaultRestrictedStickers: String { return self._s[241]! } + public var NotificationSettings_ShowNotificationsFromAccountsSection: String { return self._s[241]! } + public var Channel_AdminLogFilter_EventsAdmins: String { return self._s[242]! } + public var Conversation_DefaultRestrictedStickers: String { return self._s[243]! } public func Notification_PinnedDeletedMessage(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[242]!, self._r[242]!, [_0]) + return formatWithArgumentRanges(self._s[244]!, self._r[244]!, [_0]) } - public var Wallet_TransactionInfo_CopyAddress: String { return self._s[244]! } - public var Group_UpgradeConfirmation: String { return self._s[245]! } - public var DialogList_Unpin: String { return self._s[246]! } - public var Passport_Identity_DateOfBirth: String { return self._s[247]! } - public var Month_ShortOctober: String { return self._s[248]! } - public var SettingsSearch_Synonyms_Privacy_Data_ContactsSync: String { return self._s[249]! } - public var TwoFactorSetup_Done_Text: String { return self._s[250]! } - public var Notification_CallCanceledShort: String { return self._s[251]! } - public var Passport_Phone_Help: String { return self._s[252]! } - public var Passport_Language_az: String { return self._s[254]! } - public var CreatePoll_TextPlaceholder: String { return self._s[256]! } - public var VoiceOver_Chat_AnonymousPoll: String { return self._s[257]! } - public var Passport_Identity_DocumentNumber: String { return self._s[258]! } - public var PhotoEditor_CurvesRed: String { return self._s[259]! } - public var PhoneNumberHelp_Alert: String { return self._s[261]! } - public var SocksProxySetup_Port: String { return self._s[262]! } - public var Checkout_PayNone: String { return self._s[263]! } - public var AutoDownloadSettings_WiFi: String { return self._s[264]! } - public var GroupInfo_GroupType: String { return self._s[265]! } - public var StickerSettings_ContextHide: String { return self._s[266]! } - public var Passport_Address_OneOfTypeTemporaryRegistration: String { return self._s[267]! } - public var Group_Setup_HistoryTitle: String { return self._s[269]! } - public var Passport_Identity_FilesUploadNew: String { return self._s[270]! } - public var PasscodeSettings_AutoLock: String { return self._s[271]! } - public var Passport_Title: String { return self._s[272]! } - public var VoiceOver_Chat_ContactPhoneNumber: String { return self._s[273]! } - public var Channel_AdminLogFilter_EventsNewSubscribers: String { return self._s[274]! } - public var GroupPermission_NoSendGifs: String { return self._s[275]! } - public var PrivacySettings_PasscodeOn: String { return self._s[276]! } + public var Wallet_TransactionInfo_CopyAddress: String { return self._s[246]! } + public var Group_UpgradeConfirmation: String { return self._s[247]! } + public var DialogList_Unpin: String { return self._s[248]! } + public var Passport_Identity_DateOfBirth: String { return self._s[249]! } + public var Month_ShortOctober: String { return self._s[250]! } + public var SettingsSearch_Synonyms_Privacy_Data_ContactsSync: String { return self._s[251]! } + public var TwoFactorSetup_Done_Text: String { return self._s[252]! } + public var Notification_CallCanceledShort: String { return self._s[253]! } + public var Conversation_StopQuiz: String { return self._s[254]! } + public var Passport_Phone_Help: String { return self._s[255]! } + public var Passport_Language_az: String { return self._s[257]! } + public var CreatePoll_TextPlaceholder: String { return self._s[259]! } + public var VoiceOver_Chat_AnonymousPoll: String { return self._s[260]! } + public var Passport_Identity_DocumentNumber: String { return self._s[261]! } + public var PhotoEditor_CurvesRed: String { return self._s[262]! } + public var PhoneNumberHelp_Alert: String { return self._s[264]! } + public var SocksProxySetup_Port: String { return self._s[265]! } + public var Checkout_PayNone: String { return self._s[266]! } + public var AutoDownloadSettings_WiFi: String { return self._s[267]! } + public var GroupInfo_GroupType: String { return self._s[268]! } + public var StickerSettings_ContextHide: String { return self._s[269]! } + public var Passport_Address_OneOfTypeTemporaryRegistration: String { return self._s[270]! } + public var Group_Setup_HistoryTitle: String { return self._s[272]! } + public var Passport_Identity_FilesUploadNew: String { return self._s[273]! } + public var PasscodeSettings_AutoLock: String { return self._s[274]! } + public var Passport_Title: String { return self._s[275]! } + public var VoiceOver_Chat_ContactPhoneNumber: String { return self._s[276]! } + public var Channel_AdminLogFilter_EventsNewSubscribers: String { return self._s[277]! } + public var GroupPermission_NoSendGifs: String { return self._s[278]! } + public var PrivacySettings_PasscodeOn: String { return self._s[279]! } public func Conversation_ScheduleMessage_SendTomorrow(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[277]!, self._r[277]!, [_0]) + return formatWithArgumentRanges(self._s[280]!, self._r[280]!, [_0]) } - public var State_WaitingForNetwork: String { return self._s[280]! } + public var State_WaitingForNetwork: String { return self._s[283]! } public func Notification_Invited(_ _0: String, _ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[281]!, self._r[281]!, [_0, _1]) + return formatWithArgumentRanges(self._s[284]!, self._r[284]!, [_0, _1]) } - public var Calls_NotNow: String { return self._s[283]! } + public var Calls_NotNow: String { return self._s[286]! } public func Channel_DiscussionGroup_HeaderSet(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[284]!, self._r[284]!, [_0]) + return formatWithArgumentRanges(self._s[287]!, self._r[287]!, [_0]) } - public var UserInfo_SendMessage: String { return self._s[285]! } - public var TwoStepAuth_PasswordSet: String { return self._s[286]! } - public var Passport_DeleteDocument: String { return self._s[287]! } - public var SocksProxySetup_AddProxyTitle: String { return self._s[288]! } + public var UserInfo_SendMessage: String { return self._s[288]! } + public var TwoStepAuth_PasswordSet: String { return self._s[289]! } + public var Passport_DeleteDocument: String { return self._s[290]! } + public var SocksProxySetup_AddProxyTitle: String { return self._s[291]! } public func PUSH_MESSAGE_VIDEO(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[289]!, self._r[289]!, [_1]) + return formatWithArgumentRanges(self._s[292]!, self._r[292]!, [_1]) } - public var AuthSessions_AddedDeviceTitle: String { return self._s[290]! } - public var GroupRemoved_Remove: String { return self._s[291]! } - public var Passport_FieldIdentity: String { return self._s[292]! } - public var Group_Setup_TypePrivateHelp: String { return self._s[293]! } - public var Conversation_Processing: String { return self._s[296]! } - public var Wallet_Settings_BackupWallet: String { return self._s[298]! } - public var ChatSettings_AutoPlayAnimations: String { return self._s[299]! } - public var AuthSessions_LogOutApplicationsHelp: String { return self._s[302]! } - public var Month_GenFebruary: String { return self._s[303]! } - public var Wallet_Send_NetworkErrorTitle: String { return self._s[304]! } + public var AuthSessions_AddedDeviceTitle: String { return self._s[293]! } + public var GroupRemoved_Remove: String { return self._s[294]! } + public var Passport_FieldIdentity: String { return self._s[295]! } + public var Group_Setup_TypePrivateHelp: String { return self._s[296]! } + public var Conversation_Processing: String { return self._s[299]! } + public var Wallet_Settings_BackupWallet: String { return self._s[301]! } + public var ChatSettings_AutoPlayAnimations: String { return self._s[302]! } + public var AuthSessions_LogOutApplicationsHelp: String { return self._s[305]! } + public var Forward_ErrorPublicQuizDisabledInChannels: String { return self._s[306]! } + public var Month_GenFebruary: String { return self._s[307]! } + public var Wallet_Send_NetworkErrorTitle: String { return self._s[308]! } public func Login_InvalidPhoneEmailBody(_ _1: String, _ _2: String, _ _3: String, _ _4: String, _ _5: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[306]!, self._r[306]!, [_1, _2, _3, _4, _5]) + return formatWithArgumentRanges(self._s[310]!, self._r[310]!, [_1, _2, _3, _4, _5]) } - public var Passport_Identity_TypeIdentityCard: String { return self._s[307]! } - public var Wallet_Month_ShortJune: String { return self._s[309]! } - public var AutoDownloadSettings_DataUsageMedium: String { return self._s[310]! } - public var GroupInfo_AddParticipant: String { return self._s[311]! } - public var KeyCommand_SendMessage: String { return self._s[312]! } - public var VoiceOver_Chat_YourContact: String { return self._s[314]! } - public var Map_LiveLocationShowAll: String { return self._s[315]! } - public var WallpaperSearch_ColorOrange: String { return self._s[317]! } - public var Appearance_AppIconDefaultX: String { return self._s[318]! } - public var Checkout_Receipt_Title: String { return self._s[319]! } - public var Group_OwnershipTransfer_ErrorPrivacyRestricted: String { return self._s[320]! } - public var WallpaperPreview_PreviewTopText: String { return self._s[321]! } - public var Message_Contact: String { return self._s[322]! } - public var Call_StatusIncoming: String { return self._s[323]! } - public var Wallet_TransactionInfo_StorageFeeInfo: String { return self._s[324]! } + public var Passport_Identity_TypeIdentityCard: String { return self._s[311]! } + public var Wallet_Month_ShortJune: String { return self._s[313]! } + public var AutoDownloadSettings_DataUsageMedium: String { return self._s[314]! } + public var GroupInfo_AddParticipant: String { return self._s[315]! } + public var KeyCommand_SendMessage: String { return self._s[316]! } + public var VoiceOver_Chat_YourContact: String { return self._s[318]! } + public var Map_LiveLocationShowAll: String { return self._s[319]! } + public var WallpaperSearch_ColorOrange: String { return self._s[321]! } + public var Appearance_AppIconDefaultX: String { return self._s[322]! } + public var Checkout_Receipt_Title: String { return self._s[323]! } + public var Group_OwnershipTransfer_ErrorPrivacyRestricted: String { return self._s[324]! } + public var WallpaperPreview_PreviewTopText: String { return self._s[325]! } + public var Message_Contact: String { return self._s[326]! } + public var Call_StatusIncoming: String { return self._s[327]! } + public var Wallet_TransactionInfo_StorageFeeInfo: String { return self._s[328]! } public func Channel_AdminLog_MessageKickedName(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[325]!, self._r[325]!, [_1]) + return formatWithArgumentRanges(self._s[329]!, self._r[329]!, [_1]) } public func PUSH_ENCRYPTED_MESSAGE(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[327]!, self._r[327]!, [_1]) + return formatWithArgumentRanges(self._s[331]!, self._r[331]!, [_1]) } - public var VoiceOver_Media_PlaybackRate: String { return self._s[328]! } - public var Passport_FieldIdentityDetailsHelp: String { return self._s[329]! } - public var Conversation_ViewChannel: String { return self._s[330]! } + public var VoiceOver_Media_PlaybackRate: String { return self._s[332]! } + public var Passport_FieldIdentityDetailsHelp: String { return self._s[333]! } + public var Conversation_ViewChannel: String { return self._s[334]! } public func Time_TodayAt(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[331]!, self._r[331]!, [_0]) + return formatWithArgumentRanges(self._s[335]!, self._r[335]!, [_0]) } - public var Theme_Colors_Accent: String { return self._s[332]! } - public var Passport_Language_nl: String { return self._s[334]! } - public var Camera_Retake: String { return self._s[335]! } + public var Theme_Colors_Accent: String { return self._s[336]! } + public var Passport_Language_nl: String { return self._s[338]! } + public var Camera_Retake: String { return self._s[339]! } public func UserInfo_BlockActionTitle(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[336]!, self._r[336]!, [_0]) + return formatWithArgumentRanges(self._s[340]!, self._r[340]!, [_0]) } - public var AuthSessions_LogOutApplications: String { return self._s[337]! } - public var ApplyLanguage_ApplySuccess: String { return self._s[338]! } - public var Tour_Title6: String { return self._s[339]! } - public var Map_ChooseAPlace: String { return self._s[340]! } - public var CallSettings_Never: String { return self._s[342]! } + public var AuthSessions_LogOutApplications: String { return self._s[341]! } + public var ApplyLanguage_ApplySuccess: String { return self._s[342]! } + public var Tour_Title6: String { return self._s[343]! } + public var Map_ChooseAPlace: String { return self._s[344]! } + public var CallSettings_Never: String { return self._s[346]! } public func Notification_ChangedGroupPhoto(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[343]!, self._r[343]!, [_0]) - } - public var ChannelRemoved_RemoveInfo: String { return self._s[344]! } - public func AutoDownloadSettings_PreloadVideoInfo(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[345]!, self._r[345]!, [_0]) - } - public var SettingsSearch_Synonyms_Notifications_MessageNotificationsExceptions: String { return self._s[346]! } - public func Conversation_ClearChatConfirmation(_ _0: String) -> (String, [(Int, NSRange)]) { return formatWithArgumentRanges(self._s[347]!, self._r[347]!, [_0]) } - public var GroupInfo_InviteLink_Title: String { return self._s[348]! } + public var ChannelRemoved_RemoveInfo: String { return self._s[348]! } + public func AutoDownloadSettings_PreloadVideoInfo(_ _0: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[349]!, self._r[349]!, [_0]) + } + public var SettingsSearch_Synonyms_Notifications_MessageNotificationsExceptions: String { return self._s[350]! } + public func Conversation_ClearChatConfirmation(_ _0: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[351]!, self._r[351]!, [_0]) + } + public var GroupInfo_InviteLink_Title: String { return self._s[352]! } public func Channel_AdminLog_MessageUnkickedNameUsername(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[349]!, self._r[349]!, [_1, _2]) + return formatWithArgumentRanges(self._s[353]!, self._r[353]!, [_1, _2]) } - public var KeyCommand_ScrollUp: String { return self._s[350]! } - public var ContactInfo_URLLabelHomepage: String { return self._s[351]! } - public var Channel_OwnershipTransfer_ChangeOwner: String { return self._s[352]! } + public var KeyCommand_ScrollUp: String { return self._s[354]! } + public var ContactInfo_URLLabelHomepage: String { return self._s[355]! } + public var Channel_OwnershipTransfer_ChangeOwner: String { return self._s[356]! } public func Channel_AdminLog_DisabledSlowmode(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[353]!, self._r[353]!, [_0]) + return formatWithArgumentRanges(self._s[357]!, self._r[357]!, [_0]) } - public var TwoFactorSetup_Done_Title: String { return self._s[354]! } + public var TwoFactorSetup_Done_Title: String { return self._s[358]! } public func Conversation_EncryptedPlaceholderTitleOutgoing(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[355]!, self._r[355]!, [_0]) + return formatWithArgumentRanges(self._s[359]!, self._r[359]!, [_0]) } - public var CallFeedback_ReasonDistortedSpeech: String { return self._s[356]! } - public var Watch_LastSeen_WithinAWeek: String { return self._s[357]! } - public var ContactList_Context_SendMessage: String { return self._s[359]! } - public var Weekday_Tuesday: String { return self._s[360]! } - public var Wallet_Created_Title: String { return self._s[362]! } - public var ScheduledMessages_Delete: String { return self._s[363]! } - public var UserInfo_StartSecretChat: String { return self._s[364]! } - public var Passport_Identity_FilesTitle: String { return self._s[365]! } - public var Permissions_NotificationsAllow_v0: String { return self._s[366]! } - public var DialogList_DeleteConversationConfirmation: String { return self._s[368]! } - public var ChatList_UndoArchiveRevealedTitle: String { return self._s[369]! } + public var CallFeedback_ReasonDistortedSpeech: String { return self._s[360]! } + public var Watch_LastSeen_WithinAWeek: String { return self._s[361]! } + public var ContactList_Context_SendMessage: String { return self._s[363]! } + public var Weekday_Tuesday: String { return self._s[364]! } + public var Wallet_Created_Title: String { return self._s[366]! } + public var ScheduledMessages_Delete: String { return self._s[367]! } + public var UserInfo_StartSecretChat: String { return self._s[368]! } + public var Passport_Identity_FilesTitle: String { return self._s[369]! } + public var Permissions_NotificationsAllow_v0: String { return self._s[370]! } + public var DialogList_DeleteConversationConfirmation: String { return self._s[372]! } + public var ChatList_UndoArchiveRevealedTitle: String { return self._s[373]! } public func Wallet_Configuration_ApplyErrorTextURLUnreachable(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[370]!, self._r[370]!, [_0]) + return formatWithArgumentRanges(self._s[374]!, self._r[374]!, [_0]) } - public var AuthSessions_Sessions: String { return self._s[371]! } + public var AuthSessions_Sessions: String { return self._s[375]! } public func Settings_KeepPhoneNumber(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[373]!, self._r[373]!, [_0]) + return formatWithArgumentRanges(self._s[377]!, self._r[377]!, [_0]) } - public var TwoStepAuth_RecoveryEmailChangeDescription: String { return self._s[374]! } - public var Call_StatusWaiting: String { return self._s[375]! } - public var CreateGroup_SoftUserLimitAlert: String { return self._s[376]! } - public var FastTwoStepSetup_HintHelp: String { return self._s[377]! } - public var WallpaperPreview_CustomColorBottomText: String { return self._s[378]! } - public var EditTheme_Expand_Preview_OutgoingText: String { return self._s[379]! } - public var LogoutOptions_AddAccountText: String { return self._s[380]! } - public var PasscodeSettings_6DigitCode: String { return self._s[381]! } - public var Settings_LogoutConfirmationText: String { return self._s[382]! } - public var Passport_Identity_TypePassport: String { return self._s[384]! } - public var Map_Work: String { return self._s[387]! } + public var TwoStepAuth_RecoveryEmailChangeDescription: String { return self._s[378]! } + public var Call_StatusWaiting: String { return self._s[379]! } + public var CreateGroup_SoftUserLimitAlert: String { return self._s[380]! } + public var FastTwoStepSetup_HintHelp: String { return self._s[381]! } + public var WallpaperPreview_CustomColorBottomText: String { return self._s[382]! } + public var EditTheme_Expand_Preview_OutgoingText: String { return self._s[383]! } + public var LogoutOptions_AddAccountText: String { return self._s[384]! } + public var PasscodeSettings_6DigitCode: String { return self._s[385]! } + public var Settings_LogoutConfirmationText: String { return self._s[386]! } + public var Passport_Identity_TypePassport: String { return self._s[388]! } + public var Map_Work: String { return self._s[391]! } public func PUSH_MESSAGE_VIDEOS(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[388]!, self._r[388]!, [_1, _2]) + return formatWithArgumentRanges(self._s[392]!, self._r[392]!, [_1, _2]) } - public var SocksProxySetup_SaveProxy: String { return self._s[389]! } - public var AccessDenied_SaveMedia: String { return self._s[390]! } - public var Checkout_ErrorInvoiceAlreadyPaid: String { return self._s[392]! } - public var Settings_Title: String { return self._s[394]! } - public var VoiceOver_Chat_RecordModeVideoMessageInfo: String { return self._s[395]! } - public var Contacts_InviteSearchLabel: String { return self._s[397]! } - public var PrivacySettings_WebSessions: String { return self._s[398]! } - public var ConvertToSupergroup_Title: String { return self._s[399]! } + public var SocksProxySetup_SaveProxy: String { return self._s[393]! } + public var AccessDenied_SaveMedia: String { return self._s[394]! } + public var Checkout_ErrorInvoiceAlreadyPaid: String { return self._s[396]! } + public var CreatePoll_MultipleChoice: String { return self._s[397]! } + public var Settings_Title: String { return self._s[399]! } + public var VoiceOver_Chat_RecordModeVideoMessageInfo: String { return self._s[400]! } + public var Contacts_InviteSearchLabel: String { return self._s[402]! } + public var PrivacySettings_WebSessions: String { return self._s[403]! } + public var ConvertToSupergroup_Title: String { return self._s[404]! } public func Channel_AdminLog_CaptionEdited(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[400]!, self._r[400]!, [_0]) + return formatWithArgumentRanges(self._s[405]!, self._r[405]!, [_0]) } - public var TwoFactorSetup_Hint_Text: String { return self._s[401]! } - public var InfoPlist_NSSiriUsageDescription: String { return self._s[402]! } + public var TwoFactorSetup_Hint_Text: String { return self._s[406]! } + public var InfoPlist_NSSiriUsageDescription: String { return self._s[407]! } public func PUSH_MESSAGE_CHANNEL_MESSAGE_GAME_SCORE(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[403]!, self._r[403]!, [_1, _2, _3]) + return formatWithArgumentRanges(self._s[408]!, self._r[408]!, [_1, _2, _3]) } - public var ChatSettings_AutomaticPhotoDownload: String { return self._s[404]! } - public var UserInfo_BotHelp: String { return self._s[405]! } - public var PrivacySettings_LastSeenEverybody: String { return self._s[406]! } - public var Checkout_Name: String { return self._s[407]! } - public var AutoDownloadSettings_DataUsage: String { return self._s[408]! } - public var Channel_BanUser_BlockFor: String { return self._s[409]! } - public var Checkout_ShippingAddress: String { return self._s[410]! } - public var AutoDownloadSettings_MaxVideoSize: String { return self._s[411]! } - public var Privacy_PaymentsClearInfoDoneHelp: String { return self._s[412]! } - public var Privacy_Forwards: String { return self._s[413]! } - public var Channel_BanUser_PermissionSendPolls: String { return self._s[414]! } - public var Appearance_ThemeCarouselNewNight: String { return self._s[415]! } + public var ChatSettings_AutomaticPhotoDownload: String { return self._s[409]! } + public var UserInfo_BotHelp: String { return self._s[410]! } + public var PrivacySettings_LastSeenEverybody: String { return self._s[411]! } + public var Checkout_Name: String { return self._s[412]! } + public var AutoDownloadSettings_DataUsage: String { return self._s[413]! } + public var Channel_BanUser_BlockFor: String { return self._s[414]! } + public var Checkout_ShippingAddress: String { return self._s[415]! } + public var AutoDownloadSettings_MaxVideoSize: String { return self._s[416]! } + public var Privacy_PaymentsClearInfoDoneHelp: String { return self._s[417]! } + public var Privacy_Forwards: String { return self._s[418]! } + public var Channel_BanUser_PermissionSendPolls: String { return self._s[419]! } + public var Appearance_ThemeCarouselNewNight: String { return self._s[420]! } public func SecretVideo_NotViewedYet(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[418]!, self._r[418]!, [_0]) + return formatWithArgumentRanges(self._s[423]!, self._r[423]!, [_0]) } - public var Contacts_SortedByName: String { return self._s[419]! } - public var Group_OwnershipTransfer_Title: String { return self._s[420]! } - public var VoiceOver_Chat_OpenHint: String { return self._s[422]! } - public var Group_LeaveGroup: String { return self._s[423]! } - public var Settings_UsernameEmpty: String { return self._s[424]! } + public var Contacts_SortedByName: String { return self._s[424]! } + public var Group_OwnershipTransfer_Title: String { return self._s[425]! } + public var VoiceOver_Chat_OpenHint: String { return self._s[427]! } + public var Group_LeaveGroup: String { return self._s[428]! } + public var Settings_UsernameEmpty: String { return self._s[429]! } public func Notification_PinnedPollMessage(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[425]!, self._r[425]!, [_0]) + return formatWithArgumentRanges(self._s[430]!, self._r[430]!, [_0]) } public func TwoStepAuth_ConfirmEmailDescription(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[426]!, self._r[426]!, [_1]) + return formatWithArgumentRanges(self._s[431]!, self._r[431]!, [_1]) } public func Channel_OwnershipTransfer_DescriptionInfo(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[427]!, self._r[427]!, [_1, _2]) + return formatWithArgumentRanges(self._s[432]!, self._r[432]!, [_1, _2]) } - public var Message_ImageExpired: String { return self._s[428]! } - public var TwoStepAuth_RecoveryFailed: String { return self._s[430]! } - public var EditTheme_Edit_Preview_OutgoingText: String { return self._s[431]! } - public var UserInfo_AddToExisting: String { return self._s[432]! } - public var TwoStepAuth_EnabledSuccess: String { return self._s[433]! } - public var Wallet_Send_SyncInProgress: String { return self._s[434]! } - public var SettingsSearch_Synonyms_Appearance_ChatBackground_SetColor: String { return self._s[435]! } + public var Message_ImageExpired: String { return self._s[433]! } + public var TwoStepAuth_RecoveryFailed: String { return self._s[435]! } + public var EditTheme_Edit_Preview_OutgoingText: String { return self._s[436]! } + public var UserInfo_AddToExisting: String { return self._s[437]! } + public var TwoStepAuth_EnabledSuccess: String { return self._s[438]! } + public var Wallet_Send_SyncInProgress: String { return self._s[439]! } + public var SettingsSearch_Synonyms_Appearance_ChatBackground_SetColor: String { return self._s[440]! } public func PUSH_CHANNEL_MESSAGE(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[436]!, self._r[436]!, [_1]) + return formatWithArgumentRanges(self._s[441]!, self._r[441]!, [_1]) } - public var Notifications_GroupNotificationsAlert: String { return self._s[437]! } - public var Passport_Language_km: String { return self._s[438]! } - public var SocksProxySetup_AdNoticeHelp: String { return self._s[440]! } - public var VoiceOver_Media_PlaybackPlay: String { return self._s[441]! } - public var Notification_CallMissedShort: String { return self._s[442]! } - public var Wallet_Info_YourBalance: String { return self._s[443]! } - public var ReportPeer_ReasonOther_Send: String { return self._s[444]! } - public var Watch_Compose_Send: String { return self._s[445]! } - public var Passport_Identity_TypeInternalPassportUploadScan: String { return self._s[448]! } - public var TwoFactorSetup_Email_Action: String { return self._s[449]! } - public var Conversation_HoldForVideo: String { return self._s[450]! } - public var Wallet_Configuration_ApplyErrorTextURLInvalidData: String { return self._s[451]! } - public var AuthSessions_OtherDevices: String { return self._s[452]! } - public var Wallet_TransactionInfo_CommentHeader: String { return self._s[453]! } - public var CheckoutInfo_ErrorCityInvalid: String { return self._s[455]! } - public var Appearance_AutoNightThemeDisabled: String { return self._s[457]! } - public var Channel_LinkItem: String { return self._s[458]! } + public var Notifications_GroupNotificationsAlert: String { return self._s[442]! } + public var Passport_Language_km: String { return self._s[443]! } + public var SocksProxySetup_AdNoticeHelp: String { return self._s[445]! } + public var VoiceOver_Media_PlaybackPlay: String { return self._s[446]! } + public var Notification_CallMissedShort: String { return self._s[447]! } + public var Wallet_Info_YourBalance: String { return self._s[448]! } + public var ReportPeer_ReasonOther_Send: String { return self._s[449]! } + public var Watch_Compose_Send: String { return self._s[450]! } + public var Passport_Identity_TypeInternalPassportUploadScan: String { return self._s[453]! } + public var TwoFactorSetup_Email_Action: String { return self._s[454]! } + public var Conversation_HoldForVideo: String { return self._s[455]! } + public var Wallet_Configuration_ApplyErrorTextURLInvalidData: String { return self._s[456]! } + public var AuthSessions_OtherDevices: String { return self._s[457]! } + public var Wallet_TransactionInfo_CommentHeader: String { return self._s[458]! } + public var CheckoutInfo_ErrorCityInvalid: String { return self._s[460]! } + public var Appearance_AutoNightThemeDisabled: String { return self._s[462]! } + public var Channel_LinkItem: String { return self._s[463]! } public func PrivacySettings_LastSeenContactsMinusPlus(_ _0: String, _ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[459]!, self._r[459]!, [_0, _1]) + return formatWithArgumentRanges(self._s[464]!, self._r[464]!, [_0, _1]) } public func Passport_Identity_NativeNameTitle(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[462]!, self._r[462]!, [_0]) + return formatWithArgumentRanges(self._s[467]!, self._r[467]!, [_0]) } - public var VoiceOver_Recording_StopAndPreview: String { return self._s[463]! } - public var Passport_Language_dv: String { return self._s[464]! } - public var Undo_LeftChannel: String { return self._s[465]! } - public var Notifications_ExceptionsMuted: String { return self._s[466]! } - public var ChatList_UnhideAction: String { return self._s[467]! } - public var Conversation_ContextMenuShare: String { return self._s[468]! } - public var Conversation_ContextMenuStickerPackInfo: String { return self._s[469]! } - public var ShareFileTip_Title: String { return self._s[470]! } - public var NotificationsSound_Chord: String { return self._s[471]! } - public var Wallet_TransactionInfo_OtherFeeHeader: String { return self._s[472]! } + public var VoiceOver_Recording_StopAndPreview: String { return self._s[468]! } + public var Passport_Language_dv: String { return self._s[469]! } + public var Undo_LeftChannel: String { return self._s[470]! } + public var Notifications_ExceptionsMuted: String { return self._s[471]! } + public var ChatList_UnhideAction: String { return self._s[472]! } + public var Conversation_ContextMenuShare: String { return self._s[473]! } + public var Conversation_ContextMenuStickerPackInfo: String { return self._s[474]! } + public var ShareFileTip_Title: String { return self._s[475]! } + public var NotificationsSound_Chord: String { return self._s[476]! } + public var Wallet_TransactionInfo_OtherFeeHeader: String { return self._s[477]! } public func PUSH_CHAT_RETURNED(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[473]!, self._r[473]!, [_1, _2]) + return formatWithArgumentRanges(self._s[478]!, self._r[478]!, [_1, _2]) } - public var Passport_Address_EditTemporaryRegistration: String { return self._s[474]! } + public var Passport_Address_EditTemporaryRegistration: String { return self._s[479]! } public func Notification_Joined(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[475]!, self._r[475]!, [_0]) + return formatWithArgumentRanges(self._s[480]!, self._r[480]!, [_0]) } public func Wallet_Time_PreciseDate_m3(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[476]!, self._r[476]!, [_1, _2, _3]) + return formatWithArgumentRanges(self._s[481]!, self._r[481]!, [_1, _2, _3]) } - public var Wallet_Settings_ConfigurationInfo: String { return self._s[477]! } - public var Wallpaper_ErrorNotFound: String { return self._s[478]! } - public var Notification_CallOutgoingShort: String { return self._s[480]! } - public var Wallet_WordImport_IncorrectText: String { return self._s[481]! } + public var Wallet_Settings_ConfigurationInfo: String { return self._s[482]! } + public var Wallpaper_ErrorNotFound: String { return self._s[483]! } + public var Notification_CallOutgoingShort: String { return self._s[485]! } + public var Wallet_WordImport_IncorrectText: String { return self._s[486]! } public func Watch_Time_ShortFullAt(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[482]!, self._r[482]!, [_1, _2]) + return formatWithArgumentRanges(self._s[487]!, self._r[487]!, [_1, _2]) } - public var Passport_Address_TypeUtilityBill: String { return self._s[483]! } - public var Privacy_Forwards_LinkIfAllowed: String { return self._s[484]! } - public var ReportPeer_Report: String { return self._s[485]! } - public var SettingsSearch_Synonyms_Proxy_Title: String { return self._s[486]! } - public var GroupInfo_DeactivatedStatus: String { return self._s[487]! } + public var Passport_Address_TypeUtilityBill: String { return self._s[488]! } + public var Privacy_Forwards_LinkIfAllowed: String { return self._s[489]! } + public var ReportPeer_Report: String { return self._s[490]! } + public var SettingsSearch_Synonyms_Proxy_Title: String { return self._s[491]! } + public var GroupInfo_DeactivatedStatus: String { return self._s[492]! } public func VoiceOver_Chat_MusicTitle(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[488]!, self._r[488]!, [_1, _2]) + return formatWithArgumentRanges(self._s[493]!, self._r[493]!, [_1, _2]) } - public var StickerPack_Send: String { return self._s[489]! } - public var Login_CodeSentInternal: String { return self._s[490]! } - public var Wallet_Month_GenJanuary: String { return self._s[491]! } - public var GroupInfo_InviteLink_LinkSection: String { return self._s[492]! } + public var StickerPack_Send: String { return self._s[494]! } + public var Login_CodeSentInternal: String { return self._s[495]! } + public var Wallet_Month_GenJanuary: String { return self._s[496]! } + public var GroupInfo_InviteLink_LinkSection: String { return self._s[497]! } public func Channel_AdminLog_MessageDeleted(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[493]!, self._r[493]!, [_0]) + return formatWithArgumentRanges(self._s[498]!, self._r[498]!, [_0]) } public func Conversation_EncryptionWaiting(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[495]!, self._r[495]!, [_0]) + return formatWithArgumentRanges(self._s[500]!, self._r[500]!, [_0]) } - public var Channel_BanUser_PermissionSendStickersAndGifs: String { return self._s[496]! } + public var Channel_BanUser_PermissionSendStickersAndGifs: String { return self._s[501]! } public func PUSH_PINNED_GAME(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[497]!, self._r[497]!, [_1]) + return formatWithArgumentRanges(self._s[502]!, self._r[502]!, [_1]) } - public var ReportPeer_ReasonViolence: String { return self._s[499]! } - public var Appearance_ShareThemeColor: String { return self._s[500]! } - public var Map_Locating: String { return self._s[501]! } + public var ReportPeer_ReasonViolence: String { return self._s[504]! } + public var Appearance_ShareThemeColor: String { return self._s[505]! } + public var Map_Locating: String { return self._s[506]! } public func VoiceOver_Chat_VideoFrom(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[502]!, self._r[502]!, [_0]) + return formatWithArgumentRanges(self._s[507]!, self._r[507]!, [_0]) } public func PUSH_ALBUM(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[503]!, self._r[503]!, [_1]) + return formatWithArgumentRanges(self._s[508]!, self._r[508]!, [_1]) } - public var AutoDownloadSettings_GroupChats: String { return self._s[505]! } - public var CheckoutInfo_SaveInfo: String { return self._s[506]! } - public var SharedMedia_EmptyLinksText: String { return self._s[508]! } - public var Passport_Address_CityPlaceholder: String { return self._s[509]! } - public var CheckoutInfo_ErrorStateInvalid: String { return self._s[510]! } - public var Privacy_ProfilePhoto_CustomHelp: String { return self._s[511]! } - public var Wallet_Send_OwnAddressAlertTitle: String { return self._s[513]! } - public var Channel_AdminLog_CanAddAdmins: String { return self._s[514]! } + public var AutoDownloadSettings_GroupChats: String { return self._s[510]! } + public var CheckoutInfo_SaveInfo: String { return self._s[511]! } + public var SharedMedia_EmptyLinksText: String { return self._s[513]! } + public var Passport_Address_CityPlaceholder: String { return self._s[514]! } + public var CheckoutInfo_ErrorStateInvalid: String { return self._s[515]! } + public var Privacy_ProfilePhoto_CustomHelp: String { return self._s[516]! } + public var Wallet_Send_OwnAddressAlertTitle: String { return self._s[518]! } + public var Channel_AdminLog_CanAddAdmins: String { return self._s[519]! } public func PUSH_CHANNEL_MESSAGE_FWD(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[515]!, self._r[515]!, [_1]) + return formatWithArgumentRanges(self._s[520]!, self._r[520]!, [_1]) } public func Time_MonthOfYear_m8(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[516]!, self._r[516]!, [_0]) - } - public var InfoPlist_NSLocationWhenInUseUsageDescription: String { return self._s[517]! } - public var GroupInfo_InviteLink_RevokeAlert_Success: String { return self._s[518]! } - public var ChangePhoneNumberCode_Code: String { return self._s[519]! } - public var Appearance_CreateTheme: String { return self._s[520]! } - public func UserInfo_NotificationsDefaultSound(_ _0: String) -> (String, [(Int, NSRange)]) { return formatWithArgumentRanges(self._s[521]!, self._r[521]!, [_0]) } - public var TwoStepAuth_SetupEmail: String { return self._s[522]! } - public var HashtagSearch_AllChats: String { return self._s[523]! } - public var MediaPlayer_UnknownTrack: String { return self._s[524]! } - public var SettingsSearch_Synonyms_Data_AutoDownloadUsingCellular: String { return self._s[526]! } + public var InfoPlist_NSLocationWhenInUseUsageDescription: String { return self._s[522]! } + public var GroupInfo_InviteLink_RevokeAlert_Success: String { return self._s[523]! } + public var ChangePhoneNumberCode_Code: String { return self._s[524]! } + public var Appearance_CreateTheme: String { return self._s[525]! } + public func UserInfo_NotificationsDefaultSound(_ _0: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[526]!, self._r[526]!, [_0]) + } + public var TwoStepAuth_SetupEmail: String { return self._s[527]! } + public var HashtagSearch_AllChats: String { return self._s[528]! } + public var MediaPlayer_UnknownTrack: String { return self._s[529]! } + public var SettingsSearch_Synonyms_Data_AutoDownloadUsingCellular: String { return self._s[531]! } public func ChatList_DeleteForEveryone(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[527]!, self._r[527]!, [_0]) + return formatWithArgumentRanges(self._s[532]!, self._r[532]!, [_0]) } - public var PhotoEditor_QualityHigh: String { return self._s[529]! } + public var PhotoEditor_QualityHigh: String { return self._s[534]! } public func Passport_Phone_UseTelegramNumber(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[530]!, self._r[530]!, [_0]) + return formatWithArgumentRanges(self._s[535]!, self._r[535]!, [_0]) } - public var ApplyLanguage_ApplyLanguageAction: String { return self._s[531]! } - public var SettingsSearch_Synonyms_Notifications_ChannelNotificationsPreview: String { return self._s[532]! } - public var Message_LiveLocation: String { return self._s[533]! } - public var Cache_LowDiskSpaceText: String { return self._s[534]! } - public var Wallet_Receive_ShareAddress: String { return self._s[535]! } - public var EditTheme_ErrorLinkTaken: String { return self._s[536]! } - public var Conversation_SendMessage: String { return self._s[537]! } - public var AuthSessions_EmptyTitle: String { return self._s[538]! } - public var Privacy_PhoneNumber: String { return self._s[539]! } - public var PeopleNearby_CreateGroup: String { return self._s[540]! } - public var CallSettings_UseLessData: String { return self._s[541]! } - public var NetworkUsageSettings_MediaDocumentDataSection: String { return self._s[542]! } - public var Stickers_AddToFavorites: String { return self._s[543]! } - public var Wallet_WordImport_Title: String { return self._s[544]! } - public var PhotoEditor_QualityLow: String { return self._s[545]! } - public var Watch_UserInfo_Unblock: String { return self._s[546]! } - public var Settings_Logout: String { return self._s[547]! } + public var ApplyLanguage_ApplyLanguageAction: String { return self._s[536]! } + public var SettingsSearch_Synonyms_Notifications_ChannelNotificationsPreview: String { return self._s[537]! } + public var Message_LiveLocation: String { return self._s[538]! } + public var Cache_LowDiskSpaceText: String { return self._s[539]! } + public var Wallet_Receive_ShareAddress: String { return self._s[540]! } + public var EditTheme_ErrorLinkTaken: String { return self._s[541]! } + public var Conversation_SendMessage: String { return self._s[542]! } + public var AuthSessions_EmptyTitle: String { return self._s[543]! } + public var Privacy_PhoneNumber: String { return self._s[544]! } + public var PeopleNearby_CreateGroup: String { return self._s[545]! } + public var CallSettings_UseLessData: String { return self._s[546]! } + public var NetworkUsageSettings_MediaDocumentDataSection: String { return self._s[547]! } + public var Stickers_AddToFavorites: String { return self._s[548]! } + public var Wallet_WordImport_Title: String { return self._s[549]! } + public var PhotoEditor_QualityLow: String { return self._s[550]! } + public var Watch_UserInfo_Unblock: String { return self._s[551]! } + public var Settings_Logout: String { return self._s[552]! } public func PUSH_MESSAGE_ROUND(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[548]!, self._r[548]!, [_1]) + return formatWithArgumentRanges(self._s[553]!, self._r[553]!, [_1]) } - public var ContactInfo_PhoneLabelWork: String { return self._s[549]! } - public var ChannelInfo_Stats: String { return self._s[550]! } - public var TextFormat_Link: String { return self._s[551]! } + public var ContactInfo_PhoneLabelWork: String { return self._s[554]! } + public var ChannelInfo_Stats: String { return self._s[555]! } + public var TextFormat_Link: String { return self._s[556]! } public func Date_ChatDateHeader(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[552]!, self._r[552]!, [_1, _2]) + return formatWithArgumentRanges(self._s[557]!, self._r[557]!, [_1, _2]) } - public var Wallet_TransactionInfo_Title: String { return self._s[553]! } + public var Wallet_TransactionInfo_Title: String { return self._s[558]! } public func Message_ForwardedMessage(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[554]!, self._r[554]!, [_0]) + return formatWithArgumentRanges(self._s[559]!, self._r[559]!, [_0]) } - public var Watch_Notification_Joined: String { return self._s[555]! } - public var Group_Setup_TypePublicHelp: String { return self._s[556]! } - public var Passport_Scans_UploadNew: String { return self._s[557]! } - public var Checkout_LiabilityAlertTitle: String { return self._s[558]! } - public var DialogList_Title: String { return self._s[561]! } - public var NotificationSettings_ContactJoined: String { return self._s[562]! } - public var GroupInfo_LabelAdmin: String { return self._s[563]! } - public var KeyCommand_ChatInfo: String { return self._s[564]! } - public var Conversation_EditingCaptionPanelTitle: String { return self._s[565]! } - public var Call_ReportIncludeLog: String { return self._s[566]! } + public var Watch_Notification_Joined: String { return self._s[560]! } + public var Group_Setup_TypePublicHelp: String { return self._s[561]! } + public var Passport_Scans_UploadNew: String { return self._s[562]! } + public var Checkout_LiabilityAlertTitle: String { return self._s[563]! } + public var DialogList_Title: String { return self._s[566]! } + public var NotificationSettings_ContactJoined: String { return self._s[567]! } + public var GroupInfo_LabelAdmin: String { return self._s[568]! } + public var KeyCommand_ChatInfo: String { return self._s[569]! } + public var Conversation_EditingCaptionPanelTitle: String { return self._s[570]! } + public var Call_ReportIncludeLog: String { return self._s[571]! } public func Notifications_ExceptionsChangeSound(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[569]!, self._r[569]!, [_0]) + return formatWithArgumentRanges(self._s[574]!, self._r[574]!, [_0]) } - public var Channel_AdminLog_InfoPanelChannelAlertText: String { return self._s[570]! } - public var ChatAdmins_AllMembersAreAdmins: String { return self._s[571]! } - public var LocalGroup_IrrelevantWarning: String { return self._s[572]! } - public var Conversation_DefaultRestrictedInline: String { return self._s[573]! } - public var Message_Sticker: String { return self._s[574]! } - public var LastSeen_JustNow: String { return self._s[576]! } - public var Passport_Email_EmailPlaceholder: String { return self._s[578]! } - public var SettingsSearch_Synonyms_AppLanguage: String { return self._s[579]! } - public var Channel_AdminLogFilter_EventsEditedMessages: String { return self._s[580]! } - public var Channel_EditAdmin_PermissionsHeader: String { return self._s[581]! } - public var TwoStepAuth_Email: String { return self._s[582]! } - public var SettingsSearch_Synonyms_Notifications_ChannelNotificationsSound: String { return self._s[583]! } - public var PhotoEditor_BlurToolOff: String { return self._s[584]! } - public var Message_PinnedStickerMessage: String { return self._s[585]! } - public var ContactInfo_PhoneLabelPager: String { return self._s[586]! } - public var SettingsSearch_Synonyms_Appearance_TextSize: String { return self._s[587]! } - public var Passport_DiscardMessageTitle: String { return self._s[588]! } - public var Privacy_PaymentsTitle: String { return self._s[589]! } - public var EditTheme_Edit_Preview_IncomingReplyName: String { return self._s[590]! } - public var ClearCache_StorageCache: String { return self._s[591]! } - public var Appearance_TextSizeSetting: String { return self._s[592]! } - public var Channel_DiscussionGroup_Header: String { return self._s[594]! } - public var VoiceOver_Chat_OptionSelected: String { return self._s[595]! } - public var Appearance_ColorTheme: String { return self._s[596]! } - public var UserInfo_ShareContact: String { return self._s[597]! } - public var Passport_Address_TypePassportRegistration: String { return self._s[598]! } - public var Common_More: String { return self._s[599]! } - public var Watch_Message_Call: String { return self._s[600]! } - public var Profile_EncryptionKey: String { return self._s[603]! } - public var Privacy_TopPeers: String { return self._s[604]! } - public var Conversation_StopPollConfirmation: String { return self._s[605]! } - public var Wallet_Words_NotDoneText: String { return self._s[607]! } - public var Privacy_TopPeersWarning: String { return self._s[609]! } - public var SettingsSearch_Synonyms_Data_DownloadInBackground: String { return self._s[610]! } - public var SettingsSearch_Synonyms_Data_Storage_KeepMedia: String { return self._s[611]! } - public var Wallet_RestoreFailed_EnterWords: String { return self._s[614]! } - public var DialogList_SearchSectionMessages: String { return self._s[615]! } - public var Notifications_ChannelNotifications: String { return self._s[616]! } - public var CheckoutInfo_ShippingInfoAddress1Placeholder: String { return self._s[617]! } - public var Passport_Language_sk: String { return self._s[618]! } - public var Notification_MessageLifetime1h: String { return self._s[619]! } - public var Wallpaper_ResetWallpapersInfo: String { return self._s[620]! } - public var Appearance_ThemePreview_Chat_5_Text: String { return self._s[621]! } - public var Call_ReportSkip: String { return self._s[623]! } - public var Cache_ServiceFiles: String { return self._s[624]! } - public var Group_ErrorAddTooMuchAdmins: String { return self._s[625]! } - public var VoiceOver_Chat_YourFile: String { return self._s[626]! } - public var Map_Hybrid: String { return self._s[627]! } - public var Contacts_SearchUsersAndGroupsLabel: String { return self._s[629]! } - public var ChatSettings_AutoDownloadVideos: String { return self._s[631]! } - public var Channel_BanUser_PermissionEmbedLinks: String { return self._s[632]! } - public var InfoPlist_NSLocationAlwaysAndWhenInUseUsageDescription: String { return self._s[633]! } - public var SocksProxySetup_ProxyTelegram: String { return self._s[636]! } + public var Channel_AdminLog_InfoPanelChannelAlertText: String { return self._s[575]! } + public var ChatAdmins_AllMembersAreAdmins: String { return self._s[576]! } + public var LocalGroup_IrrelevantWarning: String { return self._s[577]! } + public var Conversation_DefaultRestrictedInline: String { return self._s[578]! } + public var Message_Sticker: String { return self._s[579]! } + public var LastSeen_JustNow: String { return self._s[581]! } + public var Passport_Email_EmailPlaceholder: String { return self._s[583]! } + public var SettingsSearch_Synonyms_AppLanguage: String { return self._s[584]! } + public var Channel_AdminLogFilter_EventsEditedMessages: String { return self._s[585]! } + public var Channel_EditAdmin_PermissionsHeader: String { return self._s[586]! } + public var TwoStepAuth_Email: String { return self._s[587]! } + public var SettingsSearch_Synonyms_Notifications_ChannelNotificationsSound: String { return self._s[588]! } + public var PhotoEditor_BlurToolOff: String { return self._s[589]! } + public var Message_PinnedStickerMessage: String { return self._s[590]! } + public var ContactInfo_PhoneLabelPager: String { return self._s[591]! } + public var SettingsSearch_Synonyms_Appearance_TextSize: String { return self._s[592]! } + public var Passport_DiscardMessageTitle: String { return self._s[593]! } + public var Privacy_PaymentsTitle: String { return self._s[594]! } + public var EditTheme_Edit_Preview_IncomingReplyName: String { return self._s[595]! } + public var ClearCache_StorageCache: String { return self._s[596]! } + public var Appearance_TextSizeSetting: String { return self._s[597]! } + public var Channel_DiscussionGroup_Header: String { return self._s[599]! } + public var VoiceOver_Chat_OptionSelected: String { return self._s[600]! } + public var Appearance_ColorTheme: String { return self._s[601]! } + public var UserInfo_ShareContact: String { return self._s[602]! } + public var Passport_Address_TypePassportRegistration: String { return self._s[603]! } + public var Common_More: String { return self._s[604]! } + public var Watch_Message_Call: String { return self._s[605]! } + public var Profile_EncryptionKey: String { return self._s[608]! } + public var Privacy_TopPeers: String { return self._s[609]! } + public var Conversation_StopPollConfirmation: String { return self._s[610]! } + public var Wallet_Words_NotDoneText: String { return self._s[612]! } + public var Privacy_TopPeersWarning: String { return self._s[614]! } + public var SettingsSearch_Synonyms_Data_DownloadInBackground: String { return self._s[615]! } + public var SettingsSearch_Synonyms_Data_Storage_KeepMedia: String { return self._s[616]! } + public var Wallet_RestoreFailed_EnterWords: String { return self._s[619]! } + public var DialogList_SearchSectionMessages: String { return self._s[620]! } + public var Notifications_ChannelNotifications: String { return self._s[621]! } + public var CheckoutInfo_ShippingInfoAddress1Placeholder: String { return self._s[622]! } + public var Passport_Language_sk: String { return self._s[623]! } + public var Notification_MessageLifetime1h: String { return self._s[624]! } + public var Wallpaper_ResetWallpapersInfo: String { return self._s[625]! } + public var Appearance_ThemePreview_Chat_5_Text: String { return self._s[626]! } + public var Call_ReportSkip: String { return self._s[628]! } + public var Cache_ServiceFiles: String { return self._s[629]! } + public var Group_ErrorAddTooMuchAdmins: String { return self._s[630]! } + public var VoiceOver_Chat_YourFile: String { return self._s[631]! } + public var Map_Hybrid: String { return self._s[632]! } + public var Contacts_SearchUsersAndGroupsLabel: String { return self._s[634]! } + public func PUSH_MESSAGE_QUIZ(_ _1: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[635]!, self._r[635]!, [_1]) + } + public var ChatSettings_AutoDownloadVideos: String { return self._s[637]! } + public var Channel_BanUser_PermissionEmbedLinks: String { return self._s[638]! } + public var InfoPlist_NSLocationAlwaysAndWhenInUseUsageDescription: String { return self._s[639]! } + public var SocksProxySetup_ProxyTelegram: String { return self._s[642]! } public func PUSH_MESSAGE_AUDIO(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[637]!, self._r[637]!, [_1]) + return formatWithArgumentRanges(self._s[643]!, self._r[643]!, [_1]) } - public var Channel_Username_CreatePrivateLinkHelp: String { return self._s[639]! } - public var ScheduledMessages_ScheduledToday: String { return self._s[640]! } + public var Channel_Username_CreatePrivateLinkHelp: String { return self._s[645]! } + public var ScheduledMessages_ScheduledToday: String { return self._s[646]! } public func PUSH_CHAT_TITLE_EDITED(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[641]!, self._r[641]!, [_1, _2]) + return formatWithArgumentRanges(self._s[647]!, self._r[647]!, [_1, _2]) } - public var Conversation_LiveLocationYou: String { return self._s[642]! } - public var SettingsSearch_Synonyms_Privacy_Calls: String { return self._s[643]! } - public var SettingsSearch_Synonyms_Notifications_MessageNotificationsPreview: String { return self._s[644]! } - public var UserInfo_ShareBot: String { return self._s[647]! } + public var Conversation_LiveLocationYou: String { return self._s[648]! } + public var SettingsSearch_Synonyms_Privacy_Calls: String { return self._s[649]! } + public var SettingsSearch_Synonyms_Notifications_MessageNotificationsPreview: String { return self._s[650]! } + public var UserInfo_ShareBot: String { return self._s[653]! } public func PUSH_AUTH_REGION(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[648]!, self._r[648]!, [_1, _2]) + return formatWithArgumentRanges(self._s[654]!, self._r[654]!, [_1, _2]) } - public var Conversation_ClearCache: String { return self._s[649]! } - public var PhotoEditor_ShadowsTint: String { return self._s[650]! } - public var Message_Audio: String { return self._s[651]! } - public var Passport_Language_lt: String { return self._s[652]! } + public var Conversation_ClearCache: String { return self._s[655]! } + public var PhotoEditor_ShadowsTint: String { return self._s[656]! } + public var Message_Audio: String { return self._s[657]! } + public var Passport_Language_lt: String { return self._s[658]! } public func Message_PinnedTextMessage(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[653]!, self._r[653]!, [_0]) + return formatWithArgumentRanges(self._s[659]!, self._r[659]!, [_0]) } - public var Permissions_SiriText_v0: String { return self._s[654]! } - public var Conversation_FileICloudDrive: String { return self._s[655]! } - public var ChatList_DeleteForEveryoneConfirmationTitle: String { return self._s[656]! } - public var Notifications_Badge_IncludeMutedChats: String { return self._s[657]! } + public var Permissions_SiriText_v0: String { return self._s[660]! } + public var Conversation_FileICloudDrive: String { return self._s[661]! } + public var ChatList_DeleteForEveryoneConfirmationTitle: String { return self._s[662]! } + public var Notifications_Badge_IncludeMutedChats: String { return self._s[663]! } public func Notification_NewAuthDetected(_ _1: String, _ _2: String, _ _3: String, _ _4: String, _ _5: String, _ _6: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[658]!, self._r[658]!, [_1, _2, _3, _4, _5, _6]) + return formatWithArgumentRanges(self._s[664]!, self._r[664]!, [_1, _2, _3, _4, _5, _6]) } - public var DialogList_ProxyConnectionIssuesTooltip: String { return self._s[659]! } + public var DialogList_ProxyConnectionIssuesTooltip: String { return self._s[665]! } public func Time_MonthOfYear_m5(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[660]!, self._r[660]!, [_0]) + return formatWithArgumentRanges(self._s[666]!, self._r[666]!, [_0]) } - public var Channel_SignMessages: String { return self._s[661]! } + public var Channel_SignMessages: String { return self._s[667]! } public func PUSH_MESSAGE_NOTEXT(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[662]!, self._r[662]!, [_1]) + return formatWithArgumentRanges(self._s[668]!, self._r[668]!, [_1]) } - public var Compose_ChannelTokenListPlaceholder: String { return self._s[663]! } - public var Passport_ScanPassport: String { return self._s[664]! } - public var Watch_Suggestion_Thanks: String { return self._s[665]! } - public var BlockedUsers_AddNew: String { return self._s[666]! } + public var Compose_ChannelTokenListPlaceholder: String { return self._s[669]! } + public var Passport_ScanPassport: String { return self._s[670]! } + public var Watch_Suggestion_Thanks: String { return self._s[671]! } + public var BlockedUsers_AddNew: String { return self._s[672]! } public func PUSH_CHAT_MESSAGE(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[667]!, self._r[667]!, [_1, _2]) + return formatWithArgumentRanges(self._s[673]!, self._r[673]!, [_1, _2]) } - public var Watch_Message_Invoice: String { return self._s[668]! } - public var SettingsSearch_Synonyms_Privacy_LastSeen: String { return self._s[669]! } - public var Month_GenJuly: String { return self._s[670]! } - public var UserInfo_StartSecretChatStart: String { return self._s[671]! } - public var SocksProxySetup_ProxySocks5: String { return self._s[672]! } - public var IntentsSettings_SuggestByShare: String { return self._s[674]! } - public var Notification_Exceptions_DeleteAllConfirmation: String { return self._s[675]! } - public var Notification_ChannelInviterSelf: String { return self._s[676]! } - public var CheckoutInfo_ReceiverInfoEmail: String { return self._s[677]! } + public var Watch_Message_Invoice: String { return self._s[674]! } + public var SettingsSearch_Synonyms_Privacy_LastSeen: String { return self._s[675]! } + public var Month_GenJuly: String { return self._s[676]! } + public var CreatePoll_QuizInfo: String { return self._s[677]! } + public var UserInfo_StartSecretChatStart: String { return self._s[678]! } + public var SocksProxySetup_ProxySocks5: String { return self._s[679]! } + public var IntentsSettings_SuggestByShare: String { return self._s[681]! } + public var Notification_Exceptions_DeleteAllConfirmation: String { return self._s[682]! } + public var Notification_ChannelInviterSelf: String { return self._s[683]! } + public var CheckoutInfo_ReceiverInfoEmail: String { return self._s[684]! } public func ApplyLanguage_ChangeLanguageUnofficialText(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[678]!, self._r[678]!, [_1, _2]) + return formatWithArgumentRanges(self._s[685]!, self._r[685]!, [_1, _2]) } - public var CheckoutInfo_Title: String { return self._s[679]! } - public var Watch_Stickers_RecentPlaceholder: String { return self._s[680]! } + public var CheckoutInfo_Title: String { return self._s[686]! } + public var Watch_Stickers_RecentPlaceholder: String { return self._s[687]! } public func Map_DistanceAway(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[681]!, self._r[681]!, [_0]) + return formatWithArgumentRanges(self._s[688]!, self._r[688]!, [_0]) } - public var Passport_Identity_MainPage: String { return self._s[682]! } - public var TwoStepAuth_ConfirmEmailResendCode: String { return self._s[683]! } - public var Passport_Language_de: String { return self._s[684]! } - public var Update_Title: String { return self._s[685]! } - public var ContactInfo_PhoneLabelWorkFax: String { return self._s[686]! } - public var Channel_AdminLog_BanEmbedLinks: String { return self._s[687]! } - public var Passport_Email_UseTelegramEmailHelp: String { return self._s[688]! } - public var Notifications_ChannelNotificationsPreview: String { return self._s[689]! } - public var NotificationsSound_Telegraph: String { return self._s[690]! } - public var Watch_LastSeen_ALongTimeAgo: String { return self._s[691]! } - public var ChannelMembers_WhoCanAddMembers: String { return self._s[692]! } + public var Passport_Identity_MainPage: String { return self._s[689]! } + public var TwoStepAuth_ConfirmEmailResendCode: String { return self._s[690]! } + public var Passport_Language_de: String { return self._s[691]! } + public var Update_Title: String { return self._s[692]! } + public var ContactInfo_PhoneLabelWorkFax: String { return self._s[693]! } + public var Channel_AdminLog_BanEmbedLinks: String { return self._s[694]! } + public var Passport_Email_UseTelegramEmailHelp: String { return self._s[695]! } + public var Notifications_ChannelNotificationsPreview: String { return self._s[696]! } + public var NotificationsSound_Telegraph: String { return self._s[697]! } + public var Watch_LastSeen_ALongTimeAgo: String { return self._s[698]! } + public var ChannelMembers_WhoCanAddMembers: String { return self._s[699]! } public func AutoDownloadSettings_UpTo(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[693]!, self._r[693]!, [_0]) + return formatWithArgumentRanges(self._s[700]!, self._r[700]!, [_0]) } - public var ClearCache_Description: String { return self._s[694]! } - public var Stickers_SuggestAll: String { return self._s[695]! } - public var Conversation_ForwardTitle: String { return self._s[696]! } - public var Appearance_ThemePreview_ChatList_7_Name: String { return self._s[697]! } + public var ClearCache_Description: String { return self._s[701]! } + public var Stickers_SuggestAll: String { return self._s[702]! } + public var Conversation_ForwardTitle: String { return self._s[703]! } + public var Appearance_ThemePreview_ChatList_7_Name: String { return self._s[704]! } public func Notification_JoinedChannel(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[698]!, self._r[698]!, [_0]) + return formatWithArgumentRanges(self._s[705]!, self._r[705]!, [_0]) } - public var Calls_NewCall: String { return self._s[699]! } - public var Call_StatusEnded: String { return self._s[700]! } - public var AutoDownloadSettings_DataUsageLow: String { return self._s[701]! } - public var Settings_ProxyConnected: String { return self._s[702]! } - public var Channel_AdminLogFilter_EventsPinned: String { return self._s[703]! } - public var PhotoEditor_QualityVeryLow: String { return self._s[704]! } - public var Channel_AdminLogFilter_EventsDeletedMessages: String { return self._s[705]! } - public var Passport_PasswordPlaceholder: String { return self._s[706]! } - public var Message_PinnedInvoice: String { return self._s[707]! } - public var Passport_Identity_IssueDate: String { return self._s[708]! } - public var Passport_Language_pl: String { return self._s[709]! } + public var Calls_NewCall: String { return self._s[706]! } + public var Call_StatusEnded: String { return self._s[707]! } + public var AutoDownloadSettings_DataUsageLow: String { return self._s[708]! } + public var Settings_ProxyConnected: String { return self._s[709]! } + public var Channel_AdminLogFilter_EventsPinned: String { return self._s[710]! } + public var PhotoEditor_QualityVeryLow: String { return self._s[711]! } + public var Channel_AdminLogFilter_EventsDeletedMessages: String { return self._s[712]! } + public var Passport_PasswordPlaceholder: String { return self._s[713]! } + public var Message_PinnedInvoice: String { return self._s[714]! } + public var Passport_Identity_IssueDate: String { return self._s[715]! } + public var Passport_Language_pl: String { return self._s[716]! } public func ChannelInfo_ChannelForbidden(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[710]!, self._r[710]!, [_0]) + return formatWithArgumentRanges(self._s[717]!, self._r[717]!, [_0]) } - public var SocksProxySetup_PasteFromClipboard: String { return self._s[711]! } - public var Call_StatusConnecting: String { return self._s[712]! } + public var SocksProxySetup_PasteFromClipboard: String { return self._s[718]! } + public var Call_StatusConnecting: String { return self._s[719]! } public func Username_UsernameIsAvailable(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[713]!, self._r[713]!, [_0]) + return formatWithArgumentRanges(self._s[720]!, self._r[720]!, [_0]) } - public var ChatSettings_ConnectionType_UseProxy: String { return self._s[715]! } - public var Common_Edit: String { return self._s[716]! } - public var PrivacySettings_LastSeenNobody: String { return self._s[717]! } + public var ChatSettings_ConnectionType_UseProxy: String { return self._s[722]! } + public var Common_Edit: String { return self._s[723]! } + public var PrivacySettings_LastSeenNobody: String { return self._s[724]! } public func Notification_LeftChat(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[718]!, self._r[718]!, [_0]) + return formatWithArgumentRanges(self._s[725]!, self._r[725]!, [_0]) } - public var GroupInfo_ChatAdmins: String { return self._s[719]! } - public var PrivateDataSettings_Title: String { return self._s[720]! } - public var Login_CancelPhoneVerificationStop: String { return self._s[721]! } - public var ChatList_Read: String { return self._s[722]! } - public var Wallet_WordImport_Text: String { return self._s[723]! } - public var Undo_ChatClearedForBothSides: String { return self._s[724]! } - public var GroupPermission_SectionTitle: String { return self._s[725]! } - public var TwoFactorSetup_Intro_Title: String { return self._s[727]! } + public var GroupInfo_ChatAdmins: String { return self._s[726]! } + public var PrivateDataSettings_Title: String { return self._s[727]! } + public var Login_CancelPhoneVerificationStop: String { return self._s[728]! } + public var ChatList_Read: String { return self._s[729]! } + public var Wallet_WordImport_Text: String { return self._s[730]! } + public var Undo_ChatClearedForBothSides: String { return self._s[731]! } + public var GroupPermission_SectionTitle: String { return self._s[732]! } + public var TwoFactorSetup_Intro_Title: String { return self._s[734]! } public func PUSH_CHAT_LEFT(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[728]!, self._r[728]!, [_1, _2]) + return formatWithArgumentRanges(self._s[735]!, self._r[735]!, [_1, _2]) } - public var Checkout_ErrorPaymentFailed: String { return self._s[729]! } - public var Update_UpdateApp: String { return self._s[730]! } - public var Group_Username_RevokeExistingUsernamesInfo: String { return self._s[731]! } - public var Settings_Appearance: String { return self._s[732]! } - public var SettingsSearch_Synonyms_Stickers_SuggestStickers: String { return self._s[736]! } - public var Watch_Location_Access: String { return self._s[737]! } - public var ShareMenu_CopyShareLink: String { return self._s[739]! } - public var TwoStepAuth_SetupHintTitle: String { return self._s[740]! } - public var Conversation_Theme: String { return self._s[742]! } + public var Checkout_ErrorPaymentFailed: String { return self._s[736]! } + public var Update_UpdateApp: String { return self._s[737]! } + public var Group_Username_RevokeExistingUsernamesInfo: String { return self._s[738]! } + public var Settings_Appearance: String { return self._s[739]! } + public var SettingsSearch_Synonyms_Stickers_SuggestStickers: String { return self._s[743]! } + public var Watch_Location_Access: String { return self._s[744]! } + public var ShareMenu_CopyShareLink: String { return self._s[746]! } + public var TwoStepAuth_SetupHintTitle: String { return self._s[747]! } + public var Conversation_Theme: String { return self._s[749]! } public func DialogList_SingleRecordingVideoMessageSuffix(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[743]!, self._r[743]!, [_0]) + return formatWithArgumentRanges(self._s[750]!, self._r[750]!, [_0]) } - public var Notifications_ClassicTones: String { return self._s[744]! } - public var Weekday_ShortWednesday: String { return self._s[745]! } - public var WallpaperPreview_SwipeColorsBottomText: String { return self._s[746]! } - public var Undo_LeftGroup: String { return self._s[749]! } - public var Wallet_RestoreFailed_Text: String { return self._s[750]! } - public var Conversation_LinkDialogCopy: String { return self._s[751]! } - public var Wallet_TransactionInfo_NoAddress: String { return self._s[753]! } - public var Wallet_Navigation_Back: String { return self._s[754]! } - public var KeyCommand_FocusOnInputField: String { return self._s[755]! } - public var Contacts_SelectAll: String { return self._s[756]! } - public var Preview_SaveToCameraRoll: String { return self._s[757]! } - public var PrivacySettings_PasscodeOff: String { return self._s[758]! } - public var Appearance_ThemePreview_ChatList_6_Name: String { return self._s[759]! } - public var Wallpaper_Title: String { return self._s[760]! } - public var Conversation_FilePhotoOrVideo: String { return self._s[761]! } - public var AccessDenied_Camera: String { return self._s[762]! } - public var Watch_Compose_CurrentLocation: String { return self._s[763]! } - public var Channel_DiscussionGroup_MakeHistoryPublicProceed: String { return self._s[765]! } + public var Notifications_ClassicTones: String { return self._s[751]! } + public var Weekday_ShortWednesday: String { return self._s[752]! } + public var WallpaperPreview_SwipeColorsBottomText: String { return self._s[753]! } + public var Undo_LeftGroup: String { return self._s[756]! } + public var Wallet_RestoreFailed_Text: String { return self._s[757]! } + public var Conversation_LinkDialogCopy: String { return self._s[758]! } + public var Wallet_TransactionInfo_NoAddress: String { return self._s[760]! } + public var Wallet_Navigation_Back: String { return self._s[761]! } + public var KeyCommand_FocusOnInputField: String { return self._s[762]! } + public var Contacts_SelectAll: String { return self._s[763]! } + public var Preview_SaveToCameraRoll: String { return self._s[764]! } + public var PrivacySettings_PasscodeOff: String { return self._s[765]! } + public var Appearance_ThemePreview_ChatList_6_Name: String { return self._s[766]! } + public func PUSH_CHANNEL_MESSAGE_QUIZ(_ _1: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[767]!, self._r[767]!, [_1]) + } + public var Wallpaper_Title: String { return self._s[768]! } + public var Conversation_FilePhotoOrVideo: String { return self._s[769]! } + public var AccessDenied_Camera: String { return self._s[770]! } + public var Watch_Compose_CurrentLocation: String { return self._s[771]! } + public var Channel_DiscussionGroup_MakeHistoryPublicProceed: String { return self._s[773]! } public func SecretImage_NotViewedYet(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[766]!, self._r[766]!, [_0]) + return formatWithArgumentRanges(self._s[774]!, self._r[774]!, [_0]) } - public var GroupInfo_InvitationLinkDoesNotExist: String { return self._s[767]! } - public var Passport_Language_ro: String { return self._s[768]! } - public var EditTheme_UploadNewTheme: String { return self._s[769]! } - public var CheckoutInfo_SaveInfoHelp: String { return self._s[770]! } - public var Wallet_Intro_Terms: String { return self._s[771]! } + public var GroupInfo_InvitationLinkDoesNotExist: String { return self._s[775]! } + public var Passport_Language_ro: String { return self._s[776]! } + public var EditTheme_UploadNewTheme: String { return self._s[777]! } + public var CheckoutInfo_SaveInfoHelp: String { return self._s[778]! } + public var Wallet_Intro_Terms: String { return self._s[779]! } public func Notification_SecretChatMessageScreenshot(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[772]!, self._r[772]!, [_0]) - } - public var Login_CancelPhoneVerification: String { return self._s[773]! } - public var State_ConnectingToProxy: String { return self._s[774]! } - public var Calls_RatingTitle: String { return self._s[775]! } - public var Generic_ErrorMoreInfo: String { return self._s[776]! } - public var ChatList_Search_ShowMore: String { return self._s[777]! } - public var Appearance_PreviewReplyText: String { return self._s[778]! } - public var CheckoutInfo_ShippingInfoPostcodePlaceholder: String { return self._s[779]! } - public func Wallet_Send_Balance(_ _0: String) -> (String, [(Int, NSRange)]) { return formatWithArgumentRanges(self._s[780]!, self._r[780]!, [_0]) } - public var IntentsSettings_SuggestedChatsContacts: String { return self._s[781]! } - public var SharedMedia_CategoryLinks: String { return self._s[782]! } - public var Calls_Missed: String { return self._s[783]! } - public var Cache_Photos: String { return self._s[787]! } - public var GroupPermission_NoAddMembers: String { return self._s[788]! } - public var ScheduledMessages_Title: String { return self._s[789]! } - public func Channel_AdminLog_MessageUnpinned(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[790]!, self._r[790]!, [_0]) + public var Login_CancelPhoneVerification: String { return self._s[781]! } + public var State_ConnectingToProxy: String { return self._s[782]! } + public var Calls_RatingTitle: String { return self._s[783]! } + public var Generic_ErrorMoreInfo: String { return self._s[784]! } + public var ChatList_Search_ShowMore: String { return self._s[785]! } + public var Appearance_PreviewReplyText: String { return self._s[786]! } + public var CheckoutInfo_ShippingInfoPostcodePlaceholder: String { return self._s[787]! } + public func Wallet_Send_Balance(_ _0: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[788]!, self._r[788]!, [_0]) } - public var Conversation_ShareBotLocationConfirmationTitle: String { return self._s[791]! } - public var Settings_ProxyDisabled: String { return self._s[792]! } + public var IntentsSettings_SuggestedChatsContacts: String { return self._s[789]! } + public var SharedMedia_CategoryLinks: String { return self._s[790]! } + public var Calls_Missed: String { return self._s[791]! } + public var Cache_Photos: String { return self._s[795]! } + public var GroupPermission_NoAddMembers: String { return self._s[796]! } + public var ScheduledMessages_Title: String { return self._s[797]! } + public func Channel_AdminLog_MessageUnpinned(_ _0: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[798]!, self._r[798]!, [_0]) + } + public var Conversation_ShareBotLocationConfirmationTitle: String { return self._s[799]! } + public var Settings_ProxyDisabled: String { return self._s[800]! } public func Settings_ApplyProxyAlertCredentials(_ _1: String, _ _2: String, _ _3: String, _ _4: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[793]!, self._r[793]!, [_1, _2, _3, _4]) + return formatWithArgumentRanges(self._s[801]!, self._r[801]!, [_1, _2, _3, _4]) } public func Conversation_RestrictedMediaTimed(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[794]!, self._r[794]!, [_0]) + return formatWithArgumentRanges(self._s[802]!, self._r[802]!, [_0]) } - public var ChatList_Context_RemoveFromRecents: String { return self._s[796]! } - public var Appearance_Title: String { return self._s[797]! } + public var ChatList_Context_RemoveFromRecents: String { return self._s[804]! } + public var Appearance_Title: String { return self._s[805]! } public func Time_MonthOfYear_m2(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[799]!, self._r[799]!, [_0]) + return formatWithArgumentRanges(self._s[807]!, self._r[807]!, [_0]) } - public var Conversation_WalletRequiredText: String { return self._s[800]! } - public var StickerPacksSettings_ShowStickersButtonHelp: String { return self._s[801]! } - public var OldChannels_NoticeCreateText: String { return self._s[802]! } - public var Channel_EditMessageErrorGeneric: String { return self._s[803]! } - public var Privacy_Calls_IntegrationHelp: String { return self._s[804]! } - public var Preview_DeletePhoto: String { return self._s[805]! } - public var Appearance_AppIconFilledX: String { return self._s[806]! } - public var PrivacySettings_PrivacyTitle: String { return self._s[807]! } + public var Conversation_WalletRequiredText: String { return self._s[808]! } + public var StickerPacksSettings_ShowStickersButtonHelp: String { return self._s[809]! } + public var OldChannels_NoticeCreateText: String { return self._s[810]! } + public var Channel_EditMessageErrorGeneric: String { return self._s[811]! } + public var Privacy_Calls_IntegrationHelp: String { return self._s[812]! } + public var Preview_DeletePhoto: String { return self._s[813]! } + public var Appearance_AppIconFilledX: String { return self._s[814]! } + public var PrivacySettings_PrivacyTitle: String { return self._s[815]! } public func Conversation_BotInteractiveUrlAlert(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[808]!, self._r[808]!, [_0]) + return formatWithArgumentRanges(self._s[816]!, self._r[816]!, [_0]) } - public var Coub_TapForSound: String { return self._s[810]! } - public var Map_LocatingError: String { return self._s[811]! } - public var TwoStepAuth_EmailChangeSuccess: String { return self._s[813]! } - public var Conversation_SendMessage_SendSilently: String { return self._s[814]! } - public var VoiceOver_MessageContextOpenMessageMenu: String { return self._s[815]! } + public var Coub_TapForSound: String { return self._s[819]! } + public var Map_LocatingError: String { return self._s[820]! } + public var TwoStepAuth_EmailChangeSuccess: String { return self._s[822]! } + public var Conversation_SendMessage_SendSilently: String { return self._s[823]! } + public var VoiceOver_MessageContextOpenMessageMenu: String { return self._s[824]! } public func Wallet_Time_PreciseDate_m8(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[816]!, self._r[816]!, [_1, _2, _3]) + return formatWithArgumentRanges(self._s[825]!, self._r[825]!, [_1, _2, _3]) } - public var Passport_ForgottenPassword: String { return self._s[817]! } - public var GroupInfo_InviteLink_RevokeLink: String { return self._s[818]! } - public var StickerPacksSettings_ArchivedPacks: String { return self._s[819]! } - public var Login_TermsOfServiceSignupDecline: String { return self._s[821]! } - public var Channel_Moderator_AccessLevelRevoke: String { return self._s[822]! } - public var Message_Location: String { return self._s[823]! } - public var Passport_Identity_NamePlaceholder: String { return self._s[824]! } - public var Channel_Management_Title: String { return self._s[825]! } - public var DialogList_SearchSectionDialogs: String { return self._s[827]! } - public var Compose_NewChannel_Members: String { return self._s[828]! } + public var Passport_ForgottenPassword: String { return self._s[826]! } + public var GroupInfo_InviteLink_RevokeLink: String { return self._s[827]! } + public var StickerPacksSettings_ArchivedPacks: String { return self._s[828]! } + public var Login_TermsOfServiceSignupDecline: String { return self._s[830]! } + public var Channel_Moderator_AccessLevelRevoke: String { return self._s[831]! } + public var Message_Location: String { return self._s[832]! } + public var Passport_Identity_NamePlaceholder: String { return self._s[833]! } + public var Channel_Management_Title: String { return self._s[834]! } + public var DialogList_SearchSectionDialogs: String { return self._s[836]! } + public var Compose_NewChannel_Members: String { return self._s[837]! } public func DialogList_SingleUploadingFileSuffix(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[829]!, self._r[829]!, [_0]) + return formatWithArgumentRanges(self._s[838]!, self._r[838]!, [_0]) } - public var GroupInfo_Location: String { return self._s[830]! } - public var Appearance_ThemePreview_ChatList_5_Name: String { return self._s[831]! } - public var ClearCache_Clear: String { return self._s[832]! } - public var AutoNightTheme_ScheduledFrom: String { return self._s[833]! } - public var PhotoEditor_WarmthTool: String { return self._s[834]! } - public var Passport_Language_tr: String { return self._s[835]! } + public var GroupInfo_Location: String { return self._s[839]! } + public var Appearance_ThemePreview_ChatList_5_Name: String { return self._s[840]! } + public var ClearCache_Clear: String { return self._s[841]! } + public var AutoNightTheme_ScheduledFrom: String { return self._s[842]! } + public var PhotoEditor_WarmthTool: String { return self._s[843]! } + public var Passport_Language_tr: String { return self._s[844]! } public func PUSH_MESSAGE_GAME_SCORE(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[836]!, self._r[836]!, [_1, _2, _3]) + return formatWithArgumentRanges(self._s[845]!, self._r[845]!, [_1, _2, _3]) } - public var OldChannels_NoticeUpgradeText: String { return self._s[837]! } - public var Login_ResetAccountProtected_Reset: String { return self._s[839]! } - public var Watch_PhotoView_Title: String { return self._s[840]! } - public var Passport_Phone_Delete: String { return self._s[841]! } - public var Undo_ChatDeletedForBothSides: String { return self._s[842]! } - public var Conversation_EditingMessageMediaEditCurrentPhoto: String { return self._s[843]! } - public var GroupInfo_Permissions: String { return self._s[844]! } - public var PasscodeSettings_TurnPasscodeOff: String { return self._s[845]! } - public var Profile_ShareContactButton: String { return self._s[846]! } - public var ChatSettings_Other: String { return self._s[847]! } - public var UserInfo_NotificationsDisabled: String { return self._s[848]! } - public var CheckoutInfo_ShippingInfoCity: String { return self._s[849]! } - public var LastSeen_WithinAMonth: String { return self._s[850]! } - public var VoiceOver_Chat_PlayHint: String { return self._s[851]! } - public var Conversation_ReportGroupLocation: String { return self._s[852]! } - public var Conversation_EncryptionCanceled: String { return self._s[853]! } - public var MediaPicker_GroupDescription: String { return self._s[854]! } - public var WebSearch_Images: String { return self._s[855]! } + public var OldChannels_NoticeUpgradeText: String { return self._s[846]! } + public var Login_ResetAccountProtected_Reset: String { return self._s[848]! } + public var Watch_PhotoView_Title: String { return self._s[849]! } + public var Passport_Phone_Delete: String { return self._s[850]! } + public var Undo_ChatDeletedForBothSides: String { return self._s[851]! } + public var Conversation_EditingMessageMediaEditCurrentPhoto: String { return self._s[852]! } + public var GroupInfo_Permissions: String { return self._s[853]! } + public var PasscodeSettings_TurnPasscodeOff: String { return self._s[854]! } + public var Profile_ShareContactButton: String { return self._s[855]! } + public var ChatSettings_Other: String { return self._s[856]! } + public var UserInfo_NotificationsDisabled: String { return self._s[857]! } + public var CheckoutInfo_ShippingInfoCity: String { return self._s[858]! } + public var LastSeen_WithinAMonth: String { return self._s[859]! } + public var VoiceOver_Chat_PlayHint: String { return self._s[860]! } + public var Conversation_ReportGroupLocation: String { return self._s[861]! } + public var Conversation_EncryptionCanceled: String { return self._s[862]! } + public var MediaPicker_GroupDescription: String { return self._s[863]! } + public var WebSearch_Images: String { return self._s[864]! } public func Channel_Management_PromotedBy(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[856]!, self._r[856]!, [_0]) + return formatWithArgumentRanges(self._s[865]!, self._r[865]!, [_0]) } - public var Message_Photo: String { return self._s[857]! } - public var PasscodeSettings_HelpBottom: String { return self._s[858]! } - public var AutoDownloadSettings_VideosTitle: String { return self._s[859]! } - public var VoiceOver_Media_PlaybackRateChange: String { return self._s[860]! } - public var Passport_Identity_AddDriversLicense: String { return self._s[861]! } - public var TwoStepAuth_EnterPasswordPassword: String { return self._s[862]! } - public var NotificationsSound_Calypso: String { return self._s[863]! } - public var Map_Map: String { return self._s[864]! } - public var CheckoutInfo_ReceiverInfoTitle: String { return self._s[866]! } - public var ChatSettings_TextSizeUnits: String { return self._s[867]! } + public var Message_Photo: String { return self._s[866]! } + public var PasscodeSettings_HelpBottom: String { return self._s[867]! } + public var AutoDownloadSettings_VideosTitle: String { return self._s[868]! } + public var VoiceOver_Media_PlaybackRateChange: String { return self._s[869]! } + public var Passport_Identity_AddDriversLicense: String { return self._s[870]! } + public var TwoStepAuth_EnterPasswordPassword: String { return self._s[871]! } + public var NotificationsSound_Calypso: String { return self._s[872]! } + public var Map_Map: String { return self._s[873]! } + public func Conversation_LiveLocationYouAndOther(_ _0: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[874]!, self._r[874]!, [_0]) + } + public var CheckoutInfo_ReceiverInfoTitle: String { return self._s[876]! } + public var ChatSettings_TextSizeUnits: String { return self._s[877]! } public func VoiceOver_Chat_FileFrom(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[868]!, self._r[868]!, [_0]) + return formatWithArgumentRanges(self._s[878]!, self._r[878]!, [_0]) } - public var Common_of: String { return self._s[869]! } - public var Conversation_ForwardContacts: String { return self._s[872]! } - public var IntentsSettings_SuggestByAll: String { return self._s[874]! } + public var Common_of: String { return self._s[879]! } + public var Conversation_ForwardContacts: String { return self._s[882]! } + public var IntentsSettings_SuggestByAll: String { return self._s[884]! } public func Call_AnsweringWithAccount(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[875]!, self._r[875]!, [_0]) + return formatWithArgumentRanges(self._s[885]!, self._r[885]!, [_0]) } - public var Passport_Language_hy: String { return self._s[876]! } - public var Notifications_MessageNotificationsHelp: String { return self._s[877]! } - public var AutoDownloadSettings_Reset: String { return self._s[878]! } - public var Wallet_TransactionInfo_AddressCopied: String { return self._s[879]! } - public var Paint_ClearConfirm: String { return self._s[880]! } - public var Camera_VideoMode: String { return self._s[881]! } + public var Passport_Language_hy: String { return self._s[886]! } + public var Notifications_MessageNotificationsHelp: String { return self._s[887]! } + public var AutoDownloadSettings_Reset: String { return self._s[888]! } + public var Wallet_TransactionInfo_AddressCopied: String { return self._s[889]! } + public var Paint_ClearConfirm: String { return self._s[890]! } + public var Camera_VideoMode: String { return self._s[891]! } public func Conversation_RestrictedStickersTimed(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[882]!, self._r[882]!, [_0]) + return formatWithArgumentRanges(self._s[892]!, self._r[892]!, [_0]) } - public var Privacy_Calls_AlwaysAllow_Placeholder: String { return self._s[883]! } - public var Conversation_ViewBackground: String { return self._s[884]! } + public var Privacy_Calls_AlwaysAllow_Placeholder: String { return self._s[893]! } + public var Conversation_ViewBackground: String { return self._s[894]! } public func Wallet_Info_TransactionDateHeaderYear(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[885]!, self._r[885]!, [_1, _2, _3]) + return formatWithArgumentRanges(self._s[895]!, self._r[895]!, [_1, _2, _3]) } - public var Passport_Language_el: String { return self._s[886]! } - public var PhotoEditor_Original: String { return self._s[887]! } - public var Settings_FAQ_Button: String { return self._s[889]! } - public var Channel_Setup_PublicNoLink: String { return self._s[891]! } - public var Conversation_UnsupportedMedia: String { return self._s[892]! } - public var Conversation_SlideToCancel: String { return self._s[893]! } - public var Appearance_ThemePreview_ChatList_4_Name: String { return self._s[894]! } - public var Passport_Identity_OneOfTypeInternalPassport: String { return self._s[895]! } - public var CheckoutInfo_ShippingInfoPostcode: String { return self._s[896]! } - public var Conversation_ReportSpamChannelConfirmation: String { return self._s[897]! } - public var AutoNightTheme_NotAvailable: String { return self._s[898]! } - public var Conversation_Owner: String { return self._s[899]! } - public var Common_Create: String { return self._s[900]! } - public var Settings_ApplyProxyAlertEnable: String { return self._s[901]! } - public var ContactList_Context_Call: String { return self._s[902]! } - public var Localization_ChooseLanguage: String { return self._s[904]! } - public var ChatList_Context_AddToContacts: String { return self._s[906]! } - public var OldChannels_NoticeTitle: String { return self._s[907]! } - public var Settings_Proxy: String { return self._s[909]! } - public var Privacy_TopPeersHelp: String { return self._s[910]! } - public var CheckoutInfo_ShippingInfoCountryPlaceholder: String { return self._s[911]! } - public var Chat_UnsendMyMessages: String { return self._s[912]! } + public var Passport_Language_el: String { return self._s[896]! } + public var PhotoEditor_Original: String { return self._s[897]! } + public var Settings_FAQ_Button: String { return self._s[899]! } + public var Channel_Setup_PublicNoLink: String { return self._s[901]! } + public var Conversation_UnsupportedMedia: String { return self._s[902]! } + public var Conversation_SlideToCancel: String { return self._s[903]! } + public var Appearance_ThemePreview_ChatList_4_Name: String { return self._s[904]! } + public var Passport_Identity_OneOfTypeInternalPassport: String { return self._s[905]! } + public var CheckoutInfo_ShippingInfoPostcode: String { return self._s[906]! } + public var Conversation_ReportSpamChannelConfirmation: String { return self._s[907]! } + public var AutoNightTheme_NotAvailable: String { return self._s[908]! } + public var Conversation_Owner: String { return self._s[909]! } + public var Common_Create: String { return self._s[910]! } + public var Settings_ApplyProxyAlertEnable: String { return self._s[911]! } + public var ContactList_Context_Call: String { return self._s[912]! } + public var Localization_ChooseLanguage: String { return self._s[914]! } + public var ChatList_Context_AddToContacts: String { return self._s[916]! } + public var OldChannels_NoticeTitle: String { return self._s[917]! } + public var Settings_Proxy: String { return self._s[919]! } + public var Privacy_TopPeersHelp: String { return self._s[920]! } + public var CheckoutInfo_ShippingInfoCountryPlaceholder: String { return self._s[921]! } + public var Chat_UnsendMyMessages: String { return self._s[922]! } public func VoiceOver_Chat_Duration(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[913]!, self._r[913]!, [_0]) + return formatWithArgumentRanges(self._s[923]!, self._r[923]!, [_0]) } - public var TwoStepAuth_ConfirmationAbort: String { return self._s[914]! } + public var TwoStepAuth_ConfirmationAbort: String { return self._s[924]! } public func Contacts_AccessDeniedHelpPortrait(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[916]!, self._r[916]!, [_0]) + return formatWithArgumentRanges(self._s[926]!, self._r[926]!, [_0]) } - public var Contacts_SortedByPresence: String { return self._s[917]! } - public var Passport_Identity_SurnamePlaceholder: String { return self._s[918]! } - public var Cache_Title: String { return self._s[919]! } + public var Contacts_SortedByPresence: String { return self._s[927]! } + public var Passport_Identity_SurnamePlaceholder: String { return self._s[928]! } + public var Cache_Title: String { return self._s[929]! } public func Login_PhoneBannedEmailSubject(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[920]!, self._r[920]!, [_0]) + return formatWithArgumentRanges(self._s[930]!, self._r[930]!, [_0]) } - public var TwoStepAuth_EmailCodeExpired: String { return self._s[921]! } - public var Channel_Moderator_Title: String { return self._s[922]! } - public var InstantPage_AutoNightTheme: String { return self._s[924]! } + public var TwoStepAuth_EmailCodeExpired: String { return self._s[931]! } + public var Channel_Moderator_Title: String { return self._s[932]! } + public var InstantPage_AutoNightTheme: String { return self._s[934]! } public func PUSH_MESSAGE_POLL(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[927]!, self._r[927]!, [_1]) + return formatWithArgumentRanges(self._s[937]!, self._r[937]!, [_1]) } - public var Passport_Scans_Upload: String { return self._s[928]! } - public var Undo_Undo: String { return self._s[930]! } - public var Contacts_AccessDeniedHelpON: String { return self._s[931]! } - public var TwoStepAuth_RemovePassword: String { return self._s[932]! } - public var Common_Delete: String { return self._s[933]! } - public var Contacts_AddPeopleNearby: String { return self._s[935]! } - public var Conversation_ContextMenuDelete: String { return self._s[936]! } - public var SocksProxySetup_Credentials: String { return self._s[937]! } - public var Appearance_EditTheme: String { return self._s[939]! } - public var ClearCache_StorageOtherApps: String { return self._s[940]! } - public var PasscodeSettings_AutoLock_Disabled: String { return self._s[941]! } - public var Wallet_Send_NetworkErrorText: String { return self._s[942]! } - public var AuthSessions_DevicesTitle: String { return self._s[944]! } - public var Passport_Address_OneOfTypeRentalAgreement: String { return self._s[946]! } - public var Conversation_ShareBotContactConfirmationTitle: String { return self._s[947]! } - public var Passport_Language_id: String { return self._s[949]! } - public var WallpaperSearch_ColorTeal: String { return self._s[950]! } - public var ChannelIntro_Title: String { return self._s[951]! } + public var Passport_Scans_Upload: String { return self._s[938]! } + public var Undo_Undo: String { return self._s[940]! } + public var Contacts_AccessDeniedHelpON: String { return self._s[941]! } + public var TwoStepAuth_RemovePassword: String { return self._s[942]! } + public var Common_Delete: String { return self._s[943]! } + public var Contacts_AddPeopleNearby: String { return self._s[945]! } + public var Conversation_ContextMenuDelete: String { return self._s[946]! } + public var SocksProxySetup_Credentials: String { return self._s[947]! } + public var Appearance_EditTheme: String { return self._s[949]! } + public var ClearCache_StorageOtherApps: String { return self._s[950]! } + public var PasscodeSettings_AutoLock_Disabled: String { return self._s[951]! } + public var Wallet_Send_NetworkErrorText: String { return self._s[952]! } + public var AuthSessions_DevicesTitle: String { return self._s[954]! } + public var Passport_Address_OneOfTypeRentalAgreement: String { return self._s[956]! } + public var Conversation_ShareBotContactConfirmationTitle: String { return self._s[957]! } + public var Passport_Language_id: String { return self._s[959]! } + public var WallpaperSearch_ColorTeal: String { return self._s[960]! } + public var ChannelIntro_Title: String { return self._s[961]! } public func Channel_AdminLog_MessageToggleSignaturesOff(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[952]!, self._r[952]!, [_0]) + return formatWithArgumentRanges(self._s[962]!, self._r[962]!, [_0]) } - public var VoiceOver_Chat_OpenLinkHint: String { return self._s[954]! } - public var VoiceOver_Chat_Reply: String { return self._s[955]! } - public var ScheduledMessages_BotActionUnavailable: String { return self._s[956]! } - public var Channel_Info_Description: String { return self._s[957]! } - public var Stickers_FavoriteStickers: String { return self._s[958]! } - public var Channel_BanUser_PermissionAddMembers: String { return self._s[959]! } - public var Notifications_DisplayNamesOnLockScreen: String { return self._s[960]! } - public var ChatSearch_ResultsTooltip: String { return self._s[961]! } - public var Wallet_VoiceOver_Editing_ClearText: String { return self._s[962]! } - public var Calls_NoMissedCallsPlacehoder: String { return self._s[963]! } - public var Group_PublicLink_Placeholder: String { return self._s[964]! } - public var Notifications_ExceptionsDefaultSound: String { return self._s[965]! } + public var VoiceOver_Chat_OpenLinkHint: String { return self._s[964]! } + public var VoiceOver_Chat_Reply: String { return self._s[965]! } + public var ScheduledMessages_BotActionUnavailable: String { return self._s[966]! } + public var Channel_Info_Description: String { return self._s[967]! } + public var Stickers_FavoriteStickers: String { return self._s[968]! } + public var Channel_BanUser_PermissionAddMembers: String { return self._s[969]! } + public var Notifications_DisplayNamesOnLockScreen: String { return self._s[970]! } + public var ChatSearch_ResultsTooltip: String { return self._s[971]! } + public var Wallet_VoiceOver_Editing_ClearText: String { return self._s[972]! } + public var Calls_NoMissedCallsPlacehoder: String { return self._s[973]! } + public var Group_PublicLink_Placeholder: String { return self._s[974]! } + public var Notifications_ExceptionsDefaultSound: String { return self._s[975]! } public func PUSH_CHANNEL_MESSAGE_POLL(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[966]!, self._r[966]!, [_1]) + return formatWithArgumentRanges(self._s[976]!, self._r[976]!, [_1]) } - public var TextFormat_Underline: String { return self._s[967]! } + public var TextFormat_Underline: String { return self._s[977]! } public func DialogList_SearchSubtitleFormat(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[969]!, self._r[969]!, [_1, _2]) - } - public func Channel_AdminLog_MessageRemovedGroupStickerPack(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[970]!, self._r[970]!, [_0]) - } - public var Appearance_ThemePreview_ChatList_3_Name: String { return self._s[971]! } - public func Channel_OwnershipTransfer_TransferCompleted(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[972]!, self._r[972]!, [_1, _2]) - } - public var Wallet_Intro_ImportExisting: String { return self._s[973]! } - public var GroupPermission_Delete: String { return self._s[974]! } - public var Passport_Language_uk: String { return self._s[975]! } - public var StickerPack_HideStickers: String { return self._s[977]! } - public var ChangePhoneNumberNumber_NumberPlaceholder: String { return self._s[978]! } - public func PUSH_CHAT_MESSAGE_PHOTO(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { return formatWithArgumentRanges(self._s[979]!, self._r[979]!, [_1, _2]) } - public var Activity_UploadingVideoMessage: String { return self._s[980]! } + public func Channel_AdminLog_MessageRemovedGroupStickerPack(_ _0: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[980]!, self._r[980]!, [_0]) + } + public var Appearance_ThemePreview_ChatList_3_Name: String { return self._s[981]! } + public func Channel_OwnershipTransfer_TransferCompleted(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[982]!, self._r[982]!, [_1, _2]) + } + public var Wallet_Intro_ImportExisting: String { return self._s[983]! } + public var GroupPermission_Delete: String { return self._s[984]! } + public var Passport_Language_uk: String { return self._s[985]! } + public var StickerPack_HideStickers: String { return self._s[987]! } + public var ChangePhoneNumberNumber_NumberPlaceholder: String { return self._s[988]! } + public func PUSH_CHAT_MESSAGE_PHOTO(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[989]!, self._r[989]!, [_1, _2]) + } + public var Activity_UploadingVideoMessage: String { return self._s[990]! } public func GroupPermission_ApplyAlertText(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[981]!, self._r[981]!, [_0]) + return formatWithArgumentRanges(self._s[991]!, self._r[991]!, [_0]) } - public var Channel_TitleInfo: String { return self._s[982]! } - public var StickerPacksSettings_ArchivedPacks_Info: String { return self._s[983]! } - public var Settings_CallSettings: String { return self._s[984]! } - public var Camera_SquareMode: String { return self._s[985]! } - public var Conversation_SendMessage_ScheduleMessage: String { return self._s[986]! } - public var GroupInfo_SharedMediaNone: String { return self._s[987]! } + public var Channel_TitleInfo: String { return self._s[992]! } + public var StickerPacksSettings_ArchivedPacks_Info: String { return self._s[993]! } + public var Settings_CallSettings: String { return self._s[994]! } + public var Camera_SquareMode: String { return self._s[995]! } + public var Conversation_SendMessage_ScheduleMessage: String { return self._s[996]! } + public var GroupInfo_SharedMediaNone: String { return self._s[997]! } public func PUSH_MESSAGE_VIDEO_SECRET(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[988]!, self._r[988]!, [_1]) + return formatWithArgumentRanges(self._s[998]!, self._r[998]!, [_1]) } - public var Bot_GenericBotStatus: String { return self._s[989]! } - public var Application_Update: String { return self._s[991]! } - public var Month_ShortJanuary: String { return self._s[992]! } - public var Contacts_PermissionsKeepDisabled: String { return self._s[993]! } - public var Channel_AdminLog_BanReadMessages: String { return self._s[994]! } - public var Settings_AppLanguage_Unofficial: String { return self._s[995]! } - public var Passport_Address_Street2Placeholder: String { return self._s[996]! } + public var Bot_GenericBotStatus: String { return self._s[999]! } + public var Application_Update: String { return self._s[1001]! } + public var Month_ShortJanuary: String { return self._s[1002]! } + public var Contacts_PermissionsKeepDisabled: String { return self._s[1003]! } + public var Channel_AdminLog_BanReadMessages: String { return self._s[1004]! } + public var Settings_AppLanguage_Unofficial: String { return self._s[1005]! } + public var Passport_Address_Street2Placeholder: String { return self._s[1006]! } public func Map_LiveLocationShortHour(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[997]!, self._r[997]!, [_0]) + return formatWithArgumentRanges(self._s[1007]!, self._r[1007]!, [_0]) } - public var NetworkUsageSettings_Cellular: String { return self._s[998]! } - public var Appearance_PreviewOutgoingText: String { return self._s[999]! } + public var NetworkUsageSettings_Cellular: String { return self._s[1008]! } + public var Appearance_PreviewOutgoingText: String { return self._s[1009]! } public func StickerPackActionInfo_RemovedText(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1000]!, self._r[1000]!, [_0]) + return formatWithArgumentRanges(self._s[1010]!, self._r[1010]!, [_0]) } - public var Notifications_PermissionsAllowInSettings: String { return self._s[1001]! } - public var AutoDownloadSettings_OnForAll: String { return self._s[1003]! } - public var Map_Directions: String { return self._s[1004]! } - public var Passport_FieldIdentityTranslationHelp: String { return self._s[1006]! } - public var Appearance_ThemeDay: String { return self._s[1007]! } - public var LogoutOptions_LogOut: String { return self._s[1008]! } - public var Group_PublicLink_Title: String { return self._s[1010]! } - public var Channel_AddBotErrorNoRights: String { return self._s[1011]! } - public var ChatList_Search_ShowLess: String { return self._s[1012]! } - public var Passport_Identity_AddPassport: String { return self._s[1013]! } - public var LocalGroup_ButtonTitle: String { return self._s[1014]! } - public var Call_Message: String { return self._s[1015]! } - public var PhotoEditor_ExposureTool: String { return self._s[1016]! } - public var Wallet_Receive_CommentInfo: String { return self._s[1018]! } - public var Passport_FieldOneOf_Delimeter: String { return self._s[1019]! } - public var Channel_AdminLog_CanBanUsers: String { return self._s[1021]! } - public var Appearance_ThemePreview_ChatList_2_Name: String { return self._s[1022]! } - public var Appearance_Preview: String { return self._s[1023]! } - public var Compose_ChannelMembers: String { return self._s[1024]! } - public var Conversation_DeleteManyMessages: String { return self._s[1025]! } - public var ReportPeer_ReasonOther_Title: String { return self._s[1026]! } - public var Checkout_ErrorProviderAccountTimeout: String { return self._s[1027]! } - public var TwoStepAuth_ResetAccountConfirmation: String { return self._s[1028]! } - public var Channel_Stickers_CreateYourOwn: String { return self._s[1031]! } - public var Conversation_UpdateTelegram: String { return self._s[1032]! } - public var EditTheme_Create_TopInfo: String { return self._s[1033]! } + public var Notifications_PermissionsAllowInSettings: String { return self._s[1011]! } + public var AutoDownloadSettings_OnForAll: String { return self._s[1013]! } + public var Map_Directions: String { return self._s[1014]! } + public var Passport_FieldIdentityTranslationHelp: String { return self._s[1016]! } + public var Appearance_ThemeDay: String { return self._s[1017]! } + public var LogoutOptions_LogOut: String { return self._s[1018]! } + public var Group_PublicLink_Title: String { return self._s[1020]! } + public var Channel_AddBotErrorNoRights: String { return self._s[1021]! } + public var ChatList_Search_ShowLess: String { return self._s[1022]! } + public var Passport_Identity_AddPassport: String { return self._s[1023]! } + public var LocalGroup_ButtonTitle: String { return self._s[1024]! } + public var Call_Message: String { return self._s[1025]! } + public var PhotoEditor_ExposureTool: String { return self._s[1026]! } + public var Wallet_Receive_CommentInfo: String { return self._s[1028]! } + public var Passport_FieldOneOf_Delimeter: String { return self._s[1029]! } + public var Channel_AdminLog_CanBanUsers: String { return self._s[1031]! } + public var Appearance_ThemePreview_ChatList_2_Name: String { return self._s[1032]! } + public var Appearance_Preview: String { return self._s[1033]! } + public var Compose_ChannelMembers: String { return self._s[1034]! } + public var Conversation_DeleteManyMessages: String { return self._s[1035]! } + public var ReportPeer_ReasonOther_Title: String { return self._s[1036]! } + public var Checkout_ErrorProviderAccountTimeout: String { return self._s[1037]! } + public var TwoStepAuth_ResetAccountConfirmation: String { return self._s[1038]! } + public var Channel_Stickers_CreateYourOwn: String { return self._s[1041]! } + public var Conversation_UpdateTelegram: String { return self._s[1042]! } + public var EditTheme_Create_TopInfo: String { return self._s[1043]! } public func Notification_PinnedPhotoMessage(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1034]!, self._r[1034]!, [_0]) + return formatWithArgumentRanges(self._s[1044]!, self._r[1044]!, [_0]) } - public var Wallet_WordCheck_Continue: String { return self._s[1035]! } - public var TwoFactorSetup_Hint_Action: String { return self._s[1036]! } - public var IntentsSettings_ResetAll: String { return self._s[1037]! } + public var Wallet_WordCheck_Continue: String { return self._s[1045]! } + public var TwoFactorSetup_Hint_Action: String { return self._s[1046]! } + public var IntentsSettings_ResetAll: String { return self._s[1047]! } public func PUSH_PINNED_GIF(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1038]!, self._r[1038]!, [_1]) + return formatWithArgumentRanges(self._s[1048]!, self._r[1048]!, [_1]) } - public var GroupInfo_Administrators_Title: String { return self._s[1039]! } - public var Privacy_Forwards_PreviewMessageText: String { return self._s[1040]! } + public var GroupInfo_Administrators_Title: String { return self._s[1049]! } + public var Privacy_Forwards_PreviewMessageText: String { return self._s[1050]! } public func PrivacySettings_LastSeenNobodyPlus(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1041]!, self._r[1041]!, [_0]) + return formatWithArgumentRanges(self._s[1051]!, self._r[1051]!, [_0]) } - public var Tour_Title3: String { return self._s[1042]! } - public var Channel_EditAdmin_PermissionInviteSubscribers: String { return self._s[1043]! } - public var Clipboard_SendPhoto: String { return self._s[1047]! } - public var MediaPicker_Videos: String { return self._s[1048]! } - public var Passport_Email_Title: String { return self._s[1049]! } + public var Tour_Title3: String { return self._s[1052]! } + public var Channel_EditAdmin_PermissionInviteSubscribers: String { return self._s[1053]! } + public var Clipboard_SendPhoto: String { return self._s[1057]! } + public var MediaPicker_Videos: String { return self._s[1058]! } + public var Passport_Email_Title: String { return self._s[1059]! } public func PrivacySettings_LastSeenEverybodyMinus(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1050]!, self._r[1050]!, [_0]) + return formatWithArgumentRanges(self._s[1060]!, self._r[1060]!, [_0]) } - public var StickerPacksSettings_Title: String { return self._s[1051]! } - public var Conversation_MessageDialogDelete: String { return self._s[1052]! } - public var Privacy_Calls_CustomHelp: String { return self._s[1054]! } - public var Message_Wallpaper: String { return self._s[1055]! } - public var MemberSearch_BotSection: String { return self._s[1056]! } - public var GroupInfo_SetSound: String { return self._s[1057]! } - public var Core_ServiceUserStatus: String { return self._s[1058]! } - public var LiveLocationUpdated_JustNow: String { return self._s[1059]! } - public var Call_StatusFailed: String { return self._s[1060]! } - public var TwoFactorSetup_Email_Placeholder: String { return self._s[1061]! } - public var TwoStepAuth_SetupPasswordDescription: String { return self._s[1062]! } - public var TwoStepAuth_SetPassword: String { return self._s[1063]! } - public var Permissions_PeopleNearbyText_v0: String { return self._s[1064]! } + public var StickerPacksSettings_Title: String { return self._s[1061]! } + public var Conversation_MessageDialogDelete: String { return self._s[1062]! } + public var Privacy_Calls_CustomHelp: String { return self._s[1064]! } + public var Message_Wallpaper: String { return self._s[1065]! } + public var MemberSearch_BotSection: String { return self._s[1066]! } + public var GroupInfo_SetSound: String { return self._s[1067]! } + public var Core_ServiceUserStatus: String { return self._s[1068]! } + public var LiveLocationUpdated_JustNow: String { return self._s[1069]! } + public var Call_StatusFailed: String { return self._s[1070]! } + public var TwoFactorSetup_Email_Placeholder: String { return self._s[1071]! } + public var TwoStepAuth_SetupPasswordDescription: String { return self._s[1072]! } + public var TwoStepAuth_SetPassword: String { return self._s[1073]! } + public var Permissions_PeopleNearbyText_v0: String { return self._s[1074]! } public func SocksProxySetup_ProxyStatusPing(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1066]!, self._r[1066]!, [_0]) + return formatWithArgumentRanges(self._s[1076]!, self._r[1076]!, [_0]) } - public var Calls_SubmitRating: String { return self._s[1067]! } - public var Map_NoPlacesNearby: String { return self._s[1068]! } - public var Profile_Username: String { return self._s[1069]! } - public var Bot_DescriptionTitle: String { return self._s[1070]! } - public var MaskStickerSettings_Title: String { return self._s[1071]! } - public var SharedMedia_CategoryOther: String { return self._s[1072]! } - public var GroupInfo_SetGroupPhoto: String { return self._s[1073]! } - public var Common_NotNow: String { return self._s[1074]! } - public var CallFeedback_IncludeLogsInfo: String { return self._s[1075]! } - public var Conversation_ShareMyPhoneNumber: String { return self._s[1076]! } - public var Map_Location: String { return self._s[1077]! } - public var Invitation_JoinGroup: String { return self._s[1078]! } - public var AutoDownloadSettings_Title: String { return self._s[1080]! } - public var Conversation_DiscardVoiceMessageDescription: String { return self._s[1081]! } - public var Channel_ErrorAddBlocked: String { return self._s[1082]! } - public var Conversation_UnblockUser: String { return self._s[1083]! } - public var EditTheme_Edit_TopInfo: String { return self._s[1084]! } - public var Watch_Bot_Restart: String { return self._s[1085]! } - public var TwoStepAuth_Title: String { return self._s[1086]! } - public var Channel_AdminLog_BanSendMessages: String { return self._s[1087]! } - public var Checkout_ShippingMethod: String { return self._s[1088]! } - public var Passport_Identity_OneOfTypeIdentityCard: String { return self._s[1089]! } + public var Calls_SubmitRating: String { return self._s[1077]! } + public var Map_NoPlacesNearby: String { return self._s[1078]! } + public var Profile_Username: String { return self._s[1079]! } + public var Bot_DescriptionTitle: String { return self._s[1080]! } + public var MaskStickerSettings_Title: String { return self._s[1081]! } + public var SharedMedia_CategoryOther: String { return self._s[1082]! } + public var GroupInfo_SetGroupPhoto: String { return self._s[1083]! } + public var Common_NotNow: String { return self._s[1084]! } + public var CallFeedback_IncludeLogsInfo: String { return self._s[1085]! } + public var Conversation_ShareMyPhoneNumber: String { return self._s[1086]! } + public var Map_Location: String { return self._s[1087]! } + public var Invitation_JoinGroup: String { return self._s[1088]! } + public var AutoDownloadSettings_Title: String { return self._s[1090]! } + public var Conversation_DiscardVoiceMessageDescription: String { return self._s[1091]! } + public var Channel_ErrorAddBlocked: String { return self._s[1092]! } + public var Conversation_UnblockUser: String { return self._s[1093]! } + public var EditTheme_Edit_TopInfo: String { return self._s[1094]! } + public var Watch_Bot_Restart: String { return self._s[1095]! } + public var TwoStepAuth_Title: String { return self._s[1096]! } + public var Channel_AdminLog_BanSendMessages: String { return self._s[1097]! } + public var Checkout_ShippingMethod: String { return self._s[1098]! } + public var Passport_Identity_OneOfTypeIdentityCard: String { return self._s[1099]! } public func PUSH_CHAT_MESSAGE_STICKER(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1090]!, self._r[1090]!, [_1, _2, _3]) + return formatWithArgumentRanges(self._s[1100]!, self._r[1100]!, [_1, _2, _3]) } - public var EditTheme_ChangeColors: String { return self._s[1092]! } + public var EditTheme_ChangeColors: String { return self._s[1102]! } public func Chat_UnsendMyMessagesAlertTitle(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1093]!, self._r[1093]!, [_0]) + return formatWithArgumentRanges(self._s[1103]!, self._r[1103]!, [_0]) } public func Channel_Username_LinkHint(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1094]!, self._r[1094]!, [_0]) + return formatWithArgumentRanges(self._s[1104]!, self._r[1104]!, [_0]) } - public var Appearance_ThemePreview_ChatList_1_Name: String { return self._s[1095]! } - public var SettingsSearch_Synonyms_Data_AutoplayGifs: String { return self._s[1096]! } - public var AuthSessions_TerminateOtherSessions: String { return self._s[1097]! } - public var Contacts_FailedToSendInvitesMessage: String { return self._s[1098]! } - public var PrivacySettings_TwoStepAuth: String { return self._s[1099]! } - public var Notification_Exceptions_PreviewAlwaysOn: String { return self._s[1100]! } - public var SettingsSearch_Synonyms_Privacy_Passcode: String { return self._s[1101]! } - public var Conversation_EditingMessagePanelMedia: String { return self._s[1102]! } - public var Checkout_PaymentMethod_Title: String { return self._s[1103]! } - public var SocksProxySetup_Connection: String { return self._s[1104]! } - public var Group_MessagePhotoRemoved: String { return self._s[1105]! } - public var Channel_Stickers_NotFound: String { return self._s[1108]! } - public var Group_About_Help: String { return self._s[1109]! } - public var Notification_PassportValueProofOfIdentity: String { return self._s[1110]! } - public var PeopleNearby_Title: String { return self._s[1112]! } + public var Appearance_ThemePreview_ChatList_1_Name: String { return self._s[1105]! } + public var SettingsSearch_Synonyms_Data_AutoplayGifs: String { return self._s[1106]! } + public var AuthSessions_TerminateOtherSessions: String { return self._s[1107]! } + public var Contacts_FailedToSendInvitesMessage: String { return self._s[1108]! } + public var PrivacySettings_TwoStepAuth: String { return self._s[1109]! } + public var Notification_Exceptions_PreviewAlwaysOn: String { return self._s[1110]! } + public var SettingsSearch_Synonyms_Privacy_Passcode: String { return self._s[1111]! } + public var Conversation_EditingMessagePanelMedia: String { return self._s[1112]! } + public var Checkout_PaymentMethod_Title: String { return self._s[1113]! } + public var SocksProxySetup_Connection: String { return self._s[1114]! } + public var Group_MessagePhotoRemoved: String { return self._s[1115]! } + public var Channel_Stickers_NotFound: String { return self._s[1118]! } + public var Group_About_Help: String { return self._s[1119]! } + public var Notification_PassportValueProofOfIdentity: String { return self._s[1120]! } + public var PeopleNearby_Title: String { return self._s[1122]! } public func ApplyLanguage_ChangeLanguageOfficialText(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1113]!, self._r[1113]!, [_1]) + return formatWithArgumentRanges(self._s[1123]!, self._r[1123]!, [_1]) } - public var Map_Home: String { return self._s[1114]! } - public var CheckoutInfo_ShippingInfoStatePlaceholder: String { return self._s[1116]! } - public var Notifications_GroupNotificationsExceptionsHelp: String { return self._s[1117]! } - public var SocksProxySetup_Password: String { return self._s[1118]! } - public var Notifications_PermissionsEnable: String { return self._s[1119]! } - public var TwoStepAuth_ChangeEmail: String { return self._s[1121]! } + public var Map_Home: String { return self._s[1124]! } + public var CheckoutInfo_ShippingInfoStatePlaceholder: String { return self._s[1126]! } + public var Notifications_GroupNotificationsExceptionsHelp: String { return self._s[1127]! } + public var SocksProxySetup_Password: String { return self._s[1128]! } + public var Notifications_PermissionsEnable: String { return self._s[1129]! } + public var TwoStepAuth_ChangeEmail: String { return self._s[1131]! } public func Channel_AdminLog_MessageInvitedName(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1122]!, self._r[1122]!, [_1]) + return formatWithArgumentRanges(self._s[1132]!, self._r[1132]!, [_1]) } public func Time_MonthOfYear_m10(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1124]!, self._r[1124]!, [_0]) + return formatWithArgumentRanges(self._s[1134]!, self._r[1134]!, [_0]) } - public var Passport_Identity_TypeDriversLicense: String { return self._s[1125]! } - public var ArchivedPacksAlert_Title: String { return self._s[1126]! } - public var Wallet_Receive_InvoiceUrlCopied: String { return self._s[1127]! } - public var Map_PlacesNearby: String { return self._s[1128]! } + public var Passport_Identity_TypeDriversLicense: String { return self._s[1135]! } + public var ArchivedPacksAlert_Title: String { return self._s[1136]! } + public var Wallet_Receive_InvoiceUrlCopied: String { return self._s[1137]! } + public var Map_PlacesNearby: String { return self._s[1138]! } public func Time_PreciseDate_m7(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1129]!, self._r[1129]!, [_1, _2, _3]) + return formatWithArgumentRanges(self._s[1139]!, self._r[1139]!, [_1, _2, _3]) } - public var PrivacyLastSeenSettings_GroupsAndChannelsHelp: String { return self._s[1130]! } - public var Privacy_Calls_NeverAllow_Placeholder: String { return self._s[1132]! } - public var Conversation_StatusTyping: String { return self._s[1133]! } - public var Broadcast_AdminLog_EmptyText: String { return self._s[1134]! } - public var Notification_PassportValueProofOfAddress: String { return self._s[1135]! } - public var UserInfo_CreateNewContact: String { return self._s[1136]! } - public var Passport_Identity_FrontSide: String { return self._s[1137]! } - public var Login_PhoneNumberAlreadyAuthorizedSwitch: String { return self._s[1138]! } - public var Calls_CallTabTitle: String { return self._s[1139]! } - public var Channel_AdminLog_ChannelEmptyText: String { return self._s[1140]! } + public var PrivacyLastSeenSettings_GroupsAndChannelsHelp: String { return self._s[1140]! } + public var Privacy_Calls_NeverAllow_Placeholder: String { return self._s[1142]! } + public var Conversation_StatusTyping: String { return self._s[1143]! } + public var Broadcast_AdminLog_EmptyText: String { return self._s[1144]! } + public var Notification_PassportValueProofOfAddress: String { return self._s[1145]! } + public var UserInfo_CreateNewContact: String { return self._s[1146]! } + public var Passport_Identity_FrontSide: String { return self._s[1147]! } + public var Login_PhoneNumberAlreadyAuthorizedSwitch: String { return self._s[1148]! } + public var Calls_CallTabTitle: String { return self._s[1149]! } + public var Channel_AdminLog_ChannelEmptyText: String { return self._s[1150]! } public func Login_BannedPhoneBody(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1142]!, self._r[1142]!, [_0]) + return formatWithArgumentRanges(self._s[1152]!, self._r[1152]!, [_0]) } - public var Watch_UserInfo_MuteTitle: String { return self._s[1143]! } - public var Group_EditAdmin_RankAdminPlaceholder: String { return self._s[1144]! } - public var SharedMedia_EmptyMusicText: String { return self._s[1145]! } - public var Wallet_Completed_Text: String { return self._s[1146]! } - public var PasscodeSettings_AutoLock_IfAwayFor_1minute: String { return self._s[1147]! } - public var Paint_Stickers: String { return self._s[1148]! } - public var Privacy_GroupsAndChannels: String { return self._s[1149]! } - public var ChatList_Context_Delete: String { return self._s[1151]! } - public var UserInfo_AddContact: String { return self._s[1152]! } + public var Watch_UserInfo_MuteTitle: String { return self._s[1153]! } + public var Group_EditAdmin_RankAdminPlaceholder: String { return self._s[1154]! } + public var SharedMedia_EmptyMusicText: String { return self._s[1155]! } + public var Wallet_Completed_Text: String { return self._s[1156]! } + public var PasscodeSettings_AutoLock_IfAwayFor_1minute: String { return self._s[1157]! } + public var Paint_Stickers: String { return self._s[1158]! } + public var Privacy_GroupsAndChannels: String { return self._s[1159]! } + public var ChatList_Context_Delete: String { return self._s[1161]! } + public var UserInfo_AddContact: String { return self._s[1162]! } public func Conversation_MessageViaUser(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1153]!, self._r[1153]!, [_0]) + return formatWithArgumentRanges(self._s[1163]!, self._r[1163]!, [_0]) } - public var PhoneNumberHelp_ChangeNumber: String { return self._s[1155]! } + public var PhoneNumberHelp_ChangeNumber: String { return self._s[1165]! } public func ChatList_ClearChatConfirmation(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1157]!, self._r[1157]!, [_0]) + return formatWithArgumentRanges(self._s[1167]!, self._r[1167]!, [_0]) } - public var DialogList_NoMessagesTitle: String { return self._s[1158]! } - public var EditProfile_NameAndPhotoHelp: String { return self._s[1159]! } - public var BlockedUsers_BlockUser: String { return self._s[1160]! } - public var Notifications_PermissionsOpenSettings: String { return self._s[1161]! } - public var MediaPicker_UngroupDescription: String { return self._s[1162]! } - public var Watch_NoConnection: String { return self._s[1163]! } - public var Month_GenSeptember: String { return self._s[1164]! } - public var Conversation_ViewGroup: String { return self._s[1166]! } - public var Channel_AdminLogFilter_EventsLeavingSubscribers: String { return self._s[1169]! } - public var Privacy_Forwards_AlwaysLink: String { return self._s[1170]! } - public var Channel_OwnershipTransfer_ErrorAdminsTooMuch: String { return self._s[1171]! } - public var Passport_FieldOneOf_FinalDelimeter: String { return self._s[1172]! } - public var Wallet_WordCheck_IncorrectHeader: String { return self._s[1173]! } - public var MediaPicker_CameraRoll: String { return self._s[1175]! } - public var Month_GenAugust: String { return self._s[1176]! } - public var Wallet_Configuration_SourceHeader: String { return self._s[1177]! } - public var AccessDenied_VideoMessageMicrophone: String { return self._s[1178]! } - public var SharedMedia_EmptyText: String { return self._s[1179]! } - public var Map_ShareLiveLocation: String { return self._s[1180]! } - public var Calls_All: String { return self._s[1181]! } - public var Map_SendThisPlace: String { return self._s[1183]! } - public var Appearance_ThemeNight: String { return self._s[1185]! } - public var Conversation_HoldForAudio: String { return self._s[1186]! } - public var SettingsSearch_Synonyms_Support: String { return self._s[1189]! } - public var GroupInfo_GroupHistoryHidden: String { return self._s[1190]! } - public var SocksProxySetup_Secret: String { return self._s[1191]! } + public var DialogList_NoMessagesTitle: String { return self._s[1168]! } + public var EditProfile_NameAndPhotoHelp: String { return self._s[1169]! } + public var BlockedUsers_BlockUser: String { return self._s[1170]! } + public var Notifications_PermissionsOpenSettings: String { return self._s[1171]! } + public var MediaPicker_UngroupDescription: String { return self._s[1173]! } + public var Watch_NoConnection: String { return self._s[1174]! } + public var Month_GenSeptember: String { return self._s[1175]! } + public var Conversation_ViewGroup: String { return self._s[1177]! } + public var Channel_AdminLogFilter_EventsLeavingSubscribers: String { return self._s[1180]! } + public var Privacy_Forwards_AlwaysLink: String { return self._s[1181]! } + public var Channel_OwnershipTransfer_ErrorAdminsTooMuch: String { return self._s[1182]! } + public var Passport_FieldOneOf_FinalDelimeter: String { return self._s[1183]! } + public var Wallet_WordCheck_IncorrectHeader: String { return self._s[1184]! } + public var MediaPicker_CameraRoll: String { return self._s[1186]! } + public var Month_GenAugust: String { return self._s[1187]! } + public var Wallet_Configuration_SourceHeader: String { return self._s[1188]! } + public var AccessDenied_VideoMessageMicrophone: String { return self._s[1189]! } + public var SharedMedia_EmptyText: String { return self._s[1190]! } + public var Map_ShareLiveLocation: String { return self._s[1191]! } + public var Calls_All: String { return self._s[1192]! } + public var Map_SendThisPlace: String { return self._s[1194]! } + public var Appearance_ThemeNight: String { return self._s[1196]! } + public var Conversation_HoldForAudio: String { return self._s[1197]! } + public var SettingsSearch_Synonyms_Support: String { return self._s[1200]! } + public var GroupInfo_GroupHistoryHidden: String { return self._s[1201]! } + public var SocksProxySetup_Secret: String { return self._s[1202]! } public func Activity_RemindAboutChannel(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1192]!, self._r[1192]!, [_0]) + return formatWithArgumentRanges(self._s[1203]!, self._r[1203]!, [_0]) } - public var Channel_BanList_RestrictedTitle: String { return self._s[1194]! } - public var Conversation_Location: String { return self._s[1195]! } + public var Channel_BanList_RestrictedTitle: String { return self._s[1205]! } + public var Conversation_Location: String { return self._s[1206]! } public func AutoDownloadSettings_UpToFor(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1196]!, self._r[1196]!, [_1, _2]) + return formatWithArgumentRanges(self._s[1207]!, self._r[1207]!, [_1, _2]) } - public var ChatSettings_AutoDownloadPhotos: String { return self._s[1198]! } - public var SettingsSearch_Synonyms_Privacy_Title: String { return self._s[1199]! } - public var Notifications_PermissionsText: String { return self._s[1200]! } - public var SettingsSearch_Synonyms_Data_SaveIncomingPhotos: String { return self._s[1201]! } - public var Call_Flip: String { return self._s[1202]! } - public var Channel_AdminLog_CanDeleteMessagesOfOthers: String { return self._s[1204]! } - public var SocksProxySetup_ProxyStatusConnecting: String { return self._s[1205]! } - public var Wallet_TransactionInfo_StorageFeeInfoUrl: String { return self._s[1206]! } - public var PrivacyPhoneNumberSettings_DiscoveryHeader: String { return self._s[1207]! } - public var Channel_EditAdmin_PermissionPinMessages: String { return self._s[1209]! } - public var TwoStepAuth_ReEnterPasswordDescription: String { return self._s[1211]! } - public var Channel_TooMuchBots: String { return self._s[1213]! } - public var Passport_DeletePassportConfirmation: String { return self._s[1214]! } - public var Login_InvalidCodeError: String { return self._s[1215]! } - public var StickerPacksSettings_FeaturedPacks: String { return self._s[1216]! } + public var ChatSettings_AutoDownloadPhotos: String { return self._s[1209]! } + public var SettingsSearch_Synonyms_Privacy_Title: String { return self._s[1210]! } + public var Notifications_PermissionsText: String { return self._s[1211]! } + public var SettingsSearch_Synonyms_Data_SaveIncomingPhotos: String { return self._s[1212]! } + public var Call_Flip: String { return self._s[1213]! } + public var Channel_AdminLog_CanDeleteMessagesOfOthers: String { return self._s[1215]! } + public var SocksProxySetup_ProxyStatusConnecting: String { return self._s[1216]! } + public var Wallet_TransactionInfo_StorageFeeInfoUrl: String { return self._s[1217]! } + public var PrivacyPhoneNumberSettings_DiscoveryHeader: String { return self._s[1218]! } + public var Channel_EditAdmin_PermissionPinMessages: String { return self._s[1220]! } + public var TwoStepAuth_ReEnterPasswordDescription: String { return self._s[1222]! } + public var Channel_TooMuchBots: String { return self._s[1224]! } + public var Passport_DeletePassportConfirmation: String { return self._s[1225]! } + public var Login_InvalidCodeError: String { return self._s[1226]! } + public var StickerPacksSettings_FeaturedPacks: String { return self._s[1227]! } public func ChatList_DeleteSecretChatConfirmation(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1217]!, self._r[1217]!, [_0]) + return formatWithArgumentRanges(self._s[1228]!, self._r[1228]!, [_0]) } public func GroupInfo_InvitationLinkAcceptChannel(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1218]!, self._r[1218]!, [_0]) + return formatWithArgumentRanges(self._s[1229]!, self._r[1229]!, [_0]) } - public var VoiceOver_Navigation_ProxySettings: String { return self._s[1219]! } - public var Call_CallInProgressTitle: String { return self._s[1220]! } - public var Month_ShortSeptember: String { return self._s[1221]! } - public var Watch_ChannelInfo_Title: String { return self._s[1222]! } - public var ChatList_DeleteSavedMessagesConfirmation: String { return self._s[1225]! } - public var DialogList_PasscodeLockHelp: String { return self._s[1226]! } - public var Chat_MultipleTextMessagesDisabled: String { return self._s[1227]! } - public var Wallet_Receive_Title: String { return self._s[1228]! } - public var Notifications_Badge_IncludePublicGroups: String { return self._s[1229]! } - public var Channel_AdminLogFilter_EventsTitle: String { return self._s[1230]! } - public var PhotoEditor_CropReset: String { return self._s[1231]! } - public var Group_Username_CreatePrivateLinkHelp: String { return self._s[1233]! } - public var Channel_Management_LabelEditor: String { return self._s[1234]! } - public var Passport_Identity_LatinNameHelp: String { return self._s[1236]! } - public var PhotoEditor_HighlightsTool: String { return self._s[1237]! } - public var Wallet_Info_WalletCreated: String { return self._s[1238]! } - public var UserInfo_Title: String { return self._s[1239]! } - public var ChatList_HideAction: String { return self._s[1240]! } - public var AccessDenied_Title: String { return self._s[1241]! } - public var DialogList_SearchLabel: String { return self._s[1242]! } - public var Group_Setup_HistoryHidden: String { return self._s[1243]! } - public var TwoStepAuth_PasswordChangeSuccess: String { return self._s[1244]! } - public var State_Updating: String { return self._s[1246]! } - public var Contacts_TabTitle: String { return self._s[1247]! } - public var Notifications_Badge_CountUnreadMessages: String { return self._s[1249]! } - public var GroupInfo_GroupHistory: String { return self._s[1250]! } - public var Conversation_UnsupportedMediaPlaceholder: String { return self._s[1251]! } - public var Wallpaper_SetColor: String { return self._s[1252]! } - public var CheckoutInfo_ShippingInfoCountry: String { return self._s[1253]! } - public var SettingsSearch_Synonyms_SavedMessages: String { return self._s[1254]! } - public var Chat_AttachmentLimitReached: String { return self._s[1255]! } - public var Passport_Identity_OneOfTypeDriversLicense: String { return self._s[1256]! } - public var Contacts_NotRegisteredSection: String { return self._s[1257]! } + public var VoiceOver_Navigation_ProxySettings: String { return self._s[1230]! } + public var Call_CallInProgressTitle: String { return self._s[1231]! } + public var Month_ShortSeptember: String { return self._s[1232]! } + public var Watch_ChannelInfo_Title: String { return self._s[1233]! } + public var ChatList_DeleteSavedMessagesConfirmation: String { return self._s[1236]! } + public var DialogList_PasscodeLockHelp: String { return self._s[1237]! } + public var Chat_MultipleTextMessagesDisabled: String { return self._s[1238]! } + public var Wallet_Receive_Title: String { return self._s[1239]! } + public var Notifications_Badge_IncludePublicGroups: String { return self._s[1240]! } + public var Channel_AdminLogFilter_EventsTitle: String { return self._s[1241]! } + public var PhotoEditor_CropReset: String { return self._s[1242]! } + public var Group_Username_CreatePrivateLinkHelp: String { return self._s[1244]! } + public var Channel_Management_LabelEditor: String { return self._s[1245]! } + public var Passport_Identity_LatinNameHelp: String { return self._s[1247]! } + public var PhotoEditor_HighlightsTool: String { return self._s[1248]! } + public var Wallet_Info_WalletCreated: String { return self._s[1249]! } + public var UserInfo_Title: String { return self._s[1250]! } + public var ChatList_HideAction: String { return self._s[1251]! } + public var AccessDenied_Title: String { return self._s[1252]! } + public var DialogList_SearchLabel: String { return self._s[1253]! } + public var Group_Setup_HistoryHidden: String { return self._s[1254]! } + public var TwoStepAuth_PasswordChangeSuccess: String { return self._s[1255]! } + public var State_Updating: String { return self._s[1257]! } + public var Contacts_TabTitle: String { return self._s[1258]! } + public var Notifications_Badge_CountUnreadMessages: String { return self._s[1260]! } + public var GroupInfo_GroupHistory: String { return self._s[1261]! } + public var Conversation_UnsupportedMediaPlaceholder: String { return self._s[1262]! } + public var Wallpaper_SetColor: String { return self._s[1263]! } + public var CheckoutInfo_ShippingInfoCountry: String { return self._s[1264]! } + public var SettingsSearch_Synonyms_SavedMessages: String { return self._s[1265]! } + public var Chat_AttachmentLimitReached: String { return self._s[1266]! } + public var Passport_Identity_OneOfTypeDriversLicense: String { return self._s[1267]! } + public var Contacts_NotRegisteredSection: String { return self._s[1268]! } public func Time_PreciseDate_m4(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1258]!, self._r[1258]!, [_1, _2, _3]) + return formatWithArgumentRanges(self._s[1269]!, self._r[1269]!, [_1, _2, _3]) } - public var Paint_Clear: String { return self._s[1259]! } - public var StickerPacksSettings_ArchivedMasks: String { return self._s[1260]! } - public var SocksProxySetup_Connecting: String { return self._s[1261]! } - public var ExplicitContent_AlertChannel: String { return self._s[1262]! } - public var CreatePoll_AllOptionsAdded: String { return self._s[1263]! } - public var Conversation_Contact: String { return self._s[1264]! } - public var Login_CodeExpired: String { return self._s[1265]! } - public var Passport_DiscardMessageAction: String { return self._s[1266]! } - public var ChatList_Context_Unpin: String { return self._s[1267]! } - public var Channel_AdminLog_MessagePreviousDescription: String { return self._s[1268]! } + public var Paint_Clear: String { return self._s[1270]! } + public var StickerPacksSettings_ArchivedMasks: String { return self._s[1271]! } + public var SocksProxySetup_Connecting: String { return self._s[1272]! } + public var ExplicitContent_AlertChannel: String { return self._s[1273]! } + public var CreatePoll_AllOptionsAdded: String { return self._s[1274]! } + public var Conversation_Contact: String { return self._s[1275]! } + public var Login_CodeExpired: String { return self._s[1276]! } + public var Passport_DiscardMessageAction: String { return self._s[1277]! } + public var ChatList_Context_Unpin: String { return self._s[1278]! } + public var Channel_AdminLog_MessagePreviousDescription: String { return self._s[1279]! } public func VoiceOver_Chat_MusicFrom(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1269]!, self._r[1269]!, [_0]) + return formatWithArgumentRanges(self._s[1280]!, self._r[1280]!, [_0]) } - public var Channel_AdminLog_EmptyMessageText: String { return self._s[1270]! } - public var SettingsSearch_Synonyms_Data_NetworkUsage: String { return self._s[1271]! } + public var Channel_AdminLog_EmptyMessageText: String { return self._s[1281]! } + public var SettingsSearch_Synonyms_Data_NetworkUsage: String { return self._s[1282]! } public func Group_EditAdmin_RankInfo(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1272]!, self._r[1272]!, [_0]) + return formatWithArgumentRanges(self._s[1283]!, self._r[1283]!, [_0]) } - public var Month_ShortApril: String { return self._s[1273]! } - public var AuthSessions_CurrentSession: String { return self._s[1274]! } - public var Chat_AttachmentMultipleFilesDisabled: String { return self._s[1277]! } - public var Wallet_Navigation_Cancel: String { return self._s[1279]! } - public var WallpaperPreview_CropTopText: String { return self._s[1280]! } - public var PrivacySettings_DeleteAccountIfAwayFor: String { return self._s[1281]! } - public var CheckoutInfo_ShippingInfoTitle: String { return self._s[1282]! } + public var Month_ShortApril: String { return self._s[1284]! } + public var AuthSessions_CurrentSession: String { return self._s[1285]! } + public var Chat_AttachmentMultipleFilesDisabled: String { return self._s[1288]! } + public var Wallet_Navigation_Cancel: String { return self._s[1290]! } + public var WallpaperPreview_CropTopText: String { return self._s[1291]! } + public var PrivacySettings_DeleteAccountIfAwayFor: String { return self._s[1292]! } + public var CheckoutInfo_ShippingInfoTitle: String { return self._s[1293]! } public func Conversation_ScheduleMessage_SendOn(_ _0: String, _ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1283]!, self._r[1283]!, [_0, _1]) + return formatWithArgumentRanges(self._s[1294]!, self._r[1294]!, [_0, _1]) } - public var Appearance_ThemePreview_Chat_2_Text: String { return self._s[1284]! } - public var Channel_Setup_TypePrivate: String { return self._s[1286]! } - public var Forward_ChannelReadOnly: String { return self._s[1289]! } - public var PhotoEditor_CurvesBlue: String { return self._s[1290]! } - public var AddContact_SharedContactException: String { return self._s[1291]! } - public var UserInfo_BotPrivacy: String { return self._s[1293]! } - public var Wallet_CreateInvoice_Title: String { return self._s[1294]! } - public var Notification_PassportValueEmail: String { return self._s[1295]! } - public var EmptyGroupInfo_Subtitle: String { return self._s[1296]! } - public var GroupPermission_NewTitle: String { return self._s[1297]! } - public var CallFeedback_ReasonDropped: String { return self._s[1298]! } - public var GroupInfo_Permissions_AddException: String { return self._s[1299]! } - public var Channel_SignMessages_Help: String { return self._s[1301]! } - public var Undo_ChatDeleted: String { return self._s[1303]! } - public var Conversation_ChatBackground: String { return self._s[1304]! } + public var Appearance_ThemePreview_Chat_2_Text: String { return self._s[1295]! } + public var Channel_Setup_TypePrivate: String { return self._s[1297]! } + public var Forward_ChannelReadOnly: String { return self._s[1300]! } + public var PhotoEditor_CurvesBlue: String { return self._s[1301]! } + public var AddContact_SharedContactException: String { return self._s[1302]! } + public var UserInfo_BotPrivacy: String { return self._s[1304]! } + public var Wallet_CreateInvoice_Title: String { return self._s[1305]! } + public var Notification_PassportValueEmail: String { return self._s[1306]! } + public var EmptyGroupInfo_Subtitle: String { return self._s[1307]! } + public var GroupPermission_NewTitle: String { return self._s[1308]! } + public var CallFeedback_ReasonDropped: String { return self._s[1309]! } + public var GroupInfo_Permissions_AddException: String { return self._s[1310]! } + public var Channel_SignMessages_Help: String { return self._s[1312]! } + public var Undo_ChatDeleted: String { return self._s[1314]! } + public var Conversation_ChatBackground: String { return self._s[1315]! } public func Wallet_WordCheck_Text(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1305]!, self._r[1305]!, [_1, _2, _3]) + return formatWithArgumentRanges(self._s[1316]!, self._r[1316]!, [_1, _2, _3]) } - public var ChannelMembers_WhoCanAddMembers_Admins: String { return self._s[1306]! } - public var FastTwoStepSetup_EmailPlaceholder: String { return self._s[1307]! } - public var Passport_Language_pt: String { return self._s[1308]! } - public var VoiceOver_Chat_YourVoiceMessage: String { return self._s[1309]! } - public var NotificationsSound_Popcorn: String { return self._s[1312]! } - public var AutoNightTheme_Disabled: String { return self._s[1313]! } - public var BlockedUsers_LeavePrefix: String { return self._s[1314]! } - public var WallpaperPreview_CustomColorTopText: String { return self._s[1315]! } - public var Contacts_PermissionsSuppressWarningText: String { return self._s[1316]! } - public var WallpaperSearch_ColorBlue: String { return self._s[1317]! } + public func PUSH_CHAT_MESSAGE_QUIZ(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[1317]!, self._r[1317]!, [_1, _2, _3]) + } + public var ChannelMembers_WhoCanAddMembers_Admins: String { return self._s[1318]! } + public var FastTwoStepSetup_EmailPlaceholder: String { return self._s[1319]! } + public var Passport_Language_pt: String { return self._s[1320]! } + public var VoiceOver_Chat_YourVoiceMessage: String { return self._s[1321]! } + public var NotificationsSound_Popcorn: String { return self._s[1324]! } + public var AutoNightTheme_Disabled: String { return self._s[1325]! } + public var BlockedUsers_LeavePrefix: String { return self._s[1326]! } + public var WallpaperPreview_CustomColorTopText: String { return self._s[1327]! } + public var Contacts_PermissionsSuppressWarningText: String { return self._s[1328]! } + public var WallpaperSearch_ColorBlue: String { return self._s[1329]! } public func CancelResetAccount_TextSMS(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1318]!, self._r[1318]!, [_0]) + return formatWithArgumentRanges(self._s[1330]!, self._r[1330]!, [_0]) } - public var CheckoutInfo_ErrorNameInvalid: String { return self._s[1319]! } - public var SocksProxySetup_UseForCalls: String { return self._s[1320]! } - public var Passport_DeleteDocumentConfirmation: String { return self._s[1322]! } + public var CheckoutInfo_ErrorNameInvalid: String { return self._s[1331]! } + public var SocksProxySetup_UseForCalls: String { return self._s[1332]! } + public var Passport_DeleteDocumentConfirmation: String { return self._s[1334]! } public func Conversation_Megabytes(_ _0: Float) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1323]!, self._r[1323]!, ["\(_0)"]) + return formatWithArgumentRanges(self._s[1335]!, self._r[1335]!, ["\(_0)"]) } - public var SocksProxySetup_Hostname: String { return self._s[1326]! } - public var ChatSettings_AutoDownloadSettings_OffForAll: String { return self._s[1327]! } - public var Compose_NewEncryptedChat: String { return self._s[1328]! } - public var Login_CodeFloodError: String { return self._s[1329]! } - public var Calls_TabTitle: String { return self._s[1330]! } - public var Privacy_ProfilePhoto: String { return self._s[1331]! } - public var Passport_Language_he: String { return self._s[1332]! } + public var SocksProxySetup_Hostname: String { return self._s[1338]! } + public var ChatSettings_AutoDownloadSettings_OffForAll: String { return self._s[1339]! } + public var Compose_NewEncryptedChat: String { return self._s[1340]! } + public var Login_CodeFloodError: String { return self._s[1341]! } + public var Calls_TabTitle: String { return self._s[1342]! } + public var Privacy_ProfilePhoto: String { return self._s[1343]! } + public var Passport_Language_he: String { return self._s[1344]! } public func Conversation_SetReminder_RemindToday(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1333]!, self._r[1333]!, [_0]) + return formatWithArgumentRanges(self._s[1345]!, self._r[1345]!, [_0]) } - public var GroupPermission_Title: String { return self._s[1334]! } + public var GroupPermission_Title: String { return self._s[1346]! } public func Channel_AdminLog_MessageGroupPreHistoryHidden(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1335]!, self._r[1335]!, [_0]) + return formatWithArgumentRanges(self._s[1347]!, self._r[1347]!, [_0]) } - public var Wallet_TransactionInfo_SenderHeader: String { return self._s[1336]! } - public var GroupPermission_NoChangeInfo: String { return self._s[1337]! } - public var ChatList_DeleteForCurrentUser: String { return self._s[1338]! } - public var Tour_Text1: String { return self._s[1339]! } - public var Channel_EditAdmin_TransferOwnership: String { return self._s[1340]! } - public var Month_ShortFebruary: String { return self._s[1341]! } - public var TwoStepAuth_EmailSkip: String { return self._s[1342]! } + public var Wallet_TransactionInfo_SenderHeader: String { return self._s[1348]! } + public var GroupPermission_NoChangeInfo: String { return self._s[1349]! } + public var ChatList_DeleteForCurrentUser: String { return self._s[1350]! } + public var Tour_Text1: String { return self._s[1351]! } + public var Channel_EditAdmin_TransferOwnership: String { return self._s[1352]! } + public var Month_ShortFebruary: String { return self._s[1353]! } + public var TwoStepAuth_EmailSkip: String { return self._s[1354]! } public func Wallet_Time_PreciseDate_m4(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1343]!, self._r[1343]!, [_1, _2, _3]) + return formatWithArgumentRanges(self._s[1355]!, self._r[1355]!, [_1, _2, _3]) } - public var NotificationsSound_Glass: String { return self._s[1344]! } - public var Appearance_ThemeNightBlue: String { return self._s[1345]! } - public var CheckoutInfo_Pay: String { return self._s[1346]! } - public var Invite_LargeRecipientsCountWarning: String { return self._s[1348]! } - public var Call_CallAgain: String { return self._s[1350]! } - public var AttachmentMenu_SendAsFile: String { return self._s[1351]! } - public var AccessDenied_MicrophoneRestricted: String { return self._s[1352]! } - public var Passport_InvalidPasswordError: String { return self._s[1353]! } - public var Watch_Message_Game: String { return self._s[1354]! } - public var Stickers_Install: String { return self._s[1355]! } - public var VoiceOver_Chat_Message: String { return self._s[1356]! } - public var PrivacyLastSeenSettings_NeverShareWith: String { return self._s[1357]! } - public var Passport_Identity_ResidenceCountry: String { return self._s[1359]! } - public var Notifications_GroupNotificationsHelp: String { return self._s[1360]! } - public var AuthSessions_OtherSessions: String { return self._s[1361]! } - public var Channel_Username_Help: String { return self._s[1362]! } - public var Camera_Title: String { return self._s[1363]! } - public var IntentsSettings_Title: String { return self._s[1364]! } - public var GroupInfo_SetGroupPhotoDelete: String { return self._s[1366]! } - public var Privacy_ProfilePhoto_NeverShareWith_Title: String { return self._s[1367]! } - public var Channel_AdminLog_SendPolls: String { return self._s[1368]! } - public var Channel_AdminLog_TitleAllEvents: String { return self._s[1369]! } - public var Channel_EditAdmin_PermissionInviteMembers: String { return self._s[1370]! } - public var Contacts_MemberSearchSectionTitleGroup: String { return self._s[1371]! } - public var ScheduledMessages_DeleteMany: String { return self._s[1372]! } - public var Conversation_RestrictedStickers: String { return self._s[1373]! } - public var Notifications_ExceptionsResetToDefaults: String { return self._s[1375]! } - public var UserInfo_TelegramCall: String { return self._s[1377]! } - public var TwoStepAuth_SetupResendEmailCode: String { return self._s[1378]! } - public var CreatePoll_OptionsHeader: String { return self._s[1379]! } - public var SettingsSearch_Synonyms_Data_CallsUseLessData: String { return self._s[1380]! } - public var ArchivedChats_IntroTitle1: String { return self._s[1381]! } - public var Privacy_GroupsAndChannels_AlwaysAllow_Title: String { return self._s[1382]! } - public var Theme_Colors_Proceed: String { return self._s[1383]! } - public var Passport_Identity_EditPersonalDetails: String { return self._s[1384]! } + public var NotificationsSound_Glass: String { return self._s[1356]! } + public var Appearance_ThemeNightBlue: String { return self._s[1357]! } + public var CheckoutInfo_Pay: String { return self._s[1358]! } + public var Invite_LargeRecipientsCountWarning: String { return self._s[1360]! } + public var Call_CallAgain: String { return self._s[1362]! } + public var AttachmentMenu_SendAsFile: String { return self._s[1363]! } + public var AccessDenied_MicrophoneRestricted: String { return self._s[1364]! } + public var Passport_InvalidPasswordError: String { return self._s[1365]! } + public var Watch_Message_Game: String { return self._s[1366]! } + public var Stickers_Install: String { return self._s[1367]! } + public var VoiceOver_Chat_Message: String { return self._s[1368]! } + public var PrivacyLastSeenSettings_NeverShareWith: String { return self._s[1369]! } + public var Passport_Identity_ResidenceCountry: String { return self._s[1371]! } + public var Notifications_GroupNotificationsHelp: String { return self._s[1372]! } + public var AuthSessions_OtherSessions: String { return self._s[1373]! } + public var Channel_Username_Help: String { return self._s[1374]! } + public var Camera_Title: String { return self._s[1375]! } + public var IntentsSettings_Title: String { return self._s[1376]! } + public var GroupInfo_SetGroupPhotoDelete: String { return self._s[1378]! } + public var Privacy_ProfilePhoto_NeverShareWith_Title: String { return self._s[1379]! } + public var Channel_AdminLog_SendPolls: String { return self._s[1380]! } + public var Channel_AdminLog_TitleAllEvents: String { return self._s[1381]! } + public var Channel_EditAdmin_PermissionInviteMembers: String { return self._s[1382]! } + public var Contacts_MemberSearchSectionTitleGroup: String { return self._s[1383]! } + public var ScheduledMessages_DeleteMany: String { return self._s[1384]! } + public var Conversation_RestrictedStickers: String { return self._s[1385]! } + public var Notifications_ExceptionsResetToDefaults: String { return self._s[1387]! } + public var UserInfo_TelegramCall: String { return self._s[1389]! } + public var TwoStepAuth_SetupResendEmailCode: String { return self._s[1390]! } + public var CreatePoll_OptionsHeader: String { return self._s[1391]! } + public var SettingsSearch_Synonyms_Data_CallsUseLessData: String { return self._s[1392]! } + public var ArchivedChats_IntroTitle1: String { return self._s[1393]! } + public var Privacy_GroupsAndChannels_AlwaysAllow_Title: String { return self._s[1394]! } + public var Theme_Colors_Proceed: String { return self._s[1395]! } + public var Passport_Identity_EditPersonalDetails: String { return self._s[1396]! } public func Time_PreciseDate_m1(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1385]!, self._r[1385]!, [_1, _2, _3]) + return formatWithArgumentRanges(self._s[1397]!, self._r[1397]!, [_1, _2, _3]) } - public var Wallet_Month_GenAugust: String { return self._s[1386]! } - public var Settings_SaveEditedPhotos: String { return self._s[1387]! } - public var TwoStepAuth_ConfirmationTitle: String { return self._s[1388]! } - public var Privacy_GroupsAndChannels_NeverAllow_Title: String { return self._s[1389]! } - public var Conversation_MessageDialogRetry: String { return self._s[1390]! } - public var ChatList_Context_MarkAsUnread: String { return self._s[1391]! } - public var Conversation_DiscardVoiceMessageAction: String { return self._s[1392]! } - public var Permissions_PeopleNearbyTitle_v0: String { return self._s[1393]! } - public var Group_Setup_TypeHeader: String { return self._s[1394]! } - public var Paint_RecentStickers: String { return self._s[1395]! } - public var PhotoEditor_GrainTool: String { return self._s[1396]! } - public var CheckoutInfo_ShippingInfoState: String { return self._s[1397]! } - public var EmptyGroupInfo_Line4: String { return self._s[1398]! } - public var Watch_AuthRequired: String { return self._s[1400]! } + public var Wallet_Month_GenAugust: String { return self._s[1398]! } + public var Settings_SaveEditedPhotos: String { return self._s[1399]! } + public var TwoStepAuth_ConfirmationTitle: String { return self._s[1400]! } + public var Privacy_GroupsAndChannels_NeverAllow_Title: String { return self._s[1401]! } + public var Conversation_MessageDialogRetry: String { return self._s[1402]! } + public var ChatList_Context_MarkAsUnread: String { return self._s[1403]! } + public var MessagePoll_SubmitVote: String { return self._s[1404]! } + public var Conversation_DiscardVoiceMessageAction: String { return self._s[1405]! } + public var Permissions_PeopleNearbyTitle_v0: String { return self._s[1406]! } + public var Group_Setup_TypeHeader: String { return self._s[1407]! } + public var Paint_RecentStickers: String { return self._s[1408]! } + public var PhotoEditor_GrainTool: String { return self._s[1409]! } + public var CheckoutInfo_ShippingInfoState: String { return self._s[1410]! } + public var EmptyGroupInfo_Line4: String { return self._s[1411]! } + public var Watch_AuthRequired: String { return self._s[1413]! } public func Passport_Email_UseTelegramEmail(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1401]!, self._r[1401]!, [_0]) + return formatWithArgumentRanges(self._s[1414]!, self._r[1414]!, [_0]) } - public var Conversation_EncryptedDescriptionTitle: String { return self._s[1402]! } - public var ChannelIntro_Text: String { return self._s[1403]! } - public var DialogList_DeleteBotConfirmation: String { return self._s[1404]! } - public var GroupPermission_NoSendMedia: String { return self._s[1405]! } - public var Calls_AddTab: String { return self._s[1406]! } - public var Message_ReplyActionButtonShowReceipt: String { return self._s[1407]! } - public var Channel_AdminLog_EmptyFilterText: String { return self._s[1408]! } - public var Conversation_WalletRequiredSetup: String { return self._s[1409]! } - public var Notification_MessageLifetime1d: String { return self._s[1410]! } - public var Notifications_ChannelNotificationsExceptionsHelp: String { return self._s[1411]! } - public var Channel_BanUser_PermissionsHeader: String { return self._s[1412]! } - public var Passport_Identity_GenderFemale: String { return self._s[1413]! } - public var BlockedUsers_BlockTitle: String { return self._s[1414]! } + public var Conversation_EncryptedDescriptionTitle: String { return self._s[1415]! } + public var ChannelIntro_Text: String { return self._s[1416]! } + public var DialogList_DeleteBotConfirmation: String { return self._s[1417]! } + public var GroupPermission_NoSendMedia: String { return self._s[1418]! } + public var Calls_AddTab: String { return self._s[1419]! } + public var Message_ReplyActionButtonShowReceipt: String { return self._s[1420]! } + public var Channel_AdminLog_EmptyFilterText: String { return self._s[1421]! } + public var Conversation_WalletRequiredSetup: String { return self._s[1422]! } + public var Notification_MessageLifetime1d: String { return self._s[1423]! } + public var Notifications_ChannelNotificationsExceptionsHelp: String { return self._s[1424]! } + public var Channel_BanUser_PermissionsHeader: String { return self._s[1425]! } + public var Passport_Identity_GenderFemale: String { return self._s[1426]! } + public var BlockedUsers_BlockTitle: String { return self._s[1427]! } public func PUSH_CHANNEL_MESSAGE_GIF(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1415]!, self._r[1415]!, [_1]) + return formatWithArgumentRanges(self._s[1428]!, self._r[1428]!, [_1]) } - public var Weekday_Yesterday: String { return self._s[1416]! } - public var WallpaperSearch_ColorBlack: String { return self._s[1417]! } - public var Settings_Context_Logout: String { return self._s[1418]! } - public var Wallet_Info_UnknownTransaction: String { return self._s[1419]! } - public var ChatList_ArchiveAction: String { return self._s[1420]! } - public var AutoNightTheme_Scheduled: String { return self._s[1421]! } - public var TwoFactorSetup_Email_SkipAction: String { return self._s[1422]! } - public var Settings_Devices: String { return self._s[1423]! } - public var ContactInfo_Note: String { return self._s[1424]! } + public var Weekday_Yesterday: String { return self._s[1429]! } + public var WallpaperSearch_ColorBlack: String { return self._s[1430]! } + public var Settings_Context_Logout: String { return self._s[1431]! } + public var Wallet_Info_UnknownTransaction: String { return self._s[1432]! } + public var ChatList_ArchiveAction: String { return self._s[1433]! } + public var AutoNightTheme_Scheduled: String { return self._s[1434]! } + public var TwoFactorSetup_Email_SkipAction: String { return self._s[1435]! } + public var Settings_Devices: String { return self._s[1436]! } + public var ContactInfo_Note: String { return self._s[1437]! } public func Login_PhoneGenericEmailBody(_ _1: String, _ _2: String, _ _3: String, _ _4: String, _ _5: String, _ _6: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1425]!, self._r[1425]!, [_1, _2, _3, _4, _5, _6]) + return formatWithArgumentRanges(self._s[1438]!, self._r[1438]!, [_1, _2, _3, _4, _5, _6]) } - public var EditTheme_ThemeTemplateAlertTitle: String { return self._s[1426]! } - public var Wallet_Receive_CreateInvoice: String { return self._s[1427]! } - public var PrivacyPolicy_DeclineDeleteNow: String { return self._s[1428]! } - public var Theme_Colors_ColorWallpaperWarningProceed: String { return self._s[1429]! } + public var EditTheme_ThemeTemplateAlertTitle: String { return self._s[1439]! } + public var Wallet_Receive_CreateInvoice: String { return self._s[1440]! } + public var PrivacyPolicy_DeclineDeleteNow: String { return self._s[1441]! } + public var Theme_Colors_ColorWallpaperWarningProceed: String { return self._s[1442]! } public func PUSH_CHAT_JOINED(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1430]!, self._r[1430]!, [_1, _2]) + return formatWithArgumentRanges(self._s[1443]!, self._r[1443]!, [_1, _2]) } - public var CreatePoll_Create: String { return self._s[1431]! } - public var Channel_Members_AddBannedErrorAdmin: String { return self._s[1432]! } + public var CreatePoll_Create: String { return self._s[1444]! } + public var Channel_Members_AddBannedErrorAdmin: String { return self._s[1445]! } public func Notification_CallFormat(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1433]!, self._r[1433]!, [_1, _2]) + return formatWithArgumentRanges(self._s[1446]!, self._r[1446]!, [_1, _2]) } - public var ScheduledMessages_ClearAllConfirmation: String { return self._s[1434]! } - public var Checkout_ErrorProviderAccountInvalid: String { return self._s[1435]! } - public var Notifications_InAppNotificationsSounds: String { return self._s[1437]! } + public var ScheduledMessages_ClearAllConfirmation: String { return self._s[1447]! } + public var Checkout_ErrorProviderAccountInvalid: String { return self._s[1448]! } + public var Notifications_InAppNotificationsSounds: String { return self._s[1450]! } public func PUSH_PINNED_GAME_SCORE(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1438]!, self._r[1438]!, [_1]) + return formatWithArgumentRanges(self._s[1451]!, self._r[1451]!, [_1]) } - public var Preview_OpenInInstagram: String { return self._s[1439]! } - public var Notification_MessageLifetimeRemovedOutgoing: String { return self._s[1440]! } + public var Preview_OpenInInstagram: String { return self._s[1452]! } + public var Notification_MessageLifetimeRemovedOutgoing: String { return self._s[1453]! } public func PUSH_CHAT_ADD_MEMBER(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1441]!, self._r[1441]!, [_1, _2, _3]) + return formatWithArgumentRanges(self._s[1454]!, self._r[1454]!, [_1, _2, _3]) } public func Passport_PrivacyPolicy(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1442]!, self._r[1442]!, [_1, _2]) + return formatWithArgumentRanges(self._s[1455]!, self._r[1455]!, [_1, _2]) } - public var Channel_AdminLog_InfoPanelAlertTitle: String { return self._s[1443]! } - public var ArchivedChats_IntroText3: String { return self._s[1444]! } - public var ChatList_UndoArchiveHiddenText: String { return self._s[1445]! } - public var NetworkUsageSettings_TotalSection: String { return self._s[1446]! } - public var Wallet_Month_GenSeptember: String { return self._s[1447]! } - public var Channel_Setup_TypePrivateHelp: String { return self._s[1448]! } + public var Channel_AdminLog_InfoPanelAlertTitle: String { return self._s[1456]! } + public var ArchivedChats_IntroText3: String { return self._s[1457]! } + public var ChatList_UndoArchiveHiddenText: String { return self._s[1458]! } + public var NetworkUsageSettings_TotalSection: String { return self._s[1459]! } + public var Wallet_Month_GenSeptember: String { return self._s[1460]! } + public var Channel_Setup_TypePrivateHelp: String { return self._s[1461]! } public func PUSH_CHAT_MESSAGE_POLL(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1449]!, self._r[1449]!, [_1, _2, _3]) + return formatWithArgumentRanges(self._s[1462]!, self._r[1462]!, [_1, _2, _3]) } - public var Privacy_GroupsAndChannels_NeverAllow_Placeholder: String { return self._s[1451]! } - public var FastTwoStepSetup_HintSection: String { return self._s[1452]! } - public var Wallpaper_PhotoLibrary: String { return self._s[1453]! } - public var TwoStepAuth_SetupResendEmailCodeAlert: String { return self._s[1454]! } - public var Gif_NoGifsFound: String { return self._s[1455]! } - public var Watch_LastSeen_WithinAMonth: String { return self._s[1456]! } - public var VoiceOver_MessageContextDelete: String { return self._s[1457]! } - public var EditTheme_Preview: String { return self._s[1458]! } + public var Privacy_GroupsAndChannels_NeverAllow_Placeholder: String { return self._s[1464]! } + public var FastTwoStepSetup_HintSection: String { return self._s[1465]! } + public var Wallpaper_PhotoLibrary: String { return self._s[1466]! } + public var TwoStepAuth_SetupResendEmailCodeAlert: String { return self._s[1467]! } + public var Gif_NoGifsFound: String { return self._s[1468]! } + public var Watch_LastSeen_WithinAMonth: String { return self._s[1469]! } + public var VoiceOver_MessageContextDelete: String { return self._s[1470]! } + public var EditTheme_Preview: String { return self._s[1471]! } public func ClearCache_StorageTitle(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1459]!, self._r[1459]!, [_0]) + return formatWithArgumentRanges(self._s[1472]!, self._r[1472]!, [_0]) } - public var GroupInfo_ActionPromote: String { return self._s[1460]! } - public var PasscodeSettings_SimplePasscode: String { return self._s[1461]! } - public var GroupInfo_Permissions_Title: String { return self._s[1462]! } - public var Permissions_ContactsText_v0: String { return self._s[1463]! } - public var PrivacyPhoneNumberSettings_CustomDisabledHelp: String { return self._s[1464]! } - public var SettingsSearch_Synonyms_Notifications_BadgeIncludeMutedPublicGroups: String { return self._s[1465]! } - public var PrivacySettings_DataSettingsHelp: String { return self._s[1468]! } - public var Passport_FieldEmailHelp: String { return self._s[1469]! } + public var GroupInfo_ActionPromote: String { return self._s[1473]! } + public var PasscodeSettings_SimplePasscode: String { return self._s[1474]! } + public var GroupInfo_Permissions_Title: String { return self._s[1475]! } + public var Permissions_ContactsText_v0: String { return self._s[1476]! } + public var PrivacyPhoneNumberSettings_CustomDisabledHelp: String { return self._s[1477]! } + public var SettingsSearch_Synonyms_Notifications_BadgeIncludeMutedPublicGroups: String { return self._s[1478]! } + public var PrivacySettings_DataSettingsHelp: String { return self._s[1481]! } + public var Passport_FieldEmailHelp: String { return self._s[1482]! } public func Activity_RemindAboutUser(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1470]!, self._r[1470]!, [_0]) + return formatWithArgumentRanges(self._s[1483]!, self._r[1483]!, [_0]) } - public var Passport_Identity_GenderPlaceholder: String { return self._s[1471]! } - public var Weekday_ShortSaturday: String { return self._s[1472]! } - public var ContactInfo_PhoneLabelMain: String { return self._s[1473]! } - public var Watch_Conversation_UserInfo: String { return self._s[1474]! } - public var CheckoutInfo_ShippingInfoCityPlaceholder: String { return self._s[1475]! } - public var GroupPermission_PermissionDisabledByDefault: String { return self._s[1476]! } - public var PrivacyLastSeenSettings_Title: String { return self._s[1477]! } - public var Conversation_ShareBotLocationConfirmation: String { return self._s[1478]! } - public var PhotoEditor_VignetteTool: String { return self._s[1479]! } - public var Passport_Address_Street1Placeholder: String { return self._s[1480]! } - public var Passport_Language_et: String { return self._s[1481]! } - public var AppUpgrade_Running: String { return self._s[1482]! } - public var Channel_DiscussionGroup_Info: String { return self._s[1484]! } - public var EditTheme_Create_Preview_IncomingReplyName: String { return self._s[1485]! } - public var Passport_Language_bg: String { return self._s[1486]! } - public var Stickers_NoStickersFound: String { return self._s[1488]! } + public var Passport_Identity_GenderPlaceholder: String { return self._s[1484]! } + public var Weekday_ShortSaturday: String { return self._s[1485]! } + public var ContactInfo_PhoneLabelMain: String { return self._s[1486]! } + public var Watch_Conversation_UserInfo: String { return self._s[1487]! } + public var CheckoutInfo_ShippingInfoCityPlaceholder: String { return self._s[1488]! } + public var GroupPermission_PermissionDisabledByDefault: String { return self._s[1489]! } + public var PrivacyLastSeenSettings_Title: String { return self._s[1490]! } + public var Conversation_ShareBotLocationConfirmation: String { return self._s[1491]! } + public var PhotoEditor_VignetteTool: String { return self._s[1492]! } + public var Passport_Address_Street1Placeholder: String { return self._s[1493]! } + public var Passport_Language_et: String { return self._s[1494]! } + public var AppUpgrade_Running: String { return self._s[1495]! } + public var Channel_DiscussionGroup_Info: String { return self._s[1497]! } + public var EditTheme_Create_Preview_IncomingReplyName: String { return self._s[1498]! } + public var Passport_Language_bg: String { return self._s[1499]! } + public var Stickers_NoStickersFound: String { return self._s[1501]! } public func PUSH_CHANNEL_MESSAGE_TEXT(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1490]!, self._r[1490]!, [_1, _2]) + return formatWithArgumentRanges(self._s[1503]!, self._r[1503]!, [_1, _2]) } public func VoiceOver_Chat_ContactFrom(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1491]!, self._r[1491]!, [_0]) + return formatWithArgumentRanges(self._s[1504]!, self._r[1504]!, [_0]) } - public var Wallet_Month_GenJuly: String { return self._s[1492]! } - public var Wallet_Receive_AddressHeader: String { return self._s[1493]! } - public var Wallet_Send_AmountText: String { return self._s[1494]! } - public var Settings_About: String { return self._s[1495]! } + public var Wallet_Month_GenJuly: String { return self._s[1505]! } + public var Wallet_Receive_AddressHeader: String { return self._s[1506]! } + public var Wallet_Send_AmountText: String { return self._s[1507]! } + public var Settings_About: String { return self._s[1508]! } public func Channel_AdminLog_MessageRestricted(_ _0: String, _ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1496]!, self._r[1496]!, [_0, _1, _2]) + return formatWithArgumentRanges(self._s[1509]!, self._r[1509]!, [_0, _1, _2]) } - public var ChatList_Context_MarkAsRead: String { return self._s[1498]! } - public var KeyCommand_NewMessage: String { return self._s[1499]! } - public var Group_ErrorAddBlocked: String { return self._s[1500]! } + public var ChatList_Context_MarkAsRead: String { return self._s[1511]! } + public var KeyCommand_NewMessage: String { return self._s[1512]! } + public var Group_ErrorAddBlocked: String { return self._s[1513]! } public func Message_PaymentSent(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1501]!, self._r[1501]!, [_0]) + return formatWithArgumentRanges(self._s[1514]!, self._r[1514]!, [_0]) } - public var Map_LocationTitle: String { return self._s[1502]! } - public var ReportGroupLocation_Title: String { return self._s[1503]! } - public var CallSettings_UseLessDataLongDescription: String { return self._s[1504]! } - public var Cache_ClearProgress: String { return self._s[1505]! } + public var Map_LocationTitle: String { return self._s[1515]! } + public var ReportGroupLocation_Title: String { return self._s[1516]! } + public var CallSettings_UseLessDataLongDescription: String { return self._s[1517]! } + public var Cache_ClearProgress: String { return self._s[1518]! } public func Channel_Management_ErrorNotMember(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1506]!, self._r[1506]!, [_0]) + return formatWithArgumentRanges(self._s[1519]!, self._r[1519]!, [_0]) } - public var GroupRemoved_AddToGroup: String { return self._s[1507]! } - public var Passport_UpdateRequiredError: String { return self._s[1508]! } - public var Wallet_SecureStorageNotAvailable_Text: String { return self._s[1509]! } + public var GroupRemoved_AddToGroup: String { return self._s[1520]! } + public var Passport_UpdateRequiredError: String { return self._s[1521]! } + public var Wallet_SecureStorageNotAvailable_Text: String { return self._s[1522]! } public func PUSH_MESSAGE_DOC(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1510]!, self._r[1510]!, [_1]) + return formatWithArgumentRanges(self._s[1523]!, self._r[1523]!, [_1]) } - public var Notifications_PermissionsSuppressWarningText: String { return self._s[1512]! } - public var Passport_Identity_MainPageHelp: String { return self._s[1513]! } - public var Conversation_StatusKickedFromGroup: String { return self._s[1514]! } - public var Passport_Language_ka: String { return self._s[1515]! } + public var Notifications_PermissionsSuppressWarningText: String { return self._s[1525]! } + public var Passport_Identity_MainPageHelp: String { return self._s[1526]! } + public var Conversation_StatusKickedFromGroup: String { return self._s[1527]! } + public var Passport_Language_ka: String { return self._s[1528]! } public func Wallet_Time_PreciseDate_m12(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1516]!, self._r[1516]!, [_1, _2, _3]) + return formatWithArgumentRanges(self._s[1529]!, self._r[1529]!, [_1, _2, _3]) } - public var Call_Decline: String { return self._s[1517]! } - public var SocksProxySetup_ProxyEnabled: String { return self._s[1518]! } - public var TwoFactorSetup_Email_SkipConfirmationText: String { return self._s[1521]! } + public var Call_Decline: String { return self._s[1530]! } + public var SocksProxySetup_ProxyEnabled: String { return self._s[1531]! } + public var TwoFactorSetup_Email_SkipConfirmationText: String { return self._s[1534]! } public func AuthCode_Alert(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1522]!, self._r[1522]!, [_0]) + return formatWithArgumentRanges(self._s[1535]!, self._r[1535]!, [_0]) } - public var CallFeedback_Send: String { return self._s[1523]! } - public var EditTheme_EditTitle: String { return self._s[1524]! } + public var CallFeedback_Send: String { return self._s[1536]! } + public var EditTheme_EditTitle: String { return self._s[1537]! } public func Channel_AdminLog_MessagePromotedNameUsername(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1525]!, self._r[1525]!, [_1, _2]) + return formatWithArgumentRanges(self._s[1538]!, self._r[1538]!, [_1, _2]) } - public var Passport_Phone_UseTelegramNumberHelp: String { return self._s[1526]! } + public var Passport_Phone_UseTelegramNumberHelp: String { return self._s[1539]! } public func Wallet_Updated_TodayAt(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1528]!, self._r[1528]!, [_0]) + return formatWithArgumentRanges(self._s[1541]!, self._r[1541]!, [_0]) } - public var SettingsSearch_Synonyms_Data_Title: String { return self._s[1529]! } - public var Passport_DeletePassport: String { return self._s[1530]! } - public var Appearance_AppIconFilled: String { return self._s[1531]! } - public var Privacy_Calls_P2PAlways: String { return self._s[1532]! } - public var Month_ShortDecember: String { return self._s[1533]! } - public var Channel_AdminLog_CanEditMessages: String { return self._s[1535]! } + public var SettingsSearch_Synonyms_Data_Title: String { return self._s[1542]! } + public var Passport_DeletePassport: String { return self._s[1543]! } + public var Appearance_AppIconFilled: String { return self._s[1544]! } + public var Privacy_Calls_P2PAlways: String { return self._s[1545]! } + public var Month_ShortDecember: String { return self._s[1546]! } + public var Channel_AdminLog_CanEditMessages: String { return self._s[1548]! } public func Contacts_AccessDeniedHelpLandscape(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1536]!, self._r[1536]!, [_0]) + return formatWithArgumentRanges(self._s[1549]!, self._r[1549]!, [_0]) } - public var Channel_Stickers_Searching: String { return self._s[1537]! } - public var Conversation_EncryptedDescription1: String { return self._s[1538]! } - public var Conversation_EncryptedDescription2: String { return self._s[1539]! } - public var PasscodeSettings_PasscodeOptions: String { return self._s[1540]! } - public var Conversation_EncryptedDescription3: String { return self._s[1542]! } - public var PhotoEditor_SharpenTool: String { return self._s[1543]! } - public var Wallet_Configuration_Title: String { return self._s[1544]! } + public var Channel_Stickers_Searching: String { return self._s[1550]! } + public var Conversation_EncryptedDescription1: String { return self._s[1551]! } + public var Conversation_EncryptedDescription2: String { return self._s[1552]! } + public var PasscodeSettings_PasscodeOptions: String { return self._s[1553]! } + public var Conversation_EncryptedDescription3: String { return self._s[1555]! } + public var PhotoEditor_SharpenTool: String { return self._s[1556]! } + public var Wallet_Configuration_Title: String { return self._s[1557]! } public func Conversation_AddNameToContacts(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1545]!, self._r[1545]!, [_0]) + return formatWithArgumentRanges(self._s[1558]!, self._r[1558]!, [_0]) } - public var Conversation_EncryptedDescription4: String { return self._s[1547]! } - public var Channel_Members_AddMembers: String { return self._s[1548]! } - public var Wallpaper_Search: String { return self._s[1549]! } - public var Weekday_Friday: String { return self._s[1551]! } - public var Privacy_ContactsSync: String { return self._s[1552]! } - public var SettingsSearch_Synonyms_Privacy_Data_ContactsReset: String { return self._s[1553]! } - public var ApplyLanguage_ChangeLanguageAction: String { return self._s[1554]! } + public var Conversation_EncryptedDescription4: String { return self._s[1560]! } + public var Channel_Members_AddMembers: String { return self._s[1561]! } + public var Wallpaper_Search: String { return self._s[1562]! } + public var Weekday_Friday: String { return self._s[1564]! } + public var Privacy_ContactsSync: String { return self._s[1565]! } + public var SettingsSearch_Synonyms_Privacy_Data_ContactsReset: String { return self._s[1566]! } + public var ApplyLanguage_ChangeLanguageAction: String { return self._s[1567]! } public func Channel_Management_RestrictedBy(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1555]!, self._r[1555]!, [_0]) + return formatWithArgumentRanges(self._s[1568]!, self._r[1568]!, [_0]) } - public var Wallet_Configuration_BlockchainIdHeader: String { return self._s[1556]! } - public var GroupInfo_Permissions_Removed: String { return self._s[1557]! } - public var ScheduledMessages_ScheduledOnline: String { return self._s[1558]! } - public var Passport_Identity_GenderMale: String { return self._s[1559]! } + public var Wallet_Configuration_BlockchainIdHeader: String { return self._s[1569]! } + public var GroupInfo_Permissions_Removed: String { return self._s[1570]! } + public var ScheduledMessages_ScheduledOnline: String { return self._s[1571]! } + public var Passport_Identity_GenderMale: String { return self._s[1572]! } public func Call_StatusBar(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1560]!, self._r[1560]!, [_0]) + return formatWithArgumentRanges(self._s[1573]!, self._r[1573]!, [_0]) } - public var Notifications_PermissionsKeepDisabled: String { return self._s[1561]! } - public var Conversation_JumpToDate: String { return self._s[1562]! } - public var Contacts_GlobalSearch: String { return self._s[1563]! } - public var AutoDownloadSettings_ResetHelp: String { return self._s[1564]! } - public var SettingsSearch_Synonyms_FAQ: String { return self._s[1565]! } - public var Profile_MessageLifetime1d: String { return self._s[1566]! } + public var Notifications_PermissionsKeepDisabled: String { return self._s[1574]! } + public var Conversation_JumpToDate: String { return self._s[1575]! } + public var Contacts_GlobalSearch: String { return self._s[1576]! } + public var AutoDownloadSettings_ResetHelp: String { return self._s[1577]! } + public var SettingsSearch_Synonyms_FAQ: String { return self._s[1578]! } + public var Profile_MessageLifetime1d: String { return self._s[1579]! } public func MESSAGE_INVOICE(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1567]!, self._r[1567]!, [_1, _2]) - } - public var StickerPack_BuiltinPackName: String { return self._s[1570]! } - public func PUSH_CHAT_MESSAGE_AUDIO(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1571]!, self._r[1571]!, [_1, _2]) - } - public var VoiceOver_Chat_RecordModeVoiceMessageInfo: String { return self._s[1572]! } - public var Passport_InfoTitle: String { return self._s[1574]! } - public var Notifications_PermissionsUnreachableText: String { return self._s[1575]! } - public func NetworkUsageSettings_CellularUsageSince(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1579]!, self._r[1579]!, [_0]) - } - public func PUSH_CHAT_MESSAGE_GEO(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { return formatWithArgumentRanges(self._s[1580]!, self._r[1580]!, [_1, _2]) } - public var Passport_Address_TypePassportRegistrationUploadScan: String { return self._s[1581]! } - public var Profile_BotInfo: String { return self._s[1582]! } - public var Watch_Compose_CreateMessage: String { return self._s[1583]! } - public var AutoDownloadSettings_VoiceMessagesInfo: String { return self._s[1584]! } - public var Month_ShortNovember: String { return self._s[1585]! } - public var Conversation_ScamWarning: String { return self._s[1586]! } - public var Wallpaper_SetCustomBackground: String { return self._s[1587]! } - public var Appearance_TextSize_Title: String { return self._s[1588]! } - public var Passport_Identity_TranslationsHelp: String { return self._s[1589]! } - public var NotificationsSound_Chime: String { return self._s[1590]! } - public var Passport_Language_ko: String { return self._s[1592]! } - public var InviteText_URL: String { return self._s[1593]! } - public var TextFormat_Monospace: String { return self._s[1594]! } - public func Time_PreciseDate_m11(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1595]!, self._r[1595]!, [_1, _2, _3]) + public var StickerPack_BuiltinPackName: String { return self._s[1583]! } + public func PUSH_CHAT_MESSAGE_AUDIO(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[1584]!, self._r[1584]!, [_1, _2]) } - public var EditTheme_Edit_BottomInfo: String { return self._s[1596]! } + public var VoiceOver_Chat_RecordModeVoiceMessageInfo: String { return self._s[1585]! } + public var Passport_InfoTitle: String { return self._s[1587]! } + public var Notifications_PermissionsUnreachableText: String { return self._s[1588]! } + public func NetworkUsageSettings_CellularUsageSince(_ _0: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[1592]!, self._r[1592]!, [_0]) + } + public func PUSH_CHAT_MESSAGE_GEO(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[1593]!, self._r[1593]!, [_1, _2]) + } + public var Passport_Address_TypePassportRegistrationUploadScan: String { return self._s[1594]! } + public var Profile_BotInfo: String { return self._s[1595]! } + public var Watch_Compose_CreateMessage: String { return self._s[1596]! } + public var AutoDownloadSettings_VoiceMessagesInfo: String { return self._s[1597]! } + public var Month_ShortNovember: String { return self._s[1598]! } + public var Conversation_ScamWarning: String { return self._s[1599]! } + public var Wallpaper_SetCustomBackground: String { return self._s[1600]! } + public var Appearance_TextSize_Title: String { return self._s[1601]! } + public var Passport_Identity_TranslationsHelp: String { return self._s[1602]! } + public var NotificationsSound_Chime: String { return self._s[1603]! } + public var Passport_Language_ko: String { return self._s[1605]! } + public var InviteText_URL: String { return self._s[1606]! } + public var TextFormat_Monospace: String { return self._s[1607]! } + public func Time_PreciseDate_m11(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[1608]!, self._r[1608]!, [_1, _2, _3]) + } + public var EditTheme_Edit_BottomInfo: String { return self._s[1609]! } public func Login_WillSendSms(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1597]!, self._r[1597]!, [_0]) + return formatWithArgumentRanges(self._s[1610]!, self._r[1610]!, [_0]) } public func Watch_Time_ShortWeekdayAt(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1598]!, self._r[1598]!, [_1, _2]) + return formatWithArgumentRanges(self._s[1611]!, self._r[1611]!, [_1, _2]) } - public var Wallet_Words_Title: String { return self._s[1599]! } - public var Wallet_Month_ShortMay: String { return self._s[1600]! } - public var EditTheme_CreateTitle: String { return self._s[1602]! } - public var Passport_InfoLearnMore: String { return self._s[1603]! } - public var TwoStepAuth_EmailPlaceholder: String { return self._s[1604]! } - public var Passport_Identity_AddIdentityCard: String { return self._s[1605]! } - public var Your_card_has_expired: String { return self._s[1606]! } - public var StickerPacksSettings_StickerPacksSection: String { return self._s[1607]! } - public var GroupInfo_InviteLink_Help: String { return self._s[1608]! } - public var TwoFactorSetup_EmailVerification_ResendAction: String { return self._s[1612]! } - public var Conversation_Report: String { return self._s[1614]! } - public var Notifications_MessageNotificationsSound: String { return self._s[1615]! } - public var Notification_MessageLifetime1m: String { return self._s[1616]! } - public var Privacy_ContactsTitle: String { return self._s[1617]! } - public var Conversation_ShareMyContactInfo: String { return self._s[1618]! } - public var Wallet_WordCheck_Title: String { return self._s[1619]! } - public var ChannelMembers_WhoCanAddMembersAdminsHelp: String { return self._s[1620]! } - public var Channel_Members_Title: String { return self._s[1621]! } - public var Map_OpenInWaze: String { return self._s[1622]! } - public var Appearance_RemoveThemeColorConfirmation: String { return self._s[1623]! } - public var Login_PhoneBannedError: String { return self._s[1624]! } + public var Wallet_Words_Title: String { return self._s[1612]! } + public var Wallet_Month_ShortMay: String { return self._s[1613]! } + public var EditTheme_CreateTitle: String { return self._s[1615]! } + public var Passport_InfoLearnMore: String { return self._s[1616]! } + public var TwoStepAuth_EmailPlaceholder: String { return self._s[1617]! } + public var Passport_Identity_AddIdentityCard: String { return self._s[1618]! } + public var Your_card_has_expired: String { return self._s[1619]! } + public var StickerPacksSettings_StickerPacksSection: String { return self._s[1620]! } + public var GroupInfo_InviteLink_Help: String { return self._s[1621]! } + public var TwoFactorSetup_EmailVerification_ResendAction: String { return self._s[1625]! } + public var Conversation_Report: String { return self._s[1627]! } + public var Notifications_MessageNotificationsSound: String { return self._s[1628]! } + public var Notification_MessageLifetime1m: String { return self._s[1629]! } + public var Privacy_ContactsTitle: String { return self._s[1630]! } + public var Conversation_ShareMyContactInfo: String { return self._s[1631]! } + public var Wallet_WordCheck_Title: String { return self._s[1632]! } + public var ChannelMembers_WhoCanAddMembersAdminsHelp: String { return self._s[1633]! } + public var Channel_Members_Title: String { return self._s[1634]! } + public var Map_OpenInWaze: String { return self._s[1635]! } + public var Appearance_RemoveThemeColorConfirmation: String { return self._s[1636]! } + public var Login_PhoneBannedError: String { return self._s[1637]! } public func LiveLocationUpdated_YesterdayAt(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1625]!, self._r[1625]!, [_0]) - } - public var IntentsSettings_MainAccount: String { return self._s[1626]! } - public var Group_Management_AddModeratorHelp: String { return self._s[1627]! } - public var AutoDownloadSettings_WifiTitle: String { return self._s[1628]! } - public var Common_OK: String { return self._s[1629]! } - public var Passport_Address_TypeBankStatementUploadScan: String { return self._s[1630]! } - public var Wallet_Words_NotDoneResponse: String { return self._s[1631]! } - public var Cache_Music: String { return self._s[1632]! } - public var Wallet_Configuration_SourceURL: String { return self._s[1633]! } - public var SettingsSearch_Synonyms_EditProfile_PhoneNumber: String { return self._s[1634]! } - public var PasscodeSettings_UnlockWithTouchId: String { return self._s[1635]! } - public var TwoStepAuth_HintPlaceholder: String { return self._s[1636]! } - public func PUSH_PINNED_INVOICE(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1637]!, self._r[1637]!, [_1]) - } - public func Passport_RequestHeader(_ _0: String) -> (String, [(Int, NSRange)]) { return formatWithArgumentRanges(self._s[1638]!, self._r[1638]!, [_0]) } - public var TwoFactorSetup_Done_Action: String { return self._s[1639]! } + public var IntentsSettings_MainAccount: String { return self._s[1639]! } + public var Group_Management_AddModeratorHelp: String { return self._s[1640]! } + public var AutoDownloadSettings_WifiTitle: String { return self._s[1641]! } + public var Common_OK: String { return self._s[1642]! } + public var Passport_Address_TypeBankStatementUploadScan: String { return self._s[1643]! } + public var Wallet_Words_NotDoneResponse: String { return self._s[1644]! } + public var Cache_Music: String { return self._s[1645]! } + public var Wallet_Configuration_SourceURL: String { return self._s[1646]! } + public var SettingsSearch_Synonyms_EditProfile_PhoneNumber: String { return self._s[1647]! } + public var PasscodeSettings_UnlockWithTouchId: String { return self._s[1649]! } + public var TwoStepAuth_HintPlaceholder: String { return self._s[1650]! } + public func PUSH_PINNED_INVOICE(_ _1: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[1651]!, self._r[1651]!, [_1]) + } + public func Passport_RequestHeader(_ _0: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[1652]!, self._r[1652]!, [_0]) + } + public var TwoFactorSetup_Done_Action: String { return self._s[1653]! } public func VoiceOver_Chat_ContactOrganization(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1640]!, self._r[1640]!, [_0]) + return formatWithArgumentRanges(self._s[1654]!, self._r[1654]!, [_0]) } - public var Wallet_Send_ErrorNotEnoughFundsText: String { return self._s[1641]! } - public var Watch_MessageView_ViewOnPhone: String { return self._s[1643]! } - public var Privacy_Calls_CustomShareHelp: String { return self._s[1644]! } - public var Wallet_Receive_CreateInvoiceInfo: String { return self._s[1646]! } - public var ChangePhoneNumberNumber_Title: String { return self._s[1647]! } - public var State_ConnectingToProxyInfo: String { return self._s[1648]! } - public var Conversation_SwipeToReplyHintTitle: String { return self._s[1649]! } - public var Message_VideoMessage: String { return self._s[1651]! } - public var ChannelInfo_DeleteChannel: String { return self._s[1652]! } - public var ContactInfo_PhoneLabelOther: String { return self._s[1653]! } - public var Channel_EditAdmin_CannotEdit: String { return self._s[1654]! } - public var Passport_DeleteAddressConfirmation: String { return self._s[1655]! } + public var Wallet_Send_ErrorNotEnoughFundsText: String { return self._s[1655]! } + public var Watch_MessageView_ViewOnPhone: String { return self._s[1657]! } + public var Privacy_Calls_CustomShareHelp: String { return self._s[1658]! } + public var Wallet_Receive_CreateInvoiceInfo: String { return self._s[1660]! } + public var ChangePhoneNumberNumber_Title: String { return self._s[1661]! } + public var State_ConnectingToProxyInfo: String { return self._s[1662]! } + public var Conversation_SwipeToReplyHintTitle: String { return self._s[1663]! } + public var Message_VideoMessage: String { return self._s[1665]! } + public var ChannelInfo_DeleteChannel: String { return self._s[1666]! } + public var ContactInfo_PhoneLabelOther: String { return self._s[1667]! } + public var Channel_EditAdmin_CannotEdit: String { return self._s[1668]! } + public var Passport_DeleteAddressConfirmation: String { return self._s[1669]! } public func Wallet_Time_PreciseDate_m9(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1656]!, self._r[1656]!, [_1, _2, _3]) + return formatWithArgumentRanges(self._s[1670]!, self._r[1670]!, [_1, _2, _3]) } - public var WallpaperPreview_SwipeBottomText: String { return self._s[1657]! } - public var Activity_RecordingAudio: String { return self._s[1658]! } - public var SettingsSearch_Synonyms_Watch: String { return self._s[1659]! } - public var PasscodeSettings_TryAgainIn1Minute: String { return self._s[1660]! } - public var Wallet_Info_Address: String { return self._s[1661]! } + public var WallpaperPreview_SwipeBottomText: String { return self._s[1671]! } + public var Activity_RecordingAudio: String { return self._s[1672]! } + public var SettingsSearch_Synonyms_Watch: String { return self._s[1673]! } + public var PasscodeSettings_TryAgainIn1Minute: String { return self._s[1674]! } + public var Wallet_Info_Address: String { return self._s[1675]! } public func Notification_ChangedGroupName(_ _0: String, _ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1663]!, self._r[1663]!, [_0, _1]) + return formatWithArgumentRanges(self._s[1677]!, self._r[1677]!, [_0, _1]) } public func EmptyGroupInfo_Line1(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1667]!, self._r[1667]!, [_0]) + return formatWithArgumentRanges(self._s[1681]!, self._r[1681]!, [_0]) } - public var Conversation_ApplyLocalization: String { return self._s[1668]! } - public var TwoFactorSetup_Intro_Action: String { return self._s[1669]! } - public var UserInfo_AddPhone: String { return self._s[1670]! } - public var Map_ShareLiveLocationHelp: String { return self._s[1671]! } + public var Conversation_ApplyLocalization: String { return self._s[1682]! } + public var TwoFactorSetup_Intro_Action: String { return self._s[1683]! } + public var UserInfo_AddPhone: String { return self._s[1684]! } + public var Map_ShareLiveLocationHelp: String { return self._s[1685]! } public func Passport_Identity_NativeNameGenericHelp(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1672]!, self._r[1672]!, [_0]) + return formatWithArgumentRanges(self._s[1686]!, self._r[1686]!, [_0]) } - public var Passport_Scans: String { return self._s[1674]! } - public var BlockedUsers_Unblock: String { return self._s[1675]! } + public var Passport_Scans: String { return self._s[1688]! } + public var BlockedUsers_Unblock: String { return self._s[1689]! } public func PUSH_ENCRYPTION_REQUEST(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1676]!, self._r[1676]!, [_1]) + return formatWithArgumentRanges(self._s[1690]!, self._r[1690]!, [_1]) } - public var Channel_Management_LabelCreator: String { return self._s[1677]! } - public var Conversation_ReportSpamAndLeave: String { return self._s[1678]! } - public var SettingsSearch_Synonyms_EditProfile_Bio: String { return self._s[1679]! } - public var ChatList_UndoArchiveMultipleTitle: String { return self._s[1680]! } - public var Passport_Identity_NativeNameGenericTitle: String { return self._s[1681]! } + public var Channel_Management_LabelCreator: String { return self._s[1691]! } + public var Conversation_ReportSpamAndLeave: String { return self._s[1692]! } + public var SettingsSearch_Synonyms_EditProfile_Bio: String { return self._s[1693]! } + public var ChatList_UndoArchiveMultipleTitle: String { return self._s[1694]! } + public var Passport_Identity_NativeNameGenericTitle: String { return self._s[1695]! } public func Login_EmailPhoneBody(_ _0: String, _ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1682]!, self._r[1682]!, [_0, _1, _2]) + return formatWithArgumentRanges(self._s[1696]!, self._r[1696]!, [_0, _1, _2]) } - public var Login_PhoneNumberHelp: String { return self._s[1683]! } - public var LastSeen_ALongTimeAgo: String { return self._s[1684]! } - public var Channel_AdminLog_CanPinMessages: String { return self._s[1685]! } - public var ChannelIntro_CreateChannel: String { return self._s[1686]! } - public var Conversation_UnreadMessages: String { return self._s[1687]! } - public var SettingsSearch_Synonyms_Stickers_ArchivedPacks: String { return self._s[1688]! } - public var Channel_AdminLog_EmptyText: String { return self._s[1689]! } - public var Theme_Context_Apply: String { return self._s[1690]! } - public var Notification_GroupActivated: String { return self._s[1691]! } - public var NotificationSettings_ContactJoinedInfo: String { return self._s[1692]! } - public var Wallet_Intro_CreateWallet: String { return self._s[1693]! } + public var Login_PhoneNumberHelp: String { return self._s[1697]! } + public var LastSeen_ALongTimeAgo: String { return self._s[1698]! } + public var Channel_AdminLog_CanPinMessages: String { return self._s[1699]! } + public var ChannelIntro_CreateChannel: String { return self._s[1700]! } + public var Conversation_UnreadMessages: String { return self._s[1701]! } + public var SettingsSearch_Synonyms_Stickers_ArchivedPacks: String { return self._s[1702]! } + public var Channel_AdminLog_EmptyText: String { return self._s[1703]! } + public var Theme_Context_Apply: String { return self._s[1704]! } + public var Notification_GroupActivated: String { return self._s[1705]! } + public var NotificationSettings_ContactJoinedInfo: String { return self._s[1706]! } + public var Wallet_Intro_CreateWallet: String { return self._s[1707]! } public func Notification_PinnedContactMessage(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1694]!, self._r[1694]!, [_0]) + return formatWithArgumentRanges(self._s[1708]!, self._r[1708]!, [_0]) } public func DownloadingStatus(_ _0: String, _ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1695]!, self._r[1695]!, [_0, _1]) + return formatWithArgumentRanges(self._s[1709]!, self._r[1709]!, [_0, _1]) } - public var GroupInfo_ConvertToSupergroup: String { return self._s[1697]! } + public var GroupInfo_ConvertToSupergroup: String { return self._s[1711]! } public func PrivacyPolicy_AgeVerificationMessage(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1698]!, self._r[1698]!, [_0]) + return formatWithArgumentRanges(self._s[1712]!, self._r[1712]!, [_0]) } - public var Undo_DeletedChannel: String { return self._s[1699]! } - public var CallFeedback_AddComment: String { return self._s[1700]! } + public var Undo_DeletedChannel: String { return self._s[1713]! } + public var CallFeedback_AddComment: String { return self._s[1714]! } public func Conversation_OpenBotLinkAllowMessages(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1701]!, self._r[1701]!, [_0]) + return formatWithArgumentRanges(self._s[1715]!, self._r[1715]!, [_0]) } - public var Document_TargetConfirmationFormat: String { return self._s[1702]! } + public var Document_TargetConfirmationFormat: String { return self._s[1716]! } public func Call_StatusOngoing(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1703]!, self._r[1703]!, [_0]) + return formatWithArgumentRanges(self._s[1717]!, self._r[1717]!, [_0]) } - public var LogoutOptions_SetPasscodeTitle: String { return self._s[1704]! } + public var LogoutOptions_SetPasscodeTitle: String { return self._s[1718]! } public func PUSH_CHAT_MESSAGE_GAME_SCORE(_ _1: String, _ _2: String, _ _3: String, _ _4: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1705]!, self._r[1705]!, [_1, _2, _3, _4]) + return formatWithArgumentRanges(self._s[1719]!, self._r[1719]!, [_1, _2, _3, _4]) } - public var Wallet_SecureStorageChanged_PasscodeText: String { return self._s[1706]! } - public var Theme_ErrorNotFound: String { return self._s[1707]! } - public var Contacts_SortByName: String { return self._s[1708]! } - public var SettingsSearch_Synonyms_Privacy_Forwards: String { return self._s[1709]! } + public var Wallet_SecureStorageChanged_PasscodeText: String { return self._s[1720]! } + public var Theme_ErrorNotFound: String { return self._s[1721]! } + public var Contacts_SortByName: String { return self._s[1722]! } + public var SettingsSearch_Synonyms_Privacy_Forwards: String { return self._s[1723]! } public func CHAT_MESSAGE_INVOICE(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1711]!, self._r[1711]!, [_1, _2, _3]) + return formatWithArgumentRanges(self._s[1725]!, self._r[1725]!, [_1, _2, _3]) } - public var Notification_Exceptions_RemoveFromExceptions: String { return self._s[1712]! } - public var ScheduledMessages_EditTime: String { return self._s[1713]! } - public var Conversation_ClearSelfHistory: String { return self._s[1714]! } - public var Checkout_NewCard_PostcodePlaceholder: String { return self._s[1715]! } - public var PasscodeSettings_DoNotMatch: String { return self._s[1716]! } - public var Stickers_SuggestNone: String { return self._s[1717]! } - public var ChatSettings_Cache: String { return self._s[1718]! } - public var Settings_SaveIncomingPhotos: String { return self._s[1719]! } - public var Media_ShareThisPhoto: String { return self._s[1720]! } - public var Chat_SlowmodeTooltipPending: String { return self._s[1721]! } - public var InfoPlist_NSContactsUsageDescription: String { return self._s[1722]! } - public var Conversation_ContextMenuCopyLink: String { return self._s[1723]! } - public var PrivacyPolicy_AgeVerificationTitle: String { return self._s[1724]! } - public var SettingsSearch_Synonyms_Stickers_Masks: String { return self._s[1725]! } - public var TwoStepAuth_SetupPasswordEnterPasswordNew: String { return self._s[1726]! } - public var Appearance_ThemePreview_Chat_6_Text: String { return self._s[1727]! } + public var Notification_Exceptions_RemoveFromExceptions: String { return self._s[1726]! } + public var ScheduledMessages_EditTime: String { return self._s[1727]! } + public var Conversation_ClearSelfHistory: String { return self._s[1728]! } + public var Checkout_NewCard_PostcodePlaceholder: String { return self._s[1729]! } + public var PasscodeSettings_DoNotMatch: String { return self._s[1730]! } + public var Stickers_SuggestNone: String { return self._s[1731]! } + public var ChatSettings_Cache: String { return self._s[1732]! } + public var Settings_SaveIncomingPhotos: String { return self._s[1733]! } + public var Media_ShareThisPhoto: String { return self._s[1734]! } + public var Chat_SlowmodeTooltipPending: String { return self._s[1735]! } + public var InfoPlist_NSContactsUsageDescription: String { return self._s[1736]! } + public var Conversation_ContextMenuCopyLink: String { return self._s[1737]! } + public var PrivacyPolicy_AgeVerificationTitle: String { return self._s[1738]! } + public var SettingsSearch_Synonyms_Stickers_Masks: String { return self._s[1739]! } + public var TwoStepAuth_SetupPasswordEnterPasswordNew: String { return self._s[1740]! } + public var Appearance_ThemePreview_Chat_6_Text: String { return self._s[1741]! } public func Wallet_SecureStorageReset_BiometryText(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1728]!, self._r[1728]!, [_0]) + return formatWithArgumentRanges(self._s[1742]!, self._r[1742]!, [_0]) } - public var Permissions_CellularDataTitle_v0: String { return self._s[1729]! } - public var WallpaperSearch_ColorWhite: String { return self._s[1731]! } - public var Channel_AdminLog_DefaultRestrictionsUpdated: String { return self._s[1732]! } - public var Conversation_ErrorInaccessibleMessage: String { return self._s[1733]! } - public var Map_OpenIn: String { return self._s[1734]! } + public var Permissions_CellularDataTitle_v0: String { return self._s[1743]! } + public var WallpaperSearch_ColorWhite: String { return self._s[1745]! } + public var Channel_AdminLog_DefaultRestrictionsUpdated: String { return self._s[1746]! } + public var Conversation_ErrorInaccessibleMessage: String { return self._s[1747]! } + public var Map_OpenIn: String { return self._s[1748]! } public func PUSH_PHONE_CALL_MISSED(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1737]!, self._r[1737]!, [_1]) + return formatWithArgumentRanges(self._s[1751]!, self._r[1751]!, [_1]) } public func ChannelInfo_AddParticipantConfirmation(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1738]!, self._r[1738]!, [_0]) + return formatWithArgumentRanges(self._s[1752]!, self._r[1752]!, [_0]) } - public var GroupInfo_Permissions_SlowmodeHeader: String { return self._s[1739]! } - public var MessagePoll_LabelClosed: String { return self._s[1740]! } - public var GroupPermission_PermissionGloballyDisabled: String { return self._s[1742]! } - public var Wallet_Send_SendAnyway: String { return self._s[1743]! } - public var Passport_Identity_MiddleNamePlaceholder: String { return self._s[1744]! } - public var UserInfo_FirstNamePlaceholder: String { return self._s[1745]! } - public var PrivacyLastSeenSettings_WhoCanSeeMyTimestamp: String { return self._s[1746]! } - public var Map_SetThisPlace: String { return self._s[1747]! } - public var Login_SelectCountry_Title: String { return self._s[1748]! } - public var Channel_EditAdmin_PermissionBanUsers: String { return self._s[1749]! } + public var GroupInfo_Permissions_SlowmodeHeader: String { return self._s[1753]! } + public var MessagePoll_LabelClosed: String { return self._s[1754]! } + public var GroupPermission_PermissionGloballyDisabled: String { return self._s[1756]! } + public var Wallet_Send_SendAnyway: String { return self._s[1757]! } + public var Passport_Identity_MiddleNamePlaceholder: String { return self._s[1758]! } + public var UserInfo_FirstNamePlaceholder: String { return self._s[1759]! } + public var PrivacyLastSeenSettings_WhoCanSeeMyTimestamp: String { return self._s[1760]! } + public var Map_SetThisPlace: String { return self._s[1761]! } + public var Login_SelectCountry_Title: String { return self._s[1762]! } + public var Channel_EditAdmin_PermissionBanUsers: String { return self._s[1763]! } public func Conversation_OpenBotLinkLogin(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1750]!, self._r[1750]!, [_1, _2]) + return formatWithArgumentRanges(self._s[1764]!, self._r[1764]!, [_1, _2]) } - public var Channel_AdminLog_ChangeInfo: String { return self._s[1751]! } - public var Watch_Suggestion_BRB: String { return self._s[1752]! } - public var Passport_Identity_EditIdentityCard: String { return self._s[1753]! } - public var Contacts_PermissionsTitle: String { return self._s[1754]! } - public var Conversation_RestrictedInline: String { return self._s[1755]! } - public var Appearance_RemoveThemeColor: String { return self._s[1757]! } - public var StickerPack_ViewPack: String { return self._s[1758]! } - public var Wallet_UnknownError: String { return self._s[1759]! } + public var Channel_AdminLog_ChangeInfo: String { return self._s[1765]! } + public var Watch_Suggestion_BRB: String { return self._s[1766]! } + public var Passport_Identity_EditIdentityCard: String { return self._s[1767]! } + public var Contacts_PermissionsTitle: String { return self._s[1768]! } + public var Conversation_RestrictedInline: String { return self._s[1769]! } + public var Appearance_RemoveThemeColor: String { return self._s[1771]! } + public var StickerPack_ViewPack: String { return self._s[1772]! } + public var Wallet_UnknownError: String { return self._s[1773]! } public func Update_AppVersion(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1760]!, self._r[1760]!, [_0]) + return formatWithArgumentRanges(self._s[1774]!, self._r[1774]!, [_0]) } - public var Compose_NewChannel: String { return self._s[1762]! } - public var ChatSettings_AutoDownloadSettings_TypePhoto: String { return self._s[1765]! } - public var Conversation_ReportSpamGroupConfirmation: String { return self._s[1767]! } - public var Channel_Info_Stickers: String { return self._s[1768]! } - public var AutoNightTheme_PreferredTheme: String { return self._s[1769]! } - public var PrivacyPolicy_AgeVerificationAgree: String { return self._s[1770]! } - public var Passport_DeletePersonalDetails: String { return self._s[1771]! } - public var LogoutOptions_AddAccountTitle: String { return self._s[1772]! } - public var Channel_DiscussionGroupInfo: String { return self._s[1773]! } - public var Group_EditAdmin_RankOwnerPlaceholder: String { return self._s[1774]! } - public var Conversation_SearchNoResults: String { return self._s[1777]! } - public var Wallet_Configuration_ApplyErrorTextURLInvalid: String { return self._s[1778]! } - public var MessagePoll_LabelAnonymous: String { return self._s[1779]! } - public var Channel_Members_AddAdminErrorNotAMember: String { return self._s[1780]! } - public var Login_Code: String { return self._s[1781]! } - public var EditTheme_Create_BottomInfo: String { return self._s[1782]! } - public var Watch_Suggestion_WhatsUp: String { return self._s[1783]! } - public var Weekday_ShortThursday: String { return self._s[1784]! } - public var Resolve_ErrorNotFound: String { return self._s[1786]! } - public var LastSeen_Offline: String { return self._s[1787]! } - public var PeopleNearby_NoMembers: String { return self._s[1788]! } - public var GroupPermission_AddMembersNotAvailable: String { return self._s[1789]! } - public var Privacy_Calls_AlwaysAllow_Title: String { return self._s[1790]! } - public var GroupInfo_Title: String { return self._s[1792]! } - public var NotificationsSound_Note: String { return self._s[1793]! } - public var Conversation_EditingMessagePanelTitle: String { return self._s[1794]! } - public var Watch_Message_Poll: String { return self._s[1795]! } - public var Privacy_Calls: String { return self._s[1796]! } + public var Compose_NewChannel: String { return self._s[1776]! } + public var ChatSettings_AutoDownloadSettings_TypePhoto: String { return self._s[1779]! } + public var MessagePoll_LabelQuiz: String { return self._s[1781]! } + public var Conversation_ReportSpamGroupConfirmation: String { return self._s[1782]! } + public var Channel_Info_Stickers: String { return self._s[1783]! } + public var AutoNightTheme_PreferredTheme: String { return self._s[1784]! } + public var PrivacyPolicy_AgeVerificationAgree: String { return self._s[1785]! } + public var Passport_DeletePersonalDetails: String { return self._s[1786]! } + public var LogoutOptions_AddAccountTitle: String { return self._s[1787]! } + public var Channel_DiscussionGroupInfo: String { return self._s[1788]! } + public var Group_EditAdmin_RankOwnerPlaceholder: String { return self._s[1789]! } + public var Conversation_SearchNoResults: String { return self._s[1792]! } + public var Wallet_Configuration_ApplyErrorTextURLInvalid: String { return self._s[1793]! } + public var MessagePoll_LabelAnonymous: String { return self._s[1794]! } + public var Channel_Members_AddAdminErrorNotAMember: String { return self._s[1795]! } + public var Login_Code: String { return self._s[1796]! } + public var EditTheme_Create_BottomInfo: String { return self._s[1797]! } + public var Watch_Suggestion_WhatsUp: String { return self._s[1798]! } + public var Weekday_ShortThursday: String { return self._s[1799]! } + public var Resolve_ErrorNotFound: String { return self._s[1801]! } + public var LastSeen_Offline: String { return self._s[1802]! } + public var PeopleNearby_NoMembers: String { return self._s[1803]! } + public var GroupPermission_AddMembersNotAvailable: String { return self._s[1804]! } + public var Privacy_Calls_AlwaysAllow_Title: String { return self._s[1805]! } + public var GroupInfo_Title: String { return self._s[1807]! } + public var NotificationsSound_Note: String { return self._s[1808]! } + public var Conversation_EditingMessagePanelTitle: String { return self._s[1809]! } + public var Watch_Message_Poll: String { return self._s[1810]! } + public var Privacy_Calls: String { return self._s[1811]! } public func Channel_AdminLog_MessageRankUsername(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1797]!, self._r[1797]!, [_1, _2, _3]) + return formatWithArgumentRanges(self._s[1812]!, self._r[1812]!, [_1, _2, _3]) } - public var Month_ShortAugust: String { return self._s[1798]! } - public var TwoStepAuth_SetPasswordHelp: String { return self._s[1799]! } - public var Notifications_Reset: String { return self._s[1800]! } - public var Conversation_Pin: String { return self._s[1801]! } - public var Passport_Language_lv: String { return self._s[1802]! } - public var Permissions_PeopleNearbyAllowInSettings_v0: String { return self._s[1803]! } - public var BlockedUsers_Info: String { return self._s[1804]! } - public var SettingsSearch_Synonyms_Data_AutoplayVideos: String { return self._s[1806]! } - public var Watch_Conversation_Unblock: String { return self._s[1808]! } + public var Month_ShortAugust: String { return self._s[1813]! } + public var TwoStepAuth_SetPasswordHelp: String { return self._s[1814]! } + public var Notifications_Reset: String { return self._s[1815]! } + public var Conversation_Pin: String { return self._s[1816]! } + public var Passport_Language_lv: String { return self._s[1817]! } + public var Permissions_PeopleNearbyAllowInSettings_v0: String { return self._s[1818]! } + public var BlockedUsers_Info: String { return self._s[1819]! } + public var SettingsSearch_Synonyms_Data_AutoplayVideos: String { return self._s[1821]! } + public var Watch_Conversation_Unblock: String { return self._s[1823]! } public func Time_MonthOfYear_m9(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1809]!, self._r[1809]!, [_0]) + return formatWithArgumentRanges(self._s[1824]!, self._r[1824]!, [_0]) } - public var CloudStorage_Title: String { return self._s[1810]! } - public var GroupInfo_DeleteAndExitConfirmation: String { return self._s[1811]! } + public var CloudStorage_Title: String { return self._s[1825]! } + public var GroupInfo_DeleteAndExitConfirmation: String { return self._s[1826]! } public func NetworkUsageSettings_WifiUsageSince(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1812]!, self._r[1812]!, [_0]) + return formatWithArgumentRanges(self._s[1827]!, self._r[1827]!, [_0]) } - public var Channel_AdminLogFilter_AdminsTitle: String { return self._s[1813]! } - public var Watch_Suggestion_OnMyWay: String { return self._s[1814]! } - public var TwoStepAuth_RecoveryEmailTitle: String { return self._s[1815]! } - public var Passport_Address_EditBankStatement: String { return self._s[1816]! } + public var Channel_AdminLogFilter_AdminsTitle: String { return self._s[1828]! } + public var Watch_Suggestion_OnMyWay: String { return self._s[1829]! } + public var TwoStepAuth_RecoveryEmailTitle: String { return self._s[1830]! } + public var Passport_Address_EditBankStatement: String { return self._s[1831]! } public func Channel_AdminLog_MessageChangedUnlinkedGroup(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1817]!, self._r[1817]!, [_1, _2]) + return formatWithArgumentRanges(self._s[1832]!, self._r[1832]!, [_1, _2]) } - public var ChatSettings_DownloadInBackgroundInfo: String { return self._s[1818]! } - public var ShareMenu_Comment: String { return self._s[1819]! } - public var Permissions_ContactsTitle_v0: String { return self._s[1820]! } - public var Notifications_PermissionsTitle: String { return self._s[1821]! } - public var GroupPermission_NoSendLinks: String { return self._s[1822]! } - public var Privacy_Forwards_NeverAllow_Title: String { return self._s[1823]! } - public var Wallet_SecureStorageChanged_ImportWallet: String { return self._s[1824]! } - public var Settings_Support: String { return self._s[1825]! } - public var Notifications_ChannelNotificationsSound: String { return self._s[1826]! } - public var SettingsSearch_Synonyms_Data_AutoDownloadReset: String { return self._s[1827]! } - public var Privacy_Forwards_Preview: String { return self._s[1828]! } - public var GroupPermission_ApplyAlertAction: String { return self._s[1829]! } - public var Watch_Stickers_StickerPacks: String { return self._s[1830]! } - public var Common_Select: String { return self._s[1832]! } - public var CheckoutInfo_ErrorEmailInvalid: String { return self._s[1833]! } - public var WallpaperSearch_ColorGray: String { return self._s[1836]! } - public var TwoFactorSetup_Password_PlaceholderPassword: String { return self._s[1837]! } - public var TwoFactorSetup_Hint_SkipAction: String { return self._s[1838]! } - public var ChatAdmins_AllMembersAreAdminsOffHelp: String { return self._s[1839]! } - public var PasscodeSettings_AutoLock_IfAwayFor_5hours: String { return self._s[1840]! } - public var Appearance_PreviewReplyAuthor: String { return self._s[1841]! } - public var TwoStepAuth_RecoveryTitle: String { return self._s[1842]! } - public var Widget_AuthRequired: String { return self._s[1843]! } - public var Camera_FlashOn: String { return self._s[1844]! } - public var Conversation_ContextMenuLookUp: String { return self._s[1845]! } - public var Channel_Stickers_NotFoundHelp: String { return self._s[1846]! } - public var Watch_Suggestion_OK: String { return self._s[1847]! } + public var ChatSettings_DownloadInBackgroundInfo: String { return self._s[1833]! } + public var ShareMenu_Comment: String { return self._s[1834]! } + public var Permissions_ContactsTitle_v0: String { return self._s[1835]! } + public var Notifications_PermissionsTitle: String { return self._s[1836]! } + public var GroupPermission_NoSendLinks: String { return self._s[1837]! } + public var Privacy_Forwards_NeverAllow_Title: String { return self._s[1838]! } + public var Wallet_SecureStorageChanged_ImportWallet: String { return self._s[1839]! } + public var Settings_Support: String { return self._s[1840]! } + public var Notifications_ChannelNotificationsSound: String { return self._s[1841]! } + public var SettingsSearch_Synonyms_Data_AutoDownloadReset: String { return self._s[1842]! } + public var Privacy_Forwards_Preview: String { return self._s[1843]! } + public var GroupPermission_ApplyAlertAction: String { return self._s[1844]! } + public var Watch_Stickers_StickerPacks: String { return self._s[1845]! } + public var Common_Select: String { return self._s[1847]! } + public var CheckoutInfo_ErrorEmailInvalid: String { return self._s[1848]! } + public var WallpaperSearch_ColorGray: String { return self._s[1851]! } + public var TwoFactorSetup_Password_PlaceholderPassword: String { return self._s[1852]! } + public var TwoFactorSetup_Hint_SkipAction: String { return self._s[1853]! } + public var ChatAdmins_AllMembersAreAdminsOffHelp: String { return self._s[1854]! } + public var PollResults_Title: String { return self._s[1855]! } + public var PasscodeSettings_AutoLock_IfAwayFor_5hours: String { return self._s[1856]! } + public var Appearance_PreviewReplyAuthor: String { return self._s[1857]! } + public var TwoStepAuth_RecoveryTitle: String { return self._s[1858]! } + public var Widget_AuthRequired: String { return self._s[1859]! } + public var Camera_FlashOn: String { return self._s[1860]! } + public var Conversation_ContextMenuLookUp: String { return self._s[1861]! } + public var Channel_Stickers_NotFoundHelp: String { return self._s[1862]! } + public var Watch_Suggestion_OK: String { return self._s[1863]! } public func Username_LinkHint(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1849]!, self._r[1849]!, [_0]) + return formatWithArgumentRanges(self._s[1865]!, self._r[1865]!, [_0]) } public func Notification_PinnedLiveLocationMessage(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1851]!, self._r[1851]!, [_0]) + return formatWithArgumentRanges(self._s[1867]!, self._r[1867]!, [_0]) } - public var TextFormat_Strikethrough: String { return self._s[1852]! } - public var DialogList_AdLabel: String { return self._s[1853]! } - public var WatchRemote_NotificationText: String { return self._s[1854]! } - public var IntentsSettings_SuggestedChatsSavedMessages: String { return self._s[1855]! } - public var SettingsSearch_Synonyms_Notifications_MessageNotificationsAlert: String { return self._s[1856]! } - public var Conversation_ReportSpam: String { return self._s[1857]! } - public var SettingsSearch_Synonyms_Privacy_Data_TopPeers: String { return self._s[1858]! } - public var Settings_LogoutConfirmationTitle: String { return self._s[1860]! } - public var PhoneLabel_Title: String { return self._s[1861]! } - public var Passport_Address_EditRentalAgreement: String { return self._s[1862]! } - public var Settings_ChangePhoneNumber: String { return self._s[1863]! } - public var Notifications_ExceptionsTitle: String { return self._s[1864]! } - public var Notifications_AlertTones: String { return self._s[1865]! } - public var Call_ReportIncludeLogDescription: String { return self._s[1866]! } - public var SettingsSearch_Synonyms_Notifications_ResetAllNotifications: String { return self._s[1867]! } - public var AutoDownloadSettings_PrivateChats: String { return self._s[1868]! } - public var VoiceOver_Chat_Photo: String { return self._s[1870]! } - public var TwoStepAuth_AddHintTitle: String { return self._s[1871]! } - public var ReportPeer_ReasonOther: String { return self._s[1872]! } - public var ChatList_Context_JoinChannel: String { return self._s[1873]! } - public var KeyCommand_ScrollDown: String { return self._s[1875]! } - public var Conversation_ScheduleMessage_Title: String { return self._s[1876]! } + public var TextFormat_Strikethrough: String { return self._s[1868]! } + public var DialogList_AdLabel: String { return self._s[1869]! } + public var WatchRemote_NotificationText: String { return self._s[1870]! } + public var IntentsSettings_SuggestedChatsSavedMessages: String { return self._s[1871]! } + public var SettingsSearch_Synonyms_Notifications_MessageNotificationsAlert: String { return self._s[1872]! } + public var Conversation_ReportSpam: String { return self._s[1873]! } + public var SettingsSearch_Synonyms_Privacy_Data_TopPeers: String { return self._s[1874]! } + public var Settings_LogoutConfirmationTitle: String { return self._s[1876]! } + public var PhoneLabel_Title: String { return self._s[1877]! } + public var Passport_Address_EditRentalAgreement: String { return self._s[1878]! } + public var Settings_ChangePhoneNumber: String { return self._s[1879]! } + public var Notifications_ExceptionsTitle: String { return self._s[1880]! } + public var Notifications_AlertTones: String { return self._s[1881]! } + public var Call_ReportIncludeLogDescription: String { return self._s[1882]! } + public var SettingsSearch_Synonyms_Notifications_ResetAllNotifications: String { return self._s[1883]! } + public var AutoDownloadSettings_PrivateChats: String { return self._s[1884]! } + public var VoiceOver_Chat_Photo: String { return self._s[1886]! } + public var TwoStepAuth_AddHintTitle: String { return self._s[1887]! } + public var ReportPeer_ReasonOther: String { return self._s[1888]! } + public var ChatList_Context_JoinChannel: String { return self._s[1889]! } + public var KeyCommand_ScrollDown: String { return self._s[1891]! } + public var Conversation_ScheduleMessage_Title: String { return self._s[1892]! } public func Login_BannedPhoneSubject(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1877]!, self._r[1877]!, [_0]) + return formatWithArgumentRanges(self._s[1893]!, self._r[1893]!, [_0]) } - public var NetworkUsageSettings_MediaVideoDataSection: String { return self._s[1878]! } - public var ChannelInfo_DeleteGroupConfirmation: String { return self._s[1879]! } - public var AuthSessions_LogOut: String { return self._s[1880]! } - public var Passport_Identity_TypeInternalPassport: String { return self._s[1881]! } - public var ChatSettings_AutoDownloadVoiceMessages: String { return self._s[1882]! } - public var Passport_Phone_Title: String { return self._s[1883]! } - public var ContactList_Context_StartSecretChat: String { return self._s[1884]! } - public var Settings_PhoneNumber: String { return self._s[1885]! } + public var NetworkUsageSettings_MediaVideoDataSection: String { return self._s[1894]! } + public var ChannelInfo_DeleteGroupConfirmation: String { return self._s[1895]! } + public var AuthSessions_LogOut: String { return self._s[1896]! } + public var Passport_Identity_TypeInternalPassport: String { return self._s[1897]! } + public var ChatSettings_AutoDownloadVoiceMessages: String { return self._s[1898]! } + public var Passport_Phone_Title: String { return self._s[1899]! } + public var ContactList_Context_StartSecretChat: String { return self._s[1900]! } + public var Settings_PhoneNumber: String { return self._s[1901]! } public func Conversation_ScheduleMessage_SendToday(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1886]!, self._r[1886]!, [_0]) + return formatWithArgumentRanges(self._s[1902]!, self._r[1902]!, [_0]) } - public var NotificationsSound_Alert: String { return self._s[1888]! } - public var Wallet_SecureStorageChanged_CreateWallet: String { return self._s[1889]! } - public var WebSearch_SearchNoResults: String { return self._s[1890]! } - public var Privacy_ProfilePhoto_AlwaysShareWith_Title: String { return self._s[1892]! } - public var Wallet_Configuration_SourceInfo: String { return self._s[1893]! } - public var LogoutOptions_AlternativeOptionsSection: String { return self._s[1894]! } - public var SettingsSearch_Synonyms_Passport: String { return self._s[1895]! } - public var PhotoEditor_CurvesTool: String { return self._s[1896]! } - public var Checkout_PaymentMethod: String { return self._s[1898]! } + public var NotificationsSound_Alert: String { return self._s[1904]! } + public var Wallet_SecureStorageChanged_CreateWallet: String { return self._s[1905]! } + public var WebSearch_SearchNoResults: String { return self._s[1906]! } + public var Privacy_ProfilePhoto_AlwaysShareWith_Title: String { return self._s[1908]! } + public var Wallet_Configuration_SourceInfo: String { return self._s[1909]! } + public var LogoutOptions_AlternativeOptionsSection: String { return self._s[1910]! } + public var SettingsSearch_Synonyms_Passport: String { return self._s[1911]! } + public var PhotoEditor_CurvesTool: String { return self._s[1912]! } + public var Checkout_PaymentMethod: String { return self._s[1914]! } public func PUSH_CHAT_ADD_YOU(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1899]!, self._r[1899]!, [_1, _2]) + return formatWithArgumentRanges(self._s[1915]!, self._r[1915]!, [_1, _2]) } - public var Contacts_AccessDeniedError: String { return self._s[1900]! } - public var Camera_PhotoMode: String { return self._s[1903]! } - public var EditTheme_Expand_Preview_IncomingText: String { return self._s[1904]! } - public var Appearance_TextSize_Apply: String { return self._s[1905]! } - public var Passport_Address_AddUtilityBill: String { return self._s[1907]! } - public var CallSettings_OnMobile: String { return self._s[1908]! } - public var Tour_Text2: String { return self._s[1909]! } + public var Contacts_AccessDeniedError: String { return self._s[1916]! } + public var Camera_PhotoMode: String { return self._s[1919]! } + public var EditTheme_Expand_Preview_IncomingText: String { return self._s[1920]! } + public var Appearance_TextSize_Apply: String { return self._s[1921]! } + public var Passport_Address_AddUtilityBill: String { return self._s[1923]! } + public var CallSettings_OnMobile: String { return self._s[1924]! } + public var Tour_Text2: String { return self._s[1925]! } public func PUSH_CHAT_MESSAGE_ROUND(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1910]!, self._r[1910]!, [_1, _2]) + return formatWithArgumentRanges(self._s[1926]!, self._r[1926]!, [_1, _2]) } - public var DialogList_EncryptionProcessing: String { return self._s[1912]! } - public var Permissions_Skip: String { return self._s[1913]! } - public var Wallet_Words_NotDoneOk: String { return self._s[1914]! } - public var SecretImage_Title: String { return self._s[1915]! } - public var Watch_MessageView_Title: String { return self._s[1916]! } - public var Channel_DiscussionGroupAdd: String { return self._s[1917]! } - public var AttachmentMenu_Poll: String { return self._s[1918]! } + public var DialogList_EncryptionProcessing: String { return self._s[1928]! } + public var Permissions_Skip: String { return self._s[1929]! } + public var Wallet_Words_NotDoneOk: String { return self._s[1930]! } + public var SecretImage_Title: String { return self._s[1931]! } + public var Watch_MessageView_Title: String { return self._s[1932]! } + public var Channel_DiscussionGroupAdd: String { return self._s[1933]! } + public var AttachmentMenu_Poll: String { return self._s[1934]! } public func Notification_GroupInviter(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1919]!, self._r[1919]!, [_0]) + return formatWithArgumentRanges(self._s[1935]!, self._r[1935]!, [_0]) } public func Channel_DiscussionGroup_PrivateChannelLink(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1920]!, self._r[1920]!, [_1, _2]) + return formatWithArgumentRanges(self._s[1936]!, self._r[1936]!, [_1, _2]) } - public var Notification_CallCanceled: String { return self._s[1921]! } - public var WallpaperPreview_Title: String { return self._s[1922]! } - public var Privacy_PaymentsClear_PaymentInfo: String { return self._s[1923]! } - public var Settings_ProxyConnecting: String { return self._s[1924]! } - public var Settings_CheckPhoneNumberText: String { return self._s[1926]! } - public var VoiceOver_Chat_YourVideo: String { return self._s[1927]! } - public var Wallet_Intro_Title: String { return self._s[1928]! } - public var TwoFactorSetup_Password_Action: String { return self._s[1929]! } - public var Profile_MessageLifetime5s: String { return self._s[1930]! } - public var Username_InvalidCharacters: String { return self._s[1931]! } - public var VoiceOver_Media_PlaybackRateFast: String { return self._s[1932]! } - public var ScheduledMessages_ClearAll: String { return self._s[1933]! } - public var WallpaperPreview_CropBottomText: String { return self._s[1934]! } - public var AutoDownloadSettings_LimitBySize: String { return self._s[1935]! } - public var Settings_AddAccount: String { return self._s[1936]! } - public var Notification_CreatedChannel: String { return self._s[1939]! } + public var Notification_CallCanceled: String { return self._s[1937]! } + public var WallpaperPreview_Title: String { return self._s[1938]! } + public var Privacy_PaymentsClear_PaymentInfo: String { return self._s[1939]! } + public var Settings_ProxyConnecting: String { return self._s[1940]! } + public var Settings_CheckPhoneNumberText: String { return self._s[1942]! } + public var VoiceOver_Chat_YourVideo: String { return self._s[1943]! } + public var Wallet_Intro_Title: String { return self._s[1944]! } + public var TwoFactorSetup_Password_Action: String { return self._s[1945]! } + public var Profile_MessageLifetime5s: String { return self._s[1946]! } + public var Username_InvalidCharacters: String { return self._s[1947]! } + public var VoiceOver_Media_PlaybackRateFast: String { return self._s[1948]! } + public var ScheduledMessages_ClearAll: String { return self._s[1949]! } + public var WallpaperPreview_CropBottomText: String { return self._s[1950]! } + public var AutoDownloadSettings_LimitBySize: String { return self._s[1951]! } + public var Settings_AddAccount: String { return self._s[1952]! } + public var Notification_CreatedChannel: String { return self._s[1955]! } public func PUSH_CHAT_DELETE_MEMBER(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1940]!, self._r[1940]!, [_1, _2, _3]) + return formatWithArgumentRanges(self._s[1956]!, self._r[1956]!, [_1, _2, _3]) } - public var Passcode_AppLockedAlert: String { return self._s[1942]! } - public var StickerPacksSettings_AnimatedStickersInfo: String { return self._s[1943]! } - public var VoiceOver_Media_PlaybackStop: String { return self._s[1944]! } - public var Contacts_TopSection: String { return self._s[1945]! } - public var ChatList_DeleteForEveryoneConfirmationAction: String { return self._s[1946]! } + public var Passcode_AppLockedAlert: String { return self._s[1958]! } + public var StickerPacksSettings_AnimatedStickersInfo: String { return self._s[1959]! } + public var VoiceOver_Media_PlaybackStop: String { return self._s[1960]! } + public var Contacts_TopSection: String { return self._s[1961]! } + public var ChatList_DeleteForEveryoneConfirmationAction: String { return self._s[1962]! } public func Conversation_SetReminder_RemindOn(_ _0: String, _ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1947]!, self._r[1947]!, [_0, _1]) + return formatWithArgumentRanges(self._s[1963]!, self._r[1963]!, [_0, _1]) } - public var Wallet_Info_Receive: String { return self._s[1948]! } - public var Wallet_Completed_ViewWallet: String { return self._s[1949]! } + public var Wallet_Info_Receive: String { return self._s[1964]! } + public var Wallet_Completed_ViewWallet: String { return self._s[1965]! } public func Time_MonthOfYear_m6(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1950]!, self._r[1950]!, [_0]) + return formatWithArgumentRanges(self._s[1966]!, self._r[1966]!, [_0]) } - public var ReportPeer_ReasonSpam: String { return self._s[1951]! } - public var UserInfo_TapToCall: String { return self._s[1952]! } - public var Conversation_ForwardAuthorHiddenTooltip: String { return self._s[1954]! } - public var AutoDownloadSettings_DataUsageCustom: String { return self._s[1955]! } - public var Common_Search: String { return self._s[1956]! } - public var ScheduledMessages_EmptyPlaceholder: String { return self._s[1957]! } + public var ReportPeer_ReasonSpam: String { return self._s[1967]! } + public var UserInfo_TapToCall: String { return self._s[1968]! } + public var Conversation_ForwardAuthorHiddenTooltip: String { return self._s[1970]! } + public var AutoDownloadSettings_DataUsageCustom: String { return self._s[1971]! } + public var Common_Search: String { return self._s[1972]! } + public var ScheduledMessages_EmptyPlaceholder: String { return self._s[1973]! } public func Channel_AdminLog_MessageChangedGroupGeoLocation(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1958]!, self._r[1958]!, [_0]) + return formatWithArgumentRanges(self._s[1974]!, self._r[1974]!, [_0]) } - public var Wallet_Month_ShortJuly: String { return self._s[1959]! } - public var AuthSessions_IncompleteAttemptsInfo: String { return self._s[1961]! } - public var Message_InvoiceLabel: String { return self._s[1962]! } - public var Conversation_InputTextPlaceholder: String { return self._s[1963]! } - public var NetworkUsageSettings_MediaImageDataSection: String { return self._s[1964]! } + public var Wallet_Month_ShortJuly: String { return self._s[1975]! } + public var AuthSessions_IncompleteAttemptsInfo: String { return self._s[1977]! } + public var Message_InvoiceLabel: String { return self._s[1978]! } + public var Conversation_InputTextPlaceholder: String { return self._s[1979]! } + public var NetworkUsageSettings_MediaImageDataSection: String { return self._s[1980]! } public func Passport_Address_UploadOneOfScan(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1965]!, self._r[1965]!, [_0]) + return formatWithArgumentRanges(self._s[1981]!, self._r[1981]!, [_0]) } - public var IntentsSettings_Reset: String { return self._s[1966]! } - public var Conversation_Info: String { return self._s[1967]! } - public var Login_InfoDeletePhoto: String { return self._s[1968]! } - public var Passport_Language_vi: String { return self._s[1970]! } - public var UserInfo_ScamUserWarning: String { return self._s[1971]! } - public var Conversation_Search: String { return self._s[1972]! } - public var DialogList_DeleteBotConversationConfirmation: String { return self._s[1974]! } - public var ReportPeer_ReasonPornography: String { return self._s[1975]! } - public var AutoDownloadSettings_PhotosTitle: String { return self._s[1976]! } - public var Conversation_SendMessageErrorGroupRestricted: String { return self._s[1977]! } - public var Map_LiveLocationGroupDescription: String { return self._s[1978]! } - public var Channel_Setup_TypeHeader: String { return self._s[1979]! } - public var AuthSessions_LoggedIn: String { return self._s[1980]! } - public var Privacy_Forwards_AlwaysAllow_Title: String { return self._s[1981]! } - public var Login_SmsRequestState3: String { return self._s[1982]! } - public var Passport_Address_EditUtilityBill: String { return self._s[1983]! } - public var Appearance_ReduceMotionInfo: String { return self._s[1984]! } - public var Join_ChannelsTooMuch: String { return self._s[1985]! } - public var Channel_Edit_LinkItem: String { return self._s[1986]! } - public var Privacy_Calls_P2PNever: String { return self._s[1987]! } - public var Conversation_AddToReadingList: String { return self._s[1989]! } - public var Share_MultipleMessagesDisabled: String { return self._s[1990]! } - public var Message_Animation: String { return self._s[1991]! } - public var Conversation_DefaultRestrictedMedia: String { return self._s[1992]! } - public var Map_Unknown: String { return self._s[1993]! } - public var AutoDownloadSettings_LastDelimeter: String { return self._s[1994]! } + public var IntentsSettings_Reset: String { return self._s[1982]! } + public var Conversation_Info: String { return self._s[1983]! } + public var Login_InfoDeletePhoto: String { return self._s[1984]! } + public var Passport_Language_vi: String { return self._s[1986]! } + public var UserInfo_ScamUserWarning: String { return self._s[1987]! } + public var Conversation_Search: String { return self._s[1988]! } + public var DialogList_DeleteBotConversationConfirmation: String { return self._s[1990]! } + public var ReportPeer_ReasonPornography: String { return self._s[1991]! } + public var AutoDownloadSettings_PhotosTitle: String { return self._s[1992]! } + public var Conversation_SendMessageErrorGroupRestricted: String { return self._s[1993]! } + public var Map_LiveLocationGroupDescription: String { return self._s[1994]! } + public var Channel_Setup_TypeHeader: String { return self._s[1995]! } + public var AuthSessions_LoggedIn: String { return self._s[1996]! } + public var Privacy_Forwards_AlwaysAllow_Title: String { return self._s[1997]! } + public var Login_SmsRequestState3: String { return self._s[1998]! } + public var Passport_Address_EditUtilityBill: String { return self._s[1999]! } + public var Appearance_ReduceMotionInfo: String { return self._s[2000]! } + public var Join_ChannelsTooMuch: String { return self._s[2001]! } + public var Channel_Edit_LinkItem: String { return self._s[2002]! } + public var Privacy_Calls_P2PNever: String { return self._s[2003]! } + public var Conversation_AddToReadingList: String { return self._s[2005]! } + public var Share_MultipleMessagesDisabled: String { return self._s[2006]! } + public var Message_Animation: String { return self._s[2007]! } + public var Conversation_DefaultRestrictedMedia: String { return self._s[2008]! } + public var Map_Unknown: String { return self._s[2009]! } + public var AutoDownloadSettings_LastDelimeter: String { return self._s[2010]! } public func PUSH_PINNED_TEXT(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1995]!, self._r[1995]!, [_1, _2]) + return formatWithArgumentRanges(self._s[2011]!, self._r[2011]!, [_1, _2]) } public func Passport_FieldOneOf_Or(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1996]!, self._r[1996]!, [_1, _2]) + return formatWithArgumentRanges(self._s[2012]!, self._r[2012]!, [_1, _2]) } - public var Call_StatusRequesting: String { return self._s[1997]! } - public var Conversation_SecretChatContextBotAlert: String { return self._s[1998]! } - public var SocksProxySetup_ProxyStatusChecking: String { return self._s[1999]! } + public var Call_StatusRequesting: String { return self._s[2013]! } + public var Conversation_SecretChatContextBotAlert: String { return self._s[2014]! } + public var SocksProxySetup_ProxyStatusChecking: String { return self._s[2015]! } public func PUSH_CHAT_MESSAGE_DOC(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2000]!, self._r[2000]!, [_1, _2]) + return formatWithArgumentRanges(self._s[2016]!, self._r[2016]!, [_1, _2]) } public func Notification_PinnedLocationMessage(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2001]!, self._r[2001]!, [_0]) + return formatWithArgumentRanges(self._s[2017]!, self._r[2017]!, [_0]) } - public var Update_Skip: String { return self._s[2002]! } - public var Group_Username_RemoveExistingUsernamesInfo: String { return self._s[2003]! } - public var Message_PinnedPollMessage: String { return self._s[2004]! } - public var BlockedUsers_Title: String { return self._s[2005]! } + public var Update_Skip: String { return self._s[2018]! } + public var Group_Username_RemoveExistingUsernamesInfo: String { return self._s[2019]! } + public var BlockedUsers_Title: String { return self._s[2020]! } + public var Weekday_Monday: String { return self._s[2021]! } public func PUSH_CHANNEL_MESSAGE_AUDIO(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2006]!, self._r[2006]!, [_1]) + return formatWithArgumentRanges(self._s[2022]!, self._r[2022]!, [_1]) } - public var Username_CheckingUsername: String { return self._s[2007]! } - public var NotificationsSound_Bell: String { return self._s[2008]! } - public var Conversation_SendMessageErrorFlood: String { return self._s[2009]! } - public var Weekday_Monday: String { return self._s[2010]! } - public var SettingsSearch_Synonyms_Notifications_DisplayNamesOnLockScreen: String { return self._s[2011]! } - public var ChannelMembers_ChannelAdminsTitle: String { return self._s[2012]! } - public var ChatSettings_Groups: String { return self._s[2013]! } - public var WallpaperPreview_PatternPaternDiscard: String { return self._s[2014]! } + public var Username_CheckingUsername: String { return self._s[2023]! } + public var NotificationsSound_Bell: String { return self._s[2024]! } + public var Conversation_SendMessageErrorFlood: String { return self._s[2025]! } + public var SettingsSearch_Synonyms_Notifications_DisplayNamesOnLockScreen: String { return self._s[2026]! } + public var ChannelMembers_ChannelAdminsTitle: String { return self._s[2027]! } + public var ChatSettings_Groups: String { return self._s[2028]! } + public var WallpaperPreview_PatternPaternDiscard: String { return self._s[2029]! } public func Conversation_SetReminder_RemindTomorrow(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2015]!, self._r[2015]!, [_0]) + return formatWithArgumentRanges(self._s[2030]!, self._r[2030]!, [_0]) } - public var Your_card_was_declined: String { return self._s[2016]! } - public var TwoStepAuth_EnterPasswordHelp: String { return self._s[2018]! } - public var Wallet_Month_ShortApril: String { return self._s[2019]! } - public var ChatList_Unmute: String { return self._s[2020]! } - public var AuthSessions_AddDevice_ScanTitle: String { return self._s[2021]! } - public var PhotoEditor_CurvesAll: String { return self._s[2022]! } - public var Weekday_ShortTuesday: String { return self._s[2023]! } - public var DialogList_Read: String { return self._s[2024]! } - public var Appearance_AppIconClassic: String { return self._s[2025]! } - public var ChannelMembers_WhoCanAddMembers_AllMembers: String { return self._s[2026]! } - public var Passport_Identity_Gender: String { return self._s[2027]! } + public var Your_card_was_declined: String { return self._s[2031]! } + public var TwoStepAuth_EnterPasswordHelp: String { return self._s[2033]! } + public var Wallet_Month_ShortApril: String { return self._s[2034]! } + public var ChatList_Unmute: String { return self._s[2035]! } + public var AuthSessions_AddDevice_ScanTitle: String { return self._s[2036]! } + public var PhotoEditor_CurvesAll: String { return self._s[2037]! } + public var Weekday_ShortTuesday: String { return self._s[2038]! } + public var DialogList_Read: String { return self._s[2039]! } + public var Appearance_AppIconClassic: String { return self._s[2040]! } + public var ChannelMembers_WhoCanAddMembers_AllMembers: String { return self._s[2041]! } + public var Passport_Identity_Gender: String { return self._s[2042]! } public func Target_ShareGameConfirmationPrivate(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2028]!, self._r[2028]!, [_0]) + return formatWithArgumentRanges(self._s[2043]!, self._r[2043]!, [_0]) } - public var Target_SelectGroup: String { return self._s[2029]! } - public var Map_HomeAndWorkInfo: String { return self._s[2031]! } + public var Target_SelectGroup: String { return self._s[2044]! } + public var Map_HomeAndWorkInfo: String { return self._s[2046]! } public func DialogList_EncryptedChatStartedIncoming(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2032]!, self._r[2032]!, [_0]) + return formatWithArgumentRanges(self._s[2047]!, self._r[2047]!, [_0]) } - public var Passport_Language_en: String { return self._s[2033]! } - public var AutoDownloadSettings_AutodownloadPhotos: String { return self._s[2034]! } - public var Channel_Username_CreatePublicLinkHelp: String { return self._s[2035]! } - public var Login_CancelPhoneVerificationContinue: String { return self._s[2036]! } - public var ScheduledMessages_SendNow: String { return self._s[2037]! } - public var Checkout_NewCard_PaymentCard: String { return self._s[2039]! } - public var Login_InfoHelp: String { return self._s[2040]! } - public var Contacts_PermissionsSuppressWarningTitle: String { return self._s[2041]! } - public var SettingsSearch_Synonyms_Stickers_FeaturedPacks: String { return self._s[2042]! } + public var Passport_Language_en: String { return self._s[2048]! } + public var AutoDownloadSettings_AutodownloadPhotos: String { return self._s[2049]! } + public var Channel_Username_CreatePublicLinkHelp: String { return self._s[2050]! } + public var Login_CancelPhoneVerificationContinue: String { return self._s[2051]! } + public var ScheduledMessages_SendNow: String { return self._s[2052]! } + public var Checkout_NewCard_PaymentCard: String { return self._s[2054]! } + public var Login_InfoHelp: String { return self._s[2055]! } + public var Appearance_BubbleCorners_AdjustAdjacent: String { return self._s[2056]! } + public var Contacts_PermissionsSuppressWarningTitle: String { return self._s[2057]! } + public var SettingsSearch_Synonyms_Stickers_FeaturedPacks: String { return self._s[2058]! } public func Channel_AdminLog_MessageChangedLinkedChannel(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2043]!, self._r[2043]!, [_1, _2]) + return formatWithArgumentRanges(self._s[2059]!, self._r[2059]!, [_1, _2]) } - public var SocksProxySetup_AddProxy: String { return self._s[2046]! } - public var CreatePoll_Title: String { return self._s[2047]! } - public var Conversation_ViewTheme: String { return self._s[2048]! } - public var SettingsSearch_Synonyms_Privacy_Data_SecretChatLinkPreview: String { return self._s[2049]! } - public var PasscodeSettings_SimplePasscodeHelp: String { return self._s[2050]! } - public var TwoFactorSetup_Intro_Text: String { return self._s[2051]! } - public var UserInfo_GroupsInCommon: String { return self._s[2052]! } - public var TelegramWallet_Intro_TermsUrl: String { return self._s[2053]! } - public var Call_AudioRouteHide: String { return self._s[2054]! } + public var SocksProxySetup_AddProxy: String { return self._s[2062]! } + public var CreatePoll_Title: String { return self._s[2063]! } + public var MessagePoll_QuizNoUsers: String { return self._s[2064]! } + public var Conversation_ViewTheme: String { return self._s[2065]! } + public var SettingsSearch_Synonyms_Privacy_Data_SecretChatLinkPreview: String { return self._s[2066]! } + public var PasscodeSettings_SimplePasscodeHelp: String { return self._s[2067]! } + public var TwoFactorSetup_Intro_Text: String { return self._s[2068]! } + public var UserInfo_GroupsInCommon: String { return self._s[2069]! } + public var TelegramWallet_Intro_TermsUrl: String { return self._s[2070]! } + public var Call_AudioRouteHide: String { return self._s[2071]! } public func Wallet_Info_TransactionDateHeader(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2056]!, self._r[2056]!, [_1, _2]) + return formatWithArgumentRanges(self._s[2073]!, self._r[2073]!, [_1, _2]) } - public var ContactInfo_PhoneLabelMobile: String { return self._s[2057]! } - public var IntentsSettings_SuggestedChatsInfo: String { return self._s[2058]! } + public var ContactInfo_PhoneLabelMobile: String { return self._s[2074]! } + public var IntentsSettings_SuggestedChatsInfo: String { return self._s[2075]! } + public var CreatePoll_QuizOptionsHeader: String { return self._s[2076]! } public func ChatList_LeaveGroupConfirmation(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2059]!, self._r[2059]!, [_0]) + return formatWithArgumentRanges(self._s[2077]!, self._r[2077]!, [_0]) } - public var TextFormat_Bold: String { return self._s[2060]! } - public var FastTwoStepSetup_EmailSection: String { return self._s[2061]! } - public var StickerPackActionInfo_AddedTitle: String { return self._s[2062]! } - public var Notifications_Title: String { return self._s[2063]! } - public var Group_Username_InvalidTooShort: String { return self._s[2064]! } - public var Channel_ErrorAddTooMuch: String { return self._s[2065]! } + public var TextFormat_Bold: String { return self._s[2078]! } + public var FastTwoStepSetup_EmailSection: String { return self._s[2079]! } + public var StickerPackActionInfo_AddedTitle: String { return self._s[2080]! } + public var Notifications_Title: String { return self._s[2081]! } + public var Group_Username_InvalidTooShort: String { return self._s[2082]! } + public var Channel_ErrorAddTooMuch: String { return self._s[2083]! } public func DialogList_MultipleTypingSuffix(_ _0: Int) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2066]!, self._r[2066]!, ["\(_0)"]) + return formatWithArgumentRanges(self._s[2084]!, self._r[2084]!, ["\(_0)"]) } - public var VoiceOver_DiscardPreparedContent: String { return self._s[2068]! } - public var Stickers_SuggestAdded: String { return self._s[2069]! } - public var Login_CountryCode: String { return self._s[2070]! } - public var ChatSettings_AutoPlayVideos: String { return self._s[2071]! } - public var Map_GetDirections: String { return self._s[2072]! } - public var Wallet_Receive_ShareInvoiceUrl: String { return self._s[2073]! } - public var Login_PhoneFloodError: String { return self._s[2074]! } + public var VoiceOver_DiscardPreparedContent: String { return self._s[2086]! } + public var Stickers_SuggestAdded: String { return self._s[2087]! } + public var Login_CountryCode: String { return self._s[2088]! } + public var ChatSettings_AutoPlayVideos: String { return self._s[2089]! } + public var Map_GetDirections: String { return self._s[2090]! } + public var Wallet_Receive_ShareInvoiceUrl: String { return self._s[2091]! } + public var Login_PhoneFloodError: String { return self._s[2092]! } public func Time_MonthOfYear_m3(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2075]!, self._r[2075]!, [_0]) + return formatWithArgumentRanges(self._s[2093]!, self._r[2093]!, [_0]) } public func Wallet_Time_PreciseDate_m10(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2076]!, self._r[2076]!, [_1, _2, _3]) + return formatWithArgumentRanges(self._s[2094]!, self._r[2094]!, [_1, _2, _3]) } - public var IntentsSettings_SuggestedChatsPrivateChats: String { return self._s[2077]! } - public var Settings_SetUsername: String { return self._s[2079]! } - public var Group_Location_ChangeLocation: String { return self._s[2080]! } - public var Notification_GroupInviterSelf: String { return self._s[2081]! } - public var InstantPage_TapToOpenLink: String { return self._s[2082]! } + public var IntentsSettings_SuggestedChatsPrivateChats: String { return self._s[2095]! } + public var Settings_SetUsername: String { return self._s[2097]! } + public var Group_Location_ChangeLocation: String { return self._s[2098]! } + public var Notification_GroupInviterSelf: String { return self._s[2099]! } + public var InstantPage_TapToOpenLink: String { return self._s[2100]! } public func Notification_ChannelInviter(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2083]!, self._r[2083]!, [_0]) - } - public var Watch_Suggestion_TalkLater: String { return self._s[2084]! } - public var SecretChat_Title: String { return self._s[2085]! } - public var Group_UpgradeNoticeText1: String { return self._s[2086]! } - public var AuthSessions_Title: String { return self._s[2087]! } - public func TextFormat_AddLinkText(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2088]!, self._r[2088]!, [_0]) - } - public var PhotoEditor_CropAuto: String { return self._s[2089]! } - public var Channel_About_Title: String { return self._s[2090]! } - public var Theme_ThemeChanged: String { return self._s[2091]! } - public var FastTwoStepSetup_EmailHelp: String { return self._s[2092]! } - public func Conversation_Bytes(_ _0: Int) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2094]!, self._r[2094]!, ["\(_0)"]) - } - public var VoiceOver_MessageContextReport: String { return self._s[2095]! } - public var Conversation_PinMessageAlert_OnlyPin: String { return self._s[2097]! } - public var Group_Setup_HistoryVisibleHelp: String { return self._s[2098]! } - public func PUSH_MESSAGE_GIF(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2099]!, self._r[2099]!, [_1]) - } - public func SharedMedia_SearchNoResultsDescription(_ _0: String) -> (String, [(Int, NSRange)]) { return formatWithArgumentRanges(self._s[2101]!, self._r[2101]!, [_0]) } + public var Watch_Suggestion_TalkLater: String { return self._s[2102]! } + public var SecretChat_Title: String { return self._s[2103]! } + public var Group_UpgradeNoticeText1: String { return self._s[2104]! } + public var AuthSessions_Title: String { return self._s[2105]! } + public func TextFormat_AddLinkText(_ _0: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[2106]!, self._r[2106]!, [_0]) + } + public var PhotoEditor_CropAuto: String { return self._s[2107]! } + public var Channel_About_Title: String { return self._s[2108]! } + public var Theme_ThemeChanged: String { return self._s[2109]! } + public var FastTwoStepSetup_EmailHelp: String { return self._s[2110]! } + public func Conversation_Bytes(_ _0: Int) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[2112]!, self._r[2112]!, ["\(_0)"]) + } + public var VoiceOver_MessageContextReport: String { return self._s[2113]! } + public var Conversation_PinMessageAlert_OnlyPin: String { return self._s[2115]! } + public var Group_Setup_HistoryVisibleHelp: String { return self._s[2116]! } + public func PUSH_MESSAGE_GIF(_ _1: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[2117]!, self._r[2117]!, [_1]) + } + public func SharedMedia_SearchNoResultsDescription(_ _0: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[2119]!, self._r[2119]!, [_0]) + } public func TwoStepAuth_RecoveryEmailUnavailable(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2102]!, self._r[2102]!, [_0]) + return formatWithArgumentRanges(self._s[2120]!, self._r[2120]!, [_0]) } - public var Privacy_PaymentsClearInfoHelp: String { return self._s[2103]! } - public var Presence_online: String { return self._s[2106]! } - public var PasscodeSettings_Title: String { return self._s[2107]! } - public var Passport_Identity_ExpiryDatePlaceholder: String { return self._s[2108]! } - public var Web_OpenExternal: String { return self._s[2109]! } - public var AutoDownloadSettings_AutoDownload: String { return self._s[2111]! } - public var Channel_OwnershipTransfer_EnterPasswordText: String { return self._s[2112]! } - public var LocalGroup_Title: String { return self._s[2113]! } + public var Privacy_PaymentsClearInfoHelp: String { return self._s[2121]! } + public var Presence_online: String { return self._s[2124]! } + public var PasscodeSettings_Title: String { return self._s[2125]! } + public var Passport_Identity_ExpiryDatePlaceholder: String { return self._s[2126]! } + public var Web_OpenExternal: String { return self._s[2127]! } + public var AutoDownloadSettings_AutoDownload: String { return self._s[2129]! } + public var Channel_OwnershipTransfer_EnterPasswordText: String { return self._s[2130]! } + public var LocalGroup_Title: String { return self._s[2131]! } public func AutoNightTheme_AutomaticHelp(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2114]!, self._r[2114]!, [_0]) + return formatWithArgumentRanges(self._s[2132]!, self._r[2132]!, [_0]) } - public var FastTwoStepSetup_PasswordConfirmationPlaceholder: String { return self._s[2115]! } - public var Map_YouAreHere: String { return self._s[2116]! } + public var FastTwoStepSetup_PasswordConfirmationPlaceholder: String { return self._s[2133]! } + public var Conversation_StopQuizConfirmation: String { return self._s[2134]! } + public var Map_YouAreHere: String { return self._s[2135]! } public func AuthSessions_Message(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2117]!, self._r[2117]!, [_0]) + return formatWithArgumentRanges(self._s[2136]!, self._r[2136]!, [_0]) } public func ChatList_DeleteChatConfirmation(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2118]!, self._r[2118]!, [_0]) + return formatWithArgumentRanges(self._s[2137]!, self._r[2137]!, [_0]) } - public var Theme_Context_ChangeColors: String { return self._s[2119]! } - public var PrivacyLastSeenSettings_AlwaysShareWith: String { return self._s[2120]! } - public var Target_InviteToGroupErrorAlreadyInvited: String { return self._s[2121]! } + public var Theme_Context_ChangeColors: String { return self._s[2138]! } + public var PrivacyLastSeenSettings_AlwaysShareWith: String { return self._s[2139]! } + public var Target_InviteToGroupErrorAlreadyInvited: String { return self._s[2140]! } public func AuthSessions_AppUnofficial(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2122]!, self._r[2122]!, [_0]) + return formatWithArgumentRanges(self._s[2141]!, self._r[2141]!, [_0]) } public func DialogList_LiveLocationSharingTo(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2123]!, self._r[2123]!, [_0]) + return formatWithArgumentRanges(self._s[2142]!, self._r[2142]!, [_0]) } - public var SocksProxySetup_Username: String { return self._s[2124]! } - public var Bot_Start: String { return self._s[2125]! } + public var SocksProxySetup_Username: String { return self._s[2143]! } + public var Bot_Start: String { return self._s[2144]! } public func Channel_AdminLog_EmptyFilterQueryText(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2126]!, self._r[2126]!, [_0]) + return formatWithArgumentRanges(self._s[2145]!, self._r[2145]!, [_0]) } public func Channel_AdminLog_MessagePinned(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2127]!, self._r[2127]!, [_0]) + return formatWithArgumentRanges(self._s[2146]!, self._r[2146]!, [_0]) } - public var Contacts_SortByPresence: String { return self._s[2128]! } - public var AccentColor_Title: String { return self._s[2130]! } - public var Conversation_DiscardVoiceMessageTitle: String { return self._s[2131]! } + public var Contacts_SortByPresence: String { return self._s[2147]! } + public var AccentColor_Title: String { return self._s[2149]! } + public var Conversation_DiscardVoiceMessageTitle: String { return self._s[2150]! } public func PUSH_CHAT_CREATED(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2132]!, self._r[2132]!, [_1, _2]) + return formatWithArgumentRanges(self._s[2151]!, self._r[2151]!, [_1, _2]) } public func PrivacySettings_LastSeenContactsMinus(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2133]!, self._r[2133]!, [_0]) + return formatWithArgumentRanges(self._s[2152]!, self._r[2152]!, [_0]) } public func Channel_AdminLog_MessageChangedLinkedGroup(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2134]!, self._r[2134]!, [_1, _2]) + return formatWithArgumentRanges(self._s[2153]!, self._r[2153]!, [_1, _2]) } - public var Passport_Email_EnterOtherEmail: String { return self._s[2135]! } - public var Login_InfoAvatarPhoto: String { return self._s[2136]! } - public var Privacy_PaymentsClear_ShippingInfo: String { return self._s[2137]! } - public var Tour_Title4: String { return self._s[2138]! } - public var Passport_Identity_Translation: String { return self._s[2139]! } - public var SettingsSearch_Synonyms_Notifications_ContactJoined: String { return self._s[2140]! } - public var Login_TermsOfServiceLabel: String { return self._s[2142]! } - public var Passport_Language_it: String { return self._s[2143]! } - public var KeyCommand_JumpToNextUnreadChat: String { return self._s[2144]! } - public var Passport_Identity_SelfieHelp: String { return self._s[2145]! } - public var Conversation_ClearAll: String { return self._s[2147]! } - public var Wallet_Send_UninitializedText: String { return self._s[2149]! } - public var Channel_OwnershipTransfer_Title: String { return self._s[2150]! } - public var TwoStepAuth_FloodError: String { return self._s[2151]! } + public var Passport_Email_EnterOtherEmail: String { return self._s[2154]! } + public var Login_InfoAvatarPhoto: String { return self._s[2155]! } + public var Privacy_PaymentsClear_ShippingInfo: String { return self._s[2156]! } + public var Tour_Title4: String { return self._s[2157]! } + public var Passport_Identity_Translation: String { return self._s[2158]! } + public var SettingsSearch_Synonyms_Notifications_ContactJoined: String { return self._s[2159]! } + public var Login_TermsOfServiceLabel: String { return self._s[2161]! } + public var Passport_Language_it: String { return self._s[2162]! } + public var KeyCommand_JumpToNextUnreadChat: String { return self._s[2163]! } + public var Passport_Identity_SelfieHelp: String { return self._s[2164]! } + public var Conversation_ClearAll: String { return self._s[2166]! } + public var Wallet_Send_UninitializedText: String { return self._s[2168]! } + public var Channel_OwnershipTransfer_Title: String { return self._s[2169]! } + public var TwoStepAuth_FloodError: String { return self._s[2170]! } public func PUSH_CHANNEL_MESSAGE_GEO(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2152]!, self._r[2152]!, [_1]) - } - public var Paint_Delete: String { return self._s[2153]! } - public func Wallet_Sent_Text(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2154]!, self._r[2154]!, [_0]) - } - public var Privacy_AddNewPeer: String { return self._s[2155]! } - public func Channel_AdminLog_MessageRank(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2156]!, self._r[2156]!, [_1]) - } - public var LogoutOptions_SetPasscodeText: String { return self._s[2157]! } - public func Passport_AcceptHelp(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2158]!, self._r[2158]!, [_1, _2]) - } - public var Message_PinnedAudioMessage: String { return self._s[2159]! } - public func Watch_Time_ShortTodayAt(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2160]!, self._r[2160]!, [_0]) - } - public var Notification_Mute1hMin: String { return self._s[2161]! } - public var Notifications_GroupNotificationsSound: String { return self._s[2162]! } - public var Wallet_Month_GenNovember: String { return self._s[2163]! } - public var SocksProxySetup_ShareProxyList: String { return self._s[2164]! } - public var Conversation_MessageEditedLabel: String { return self._s[2165]! } - public func ClearCache_Success(_ _0: String, _ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2166]!, self._r[2166]!, [_0, _1]) - } - public var Notification_Exceptions_AlwaysOff: String { return self._s[2167]! } - public var Notification_Exceptions_NewException_MessagePreviewHeader: String { return self._s[2168]! } - public func Channel_AdminLog_MessageAdmin(_ _0: String, _ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2169]!, self._r[2169]!, [_0, _1, _2]) - } - public var NetworkUsageSettings_ResetStats: String { return self._s[2170]! } - public func PUSH_MESSAGE_GEOLIVE(_ _1: String) -> (String, [(Int, NSRange)]) { return formatWithArgumentRanges(self._s[2171]!, self._r[2171]!, [_1]) } - public var AccessDenied_LocationTracking: String { return self._s[2172]! } - public var Month_GenOctober: String { return self._s[2173]! } - public var GroupInfo_InviteLink_RevokeAlert_Revoke: String { return self._s[2174]! } - public var EnterPasscode_EnterPasscode: String { return self._s[2175]! } - public var MediaPicker_TimerTooltip: String { return self._s[2177]! } - public var SharedMedia_TitleAll: String { return self._s[2178]! } - public var SettingsSearch_Synonyms_Notifications_ChannelNotificationsExceptions: String { return self._s[2181]! } - public var Conversation_RestrictedMedia: String { return self._s[2182]! } - public var AccessDenied_PhotosRestricted: String { return self._s[2183]! } - public var Privacy_Forwards_WhoCanForward: String { return self._s[2185]! } - public var ChangePhoneNumberCode_Called: String { return self._s[2186]! } + public var Paint_Delete: String { return self._s[2172]! } + public func Wallet_Sent_Text(_ _0: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[2173]!, self._r[2173]!, [_0]) + } + public var Privacy_AddNewPeer: String { return self._s[2174]! } + public func Channel_AdminLog_MessageRank(_ _1: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[2175]!, self._r[2175]!, [_1]) + } + public var LogoutOptions_SetPasscodeText: String { return self._s[2176]! } + public func Passport_AcceptHelp(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[2177]!, self._r[2177]!, [_1, _2]) + } + public var Message_PinnedAudioMessage: String { return self._s[2178]! } + public func Watch_Time_ShortTodayAt(_ _0: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[2179]!, self._r[2179]!, [_0]) + } + public var Notification_Mute1hMin: String { return self._s[2180]! } + public var Notifications_GroupNotificationsSound: String { return self._s[2181]! } + public var Wallet_Month_GenNovember: String { return self._s[2182]! } + public var SocksProxySetup_ShareProxyList: String { return self._s[2183]! } + public var Conversation_MessageEditedLabel: String { return self._s[2184]! } + public func ClearCache_Success(_ _0: String, _ _1: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[2185]!, self._r[2185]!, [_0, _1]) + } + public var Notification_Exceptions_AlwaysOff: String { return self._s[2186]! } + public var Notification_Exceptions_NewException_MessagePreviewHeader: String { return self._s[2187]! } + public func Channel_AdminLog_MessageAdmin(_ _0: String, _ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[2188]!, self._r[2188]!, [_0, _1, _2]) + } + public var NetworkUsageSettings_ResetStats: String { return self._s[2189]! } + public func PUSH_MESSAGE_GEOLIVE(_ _1: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[2190]!, self._r[2190]!, [_1]) + } + public var AccessDenied_LocationTracking: String { return self._s[2191]! } + public var Month_GenOctober: String { return self._s[2192]! } + public var GroupInfo_InviteLink_RevokeAlert_Revoke: String { return self._s[2193]! } + public var EnterPasscode_EnterPasscode: String { return self._s[2194]! } + public var MediaPicker_TimerTooltip: String { return self._s[2196]! } + public var SharedMedia_TitleAll: String { return self._s[2197]! } + public var SettingsSearch_Synonyms_Notifications_ChannelNotificationsExceptions: String { return self._s[2200]! } + public var Conversation_RestrictedMedia: String { return self._s[2201]! } + public var AccessDenied_PhotosRestricted: String { return self._s[2202]! } + public var Privacy_Forwards_WhoCanForward: String { return self._s[2204]! } + public var ChangePhoneNumberCode_Called: String { return self._s[2205]! } public func Notification_PinnedDocumentMessage(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2187]!, self._r[2187]!, [_0]) + return formatWithArgumentRanges(self._s[2206]!, self._r[2206]!, [_0]) } - public var Conversation_SavedMessages: String { return self._s[2190]! } - public var Your_cards_expiration_month_is_invalid: String { return self._s[2192]! } - public var FastTwoStepSetup_PasswordPlaceholder: String { return self._s[2193]! } + public var Conversation_SavedMessages: String { return self._s[2209]! } + public var Your_cards_expiration_month_is_invalid: String { return self._s[2211]! } + public var FastTwoStepSetup_PasswordPlaceholder: String { return self._s[2212]! } public func Target_ShareGameConfirmationGroup(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2195]!, self._r[2195]!, [_0]) + return formatWithArgumentRanges(self._s[2214]!, self._r[2214]!, [_0]) } - public var VoiceOver_Chat_YourMessage: String { return self._s[2196]! } + public var VoiceOver_Chat_YourMessage: String { return self._s[2215]! } public func VoiceOver_Chat_Title(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2197]!, self._r[2197]!, [_0]) + return formatWithArgumentRanges(self._s[2216]!, self._r[2216]!, [_0]) } - public var ReportPeer_AlertSuccess: String { return self._s[2198]! } - public var PhotoEditor_CropAspectRatioOriginal: String { return self._s[2199]! } + public var ReportPeer_AlertSuccess: String { return self._s[2217]! } + public var PhotoEditor_CropAspectRatioOriginal: String { return self._s[2218]! } public func InstantPage_RelatedArticleAuthorAndDateTitle(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2200]!, self._r[2200]!, [_1, _2]) + return formatWithArgumentRanges(self._s[2219]!, self._r[2219]!, [_1, _2]) } - public var Checkout_PasswordEntry_Title: String { return self._s[2201]! } - public var PhotoEditor_FadeTool: String { return self._s[2202]! } - public var Privacy_ContactsReset: String { return self._s[2203]! } + public var Checkout_PasswordEntry_Title: String { return self._s[2220]! } + public var PhotoEditor_FadeTool: String { return self._s[2221]! } + public var Privacy_ContactsReset: String { return self._s[2222]! } public func Channel_AdminLog_MessageRestrictedUntil(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2205]!, self._r[2205]!, [_0]) + return formatWithArgumentRanges(self._s[2224]!, self._r[2224]!, [_0]) } - public var Message_PinnedVideoMessage: String { return self._s[2206]! } - public var ChatList_Mute: String { return self._s[2207]! } + public var Message_PinnedVideoMessage: String { return self._s[2225]! } + public var ChatList_Mute: String { return self._s[2226]! } public func Wallet_Time_PreciseDate_m5(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2208]!, self._r[2208]!, [_1, _2, _3]) + return formatWithArgumentRanges(self._s[2227]!, self._r[2227]!, [_1, _2, _3]) } - public var Permissions_CellularDataText_v0: String { return self._s[2209]! } - public var ShareMenu_SelectChats: String { return self._s[2212]! } - public var ChatList_Context_Unarchive: String { return self._s[2213]! } - public var MusicPlayer_VoiceNote: String { return self._s[2214]! } - public var Conversation_RestrictedText: String { return self._s[2215]! } - public var SettingsSearch_Synonyms_Privacy_Data_DeleteDrafts: String { return self._s[2216]! } - public var Wallet_Month_GenApril: String { return self._s[2217]! } - public var Wallet_Month_ShortMarch: String { return self._s[2218]! } - public var TwoStepAuth_DisableSuccess: String { return self._s[2219]! } - public var Cache_Videos: String { return self._s[2220]! } - public var PrivacySettings_PhoneNumber: String { return self._s[2221]! } - public var Wallet_Month_GenFebruary: String { return self._s[2222]! } - public var FeatureDisabled_Oops: String { return self._s[2224]! } - public var Passport_Address_PostcodePlaceholder: String { return self._s[2225]! } + public var Permissions_CellularDataText_v0: String { return self._s[2228]! } + public var Conversation_PinnedQuiz: String { return self._s[2230]! } + public var ShareMenu_SelectChats: String { return self._s[2232]! } + public var ChatList_Context_Unarchive: String { return self._s[2233]! } + public var MusicPlayer_VoiceNote: String { return self._s[2234]! } + public var Conversation_RestrictedText: String { return self._s[2235]! } + public var SettingsSearch_Synonyms_Privacy_Data_DeleteDrafts: String { return self._s[2236]! } + public var Wallet_Month_GenApril: String { return self._s[2237]! } + public var Wallet_Month_ShortMarch: String { return self._s[2238]! } + public var TwoStepAuth_DisableSuccess: String { return self._s[2239]! } + public var Cache_Videos: String { return self._s[2240]! } + public var PrivacySettings_PhoneNumber: String { return self._s[2241]! } + public var Wallet_Month_GenFebruary: String { return self._s[2242]! } + public var FeatureDisabled_Oops: String { return self._s[2244]! } + public var Passport_Address_PostcodePlaceholder: String { return self._s[2245]! } public func AddContact_StatusSuccess(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2226]!, self._r[2226]!, [_0]) + return formatWithArgumentRanges(self._s[2246]!, self._r[2246]!, [_0]) } - public var Stickers_GroupStickersHelp: String { return self._s[2227]! } - public var GroupPermission_NoSendPolls: String { return self._s[2228]! } - public var Wallet_Qr_ScanCode: String { return self._s[2229]! } - public var Message_VideoExpired: String { return self._s[2231]! } - public var GroupInfo_GroupHistoryVisible: String { return self._s[2232]! } - public var Notifications_Badge: String { return self._s[2233]! } - public var Wallet_Receive_AddressCopied: String { return self._s[2234]! } - public var CreatePoll_OptionPlaceholder: String { return self._s[2235]! } - public var Username_InvalidTooShort: String { return self._s[2236]! } - public var EnterPasscode_EnterNewPasscodeChange: String { return self._s[2237]! } - public var Channel_AdminLog_PinMessages: String { return self._s[2238]! } - public var ArchivedChats_IntroTitle3: String { return self._s[2239]! } + public var Stickers_GroupStickersHelp: String { return self._s[2247]! } + public var GroupPermission_NoSendPolls: String { return self._s[2248]! } + public var Wallet_Qr_ScanCode: String { return self._s[2249]! } + public var Message_VideoExpired: String { return self._s[2251]! } + public var GroupInfo_GroupHistoryVisible: String { return self._s[2252]! } + public var Notifications_Badge: String { return self._s[2253]! } + public var Wallet_Receive_AddressCopied: String { return self._s[2254]! } + public var CreatePoll_OptionPlaceholder: String { return self._s[2255]! } + public var Username_InvalidTooShort: String { return self._s[2256]! } + public var EnterPasscode_EnterNewPasscodeChange: String { return self._s[2257]! } + public var Channel_AdminLog_PinMessages: String { return self._s[2258]! } + public var ArchivedChats_IntroTitle3: String { return self._s[2259]! } public func Notification_MessageLifetimeRemoved(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2240]!, self._r[2240]!, [_1]) + return formatWithArgumentRanges(self._s[2260]!, self._r[2260]!, [_1]) } - public var Permissions_SiriAllowInSettings_v0: String { return self._s[2241]! } - public var Conversation_DefaultRestrictedText: String { return self._s[2242]! } - public var SharedMedia_CategoryDocs: String { return self._s[2245]! } + public var Permissions_SiriAllowInSettings_v0: String { return self._s[2261]! } + public var Conversation_DefaultRestrictedText: String { return self._s[2262]! } + public var SharedMedia_CategoryDocs: String { return self._s[2265]! } public func PUSH_MESSAGE_CONTACT(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2246]!, self._r[2246]!, [_1]) + return formatWithArgumentRanges(self._s[2266]!, self._r[2266]!, [_1]) } - public var Wallet_Send_UninitializedTitle: String { return self._s[2247]! } - public var StickerPackActionInfo_ArchivedTitle: String { return self._s[2248]! } - public var Privacy_Forwards_NeverLink: String { return self._s[2250]! } + public var Wallet_Send_UninitializedTitle: String { return self._s[2267]! } + public var StickerPackActionInfo_ArchivedTitle: String { return self._s[2268]! } + public var Privacy_Forwards_NeverLink: String { return self._s[2270]! } public func Notification_MessageLifetimeChangedOutgoing(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2251]!, self._r[2251]!, [_1]) + return formatWithArgumentRanges(self._s[2271]!, self._r[2271]!, [_1]) } - public var CheckoutInfo_ErrorShippingNotAvailable: String { return self._s[2252]! } + public var CheckoutInfo_ErrorShippingNotAvailable: String { return self._s[2272]! } public func Time_MonthOfYear_m12(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2253]!, self._r[2253]!, [_0]) + return formatWithArgumentRanges(self._s[2273]!, self._r[2273]!, [_0]) } - public var ChatSettings_PrivateChats: String { return self._s[2254]! } - public var SettingsSearch_Synonyms_EditProfile_Logout: String { return self._s[2255]! } - public var Conversation_PrivateMessageLinkCopied: String { return self._s[2256]! } - public var Channel_UpdatePhotoItem: String { return self._s[2257]! } - public var GroupInfo_LeftStatus: String { return self._s[2258]! } - public var Watch_MessageView_Forward: String { return self._s[2260]! } - public var ReportPeer_ReasonChildAbuse: String { return self._s[2261]! } - public var Cache_ClearEmpty: String { return self._s[2263]! } - public var Localization_LanguageName: String { return self._s[2264]! } - public var Wallet_AccessDenied_Title: String { return self._s[2265]! } - public var WebSearch_GIFs: String { return self._s[2266]! } - public var Notifications_DisplayNamesOnLockScreenInfoWithLink: String { return self._s[2267]! } - public var Wallet_AccessDenied_Settings: String { return self._s[2268]! } - public var Username_InvalidStartsWithNumber: String { return self._s[2269]! } - public var Common_Back: String { return self._s[2270]! } - public var GroupInfo_Permissions_EditingDisabled: String { return self._s[2271]! } - public var Passport_Identity_DateOfBirthPlaceholder: String { return self._s[2272]! } - public var Wallet_Send_Send: String { return self._s[2273]! } + public var ChatSettings_PrivateChats: String { return self._s[2274]! } + public var SettingsSearch_Synonyms_EditProfile_Logout: String { return self._s[2275]! } + public var Conversation_PrivateMessageLinkCopied: String { return self._s[2276]! } + public var Channel_UpdatePhotoItem: String { return self._s[2277]! } + public var GroupInfo_LeftStatus: String { return self._s[2278]! } + public var Watch_MessageView_Forward: String { return self._s[2280]! } + public var ReportPeer_ReasonChildAbuse: String { return self._s[2281]! } + public var Cache_ClearEmpty: String { return self._s[2283]! } + public var Localization_LanguageName: String { return self._s[2284]! } + public var Wallet_AccessDenied_Title: String { return self._s[2285]! } + public var WebSearch_GIFs: String { return self._s[2286]! } + public var Notifications_DisplayNamesOnLockScreenInfoWithLink: String { return self._s[2287]! } + public var Wallet_AccessDenied_Settings: String { return self._s[2288]! } + public var Username_InvalidStartsWithNumber: String { return self._s[2289]! } + public var Common_Back: String { return self._s[2290]! } + public var GroupInfo_Permissions_EditingDisabled: String { return self._s[2291]! } + public var Passport_Identity_DateOfBirthPlaceholder: String { return self._s[2292]! } + public var Wallet_Send_Send: String { return self._s[2293]! } public func PUSH_CHANNEL_MESSAGE_STICKER(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2275]!, self._r[2275]!, [_1, _2]) + return formatWithArgumentRanges(self._s[2295]!, self._r[2295]!, [_1, _2]) } - public var Wallet_Info_RefreshErrorTitle: String { return self._s[2276]! } - public var Wallet_Month_GenJune: String { return self._s[2277]! } - public var Passport_Email_Help: String { return self._s[2278]! } - public var Watch_Conversation_Reply: String { return self._s[2280]! } - public var Conversation_EditingMessageMediaChange: String { return self._s[2283]! } - public var Passport_Identity_IssueDatePlaceholder: String { return self._s[2284]! } - public var Channel_BanUser_Unban: String { return self._s[2286]! } - public var Channel_EditAdmin_PermissionPostMessages: String { return self._s[2287]! } - public var Group_Username_CreatePublicLinkHelp: String { return self._s[2288]! } - public var TwoStepAuth_ConfirmEmailCodePlaceholder: String { return self._s[2290]! } - public var Wallet_Send_AddressHeader: String { return self._s[2291]! } - public var Passport_Identity_Name: String { return self._s[2292]! } + public var Wallet_Info_RefreshErrorTitle: String { return self._s[2296]! } + public var Wallet_Month_GenJune: String { return self._s[2297]! } + public var Passport_Email_Help: String { return self._s[2298]! } + public var Watch_Conversation_Reply: String { return self._s[2300]! } + public var Conversation_EditingMessageMediaChange: String { return self._s[2303]! } + public var Passport_Identity_IssueDatePlaceholder: String { return self._s[2304]! } + public var Channel_BanUser_Unban: String { return self._s[2306]! } + public var Channel_EditAdmin_PermissionPostMessages: String { return self._s[2307]! } + public var Group_Username_CreatePublicLinkHelp: String { return self._s[2308]! } + public var TwoStepAuth_ConfirmEmailCodePlaceholder: String { return self._s[2310]! } + public var Wallet_Send_AddressHeader: String { return self._s[2311]! } + public var Passport_Identity_Name: String { return self._s[2312]! } public func Channel_DiscussionGroup_HeaderGroupSet(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2293]!, self._r[2293]!, [_0]) + return formatWithArgumentRanges(self._s[2313]!, self._r[2313]!, [_0]) } - public var GroupRemoved_ViewUserInfo: String { return self._s[2294]! } - public var Conversation_BlockUser: String { return self._s[2295]! } - public var Month_GenJanuary: String { return self._s[2296]! } - public var ChatSettings_TextSize: String { return self._s[2297]! } - public var Notification_PassportValuePhone: String { return self._s[2298]! } - public var MediaPlayer_UnknownArtist: String { return self._s[2299]! } - public var Passport_Language_ne: String { return self._s[2300]! } - public var Notification_CallBack: String { return self._s[2301]! } - public var Wallet_SecureStorageReset_BiometryTouchId: String { return self._s[2302]! } - public var TwoStepAuth_EmailHelp: String { return self._s[2303]! } + public var GroupRemoved_ViewUserInfo: String { return self._s[2314]! } + public var Conversation_BlockUser: String { return self._s[2315]! } + public var Month_GenJanuary: String { return self._s[2316]! } + public var ChatSettings_TextSize: String { return self._s[2317]! } + public var Notification_PassportValuePhone: String { return self._s[2318]! } + public var MediaPlayer_UnknownArtist: String { return self._s[2319]! } + public var Passport_Language_ne: String { return self._s[2320]! } + public var Notification_CallBack: String { return self._s[2321]! } + public var Wallet_SecureStorageReset_BiometryTouchId: String { return self._s[2322]! } + public var TwoStepAuth_EmailHelp: String { return self._s[2323]! } public func Time_YesterdayAt(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2304]!, self._r[2304]!, [_0]) + return formatWithArgumentRanges(self._s[2324]!, self._r[2324]!, [_0]) } - public var Channel_Info_Management: String { return self._s[2305]! } - public var Passport_FieldIdentityUploadHelp: String { return self._s[2306]! } - public var Stickers_FrequentlyUsed: String { return self._s[2307]! } - public var Channel_BanUser_PermissionSendMessages: String { return self._s[2308]! } - public var Passport_Address_OneOfTypeUtilityBill: String { return self._s[2310]! } + public var Channel_Info_Management: String { return self._s[2325]! } + public var Passport_FieldIdentityUploadHelp: String { return self._s[2326]! } + public var Stickers_FrequentlyUsed: String { return self._s[2327]! } + public var Channel_BanUser_PermissionSendMessages: String { return self._s[2328]! } + public var Passport_Address_OneOfTypeUtilityBill: String { return self._s[2330]! } public func LOCAL_CHANNEL_MESSAGE_FWDS(_ _1: String, _ _2: Int) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2311]!, self._r[2311]!, [_1, "\(_2)"]) + return formatWithArgumentRanges(self._s[2331]!, self._r[2331]!, [_1, "\(_2)"]) } - public var TwoFactorSetup_Password_Title: String { return self._s[2312]! } - public var Passport_Address_EditResidentialAddress: String { return self._s[2313]! } - public var PrivacyPolicy_DeclineTitle: String { return self._s[2314]! } - public var CreatePoll_TextHeader: String { return self._s[2315]! } + public var TwoFactorSetup_Password_Title: String { return self._s[2332]! } + public var Passport_Address_EditResidentialAddress: String { return self._s[2333]! } + public var PrivacyPolicy_DeclineTitle: String { return self._s[2334]! } + public var CreatePoll_TextHeader: String { return self._s[2335]! } public func Checkout_SavePasswordTimeoutAndTouchId(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2316]!, self._r[2316]!, [_0]) + return formatWithArgumentRanges(self._s[2336]!, self._r[2336]!, [_0]) } - public var PhotoEditor_QualityMedium: String { return self._s[2317]! } - public var InfoPlist_NSMicrophoneUsageDescription: String { return self._s[2318]! } - public var Conversation_StatusKickedFromChannel: String { return self._s[2320]! } - public var CheckoutInfo_ReceiverInfoName: String { return self._s[2321]! } - public var Group_ErrorSendRestrictedStickers: String { return self._s[2322]! } + public var PhotoEditor_QualityMedium: String { return self._s[2337]! } + public var InfoPlist_NSMicrophoneUsageDescription: String { return self._s[2338]! } + public var Conversation_StatusKickedFromChannel: String { return self._s[2340]! } + public var CheckoutInfo_ReceiverInfoName: String { return self._s[2341]! } + public var Group_ErrorSendRestrictedStickers: String { return self._s[2342]! } public func Conversation_RestrictedInlineTimed(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2323]!, self._r[2323]!, [_0]) + return formatWithArgumentRanges(self._s[2343]!, self._r[2343]!, [_0]) } public func Channel_AdminLog_MessageTransferedName(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2324]!, self._r[2324]!, [_1]) + return formatWithArgumentRanges(self._s[2344]!, self._r[2344]!, [_1]) } - public var LogoutOptions_LogOutWalletInfo: String { return self._s[2325]! } - public var TwoFactorSetup_Email_SkipConfirmationTitle: String { return self._s[2326]! } - public var Conversation_LinkDialogOpen: String { return self._s[2328]! } - public var TwoFactorSetup_Hint_Title: String { return self._s[2329]! } - public var VoiceOver_Chat_PollNoVotes: String { return self._s[2330]! } - public var Settings_Username: String { return self._s[2332]! } - public var Conversation_Block: String { return self._s[2334]! } - public var Wallpaper_Wallpaper: String { return self._s[2335]! } - public var SocksProxySetup_UseProxy: String { return self._s[2337]! } - public var Wallet_Send_Confirmation: String { return self._s[2338]! } - public var EditTheme_UploadEditedTheme: String { return self._s[2339]! } - public var UserInfo_ShareMyContactInfo: String { return self._s[2340]! } - public var MessageTimer_Forever: String { return self._s[2341]! } - public var Privacy_Calls_WhoCanCallMe: String { return self._s[2342]! } - public var PhotoEditor_DiscardChanges: String { return self._s[2343]! } - public var AuthSessions_TerminateOtherSessionsHelp: String { return self._s[2344]! } - public var Passport_Language_da: String { return self._s[2345]! } - public var SocksProxySetup_PortPlaceholder: String { return self._s[2346]! } + public var LogoutOptions_LogOutWalletInfo: String { return self._s[2345]! } + public var TwoFactorSetup_Email_SkipConfirmationTitle: String { return self._s[2346]! } + public var Conversation_LinkDialogOpen: String { return self._s[2348]! } + public var TwoFactorSetup_Hint_Title: String { return self._s[2349]! } + public var VoiceOver_Chat_PollNoVotes: String { return self._s[2350]! } + public var Settings_Username: String { return self._s[2352]! } + public var Conversation_Block: String { return self._s[2354]! } + public var Wallpaper_Wallpaper: String { return self._s[2355]! } + public var SocksProxySetup_UseProxy: String { return self._s[2357]! } + public var Wallet_Send_Confirmation: String { return self._s[2358]! } + public var EditTheme_UploadEditedTheme: String { return self._s[2359]! } + public var UserInfo_ShareMyContactInfo: String { return self._s[2360]! } + public var MessageTimer_Forever: String { return self._s[2361]! } + public var Privacy_Calls_WhoCanCallMe: String { return self._s[2362]! } + public var PhotoEditor_DiscardChanges: String { return self._s[2363]! } + public var AuthSessions_TerminateOtherSessionsHelp: String { return self._s[2364]! } + public var Passport_Language_da: String { return self._s[2365]! } + public var SocksProxySetup_PortPlaceholder: String { return self._s[2366]! } public func SecretGIF_NotViewedYet(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2347]!, self._r[2347]!, [_0]) + return formatWithArgumentRanges(self._s[2367]!, self._r[2367]!, [_0]) } - public var Passport_Address_EditPassportRegistration: String { return self._s[2348]! } + public var Passport_Address_EditPassportRegistration: String { return self._s[2368]! } public func Channel_AdminLog_MessageChangedGroupAbout(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2350]!, self._r[2350]!, [_0]) + return formatWithArgumentRanges(self._s[2370]!, self._r[2370]!, [_0]) } - public var Settings_AddDevice: String { return self._s[2351]! } - public var Passport_Identity_ResidenceCountryPlaceholder: String { return self._s[2353]! } - public var AuthSessions_AddDeviceIntro_Text1: String { return self._s[2354]! } - public var Conversation_SearchByName_Prefix: String { return self._s[2355]! } - public var Conversation_PinnedPoll: String { return self._s[2356]! } - public var AuthSessions_AddDeviceIntro_Text2: String { return self._s[2357]! } - public var Conversation_EmptyGifPanelPlaceholder: String { return self._s[2358]! } - public var AuthSessions_AddDeviceIntro_Text3: String { return self._s[2359]! } + public var Settings_AddDevice: String { return self._s[2371]! } + public var Passport_Identity_ResidenceCountryPlaceholder: String { return self._s[2373]! } + public var AuthSessions_AddDeviceIntro_Text1: String { return self._s[2374]! } + public var Conversation_SearchByName_Prefix: String { return self._s[2375]! } + public var Conversation_PinnedPoll: String { return self._s[2376]! } + public var AuthSessions_AddDeviceIntro_Text2: String { return self._s[2377]! } + public var Conversation_EmptyGifPanelPlaceholder: String { return self._s[2378]! } + public var AuthSessions_AddDeviceIntro_Text3: String { return self._s[2379]! } public func PUSH_ENCRYPTION_ACCEPT(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2360]!, self._r[2360]!, [_1]) + return formatWithArgumentRanges(self._s[2380]!, self._r[2380]!, [_1]) } - public var WallpaperSearch_ColorPurple: String { return self._s[2361]! } - public var Cache_ByPeerHeader: String { return self._s[2362]! } + public var WallpaperSearch_ColorPurple: String { return self._s[2381]! } + public var Cache_ByPeerHeader: String { return self._s[2382]! } public func Conversation_EncryptedPlaceholderTitleIncoming(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2363]!, self._r[2363]!, [_0]) + return formatWithArgumentRanges(self._s[2383]!, self._r[2383]!, [_0]) } - public var ChatSettings_AutoDownloadDocuments: String { return self._s[2364]! } - public var Appearance_ThemePreview_Chat_3_Text: String { return self._s[2367]! } - public var Wallet_Completed_Title: String { return self._s[2368]! } - public var Notification_PinnedMessage: String { return self._s[2369]! } - public var TwoFactorSetup_EmailVerification_Placeholder: String { return self._s[2370]! } - public var VoiceOver_Chat_RecordModeVideoMessage: String { return self._s[2372]! } - public var Contacts_SortBy: String { return self._s[2373]! } + public var ChatSettings_AutoDownloadDocuments: String { return self._s[2384]! } + public var Appearance_ThemePreview_Chat_3_Text: String { return self._s[2387]! } + public var Wallet_Completed_Title: String { return self._s[2388]! } + public var Notification_PinnedMessage: String { return self._s[2389]! } + public var TwoFactorSetup_EmailVerification_Placeholder: String { return self._s[2390]! } + public var VoiceOver_Chat_RecordModeVideoMessage: String { return self._s[2392]! } + public var Contacts_SortBy: String { return self._s[2393]! } public func PUSH_CHANNEL_MESSAGE_NOTEXT(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2374]!, self._r[2374]!, [_1]) + return formatWithArgumentRanges(self._s[2394]!, self._r[2394]!, [_1]) } - public var Appearance_ColorThemeNight: String { return self._s[2376]! } + public var Appearance_ColorThemeNight: String { return self._s[2396]! } public func PUSH_MESSAGE_GAME(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2377]!, self._r[2377]!, [_1, _2]) + return formatWithArgumentRanges(self._s[2397]!, self._r[2397]!, [_1, _2]) } - public var Call_EncryptionKey_Title: String { return self._s[2378]! } - public var Watch_UserInfo_Service: String { return self._s[2379]! } - public var SettingsSearch_Synonyms_Data_SaveEditedPhotos: String { return self._s[2381]! } - public var Conversation_Unpin: String { return self._s[2383]! } - public var CancelResetAccount_Title: String { return self._s[2384]! } - public var Map_LiveLocationFor15Minutes: String { return self._s[2385]! } + public var Call_EncryptionKey_Title: String { return self._s[2398]! } + public var Watch_UserInfo_Service: String { return self._s[2399]! } + public var SettingsSearch_Synonyms_Data_SaveEditedPhotos: String { return self._s[2401]! } + public var Conversation_Unpin: String { return self._s[2403]! } + public var CancelResetAccount_Title: String { return self._s[2404]! } + public var Map_LiveLocationFor15Minutes: String { return self._s[2405]! } public func Time_PreciseDate_m8(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2387]!, self._r[2387]!, [_1, _2, _3]) + return formatWithArgumentRanges(self._s[2407]!, self._r[2407]!, [_1, _2, _3]) } - public var Group_Members_AddMemberBotErrorNotAllowed: String { return self._s[2388]! } - public var CallSettings_Title: String { return self._s[2389]! } - public var SettingsSearch_Synonyms_Appearance_ChatBackground: String { return self._s[2390]! } - public var PasscodeSettings_EncryptDataHelp: String { return self._s[2392]! } - public var AutoDownloadSettings_Contacts: String { return self._s[2393]! } + public var Group_Members_AddMemberBotErrorNotAllowed: String { return self._s[2408]! } + public var Appearance_BubbleCorners_Title: String { return self._s[2409]! } + public var CallSettings_Title: String { return self._s[2410]! } + public var SettingsSearch_Synonyms_Appearance_ChatBackground: String { return self._s[2411]! } + public var PasscodeSettings_EncryptDataHelp: String { return self._s[2413]! } + public var AutoDownloadSettings_Contacts: String { return self._s[2414]! } public func Channel_AdminLog_MessageRankName(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2394]!, self._r[2394]!, [_1, _2]) + return formatWithArgumentRanges(self._s[2415]!, self._r[2415]!, [_1, _2]) } - public var Passport_Identity_DocumentDetails: String { return self._s[2395]! } - public var LoginPassword_PasswordHelp: String { return self._s[2396]! } - public var SettingsSearch_Synonyms_Data_AutoDownloadUsingWifi: String { return self._s[2397]! } - public var PrivacyLastSeenSettings_CustomShareSettings_Delete: String { return self._s[2398]! } - public var ChatContextMenu_TextSelectionTip: String { return self._s[2399]! } - public var Checkout_TotalPaidAmount: String { return self._s[2400]! } + public var Passport_Identity_DocumentDetails: String { return self._s[2416]! } + public var LoginPassword_PasswordHelp: String { return self._s[2417]! } + public var SettingsSearch_Synonyms_Data_AutoDownloadUsingWifi: String { return self._s[2418]! } + public var PrivacyLastSeenSettings_CustomShareSettings_Delete: String { return self._s[2419]! } + public var ChatContextMenu_TextSelectionTip: String { return self._s[2420]! } + public var Checkout_TotalPaidAmount: String { return self._s[2421]! } public func FileSize_KB(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2401]!, self._r[2401]!, [_0]) + return formatWithArgumentRanges(self._s[2422]!, self._r[2422]!, [_0]) } - public var PasscodeSettings_ChangePasscode: String { return self._s[2402]! } - public var Conversation_SecretLinkPreviewAlert: String { return self._s[2404]! } - public var Privacy_SecretChatsLinkPreviews: String { return self._s[2405]! } + public var PasscodeSettings_ChangePasscode: String { return self._s[2423]! } + public var Conversation_SecretLinkPreviewAlert: String { return self._s[2425]! } + public var Privacy_SecretChatsLinkPreviews: String { return self._s[2426]! } public func PUSH_CHANNEL_MESSAGE_DOC(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2406]!, self._r[2406]!, [_1]) + return formatWithArgumentRanges(self._s[2427]!, self._r[2427]!, [_1]) } - public var VoiceOver_Chat_ReplyToYourMessage: String { return self._s[2407]! } - public var Contacts_InviteFriends: String { return self._s[2409]! } - public var Map_ChooseLocationTitle: String { return self._s[2410]! } - public var Conversation_StopPoll: String { return self._s[2412]! } + public var VoiceOver_Chat_ReplyToYourMessage: String { return self._s[2428]! } + public var Contacts_InviteFriends: String { return self._s[2430]! } + public var Map_ChooseLocationTitle: String { return self._s[2431]! } + public var Conversation_StopPoll: String { return self._s[2433]! } public func WebSearch_SearchNoResultsDescription(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2413]!, self._r[2413]!, [_0]) + return formatWithArgumentRanges(self._s[2434]!, self._r[2434]!, [_0]) } - public var Call_Camera: String { return self._s[2414]! } - public var LogoutOptions_ChangePhoneNumberTitle: String { return self._s[2415]! } - public var AppWallet_Intro_Text: String { return self._s[2416]! } - public var Calls_RatingFeedback: String { return self._s[2417]! } - public var GroupInfo_BroadcastListNamePlaceholder: String { return self._s[2419]! } - public var Wallet_Alert_OK: String { return self._s[2420]! } - public var NotificationsSound_Pulse: String { return self._s[2421]! } - public var Watch_LastSeen_Lately: String { return self._s[2422]! } - public var ReportGroupLocation_Report: String { return self._s[2425]! } - public var Widget_NoUsers: String { return self._s[2426]! } - public var Conversation_UnvotePoll: String { return self._s[2427]! } - public var SettingsSearch_Synonyms_Privacy_ProfilePhoto: String { return self._s[2429]! } - public var Privacy_ProfilePhoto_WhoCanSeeMyPhoto: String { return self._s[2430]! } - public var NotificationsSound_Circles: String { return self._s[2431]! } - public var PrivacyLastSeenSettings_AlwaysShareWith_Title: String { return self._s[2434]! } - public var Wallet_Settings_DeleteWallet: String { return self._s[2435]! } - public var TwoStepAuth_RecoveryCodeExpired: String { return self._s[2436]! } - public var Proxy_TooltipUnavailable: String { return self._s[2437]! } - public var Passport_Identity_CountryPlaceholder: String { return self._s[2439]! } - public var GroupInfo_Permissions_SlowmodeInfo: String { return self._s[2441]! } - public var Conversation_FileDropbox: String { return self._s[2442]! } - public var Notifications_ExceptionsUnmuted: String { return self._s[2443]! } - public var Tour_Text3: String { return self._s[2445]! } - public var Login_ResetAccountProtected_Title: String { return self._s[2447]! } - public var GroupPermission_NoSendMessages: String { return self._s[2448]! } - public var WallpaperSearch_ColorTitle: String { return self._s[2449]! } - public var ChatAdmins_AllMembersAreAdminsOnHelp: String { return self._s[2450]! } + public var Call_Camera: String { return self._s[2435]! } + public var LogoutOptions_ChangePhoneNumberTitle: String { return self._s[2436]! } + public var AppWallet_Intro_Text: String { return self._s[2437]! } + public var Appearance_BubbleCornersSetting: String { return self._s[2438]! } + public var Calls_RatingFeedback: String { return self._s[2439]! } + public var GroupInfo_BroadcastListNamePlaceholder: String { return self._s[2441]! } + public var Wallet_Alert_OK: String { return self._s[2442]! } + public var NotificationsSound_Pulse: String { return self._s[2443]! } + public var Watch_LastSeen_Lately: String { return self._s[2444]! } + public var ReportGroupLocation_Report: String { return self._s[2447]! } + public var Widget_NoUsers: String { return self._s[2448]! } + public var Conversation_UnvotePoll: String { return self._s[2449]! } + public var SettingsSearch_Synonyms_Privacy_ProfilePhoto: String { return self._s[2451]! } + public var Privacy_ProfilePhoto_WhoCanSeeMyPhoto: String { return self._s[2452]! } + public var NotificationsSound_Circles: String { return self._s[2453]! } + public var PrivacyLastSeenSettings_AlwaysShareWith_Title: String { return self._s[2456]! } + public var Wallet_Settings_DeleteWallet: String { return self._s[2457]! } + public var TwoStepAuth_RecoveryCodeExpired: String { return self._s[2458]! } + public var Proxy_TooltipUnavailable: String { return self._s[2459]! } + public var Passport_Identity_CountryPlaceholder: String { return self._s[2461]! } + public var GroupInfo_Permissions_SlowmodeInfo: String { return self._s[2463]! } + public var Conversation_FileDropbox: String { return self._s[2464]! } + public var Notifications_ExceptionsUnmuted: String { return self._s[2465]! } + public var Tour_Text3: String { return self._s[2467]! } + public var Login_ResetAccountProtected_Title: String { return self._s[2469]! } + public var GroupPermission_NoSendMessages: String { return self._s[2470]! } + public var WallpaperSearch_ColorTitle: String { return self._s[2471]! } + public var ChatAdmins_AllMembersAreAdminsOnHelp: String { return self._s[2472]! } public func Conversation_LiveLocationYouAnd(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2452]!, self._r[2452]!, [_0]) + return formatWithArgumentRanges(self._s[2474]!, self._r[2474]!, [_0]) } - public var GroupInfo_AddParticipantTitle: String { return self._s[2453]! } - public var Checkout_ShippingOption_Title: String { return self._s[2454]! } - public var ChatSettings_AutoDownloadTitle: String { return self._s[2455]! } + public var GroupInfo_AddParticipantTitle: String { return self._s[2475]! } + public var Checkout_ShippingOption_Title: String { return self._s[2476]! } + public var ChatSettings_AutoDownloadTitle: String { return self._s[2477]! } public func DialogList_SingleTypingSuffix(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2456]!, self._r[2456]!, [_0]) + return formatWithArgumentRanges(self._s[2478]!, self._r[2478]!, [_0]) } public func ChatSettings_AutoDownloadSettings_TypeVideo(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2457]!, self._r[2457]!, [_0]) + return formatWithArgumentRanges(self._s[2479]!, self._r[2479]!, [_0]) } - public var Channel_Management_LabelAdministrator: String { return self._s[2458]! } - public var EditTheme_FileReadError: String { return self._s[2459]! } - public var OwnershipTransfer_ComeBackLater: String { return self._s[2460]! } - public var PrivacyLastSeenSettings_NeverShareWith_Placeholder: String { return self._s[2461]! } - public var AutoDownloadSettings_Photos: String { return self._s[2463]! } - public var Appearance_PreviewIncomingText: String { return self._s[2464]! } - public var ChatList_Context_MarkAllAsRead: String { return self._s[2465]! } - public var ChannelInfo_ConfirmLeave: String { return self._s[2466]! } - public var MediaPicker_MomentsDateRangeSameMonthYearFormat: String { return self._s[2467]! } - public var Passport_Identity_DocumentNumberPlaceholder: String { return self._s[2468]! } - public var Channel_AdminLogFilter_EventsNewMembers: String { return self._s[2469]! } - public var PasscodeSettings_AutoLock_IfAwayFor_5minutes: String { return self._s[2470]! } - public var GroupInfo_SetGroupPhotoStop: String { return self._s[2471]! } - public var Notification_SecretChatScreenshot: String { return self._s[2472]! } - public var AccessDenied_Wallpapers: String { return self._s[2473]! } - public var ChatList_Context_Mute: String { return self._s[2475]! } - public var Passport_Address_City: String { return self._s[2476]! } - public var InfoPlist_NSPhotoLibraryAddUsageDescription: String { return self._s[2477]! } - public var Appearance_ThemeCarouselClassic: String { return self._s[2478]! } - public var SocksProxySetup_SecretPlaceholder: String { return self._s[2479]! } - public var AccessDenied_LocationDisabled: String { return self._s[2480]! } - public var Group_Location_Title: String { return self._s[2481]! } - public var SocksProxySetup_HostnamePlaceholder: String { return self._s[2483]! } - public var GroupInfo_Sound: String { return self._s[2484]! } - public var SettingsSearch_Synonyms_ChatSettings_OpenLinksIn: String { return self._s[2485]! } - public var ChannelInfo_ScamChannelWarning: String { return self._s[2486]! } - public var Stickers_RemoveFromFavorites: String { return self._s[2487]! } - public var Contacts_Title: String { return self._s[2488]! } - public var EditTheme_ThemeTemplateAlertText: String { return self._s[2489]! } - public var Passport_Language_fr: String { return self._s[2490]! } - public var TwoFactorSetup_EmailVerification_Action: String { return self._s[2491]! } - public var Notifications_ResetAllNotifications: String { return self._s[2492]! } - public var IntentsSettings_SuggestedChats: String { return self._s[2494]! } - public var PrivacySettings_SecurityTitle: String { return self._s[2496]! } - public var Checkout_NewCard_Title: String { return self._s[2497]! } - public var Login_HaveNotReceivedCodeInternal: String { return self._s[2498]! } - public var Conversation_ForwardChats: String { return self._s[2499]! } - public var Wallet_SecureStorageReset_PasscodeText: String { return self._s[2501]! } - public var PasscodeSettings_4DigitCode: String { return self._s[2502]! } - public var Settings_FAQ: String { return self._s[2504]! } - public var AutoDownloadSettings_DocumentsTitle: String { return self._s[2505]! } - public var Conversation_ContextMenuForward: String { return self._s[2506]! } - public var VoiceOver_Chat_YourPhoto: String { return self._s[2509]! } - public var PrivacyPolicy_Title: String { return self._s[2512]! } - public var Notifications_TextTone: String { return self._s[2513]! } - public var Profile_CreateNewContact: String { return self._s[2514]! } - public var PrivacyPhoneNumberSettings_WhoCanSeeMyPhoneNumber: String { return self._s[2515]! } - public var TwoFactorSetup_EmailVerification_Title: String { return self._s[2517]! } - public var Call_Speaker: String { return self._s[2518]! } - public var AutoNightTheme_AutomaticSection: String { return self._s[2519]! } - public var Channel_OwnershipTransfer_EnterPassword: String { return self._s[2521]! } - public var Channel_Username_InvalidCharacters: String { return self._s[2522]! } + public var Channel_Management_LabelAdministrator: String { return self._s[2480]! } + public var EditTheme_FileReadError: String { return self._s[2481]! } + public var OwnershipTransfer_ComeBackLater: String { return self._s[2482]! } + public var PrivacyLastSeenSettings_NeverShareWith_Placeholder: String { return self._s[2483]! } + public var AutoDownloadSettings_Photos: String { return self._s[2485]! } + public var Appearance_PreviewIncomingText: String { return self._s[2486]! } + public var ChatList_Context_MarkAllAsRead: String { return self._s[2487]! } + public var ChannelInfo_ConfirmLeave: String { return self._s[2488]! } + public var MediaPicker_MomentsDateRangeSameMonthYearFormat: String { return self._s[2489]! } + public var Passport_Identity_DocumentNumberPlaceholder: String { return self._s[2490]! } + public var Channel_AdminLogFilter_EventsNewMembers: String { return self._s[2491]! } + public var PasscodeSettings_AutoLock_IfAwayFor_5minutes: String { return self._s[2492]! } + public var GroupInfo_SetGroupPhotoStop: String { return self._s[2493]! } + public var Notification_SecretChatScreenshot: String { return self._s[2494]! } + public var AccessDenied_Wallpapers: String { return self._s[2495]! } + public var ChatList_Context_Mute: String { return self._s[2497]! } + public var Passport_Address_City: String { return self._s[2498]! } + public var InfoPlist_NSPhotoLibraryAddUsageDescription: String { return self._s[2499]! } + public var Appearance_ThemeCarouselClassic: String { return self._s[2500]! } + public var SocksProxySetup_SecretPlaceholder: String { return self._s[2501]! } + public var AccessDenied_LocationDisabled: String { return self._s[2502]! } + public var Group_Location_Title: String { return self._s[2503]! } + public var SocksProxySetup_HostnamePlaceholder: String { return self._s[2505]! } + public var GroupInfo_Sound: String { return self._s[2506]! } + public var SettingsSearch_Synonyms_ChatSettings_OpenLinksIn: String { return self._s[2507]! } + public var ChannelInfo_ScamChannelWarning: String { return self._s[2508]! } + public var Stickers_RemoveFromFavorites: String { return self._s[2509]! } + public var Contacts_Title: String { return self._s[2510]! } + public var EditTheme_ThemeTemplateAlertText: String { return self._s[2511]! } + public var Passport_Language_fr: String { return self._s[2512]! } + public var TwoFactorSetup_EmailVerification_Action: String { return self._s[2513]! } + public var Notifications_ResetAllNotifications: String { return self._s[2514]! } + public var IntentsSettings_SuggestedChats: String { return self._s[2516]! } + public var PrivacySettings_SecurityTitle: String { return self._s[2518]! } + public var Checkout_NewCard_Title: String { return self._s[2519]! } + public var Login_HaveNotReceivedCodeInternal: String { return self._s[2520]! } + public var Conversation_ForwardChats: String { return self._s[2521]! } + public var Wallet_SecureStorageReset_PasscodeText: String { return self._s[2523]! } + public var PasscodeSettings_4DigitCode: String { return self._s[2524]! } + public var Settings_FAQ: String { return self._s[2526]! } + public var AutoDownloadSettings_DocumentsTitle: String { return self._s[2527]! } + public var Conversation_ContextMenuForward: String { return self._s[2528]! } + public var VoiceOver_Chat_YourPhoto: String { return self._s[2531]! } + public var PrivacyPolicy_Title: String { return self._s[2534]! } + public var Notifications_TextTone: String { return self._s[2535]! } + public var Profile_CreateNewContact: String { return self._s[2536]! } + public var PrivacyPhoneNumberSettings_WhoCanSeeMyPhoneNumber: String { return self._s[2537]! } + public var TwoFactorSetup_EmailVerification_Title: String { return self._s[2539]! } + public var Call_Speaker: String { return self._s[2540]! } + public var AutoNightTheme_AutomaticSection: String { return self._s[2541]! } + public var Channel_OwnershipTransfer_EnterPassword: String { return self._s[2543]! } + public var Channel_Username_InvalidCharacters: String { return self._s[2544]! } public func Channel_AdminLog_MessageChangedChannelUsername(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2523]!, self._r[2523]!, [_0]) - } - public var AutoDownloadSettings_AutodownloadFiles: String { return self._s[2524]! } - public var PrivacySettings_LastSeenTitle: String { return self._s[2525]! } - public var Channel_AdminLog_CanInviteUsers: String { return self._s[2526]! } - public var SettingsSearch_Synonyms_Privacy_Data_ClearPaymentsInfo: String { return self._s[2527]! } - public var OwnershipTransfer_SecurityCheck: String { return self._s[2528]! } - public var Conversation_MessageDeliveryFailed: String { return self._s[2529]! } - public var Watch_ChatList_NoConversationsText: String { return self._s[2530]! } - public var Bot_Unblock: String { return self._s[2531]! } - public var TextFormat_Italic: String { return self._s[2532]! } - public var WallpaperSearch_ColorPink: String { return self._s[2533]! } - public var Settings_About_Help: String { return self._s[2535]! } - public var SearchImages_Title: String { return self._s[2536]! } - public var Weekday_Wednesday: String { return self._s[2537]! } - public var Conversation_ClousStorageInfo_Description1: String { return self._s[2538]! } - public var ExplicitContent_AlertTitle: String { return self._s[2539]! } - public func Time_PreciseDate_m5(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2540]!, self._r[2540]!, [_1, _2, _3]) - } - public var Channel_DiscussionGroup_Create: String { return self._s[2541]! } - public var Weekday_Thursday: String { return self._s[2542]! } - public var Channel_BanUser_PermissionChangeGroupInfo: String { return self._s[2543]! } - public var Channel_Members_AddMembersHelp: String { return self._s[2544]! } - public func Checkout_SavePasswordTimeout(_ _0: String) -> (String, [(Int, NSRange)]) { return formatWithArgumentRanges(self._s[2545]!, self._r[2545]!, [_0]) } - public var Channel_DiscussionGroup_LinkGroup: String { return self._s[2546]! } - public var SettingsSearch_Synonyms_Notifications_InAppNotificationsVibrate: String { return self._s[2547]! } - public var Passport_RequestedInformation: String { return self._s[2548]! } - public var Login_PhoneAndCountryHelp: String { return self._s[2549]! } - public var Conversation_EncryptionProcessing: String { return self._s[2551]! } - public var Notifications_PermissionsSuppressWarningTitle: String { return self._s[2552]! } - public var PhotoEditor_EnhanceTool: String { return self._s[2554]! } - public var Channel_Setup_Title: String { return self._s[2555]! } - public var Conversation_SearchPlaceholder: String { return self._s[2556]! } - public var OldChannels_GroupEmptyFormat: String { return self._s[2557]! } - public var AccessDenied_LocationAlwaysDenied: String { return self._s[2558]! } - public var Checkout_ErrorGeneric: String { return self._s[2559]! } - public var Passport_Language_hu: String { return self._s[2560]! } - public var GroupPermission_EditingDisabled: String { return self._s[2561]! } - public var Wallet_Month_ShortSeptember: String { return self._s[2563]! } + public var AutoDownloadSettings_AutodownloadFiles: String { return self._s[2546]! } + public var PrivacySettings_LastSeenTitle: String { return self._s[2547]! } + public var Channel_AdminLog_CanInviteUsers: String { return self._s[2548]! } + public var SettingsSearch_Synonyms_Privacy_Data_ClearPaymentsInfo: String { return self._s[2549]! } + public var OwnershipTransfer_SecurityCheck: String { return self._s[2550]! } + public var Conversation_MessageDeliveryFailed: String { return self._s[2551]! } + public var Watch_ChatList_NoConversationsText: String { return self._s[2552]! } + public var Bot_Unblock: String { return self._s[2553]! } + public var TextFormat_Italic: String { return self._s[2554]! } + public var WallpaperSearch_ColorPink: String { return self._s[2555]! } + public var Settings_About_Help: String { return self._s[2557]! } + public var SearchImages_Title: String { return self._s[2558]! } + public var Weekday_Wednesday: String { return self._s[2559]! } + public var Conversation_ClousStorageInfo_Description1: String { return self._s[2560]! } + public var ExplicitContent_AlertTitle: String { return self._s[2561]! } + public func Time_PreciseDate_m5(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[2562]!, self._r[2562]!, [_1, _2, _3]) + } + public var Channel_DiscussionGroup_Create: String { return self._s[2563]! } + public var Weekday_Thursday: String { return self._s[2564]! } + public var Channel_BanUser_PermissionChangeGroupInfo: String { return self._s[2565]! } + public var Channel_Members_AddMembersHelp: String { return self._s[2566]! } + public func Checkout_SavePasswordTimeout(_ _0: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[2567]!, self._r[2567]!, [_0]) + } + public var Channel_DiscussionGroup_LinkGroup: String { return self._s[2568]! } + public var SettingsSearch_Synonyms_Notifications_InAppNotificationsVibrate: String { return self._s[2569]! } + public var Passport_RequestedInformation: String { return self._s[2570]! } + public var Login_PhoneAndCountryHelp: String { return self._s[2571]! } + public var Conversation_EncryptionProcessing: String { return self._s[2573]! } + public var Notifications_PermissionsSuppressWarningTitle: String { return self._s[2574]! } + public var PhotoEditor_EnhanceTool: String { return self._s[2576]! } + public var Channel_Setup_Title: String { return self._s[2577]! } + public var Conversation_SearchPlaceholder: String { return self._s[2578]! } + public var OldChannels_GroupEmptyFormat: String { return self._s[2579]! } + public var AccessDenied_LocationAlwaysDenied: String { return self._s[2580]! } + public var Checkout_ErrorGeneric: String { return self._s[2581]! } + public var Passport_Language_hu: String { return self._s[2582]! } + public var GroupPermission_EditingDisabled: String { return self._s[2583]! } + public var Wallet_Month_ShortSeptember: String { return self._s[2585]! } public func Passport_Identity_UploadOneOfScan(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2564]!, self._r[2564]!, [_0]) + return formatWithArgumentRanges(self._s[2586]!, self._r[2586]!, [_0]) } public func PUSH_MESSAGE(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2567]!, self._r[2567]!, [_1]) + return formatWithArgumentRanges(self._s[2589]!, self._r[2589]!, [_1]) } - public var ChatList_DeleteSavedMessagesConfirmationTitle: String { return self._s[2568]! } + public var ChatList_DeleteSavedMessagesConfirmationTitle: String { return self._s[2590]! } public func UserInfo_BlockConfirmationTitle(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2569]!, self._r[2569]!, [_0]) + return formatWithArgumentRanges(self._s[2591]!, self._r[2591]!, [_0]) } - public var Conversation_CloudStorageInfo_Title: String { return self._s[2570]! } - public var Group_Location_Info: String { return self._s[2571]! } - public var PhotoEditor_CropAspectRatioSquare: String { return self._s[2572]! } - public var Permissions_PeopleNearbyAllow_v0: String { return self._s[2573]! } + public var Conversation_CloudStorageInfo_Title: String { return self._s[2592]! } + public var Group_Location_Info: String { return self._s[2593]! } + public var PhotoEditor_CropAspectRatioSquare: String { return self._s[2594]! } + public var Permissions_PeopleNearbyAllow_v0: String { return self._s[2595]! } public func Notification_Exceptions_MutedUntil(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2574]!, self._r[2574]!, [_0]) + return formatWithArgumentRanges(self._s[2596]!, self._r[2596]!, [_0]) } - public var Conversation_ClearPrivateHistory: String { return self._s[2575]! } - public var ContactInfo_PhoneLabelHome: String { return self._s[2576]! } - public var Appearance_RemoveThemeConfirmation: String { return self._s[2577]! } - public var PrivacySettings_LastSeenContacts: String { return self._s[2578]! } + public var Conversation_ClearPrivateHistory: String { return self._s[2597]! } + public var ContactInfo_PhoneLabelHome: String { return self._s[2598]! } + public var Appearance_RemoveThemeConfirmation: String { return self._s[2599]! } + public var PrivacySettings_LastSeenContacts: String { return self._s[2600]! } public func ChangePhone_ErrorOccupied(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2579]!, self._r[2579]!, [_0]) + return formatWithArgumentRanges(self._s[2601]!, self._r[2601]!, [_0]) } - public var Passport_Language_cs: String { return self._s[2580]! } - public var Message_PinnedAnimationMessage: String { return self._s[2582]! } - public var Passport_Identity_ReverseSideHelp: String { return self._s[2584]! } - public var SettingsSearch_Synonyms_Data_Storage_Title: String { return self._s[2585]! } - public var Wallet_Info_TransactionTo: String { return self._s[2587]! } - public var ChatList_DeleteForEveryoneConfirmationText: String { return self._s[2588]! } - public var SettingsSearch_Synonyms_Privacy_PasscodeAndTouchId: String { return self._s[2589]! } - public var Embed_PlayingInPIP: String { return self._s[2590]! } - public var Appearance_ThemePreview_Chat_3_TextWithLink: String { return self._s[2591]! } - public var AutoNightTheme_ScheduleSection: String { return self._s[2592]! } + public func Notification_PinnedQuizMessage(_ _0: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[2602]!, self._r[2602]!, [_0]) + } + public var Passport_Language_cs: String { return self._s[2603]! } + public var Message_PinnedAnimationMessage: String { return self._s[2605]! } + public var Passport_Identity_ReverseSideHelp: String { return self._s[2607]! } + public var SettingsSearch_Synonyms_Data_Storage_Title: String { return self._s[2608]! } + public var Wallet_Info_TransactionTo: String { return self._s[2610]! } + public var ChatList_DeleteForEveryoneConfirmationText: String { return self._s[2611]! } + public var SettingsSearch_Synonyms_Privacy_PasscodeAndTouchId: String { return self._s[2612]! } + public var Embed_PlayingInPIP: String { return self._s[2613]! } + public var Appearance_ThemePreview_Chat_3_TextWithLink: String { return self._s[2614]! } + public var AutoNightTheme_ScheduleSection: String { return self._s[2615]! } public func Call_EmojiDescription(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2593]!, self._r[2593]!, [_0]) + return formatWithArgumentRanges(self._s[2616]!, self._r[2616]!, [_0]) } - public var MediaPicker_LivePhotoDescription: String { return self._s[2594]! } + public var MediaPicker_LivePhotoDescription: String { return self._s[2617]! } public func Channel_AdminLog_MessageRestrictedName(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2595]!, self._r[2595]!, [_1]) + return formatWithArgumentRanges(self._s[2618]!, self._r[2618]!, [_1]) } - public var Notification_PaymentSent: String { return self._s[2596]! } - public var PhotoEditor_CurvesGreen: String { return self._s[2597]! } - public var Notification_Exceptions_PreviewAlwaysOff: String { return self._s[2598]! } - public var AutoNightTheme_System: String { return self._s[2599]! } - public var SaveIncomingPhotosSettings_Title: String { return self._s[2600]! } - public var NotificationSettings_ShowNotificationsAllAccounts: String { return self._s[2601]! } - public var VoiceOver_Chat_PagePreview: String { return self._s[2602]! } + public var Notification_PaymentSent: String { return self._s[2619]! } + public var PhotoEditor_CurvesGreen: String { return self._s[2620]! } + public var Notification_Exceptions_PreviewAlwaysOff: String { return self._s[2621]! } + public var AutoNightTheme_System: String { return self._s[2622]! } + public var SaveIncomingPhotosSettings_Title: String { return self._s[2623]! } + public var CreatePoll_QuizTitle: String { return self._s[2624]! } + public var NotificationSettings_ShowNotificationsAllAccounts: String { return self._s[2625]! } + public var VoiceOver_Chat_PagePreview: String { return self._s[2626]! } public func PUSH_MESSAGE_SCREENSHOT(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2605]!, self._r[2605]!, [_1]) + return formatWithArgumentRanges(self._s[2629]!, self._r[2629]!, [_1]) } public func PUSH_MESSAGE_PHOTO_SECRET(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2606]!, self._r[2606]!, [_1]) + return formatWithArgumentRanges(self._s[2630]!, self._r[2630]!, [_1]) } public func ApplyLanguage_UnsufficientDataText(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2607]!, self._r[2607]!, [_1]) + return formatWithArgumentRanges(self._s[2631]!, self._r[2631]!, [_1]) } - public var NetworkUsageSettings_CallDataSection: String { return self._s[2609]! } - public var PasscodeSettings_HelpTop: String { return self._s[2610]! } - public var Conversation_WalletRequiredTitle: String { return self._s[2611]! } - public var Group_OwnershipTransfer_ErrorAdminsTooMuch: String { return self._s[2612]! } - public var Passport_Address_TypeRentalAgreement: String { return self._s[2613]! } - public var EditTheme_ShortLink: String { return self._s[2614]! } - public var Theme_Colors_ColorWallpaperWarning: String { return self._s[2615]! } - public var ProxyServer_VoiceOver_Active: String { return self._s[2616]! } - public var ReportPeer_ReasonOther_Placeholder: String { return self._s[2617]! } - public var CheckoutInfo_ErrorPhoneInvalid: String { return self._s[2618]! } - public var Call_Accept: String { return self._s[2620]! } - public var GroupRemoved_RemoveInfo: String { return self._s[2621]! } - public var Month_GenMarch: String { return self._s[2623]! } - public var PhotoEditor_ShadowsTool: String { return self._s[2624]! } - public var LoginPassword_Title: String { return self._s[2625]! } - public var Call_End: String { return self._s[2626]! } - public var Watch_Conversation_GroupInfo: String { return self._s[2627]! } - public var VoiceOver_Chat_Contact: String { return self._s[2628]! } - public var EditTheme_Create_Preview_IncomingText: String { return self._s[2629]! } - public var CallSettings_Always: String { return self._s[2630]! } - public var CallFeedback_Success: String { return self._s[2631]! } - public var TwoStepAuth_SetupHint: String { return self._s[2632]! } + public var NetworkUsageSettings_CallDataSection: String { return self._s[2633]! } + public var PasscodeSettings_HelpTop: String { return self._s[2634]! } + public var Conversation_WalletRequiredTitle: String { return self._s[2635]! } + public var Group_OwnershipTransfer_ErrorAdminsTooMuch: String { return self._s[2636]! } + public var Passport_Address_TypeRentalAgreement: String { return self._s[2637]! } + public var EditTheme_ShortLink: String { return self._s[2638]! } + public var Theme_Colors_ColorWallpaperWarning: String { return self._s[2639]! } + public var ProxyServer_VoiceOver_Active: String { return self._s[2640]! } + public var ReportPeer_ReasonOther_Placeholder: String { return self._s[2641]! } + public var CheckoutInfo_ErrorPhoneInvalid: String { return self._s[2642]! } + public var Call_Accept: String { return self._s[2644]! } + public var GroupRemoved_RemoveInfo: String { return self._s[2645]! } + public var Month_GenMarch: String { return self._s[2647]! } + public var PhotoEditor_ShadowsTool: String { return self._s[2648]! } + public var LoginPassword_Title: String { return self._s[2649]! } + public var Call_End: String { return self._s[2650]! } + public var Watch_Conversation_GroupInfo: String { return self._s[2651]! } + public var VoiceOver_Chat_Contact: String { return self._s[2652]! } + public var EditTheme_Create_Preview_IncomingText: String { return self._s[2653]! } + public var CallSettings_Always: String { return self._s[2654]! } + public var CallFeedback_Success: String { return self._s[2655]! } + public var TwoStepAuth_SetupHint: String { return self._s[2656]! } public func AddContact_ContactWillBeSharedAfterMutual(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2633]!, self._r[2633]!, [_1]) + return formatWithArgumentRanges(self._s[2657]!, self._r[2657]!, [_1]) } - public var ConversationProfile_UsersTooMuchError: String { return self._s[2634]! } - public var Login_PhoneTitle: String { return self._s[2635]! } - public var Passport_FieldPhoneHelp: String { return self._s[2636]! } - public var Weekday_ShortSunday: String { return self._s[2637]! } - public var Passport_InfoFAQ_URL: String { return self._s[2638]! } - public var ContactInfo_Job: String { return self._s[2640]! } - public var UserInfo_InviteBotToGroup: String { return self._s[2641]! } - public var Appearance_ThemeCarouselNightBlue: String { return self._s[2642]! } - public var TwoFactorSetup_Email_Text: String { return self._s[2643]! } - public var TwoStepAuth_PasswordRemovePassportConfirmation: String { return self._s[2644]! } - public var Invite_ChannelsTooMuch: String { return self._s[2645]! } - public var Wallet_Send_ConfirmationConfirm: String { return self._s[2646]! } - public var Wallet_TransactionInfo_OtherFeeInfo: String { return self._s[2647]! } - public var SettingsSearch_Synonyms_Notifications_InAppNotificationsPreview: String { return self._s[2648]! } - public var Wallet_Receive_AmountText: String { return self._s[2649]! } - public var Passport_DeletePersonalDetailsConfirmation: String { return self._s[2650]! } - public var CallFeedback_ReasonNoise: String { return self._s[2651]! } - public var Appearance_AppIconDefault: String { return self._s[2653]! } - public var Passport_Identity_AddInternalPassport: String { return self._s[2654]! } - public var MediaPicker_AddCaption: String { return self._s[2655]! } - public var CallSettings_TabIconDescription: String { return self._s[2656]! } + public var ConversationProfile_UsersTooMuchError: String { return self._s[2658]! } + public var Login_PhoneTitle: String { return self._s[2659]! } + public var Passport_FieldPhoneHelp: String { return self._s[2660]! } + public var Weekday_ShortSunday: String { return self._s[2661]! } + public var Passport_InfoFAQ_URL: String { return self._s[2662]! } + public var ContactInfo_Job: String { return self._s[2664]! } + public var UserInfo_InviteBotToGroup: String { return self._s[2665]! } + public var Appearance_ThemeCarouselNightBlue: String { return self._s[2666]! } + public var CreatePoll_QuizTip: String { return self._s[2667]! } + public var TwoFactorSetup_Email_Text: String { return self._s[2668]! } + public var TwoStepAuth_PasswordRemovePassportConfirmation: String { return self._s[2669]! } + public var Invite_ChannelsTooMuch: String { return self._s[2670]! } + public var Wallet_Send_ConfirmationConfirm: String { return self._s[2671]! } + public var Wallet_TransactionInfo_OtherFeeInfo: String { return self._s[2672]! } + public var SettingsSearch_Synonyms_Notifications_InAppNotificationsPreview: String { return self._s[2673]! } + public var Wallet_Receive_AmountText: String { return self._s[2674]! } + public var Passport_DeletePersonalDetailsConfirmation: String { return self._s[2675]! } + public var CallFeedback_ReasonNoise: String { return self._s[2676]! } + public var Appearance_AppIconDefault: String { return self._s[2678]! } + public var Passport_Identity_AddInternalPassport: String { return self._s[2679]! } + public var MediaPicker_AddCaption: String { return self._s[2680]! } + public var CallSettings_TabIconDescription: String { return self._s[2681]! } public func VoiceOver_Chat_Caption(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2657]!, self._r[2657]!, [_0]) + return formatWithArgumentRanges(self._s[2682]!, self._r[2682]!, [_0]) } - public var IntentsSettings_SuggestedChatsGroups: String { return self._s[2658]! } + public var IntentsSettings_SuggestedChatsGroups: String { return self._s[2683]! } public func Map_SearchNoResultsDescription(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2659]!, self._r[2659]!, [_0]) + return formatWithArgumentRanges(self._s[2684]!, self._r[2684]!, [_0]) } - public var ChatList_UndoArchiveHiddenTitle: String { return self._s[2660]! } - public var Privacy_GroupsAndChannels_AlwaysAllow: String { return self._s[2661]! } - public var Passport_Identity_TypePersonalDetails: String { return self._s[2662]! } - public var DialogList_SearchSectionRecent: String { return self._s[2663]! } - public var PrivacyPolicy_DeclineMessage: String { return self._s[2664]! } - public var LogoutOptions_ClearCacheText: String { return self._s[2667]! } - public var LastSeen_WithinAWeek: String { return self._s[2668]! } - public var ChannelMembers_GroupAdminsTitle: String { return self._s[2669]! } - public var Conversation_CloudStorage_ChatStatus: String { return self._s[2671]! } - public var VoiceOver_Media_PlaybackRateNormal: String { return self._s[2672]! } + public var ChatList_UndoArchiveHiddenTitle: String { return self._s[2685]! } + public var Privacy_GroupsAndChannels_AlwaysAllow: String { return self._s[2686]! } + public var Passport_Identity_TypePersonalDetails: String { return self._s[2687]! } + public var DialogList_SearchSectionRecent: String { return self._s[2688]! } + public var PrivacyPolicy_DeclineMessage: String { return self._s[2689]! } + public var CreatePoll_Anonymous: String { return self._s[2690]! } + public var LogoutOptions_ClearCacheText: String { return self._s[2693]! } + public var LastSeen_WithinAWeek: String { return self._s[2694]! } + public var ChannelMembers_GroupAdminsTitle: String { return self._s[2695]! } + public var Conversation_CloudStorage_ChatStatus: String { return self._s[2697]! } + public var VoiceOver_Media_PlaybackRateNormal: String { return self._s[2698]! } public func AddContact_SharedContactExceptionInfo(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2673]!, self._r[2673]!, [_0]) + return formatWithArgumentRanges(self._s[2699]!, self._r[2699]!, [_0]) } - public var Passport_Address_TypeResidentialAddress: String { return self._s[2674]! } - public var Conversation_StatusLeftGroup: String { return self._s[2675]! } - public var SocksProxySetup_ProxyDetailsTitle: String { return self._s[2676]! } - public var SettingsSearch_Synonyms_Calls_Title: String { return self._s[2678]! } - public var GroupPermission_AddSuccess: String { return self._s[2679]! } - public var PhotoEditor_BlurToolRadial: String { return self._s[2681]! } - public var Conversation_ContextMenuCopy: String { return self._s[2682]! } - public var AccessDenied_CallMicrophone: String { return self._s[2683]! } + public var Passport_Address_TypeResidentialAddress: String { return self._s[2700]! } + public var Conversation_StatusLeftGroup: String { return self._s[2701]! } + public var SocksProxySetup_ProxyDetailsTitle: String { return self._s[2702]! } + public var SettingsSearch_Synonyms_Calls_Title: String { return self._s[2704]! } + public var GroupPermission_AddSuccess: String { return self._s[2705]! } + public var PhotoEditor_BlurToolRadial: String { return self._s[2707]! } + public var Conversation_ContextMenuCopy: String { return self._s[2708]! } + public var AccessDenied_CallMicrophone: String { return self._s[2709]! } public func Time_PreciseDate_m2(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2684]!, self._r[2684]!, [_1, _2, _3]) + return formatWithArgumentRanges(self._s[2710]!, self._r[2710]!, [_1, _2, _3]) } - public var Login_InvalidFirstNameError: String { return self._s[2685]! } - public var Notifications_Badge_CountUnreadMessages_InfoOn: String { return self._s[2686]! } - public var Checkout_PaymentMethod_New: String { return self._s[2687]! } - public var ShareMenu_CopyShareLinkGame: String { return self._s[2688]! } - public var PhotoEditor_QualityTool: String { return self._s[2689]! } - public var Login_SendCodeViaSms: String { return self._s[2690]! } - public var SettingsSearch_Synonyms_Privacy_DeleteAccountIfAwayFor: String { return self._s[2691]! } - public var Chat_SlowmodeAttachmentLimitReached: String { return self._s[2692]! } - public var Wallet_Receive_CopyAddress: String { return self._s[2693]! } - public var Login_EmailNotConfiguredError: String { return self._s[2694]! } - public var SocksProxySetup_Status: String { return self._s[2695]! } - public var Conversation_ScheduleMessage_SendWhenOnline: String { return self._s[2696]! } - public var PrivacyPolicy_Accept: String { return self._s[2697]! } - public var Notifications_ExceptionsMessagePlaceholder: String { return self._s[2698]! } - public var Appearance_AppIconClassicX: String { return self._s[2699]! } + public var Login_InvalidFirstNameError: String { return self._s[2711]! } + public var Notifications_Badge_CountUnreadMessages_InfoOn: String { return self._s[2712]! } + public var Checkout_PaymentMethod_New: String { return self._s[2713]! } + public var ShareMenu_CopyShareLinkGame: String { return self._s[2714]! } + public var PhotoEditor_QualityTool: String { return self._s[2715]! } + public var Login_SendCodeViaSms: String { return self._s[2716]! } + public var SettingsSearch_Synonyms_Privacy_DeleteAccountIfAwayFor: String { return self._s[2717]! } + public var Chat_SlowmodeAttachmentLimitReached: String { return self._s[2718]! } + public var Wallet_Receive_CopyAddress: String { return self._s[2719]! } + public var Login_EmailNotConfiguredError: String { return self._s[2720]! } + public var SocksProxySetup_Status: String { return self._s[2721]! } + public var Conversation_ScheduleMessage_SendWhenOnline: String { return self._s[2722]! } + public var PrivacyPolicy_Accept: String { return self._s[2723]! } + public var Notifications_ExceptionsMessagePlaceholder: String { return self._s[2724]! } + public var Appearance_AppIconClassicX: String { return self._s[2725]! } public func PUSH_CHAT_MESSAGE_TEXT(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2700]!, self._r[2700]!, [_1, _2, _3]) + return formatWithArgumentRanges(self._s[2726]!, self._r[2726]!, [_1, _2, _3]) } - public var OwnershipTransfer_SecurityRequirements: String { return self._s[2701]! } - public var InfoPlist_NSLocationAlwaysUsageDescription: String { return self._s[2703]! } - public var AutoNightTheme_Automatic: String { return self._s[2704]! } - public var Channel_Username_InvalidStartsWithNumber: String { return self._s[2705]! } - public var Privacy_ContactsSyncHelp: String { return self._s[2706]! } - public var Cache_Help: String { return self._s[2707]! } - public var Group_ErrorAccessDenied: String { return self._s[2708]! } - public var Passport_Language_fa: String { return self._s[2709]! } - public var Wallet_Intro_Text: String { return self._s[2710]! } - public var Login_ResetAccountProtected_TimerTitle: String { return self._s[2711]! } - public var VoiceOver_Chat_YourVideoMessage: String { return self._s[2712]! } - public var PrivacySettings_LastSeen: String { return self._s[2713]! } + public var OwnershipTransfer_SecurityRequirements: String { return self._s[2727]! } + public var InfoPlist_NSLocationAlwaysUsageDescription: String { return self._s[2729]! } + public var AutoNightTheme_Automatic: String { return self._s[2730]! } + public var Channel_Username_InvalidStartsWithNumber: String { return self._s[2731]! } + public var Privacy_ContactsSyncHelp: String { return self._s[2732]! } + public var Cache_Help: String { return self._s[2733]! } + public var Group_ErrorAccessDenied: String { return self._s[2734]! } + public var Passport_Language_fa: String { return self._s[2735]! } + public var Wallet_Intro_Text: String { return self._s[2736]! } + public var Login_ResetAccountProtected_TimerTitle: String { return self._s[2737]! } + public var VoiceOver_Chat_YourVideoMessage: String { return self._s[2738]! } + public var PrivacySettings_LastSeen: String { return self._s[2739]! } public func DialogList_MultipleTyping(_ _0: String, _ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2714]!, self._r[2714]!, [_0, _1]) + return formatWithArgumentRanges(self._s[2740]!, self._r[2740]!, [_0, _1]) } - public var Wallet_Configuration_Apply: String { return self._s[2718]! } - public var Preview_SaveGif: String { return self._s[2719]! } - public var SettingsSearch_Synonyms_Privacy_TwoStepAuth: String { return self._s[2720]! } - public var Profile_About: String { return self._s[2721]! } - public var Channel_About_Placeholder: String { return self._s[2722]! } - public var Login_InfoTitle: String { return self._s[2723]! } + public var Wallet_Configuration_Apply: String { return self._s[2744]! } + public var Preview_SaveGif: String { return self._s[2745]! } + public var SettingsSearch_Synonyms_Privacy_TwoStepAuth: String { return self._s[2746]! } + public var Profile_About: String { return self._s[2747]! } + public var Channel_About_Placeholder: String { return self._s[2748]! } + public var Login_InfoTitle: String { return self._s[2749]! } public func TwoStepAuth_SetupPendingEmail(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2724]!, self._r[2724]!, [_0]) + return formatWithArgumentRanges(self._s[2750]!, self._r[2750]!, [_0]) } - public var EditTheme_Expand_Preview_IncomingReplyText: String { return self._s[2725]! } - public var Watch_Suggestion_CantTalk: String { return self._s[2727]! } - public var ContactInfo_Title: String { return self._s[2728]! } - public var Media_ShareThisVideo: String { return self._s[2729]! } - public var Weekday_ShortFriday: String { return self._s[2730]! } - public var AccessDenied_Contacts: String { return self._s[2732]! } - public var Notification_CallIncomingShort: String { return self._s[2733]! } - public var Group_Setup_TypePublic: String { return self._s[2734]! } - public var Notifications_MessageNotificationsExceptions: String { return self._s[2735]! } - public var Notifications_Badge_IncludeChannels: String { return self._s[2736]! } - public var Notifications_MessageNotificationsPreview: String { return self._s[2739]! } - public var ConversationProfile_ErrorCreatingConversation: String { return self._s[2740]! } - public var Group_ErrorAddTooMuchBots: String { return self._s[2741]! } - public var Privacy_GroupsAndChannels_CustomShareHelp: String { return self._s[2742]! } - public var Permissions_CellularDataAllowInSettings_v0: String { return self._s[2743]! } + public var EditTheme_Expand_Preview_IncomingReplyText: String { return self._s[2751]! } + public var Watch_Suggestion_CantTalk: String { return self._s[2753]! } + public var ContactInfo_Title: String { return self._s[2754]! } + public var Media_ShareThisVideo: String { return self._s[2755]! } + public var Weekday_ShortFriday: String { return self._s[2756]! } + public var AccessDenied_Contacts: String { return self._s[2758]! } + public var Notification_CallIncomingShort: String { return self._s[2759]! } + public var Group_Setup_TypePublic: String { return self._s[2760]! } + public var Notifications_MessageNotificationsExceptions: String { return self._s[2761]! } + public var Notifications_Badge_IncludeChannels: String { return self._s[2762]! } + public var Notifications_MessageNotificationsPreview: String { return self._s[2765]! } + public var ConversationProfile_ErrorCreatingConversation: String { return self._s[2766]! } + public var Group_ErrorAddTooMuchBots: String { return self._s[2767]! } + public var Privacy_GroupsAndChannels_CustomShareHelp: String { return self._s[2768]! } + public var Permissions_CellularDataAllowInSettings_v0: String { return self._s[2769]! } public func Wallet_SecureStorageChanged_BiometryText(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2744]!, self._r[2744]!, [_0]) + return formatWithArgumentRanges(self._s[2770]!, self._r[2770]!, [_0]) } - public var DialogList_Typing: String { return self._s[2745]! } - public var CallFeedback_IncludeLogs: String { return self._s[2747]! } - public var Checkout_Phone: String { return self._s[2749]! } - public var Login_InfoFirstNamePlaceholder: String { return self._s[2752]! } - public var Privacy_Calls_Integration: String { return self._s[2753]! } - public var Notifications_PermissionsAllow: String { return self._s[2754]! } - public var TwoStepAuth_AddHintDescription: String { return self._s[2758]! } - public var Settings_ChatSettings: String { return self._s[2759]! } - public var Conversation_SendingOptionsTooltip: String { return self._s[2760]! } + public var DialogList_Typing: String { return self._s[2771]! } + public var CallFeedback_IncludeLogs: String { return self._s[2773]! } + public var Checkout_Phone: String { return self._s[2775]! } + public var Login_InfoFirstNamePlaceholder: String { return self._s[2778]! } + public var Privacy_Calls_Integration: String { return self._s[2779]! } + public var Notifications_PermissionsAllow: String { return self._s[2780]! } + public var TwoStepAuth_AddHintDescription: String { return self._s[2784]! } + public var Settings_ChatSettings: String { return self._s[2785]! } + public var Conversation_SendingOptionsTooltip: String { return self._s[2786]! } public func UserInfo_StartSecretChatConfirmation(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2762]!, self._r[2762]!, [_0]) + return formatWithArgumentRanges(self._s[2788]!, self._r[2788]!, [_0]) } public func Channel_AdminLog_MessageInvitedNameUsername(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2763]!, self._r[2763]!, [_1, _2]) + return formatWithArgumentRanges(self._s[2789]!, self._r[2789]!, [_1, _2]) } - public var GroupRemoved_DeleteUser: String { return self._s[2765]! } + public var GroupRemoved_DeleteUser: String { return self._s[2791]! } public func Channel_AdminLog_PollStopped(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2766]!, self._r[2766]!, [_0]) + return formatWithArgumentRanges(self._s[2792]!, self._r[2792]!, [_0]) } public func PUSH_MESSAGE_PHOTO(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2767]!, self._r[2767]!, [_1]) + return formatWithArgumentRanges(self._s[2793]!, self._r[2793]!, [_1]) } - public var Login_ContinueWithLocalization: String { return self._s[2768]! } - public var Watch_Message_ForwardedFrom: String { return self._s[2769]! } - public var TwoStepAuth_EnterEmailCode: String { return self._s[2771]! } - public var Conversation_Unblock: String { return self._s[2772]! } - public var PrivacySettings_DataSettings: String { return self._s[2773]! } - public var WallpaperPreview_PatternPaternApply: String { return self._s[2774]! } - public var Group_PublicLink_Info: String { return self._s[2775]! } + public var Login_ContinueWithLocalization: String { return self._s[2794]! } + public var Watch_Message_ForwardedFrom: String { return self._s[2795]! } + public var TwoStepAuth_EnterEmailCode: String { return self._s[2797]! } + public var Conversation_Unblock: String { return self._s[2798]! } + public var PrivacySettings_DataSettings: String { return self._s[2799]! } + public var WallpaperPreview_PatternPaternApply: String { return self._s[2800]! } + public var Group_PublicLink_Info: String { return self._s[2801]! } public func Wallet_Time_PreciseDate_m1(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2776]!, self._r[2776]!, [_1, _2, _3]) + return formatWithArgumentRanges(self._s[2802]!, self._r[2802]!, [_1, _2, _3]) } - public var Notifications_InAppNotificationsVibrate: String { return self._s[2777]! } + public var Notifications_InAppNotificationsVibrate: String { return self._s[2803]! } public func Privacy_GroupsAndChannels_InviteToChannelError(_ _0: String, _ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2778]!, self._r[2778]!, [_0, _1]) + return formatWithArgumentRanges(self._s[2804]!, self._r[2804]!, [_0, _1]) } - public var OldChannels_ChannelsHeader: String { return self._s[2780]! } - public var Wallet_RestoreFailed_CreateWallet: String { return self._s[2781]! } - public var PrivacySettings_Passcode: String { return self._s[2783]! } - public var Call_Mute: String { return self._s[2784]! } - public var Wallet_Weekday_Yesterday: String { return self._s[2785]! } - public var Passport_Language_dz: String { return self._s[2786]! } - public var Wallet_Receive_AmountHeader: String { return self._s[2787]! } - public var Wallet_TransactionInfo_OtherFeeInfoUrl: String { return self._s[2788]! } - public var Passport_Language_tk: String { return self._s[2789]! } + public var OldChannels_ChannelsHeader: String { return self._s[2806]! } + public var Wallet_RestoreFailed_CreateWallet: String { return self._s[2807]! } + public var PrivacySettings_Passcode: String { return self._s[2809]! } + public var Call_Mute: String { return self._s[2810]! } + public var Wallet_Weekday_Yesterday: String { return self._s[2811]! } + public var Passport_Language_dz: String { return self._s[2812]! } + public var Wallet_Receive_AmountHeader: String { return self._s[2813]! } + public var Wallet_TransactionInfo_OtherFeeInfoUrl: String { return self._s[2814]! } + public var Passport_Language_tk: String { return self._s[2815]! } public func Login_EmailCodeSubject(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2790]!, self._r[2790]!, [_0]) + return formatWithArgumentRanges(self._s[2816]!, self._r[2816]!, [_0]) } - public var Settings_Search: String { return self._s[2791]! } - public var Wallet_Month_ShortFebruary: String { return self._s[2792]! } - public var InfoPlist_NSPhotoLibraryUsageDescription: String { return self._s[2793]! } - public var Wallet_Configuration_SourceJSON: String { return self._s[2794]! } - public var Conversation_ContextMenuReply: String { return self._s[2795]! } - public var WallpaperSearch_ColorBrown: String { return self._s[2796]! } - public var Chat_AttachmentMultipleForwardDisabled: String { return self._s[2797]! } - public var Tour_Title1: String { return self._s[2798]! } - public var Wallet_Alert_Cancel: String { return self._s[2799]! } - public var Conversation_ClearGroupHistory: String { return self._s[2801]! } - public var Wallet_TransactionInfo_RecipientHeader: String { return self._s[2802]! } - public var WallpaperPreview_Motion: String { return self._s[2803]! } + public var Settings_Search: String { return self._s[2817]! } + public var Wallet_Month_ShortFebruary: String { return self._s[2818]! } + public var InfoPlist_NSPhotoLibraryUsageDescription: String { return self._s[2819]! } + public var Wallet_Configuration_SourceJSON: String { return self._s[2820]! } + public var Conversation_ContextMenuReply: String { return self._s[2821]! } + public var WallpaperSearch_ColorBrown: String { return self._s[2822]! } + public var Chat_AttachmentMultipleForwardDisabled: String { return self._s[2823]! } + public var Tour_Title1: String { return self._s[2824]! } + public var Wallet_Alert_Cancel: String { return self._s[2825]! } + public var Conversation_ClearGroupHistory: String { return self._s[2827]! } + public var Wallet_TransactionInfo_RecipientHeader: String { return self._s[2828]! } + public var WallpaperPreview_Motion: String { return self._s[2829]! } public func Checkout_PasswordEntry_Text(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2804]!, self._r[2804]!, [_0]) + return formatWithArgumentRanges(self._s[2830]!, self._r[2830]!, [_0]) } - public var Wallet_Configuration_ApplyErrorTextJSONInvalidData: String { return self._s[2805]! } - public var Call_RateCall: String { return self._s[2806]! } - public var Channel_AdminLog_BanSendStickersAndGifs: String { return self._s[2807]! } - public var Passport_PasswordCompleteSetup: String { return self._s[2808]! } - public var Conversation_InputTextSilentBroadcastPlaceholder: String { return self._s[2809]! } - public var UserInfo_LastNamePlaceholder: String { return self._s[2811]! } + public var Wallet_Configuration_ApplyErrorTextJSONInvalidData: String { return self._s[2831]! } + public var Call_RateCall: String { return self._s[2832]! } + public var Channel_AdminLog_BanSendStickersAndGifs: String { return self._s[2833]! } + public var Passport_PasswordCompleteSetup: String { return self._s[2834]! } + public var Conversation_InputTextSilentBroadcastPlaceholder: String { return self._s[2835]! } + public var UserInfo_LastNamePlaceholder: String { return self._s[2837]! } public func Login_WillCallYou(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2813]!, self._r[2813]!, [_0]) + return formatWithArgumentRanges(self._s[2839]!, self._r[2839]!, [_0]) } - public var Compose_Create: String { return self._s[2814]! } - public var Contacts_InviteToTelegram: String { return self._s[2815]! } - public var GroupInfo_Notifications: String { return self._s[2816]! } - public var ChatList_DeleteSavedMessagesConfirmationAction: String { return self._s[2818]! } - public var Message_PinnedLiveLocationMessage: String { return self._s[2819]! } - public var Month_GenApril: String { return self._s[2820]! } - public var Appearance_AutoNightTheme: String { return self._s[2821]! } - public var ChatSettings_AutomaticAudioDownload: String { return self._s[2823]! } - public var Login_CodeSentSms: String { return self._s[2825]! } + public var Compose_Create: String { return self._s[2840]! } + public var Contacts_InviteToTelegram: String { return self._s[2841]! } + public var GroupInfo_Notifications: String { return self._s[2842]! } + public var ChatList_DeleteSavedMessagesConfirmationAction: String { return self._s[2844]! } + public var Message_PinnedLiveLocationMessage: String { return self._s[2845]! } + public var Month_GenApril: String { return self._s[2846]! } + public var Appearance_AutoNightTheme: String { return self._s[2847]! } + public var ChatSettings_AutomaticAudioDownload: String { return self._s[2849]! } + public var Login_CodeSentSms: String { return self._s[2851]! } public func UserInfo_UnblockConfirmation(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2826]!, self._r[2826]!, [_0]) + return formatWithArgumentRanges(self._s[2852]!, self._r[2852]!, [_0]) } - public var EmptyGroupInfo_Line3: String { return self._s[2827]! } - public var LogoutOptions_ContactSupportText: String { return self._s[2828]! } - public var Passport_Language_hr: String { return self._s[2829]! } - public var Common_ActionNotAllowedError: String { return self._s[2830]! } + public var EmptyGroupInfo_Line3: String { return self._s[2853]! } + public var LogoutOptions_ContactSupportText: String { return self._s[2854]! } + public var Passport_Language_hr: String { return self._s[2855]! } + public var Common_ActionNotAllowedError: String { return self._s[2856]! } public func Channel_AdminLog_MessageRestrictedNewSetting(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2831]!, self._r[2831]!, [_0]) + return formatWithArgumentRanges(self._s[2857]!, self._r[2857]!, [_0]) } - public var GroupInfo_InviteLink_CopyLink: String { return self._s[2832]! } - public var Wallet_Info_TransactionFrom: String { return self._s[2833]! } - public var Wallet_Send_ErrorDecryptionFailed: String { return self._s[2834]! } - public var Conversation_InputTextBroadcastPlaceholder: String { return self._s[2835]! } - public var Privacy_SecretChatsTitle: String { return self._s[2836]! } - public var Notification_SecretChatMessageScreenshotSelf: String { return self._s[2838]! } - public var GroupInfo_AddUserLeftError: String { return self._s[2839]! } - public var AutoDownloadSettings_TypePrivateChats: String { return self._s[2840]! } - public var LogoutOptions_ContactSupportTitle: String { return self._s[2841]! } - public var Appearance_ThemePreview_Chat_7_Text: String { return self._s[2842]! } - public var Channel_AddBotErrorHaveRights: String { return self._s[2843]! } - public var Preview_DeleteGif: String { return self._s[2844]! } - public var GroupInfo_Permissions_Exceptions: String { return self._s[2845]! } - public var Group_ErrorNotMutualContact: String { return self._s[2846]! } - public var Notification_MessageLifetime5s: String { return self._s[2847]! } - public var Wallet_Send_OwnAddressAlertText: String { return self._s[2848]! } - public var OldChannels_ChannelFormat: String { return self._s[2849]! } + public var GroupInfo_InviteLink_CopyLink: String { return self._s[2858]! } + public var Wallet_Info_TransactionFrom: String { return self._s[2859]! } + public var Wallet_Send_ErrorDecryptionFailed: String { return self._s[2860]! } + public var Conversation_InputTextBroadcastPlaceholder: String { return self._s[2861]! } + public var Privacy_SecretChatsTitle: String { return self._s[2862]! } + public var Notification_SecretChatMessageScreenshotSelf: String { return self._s[2864]! } + public var GroupInfo_AddUserLeftError: String { return self._s[2865]! } + public var AutoDownloadSettings_TypePrivateChats: String { return self._s[2866]! } + public var LogoutOptions_ContactSupportTitle: String { return self._s[2867]! } + public var Appearance_ThemePreview_Chat_7_Text: String { return self._s[2868]! } + public var Channel_AddBotErrorHaveRights: String { return self._s[2869]! } + public var Preview_DeleteGif: String { return self._s[2870]! } + public var GroupInfo_Permissions_Exceptions: String { return self._s[2871]! } + public var Group_ErrorNotMutualContact: String { return self._s[2872]! } + public var Notification_MessageLifetime5s: String { return self._s[2873]! } + public var Wallet_Send_OwnAddressAlertText: String { return self._s[2874]! } + public var OldChannels_ChannelFormat: String { return self._s[2875]! } public func Watch_LastSeen_AtDate(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2850]!, self._r[2850]!, [_0]) + return formatWithArgumentRanges(self._s[2876]!, self._r[2876]!, [_0]) } - public var VoiceOver_Chat_Video: String { return self._s[2851]! } - public var Channel_OwnershipTransfer_ErrorPublicChannelsTooMuch: String { return self._s[2853]! } - public var ReportSpam_DeleteThisChat: String { return self._s[2854]! } - public var Passport_Address_AddBankStatement: String { return self._s[2855]! } - public var Notification_CallIncoming: String { return self._s[2856]! } - public var Wallet_Words_NotDoneTitle: String { return self._s[2857]! } - public var Compose_NewGroupTitle: String { return self._s[2858]! } - public var TwoStepAuth_RecoveryCodeHelp: String { return self._s[2860]! } - public var Passport_Address_Postcode: String { return self._s[2862]! } + public var VoiceOver_Chat_Video: String { return self._s[2877]! } + public var Channel_OwnershipTransfer_ErrorPublicChannelsTooMuch: String { return self._s[2879]! } + public var ReportSpam_DeleteThisChat: String { return self._s[2880]! } + public var Passport_Address_AddBankStatement: String { return self._s[2881]! } + public var Notification_CallIncoming: String { return self._s[2882]! } + public var Wallet_Words_NotDoneTitle: String { return self._s[2883]! } + public var Compose_NewGroupTitle: String { return self._s[2884]! } + public var TwoStepAuth_RecoveryCodeHelp: String { return self._s[2886]! } + public var Passport_Address_Postcode: String { return self._s[2888]! } public func LastSeen_YesterdayAt(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2863]!, self._r[2863]!, [_0]) + return formatWithArgumentRanges(self._s[2889]!, self._r[2889]!, [_0]) } - public var Checkout_NewCard_SaveInfoHelp: String { return self._s[2864]! } - public var Wallet_Month_ShortOctober: String { return self._s[2865]! } - public var VoiceOver_Chat_YourMusic: String { return self._s[2866]! } - public var WallpaperColors_Title: String { return self._s[2867]! } - public var SocksProxySetup_ShareQRCodeInfo: String { return self._s[2868]! } - public var VoiceOver_MessageContextForward: String { return self._s[2869]! } - public var GroupPermission_Duration: String { return self._s[2870]! } + public var Checkout_NewCard_SaveInfoHelp: String { return self._s[2890]! } + public var Wallet_Month_ShortOctober: String { return self._s[2891]! } + public var VoiceOver_Chat_YourMusic: String { return self._s[2892]! } + public var WallpaperColors_Title: String { return self._s[2893]! } + public var SocksProxySetup_ShareQRCodeInfo: String { return self._s[2894]! } + public var VoiceOver_MessageContextForward: String { return self._s[2895]! } + public var GroupPermission_Duration: String { return self._s[2896]! } public func Cache_Clear(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2871]!, self._r[2871]!, [_0]) + return formatWithArgumentRanges(self._s[2897]!, self._r[2897]!, [_0]) } - public var Bot_GroupStatusDoesNotReadHistory: String { return self._s[2872]! } - public var Username_Placeholder: String { return self._s[2873]! } - public var CallFeedback_WhatWentWrong: String { return self._s[2874]! } - public var Passport_FieldAddressUploadHelp: String { return self._s[2875]! } - public var Permissions_NotificationsAllowInSettings_v0: String { return self._s[2876]! } + public var Bot_GroupStatusDoesNotReadHistory: String { return self._s[2898]! } + public var Username_Placeholder: String { return self._s[2899]! } + public var CallFeedback_WhatWentWrong: String { return self._s[2900]! } + public var Passport_FieldAddressUploadHelp: String { return self._s[2901]! } + public var Permissions_NotificationsAllowInSettings_v0: String { return self._s[2902]! } public func Channel_AdminLog_MessageChangedUnlinkedChannel(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2878]!, self._r[2878]!, [_1, _2]) + return formatWithArgumentRanges(self._s[2904]!, self._r[2904]!, [_1, _2]) } - public var Passport_PasswordDescription: String { return self._s[2879]! } - public var Channel_MessagePhotoUpdated: String { return self._s[2880]! } - public var MediaPicker_TapToUngroupDescription: String { return self._s[2881]! } - public var SettingsSearch_Synonyms_Notifications_BadgeCountUnreadMessages: String { return self._s[2882]! } - public var AttachmentMenu_PhotoOrVideo: String { return self._s[2883]! } - public var Conversation_ContextMenuMore: String { return self._s[2884]! } - public var Privacy_PaymentsClearInfo: String { return self._s[2885]! } - public var CallSettings_TabIcon: String { return self._s[2886]! } - public var KeyCommand_Find: String { return self._s[2887]! } - public var ClearCache_FreeSpaceDescription: String { return self._s[2888]! } - public var Appearance_ThemePreview_ChatList_7_Text: String { return self._s[2889]! } - public var EditTheme_Edit_Preview_IncomingText: String { return self._s[2890]! } - public var Message_PinnedGame: String { return self._s[2891]! } - public var VoiceOver_Chat_ForwardedFromYou: String { return self._s[2892]! } - public var Notifications_Badge_CountUnreadMessages_InfoOff: String { return self._s[2894]! } - public var Login_CallRequestState2: String { return self._s[2896]! } - public var CheckoutInfo_ReceiverInfoNamePlaceholder: String { return self._s[2898]! } + public var Passport_PasswordDescription: String { return self._s[2905]! } + public var Channel_MessagePhotoUpdated: String { return self._s[2906]! } + public var MediaPicker_TapToUngroupDescription: String { return self._s[2907]! } + public var SettingsSearch_Synonyms_Notifications_BadgeCountUnreadMessages: String { return self._s[2908]! } + public var AttachmentMenu_PhotoOrVideo: String { return self._s[2909]! } + public var Conversation_ContextMenuMore: String { return self._s[2910]! } + public var Privacy_PaymentsClearInfo: String { return self._s[2911]! } + public var CallSettings_TabIcon: String { return self._s[2912]! } + public var KeyCommand_Find: String { return self._s[2913]! } + public var ClearCache_FreeSpaceDescription: String { return self._s[2914]! } + public var Appearance_ThemePreview_ChatList_7_Text: String { return self._s[2915]! } + public var EditTheme_Edit_Preview_IncomingText: String { return self._s[2916]! } + public var Message_PinnedGame: String { return self._s[2917]! } + public var VoiceOver_Chat_ForwardedFromYou: String { return self._s[2918]! } + public var Notifications_Badge_CountUnreadMessages_InfoOff: String { return self._s[2920]! } + public var Login_CallRequestState2: String { return self._s[2922]! } + public var CheckoutInfo_ReceiverInfoNamePlaceholder: String { return self._s[2924]! } public func VoiceOver_Chat_PhotoFrom(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2899]!, self._r[2899]!, [_0]) + return formatWithArgumentRanges(self._s[2925]!, self._r[2925]!, [_0]) } public func Checkout_PayPrice(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2901]!, self._r[2901]!, [_0]) + return formatWithArgumentRanges(self._s[2927]!, self._r[2927]!, [_0]) } - public var AuthSessions_AddDevice: String { return self._s[2902]! } - public var WallpaperPreview_Blurred: String { return self._s[2903]! } - public var Conversation_InstantPagePreview: String { return self._s[2904]! } + public var AuthSessions_AddDevice: String { return self._s[2928]! } + public var WallpaperPreview_Blurred: String { return self._s[2929]! } + public var Conversation_InstantPagePreview: String { return self._s[2930]! } public func DialogList_SingleUploadingVideoSuffix(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2905]!, self._r[2905]!, [_0]) + return formatWithArgumentRanges(self._s[2931]!, self._r[2931]!, [_0]) } - public var SecretTimer_VideoDescription: String { return self._s[2908]! } - public var WallpaperSearch_ColorRed: String { return self._s[2909]! } - public var GroupPermission_NoPinMessages: String { return self._s[2910]! } - public var Passport_Language_es: String { return self._s[2911]! } - public var Permissions_ContactsAllow_v0: String { return self._s[2913]! } - public var Conversation_EditingMessageMediaEditCurrentVideo: String { return self._s[2914]! } + public var SecretTimer_VideoDescription: String { return self._s[2934]! } + public var WallpaperSearch_ColorRed: String { return self._s[2935]! } + public var GroupPermission_NoPinMessages: String { return self._s[2936]! } + public var Passport_Language_es: String { return self._s[2937]! } + public var Permissions_ContactsAllow_v0: String { return self._s[2939]! } + public var Conversation_EditingMessageMediaEditCurrentVideo: String { return self._s[2940]! } public func PUSH_CHAT_MESSAGE_CONTACT(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2915]!, self._r[2915]!, [_1, _2]) + return formatWithArgumentRanges(self._s[2941]!, self._r[2941]!, [_1, _2]) } - public var Privacy_Forwards_CustomHelp: String { return self._s[2916]! } - public var WebPreview_GettingLinkInfo: String { return self._s[2917]! } - public var Watch_UserInfo_Unmute: String { return self._s[2918]! } - public var GroupInfo_ChannelListNamePlaceholder: String { return self._s[2919]! } - public var AccessDenied_CameraRestricted: String { return self._s[2921]! } + public var Privacy_Forwards_CustomHelp: String { return self._s[2942]! } + public var WebPreview_GettingLinkInfo: String { return self._s[2943]! } + public var Watch_UserInfo_Unmute: String { return self._s[2944]! } + public var GroupInfo_ChannelListNamePlaceholder: String { return self._s[2945]! } + public var AccessDenied_CameraRestricted: String { return self._s[2947]! } public func Conversation_Kilobytes(_ _0: Int) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2922]!, self._r[2922]!, ["\(_0)"]) + return formatWithArgumentRanges(self._s[2948]!, self._r[2948]!, ["\(_0)"]) } - public var ChatList_ReadAll: String { return self._s[2924]! } - public var Settings_CopyUsername: String { return self._s[2925]! } - public var Contacts_SearchLabel: String { return self._s[2926]! } - public var Map_OpenInYandexNavigator: String { return self._s[2928]! } - public var PasscodeSettings_EncryptData: String { return self._s[2929]! } - public var Settings_Wallet: String { return self._s[2930]! } - public var Group_ErrorSupergroupConversionNotPossible: String { return self._s[2931]! } - public var WallpaperSearch_ColorPrefix: String { return self._s[2932]! } - public var Notifications_GroupNotificationsPreview: String { return self._s[2933]! } - public var DialogList_AdNoticeAlert: String { return self._s[2934]! } - public var Wallet_Month_GenMay: String { return self._s[2936]! } - public var CheckoutInfo_ShippingInfoAddress1: String { return self._s[2937]! } - public var CheckoutInfo_ShippingInfoAddress2: String { return self._s[2938]! } - public var Localization_LanguageCustom: String { return self._s[2939]! } - public var Passport_Identity_TypeDriversLicenseUploadScan: String { return self._s[2940]! } - public var CallFeedback_Title: String { return self._s[2941]! } - public var VoiceOver_Chat_RecordPreviewVoiceMessage: String { return self._s[2944]! } - public var Passport_Address_OneOfTypePassportRegistration: String { return self._s[2945]! } - public var Wallet_Intro_CreateErrorTitle: String { return self._s[2946]! } - public var Conversation_InfoGroup: String { return self._s[2947]! } - public var Compose_NewMessage: String { return self._s[2948]! } - public var FastTwoStepSetup_HintPlaceholder: String { return self._s[2949]! } - public var ChatSettings_AutoDownloadVideoMessages: String { return self._s[2950]! } - public var Wallet_SecureStorageReset_BiometryFaceId: String { return self._s[2951]! } - public var Channel_DiscussionGroup_UnlinkChannel: String { return self._s[2952]! } + public var ChatList_ReadAll: String { return self._s[2950]! } + public var Settings_CopyUsername: String { return self._s[2951]! } + public var Contacts_SearchLabel: String { return self._s[2952]! } + public var Map_OpenInYandexNavigator: String { return self._s[2954]! } + public var PasscodeSettings_EncryptData: String { return self._s[2955]! } + public var Settings_Wallet: String { return self._s[2956]! } + public var Group_ErrorSupergroupConversionNotPossible: String { return self._s[2957]! } + public var WallpaperSearch_ColorPrefix: String { return self._s[2958]! } + public var Notifications_GroupNotificationsPreview: String { return self._s[2959]! } + public var DialogList_AdNoticeAlert: String { return self._s[2960]! } + public var Wallet_Month_GenMay: String { return self._s[2962]! } + public var CheckoutInfo_ShippingInfoAddress1: String { return self._s[2963]! } + public var CheckoutInfo_ShippingInfoAddress2: String { return self._s[2964]! } + public var Localization_LanguageCustom: String { return self._s[2965]! } + public var Passport_Identity_TypeDriversLicenseUploadScan: String { return self._s[2966]! } + public var CallFeedback_Title: String { return self._s[2967]! } + public var VoiceOver_Chat_RecordPreviewVoiceMessage: String { return self._s[2970]! } + public var Passport_Address_OneOfTypePassportRegistration: String { return self._s[2971]! } + public var Wallet_Intro_CreateErrorTitle: String { return self._s[2972]! } + public var Conversation_InfoGroup: String { return self._s[2973]! } + public var Compose_NewMessage: String { return self._s[2974]! } + public var FastTwoStepSetup_HintPlaceholder: String { return self._s[2975]! } + public var ChatSettings_AutoDownloadVideoMessages: String { return self._s[2976]! } + public var Wallet_SecureStorageReset_BiometryFaceId: String { return self._s[2977]! } + public var Channel_DiscussionGroup_UnlinkChannel: String { return self._s[2978]! } public func Passport_Scans_ScanIndex(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2953]!, self._r[2953]!, [_0]) + return formatWithArgumentRanges(self._s[2979]!, self._r[2979]!, [_0]) } - public var Channel_AdminLog_CanDeleteMessages: String { return self._s[2954]! } - public var Login_CancelSignUpConfirmation: String { return self._s[2955]! } - public var ChangePhoneNumberCode_Help: String { return self._s[2956]! } - public var PrivacySettings_DeleteAccountHelp: String { return self._s[2957]! } - public var Channel_BlackList_Title: String { return self._s[2958]! } - public var UserInfo_PhoneCall: String { return self._s[2959]! } - public var Passport_Address_OneOfTypeBankStatement: String { return self._s[2961]! } - public var Wallet_Month_ShortJanuary: String { return self._s[2962]! } - public var State_connecting: String { return self._s[2963]! } - public var Appearance_ThemePreview_ChatList_6_Text: String { return self._s[2964]! } - public var Wallet_Month_GenMarch: String { return self._s[2965]! } - public var EditTheme_Expand_BottomInfo: String { return self._s[2966]! } - public var AuthSessions_AddedDeviceTerminate: String { return self._s[2967]! } + public var Channel_AdminLog_CanDeleteMessages: String { return self._s[2980]! } + public var Login_CancelSignUpConfirmation: String { return self._s[2981]! } + public var ChangePhoneNumberCode_Help: String { return self._s[2982]! } + public var PrivacySettings_DeleteAccountHelp: String { return self._s[2983]! } + public var Channel_BlackList_Title: String { return self._s[2984]! } + public var UserInfo_PhoneCall: String { return self._s[2985]! } + public var Passport_Address_OneOfTypeBankStatement: String { return self._s[2987]! } + public var Wallet_Month_ShortJanuary: String { return self._s[2988]! } + public var State_connecting: String { return self._s[2989]! } + public var Appearance_ThemePreview_ChatList_6_Text: String { return self._s[2990]! } + public var Wallet_Month_GenMarch: String { return self._s[2991]! } + public var EditTheme_Expand_BottomInfo: String { return self._s[2992]! } + public var AuthSessions_AddedDeviceTerminate: String { return self._s[2993]! } public func LastSeen_TodayAt(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2968]!, self._r[2968]!, [_0]) + return formatWithArgumentRanges(self._s[2994]!, self._r[2994]!, [_0]) } public func DialogList_SingleRecordingAudioSuffix(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2969]!, self._r[2969]!, [_0]) + return formatWithArgumentRanges(self._s[2995]!, self._r[2995]!, [_0]) } - public var Notifications_GroupNotifications: String { return self._s[2970]! } - public var Conversation_SendMessageErrorTooMuchScheduled: String { return self._s[2971]! } - public var Passport_Identity_EditPassport: String { return self._s[2972]! } - public var EnterPasscode_RepeatNewPasscode: String { return self._s[2974]! } - public var Localization_EnglishLanguageName: String { return self._s[2975]! } - public var Share_AuthDescription: String { return self._s[2976]! } - public var SettingsSearch_Synonyms_Notifications_ChannelNotificationsAlert: String { return self._s[2977]! } - public var Passport_Identity_Surname: String { return self._s[2978]! } - public var Compose_TokenListPlaceholder: String { return self._s[2979]! } - public var Wallet_AccessDenied_Camera: String { return self._s[2980]! } - public var Passport_Identity_OneOfTypePassport: String { return self._s[2981]! } - public var Settings_AboutEmpty: String { return self._s[2982]! } - public var Conversation_Unmute: String { return self._s[2983]! } - public var CreateGroup_ChannelsTooMuch: String { return self._s[2985]! } - public var Wallet_Sending_Text: String { return self._s[2986]! } + public var Notifications_GroupNotifications: String { return self._s[2996]! } + public var Conversation_SendMessageErrorTooMuchScheduled: String { return self._s[2997]! } + public var Passport_Identity_EditPassport: String { return self._s[2998]! } + public var EnterPasscode_RepeatNewPasscode: String { return self._s[3000]! } + public var Localization_EnglishLanguageName: String { return self._s[3001]! } + public var Share_AuthDescription: String { return self._s[3002]! } + public var SettingsSearch_Synonyms_Notifications_ChannelNotificationsAlert: String { return self._s[3003]! } + public var Passport_Identity_Surname: String { return self._s[3004]! } + public var Compose_TokenListPlaceholder: String { return self._s[3005]! } + public var Wallet_AccessDenied_Camera: String { return self._s[3006]! } + public var Passport_Identity_OneOfTypePassport: String { return self._s[3007]! } + public var Settings_AboutEmpty: String { return self._s[3008]! } + public var Conversation_Unmute: String { return self._s[3009]! } + public var CreateGroup_ChannelsTooMuch: String { return self._s[3011]! } + public var Wallet_Sending_Text: String { return self._s[3012]! } public func PUSH_CONTACT_JOINED(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2987]!, self._r[2987]!, [_1]) + return formatWithArgumentRanges(self._s[3013]!, self._r[3013]!, [_1]) } - public var Login_CodeSentCall: String { return self._s[2988]! } - public var ContactInfo_PhoneLabelHomeFax: String { return self._s[2990]! } - public var ChatSettings_Appearance: String { return self._s[2991]! } - public var ClearCache_StorageUsage: String { return self._s[2992]! } - public var Appearance_PickAccentColor: String { return self._s[2993]! } + public var Login_CodeSentCall: String { return self._s[3014]! } + public var ContactInfo_PhoneLabelHomeFax: String { return self._s[3016]! } + public var ChatSettings_Appearance: String { return self._s[3017]! } + public var ClearCache_StorageUsage: String { return self._s[3018]! } + public var Appearance_PickAccentColor: String { return self._s[3019]! } public func PUSH_CHAT_MESSAGE_NOTEXT(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2994]!, self._r[2994]!, [_1, _2]) + return formatWithArgumentRanges(self._s[3020]!, self._r[3020]!, [_1, _2]) } public func PUSH_MESSAGE_GEO(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2995]!, self._r[2995]!, [_1]) + return formatWithArgumentRanges(self._s[3021]!, self._r[3021]!, [_1]) } - public var Notification_CallMissed: String { return self._s[2996]! } - public var SettingsSearch_Synonyms_Appearance_ChatBackground_Custom: String { return self._s[2997]! } - public var Channel_AdminLogFilter_EventsInfo: String { return self._s[2998]! } - public var Wallet_Month_GenOctober: String { return self._s[3000]! } - public var ChatAdmins_AdminLabel: String { return self._s[3001]! } - public var KeyCommand_JumpToNextChat: String { return self._s[3002]! } - public var Conversation_StopPollConfirmationTitle: String { return self._s[3004]! } - public var ChangePhoneNumberCode_CodePlaceholder: String { return self._s[3005]! } - public var Month_GenJune: String { return self._s[3006]! } - public var IntentsSettings_MainAccountInfo: String { return self._s[3007]! } - public var Watch_Location_Current: String { return self._s[3008]! } - public var Wallet_Receive_CopyInvoiceUrl: String { return self._s[3009]! } - public var Conversation_TitleMute: String { return self._s[3010]! } + public var Notification_CallMissed: String { return self._s[3022]! } + public var SettingsSearch_Synonyms_Appearance_ChatBackground_Custom: String { return self._s[3023]! } + public var Channel_AdminLogFilter_EventsInfo: String { return self._s[3024]! } + public var Wallet_Month_GenOctober: String { return self._s[3026]! } + public var ChatAdmins_AdminLabel: String { return self._s[3027]! } + public var KeyCommand_JumpToNextChat: String { return self._s[3028]! } + public var Conversation_StopPollConfirmationTitle: String { return self._s[3030]! } + public var ChangePhoneNumberCode_CodePlaceholder: String { return self._s[3031]! } + public var Month_GenJune: String { return self._s[3032]! } + public var IntentsSettings_MainAccountInfo: String { return self._s[3033]! } + public var Watch_Location_Current: String { return self._s[3034]! } + public var Wallet_Receive_CopyInvoiceUrl: String { return self._s[3035]! } + public var Conversation_TitleMute: String { return self._s[3036]! } + public var Map_PlacesInThisArea: String { return self._s[3037]! } public func PUSH_CHANNEL_MESSAGE_ROUND(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3011]!, self._r[3011]!, [_1]) + return formatWithArgumentRanges(self._s[3038]!, self._r[3038]!, [_1]) } - public var GroupInfo_DeleteAndExit: String { return self._s[3012]! } + public var GroupInfo_DeleteAndExit: String { return self._s[3039]! } public func Conversation_Moderate_DeleteAllMessages(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3013]!, self._r[3013]!, [_0]) + return formatWithArgumentRanges(self._s[3040]!, self._r[3040]!, [_0]) } - public var Call_ReportPlaceholder: String { return self._s[3014]! } - public var Chat_SlowmodeSendError: String { return self._s[3015]! } - public var MaskStickerSettings_Info: String { return self._s[3016]! } - public var EditTheme_Expand_TopInfo: String { return self._s[3017]! } + public var Call_ReportPlaceholder: String { return self._s[3041]! } + public var Chat_SlowmodeSendError: String { return self._s[3042]! } + public var MaskStickerSettings_Info: String { return self._s[3043]! } + public var EditTheme_Expand_TopInfo: String { return self._s[3044]! } public func GroupInfo_AddParticipantConfirmation(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3018]!, self._r[3018]!, [_0]) + return formatWithArgumentRanges(self._s[3045]!, self._r[3045]!, [_0]) } - public var Checkout_NewCard_PostcodeTitle: String { return self._s[3019]! } - public var Passport_Address_RegionPlaceholder: String { return self._s[3021]! } - public var Contacts_ShareTelegram: String { return self._s[3022]! } - public var EnterPasscode_EnterNewPasscodeNew: String { return self._s[3023]! } - public var Map_AddressOnMap: String { return self._s[3024]! } - public var Channel_ErrorAccessDenied: String { return self._s[3025]! } - public var UserInfo_ScamBotWarning: String { return self._s[3027]! } - public var Stickers_GroupChooseStickerPack: String { return self._s[3028]! } - public var Call_ConnectionErrorTitle: String { return self._s[3029]! } - public var UserInfo_NotificationsEnable: String { return self._s[3030]! } - public var ArchivedChats_IntroText1: String { return self._s[3031]! } - public var Tour_Text4: String { return self._s[3034]! } - public var WallpaperSearch_Recent: String { return self._s[3035]! } - public var GroupInfo_ScamGroupWarning: String { return self._s[3036]! } - public var Profile_MessageLifetime2s: String { return self._s[3038]! } - public var Appearance_ThemePreview_ChatList_5_Text: String { return self._s[3039]! } - public var Notification_MessageLifetime2s: String { return self._s[3040]! } + public var Checkout_NewCard_PostcodeTitle: String { return self._s[3046]! } + public var Passport_Address_RegionPlaceholder: String { return self._s[3048]! } + public var Contacts_ShareTelegram: String { return self._s[3049]! } + public var EnterPasscode_EnterNewPasscodeNew: String { return self._s[3050]! } + public var Map_AddressOnMap: String { return self._s[3051]! } + public var Channel_ErrorAccessDenied: String { return self._s[3052]! } + public var UserInfo_ScamBotWarning: String { return self._s[3054]! } + public var Stickers_GroupChooseStickerPack: String { return self._s[3055]! } + public var Call_ConnectionErrorTitle: String { return self._s[3056]! } + public var UserInfo_NotificationsEnable: String { return self._s[3057]! } + public var ArchivedChats_IntroText1: String { return self._s[3058]! } + public var Tour_Text4: String { return self._s[3061]! } + public var WallpaperSearch_Recent: String { return self._s[3062]! } + public var GroupInfo_ScamGroupWarning: String { return self._s[3063]! } + public var Profile_MessageLifetime2s: String { return self._s[3065]! } + public var Appearance_ThemePreview_ChatList_5_Text: String { return self._s[3066]! } + public var Notification_MessageLifetime2s: String { return self._s[3067]! } public func Time_PreciseDate_m10(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3041]!, self._r[3041]!, [_1, _2, _3]) + return formatWithArgumentRanges(self._s[3068]!, self._r[3068]!, [_1, _2, _3]) } - public var Cache_ClearCache: String { return self._s[3042]! } - public var AutoNightTheme_UpdateLocation: String { return self._s[3043]! } - public var Permissions_NotificationsUnreachableText_v0: String { return self._s[3044]! } + public var Cache_ClearCache: String { return self._s[3069]! } + public var AutoNightTheme_UpdateLocation: String { return self._s[3070]! } + public var Permissions_NotificationsUnreachableText_v0: String { return self._s[3071]! } public func Channel_AdminLog_MessageChangedGroupUsername(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3048]!, self._r[3048]!, [_0]) + return formatWithArgumentRanges(self._s[3075]!, self._r[3075]!, [_0]) } public func Conversation_ShareMyPhoneNumber_StatusSuccess(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3050]!, self._r[3050]!, [_0]) + return formatWithArgumentRanges(self._s[3077]!, self._r[3077]!, [_0]) } - public var LocalGroup_Text: String { return self._s[3051]! } - public var Channel_AdminLog_EmptyFilterTitle: String { return self._s[3052]! } - public var SocksProxySetup_TypeSocks: String { return self._s[3053]! } - public var ChatList_UnarchiveAction: String { return self._s[3054]! } - public var AutoNightTheme_Title: String { return self._s[3055]! } - public var InstantPage_FeedbackButton: String { return self._s[3056]! } - public var Passport_FieldAddress: String { return self._s[3057]! } + public var LocalGroup_Text: String { return self._s[3078]! } + public var Channel_AdminLog_EmptyFilterTitle: String { return self._s[3079]! } + public var SocksProxySetup_TypeSocks: String { return self._s[3080]! } + public var ChatList_UnarchiveAction: String { return self._s[3081]! } + public var AutoNightTheme_Title: String { return self._s[3082]! } + public var InstantPage_FeedbackButton: String { return self._s[3083]! } + public var Passport_FieldAddress: String { return self._s[3084]! } public func Channel_AdminLog_SetSlowmode(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3058]!, self._r[3058]!, [_1, _2]) + return formatWithArgumentRanges(self._s[3085]!, self._r[3085]!, [_1, _2]) } - public var Month_ShortMarch: String { return self._s[3059]! } + public var Month_ShortMarch: String { return self._s[3086]! } public func PUSH_MESSAGE_INVOICE(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3060]!, self._r[3060]!, [_1, _2]) + return formatWithArgumentRanges(self._s[3087]!, self._r[3087]!, [_1, _2]) } - public var SocksProxySetup_UsernamePlaceholder: String { return self._s[3061]! } - public var Conversation_ShareInlineBotLocationConfirmation: String { return self._s[3062]! } - public var Passport_FloodError: String { return self._s[3063]! } - public var SecretGif_Title: String { return self._s[3064]! } - public var NotificationSettings_ShowNotificationsAllAccountsInfoOn: String { return self._s[3065]! } - public var ChatList_Context_UnhideArchive: String { return self._s[3066]! } - public var Passport_Language_th: String { return self._s[3068]! } - public var Passport_Address_Address: String { return self._s[3069]! } - public var Login_InvalidLastNameError: String { return self._s[3070]! } - public var Notifications_InAppNotificationsPreview: String { return self._s[3071]! } - public var Notifications_PermissionsUnreachableTitle: String { return self._s[3072]! } - public var ChatList_Context_Archive: String { return self._s[3073]! } - public var SettingsSearch_FAQ: String { return self._s[3074]! } - public var ShareMenu_Send: String { return self._s[3075]! } - public var WallpaperSearch_ColorYellow: String { return self._s[3077]! } - public var Month_GenNovember: String { return self._s[3079]! } - public var SettingsSearch_Synonyms_Appearance_LargeEmoji: String { return self._s[3081]! } + public var SocksProxySetup_UsernamePlaceholder: String { return self._s[3088]! } + public var Conversation_ShareInlineBotLocationConfirmation: String { return self._s[3089]! } + public var Passport_FloodError: String { return self._s[3090]! } + public var SecretGif_Title: String { return self._s[3091]! } + public var NotificationSettings_ShowNotificationsAllAccountsInfoOn: String { return self._s[3092]! } + public var ChatList_Context_UnhideArchive: String { return self._s[3093]! } + public var Passport_Language_th: String { return self._s[3095]! } + public var Passport_Address_Address: String { return self._s[3096]! } + public var Login_InvalidLastNameError: String { return self._s[3097]! } + public var Notifications_InAppNotificationsPreview: String { return self._s[3098]! } + public var Notifications_PermissionsUnreachableTitle: String { return self._s[3099]! } + public var ChatList_Context_Archive: String { return self._s[3100]! } + public var SettingsSearch_FAQ: String { return self._s[3101]! } + public var ShareMenu_Send: String { return self._s[3102]! } + public var WallpaperSearch_ColorYellow: String { return self._s[3104]! } + public var Month_GenNovember: String { return self._s[3106]! } + public var SettingsSearch_Synonyms_Appearance_LargeEmoji: String { return self._s[3108]! } public func Conversation_ShareMyPhoneNumberConfirmation(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3082]!, self._r[3082]!, [_1, _2]) + return formatWithArgumentRanges(self._s[3109]!, self._r[3109]!, [_1, _2]) } - public var Conversation_SwipeToReplyHintText: String { return self._s[3083]! } - public var Checkout_Email: String { return self._s[3084]! } - public var NotificationsSound_Tritone: String { return self._s[3085]! } - public var StickerPacksSettings_ManagingHelp: String { return self._s[3087]! } - public var Wallet_ContextMenuCopy: String { return self._s[3089]! } + public var Conversation_SwipeToReplyHintText: String { return self._s[3110]! } + public var Checkout_Email: String { return self._s[3111]! } + public var NotificationsSound_Tritone: String { return self._s[3112]! } + public var StickerPacksSettings_ManagingHelp: String { return self._s[3114]! } + public var Wallet_ContextMenuCopy: String { return self._s[3116]! } public func Wallet_Time_PreciseDate_m6(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3091]!, self._r[3091]!, [_1, _2, _3]) + return formatWithArgumentRanges(self._s[3118]!, self._r[3118]!, [_1, _2, _3]) } - public var Appearance_TextSize_Automatic: String { return self._s[3092]! } + public var Appearance_TextSize_Automatic: String { return self._s[3119]! } public func PUSH_PINNED_ROUND(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3093]!, self._r[3093]!, [_1]) + return formatWithArgumentRanges(self._s[3120]!, self._r[3120]!, [_1]) } public func StickerPackActionInfo_AddedText(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3094]!, self._r[3094]!, [_0]) + return formatWithArgumentRanges(self._s[3121]!, self._r[3121]!, [_0]) } - public var ChangePhoneNumberNumber_Help: String { return self._s[3095]! } + public var ChangePhoneNumberNumber_Help: String { return self._s[3122]! } public func Checkout_LiabilityAlert(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3096]!, self._r[3096]!, [_1, _1, _1, _2]) + return formatWithArgumentRanges(self._s[3123]!, self._r[3123]!, [_1, _1, _1, _2]) } - public var ChatList_UndoArchiveTitle: String { return self._s[3097]! } - public var Notification_Exceptions_Add: String { return self._s[3098]! } - public var DialogList_You: String { return self._s[3099]! } - public var MediaPicker_Send: String { return self._s[3102]! } - public var SettingsSearch_Synonyms_Stickers_Title: String { return self._s[3103]! } - public var Appearance_ThemePreview_ChatList_4_Text: String { return self._s[3104]! } - public var Call_AudioRouteSpeaker: String { return self._s[3105]! } - public var Watch_UserInfo_Title: String { return self._s[3106]! } - public var VoiceOver_Chat_PollFinalResults: String { return self._s[3107]! } - public var Appearance_AccentColor: String { return self._s[3109]! } + public var ChatList_UndoArchiveTitle: String { return self._s[3124]! } + public var Notification_Exceptions_Add: String { return self._s[3125]! } + public var DialogList_You: String { return self._s[3126]! } + public var MediaPicker_Send: String { return self._s[3129]! } + public var SettingsSearch_Synonyms_Stickers_Title: String { return self._s[3130]! } + public var Appearance_ThemePreview_ChatList_4_Text: String { return self._s[3131]! } + public var Call_AudioRouteSpeaker: String { return self._s[3132]! } + public var Watch_UserInfo_Title: String { return self._s[3133]! } + public var VoiceOver_Chat_PollFinalResults: String { return self._s[3134]! } + public var Appearance_AccentColor: String { return self._s[3136]! } public func Login_EmailPhoneSubject(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3110]!, self._r[3110]!, [_0]) + return formatWithArgumentRanges(self._s[3137]!, self._r[3137]!, [_0]) } - public var Permissions_ContactsAllowInSettings_v0: String { return self._s[3111]! } + public var Permissions_ContactsAllowInSettings_v0: String { return self._s[3138]! } public func PUSH_CHANNEL_MESSAGE_GAME(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3112]!, self._r[3112]!, [_1, _2]) + return formatWithArgumentRanges(self._s[3139]!, self._r[3139]!, [_1, _2]) } - public var Conversation_ClousStorageInfo_Description2: String { return self._s[3113]! } - public var WebSearch_RecentClearConfirmation: String { return self._s[3114]! } - public var Notification_CallOutgoing: String { return self._s[3115]! } - public var PrivacySettings_PasscodeAndFaceId: String { return self._s[3116]! } - public var Channel_DiscussionGroup_MakeHistoryPublic: String { return self._s[3117]! } - public var Call_RecordingDisabledMessage: String { return self._s[3118]! } - public var Message_Game: String { return self._s[3119]! } - public var Conversation_PressVolumeButtonForSound: String { return self._s[3120]! } - public var PrivacyLastSeenSettings_CustomHelp: String { return self._s[3121]! } - public var Channel_DiscussionGroup_PrivateGroup: String { return self._s[3122]! } - public var Channel_EditAdmin_PermissionAddAdmins: String { return self._s[3123]! } - public var Date_DialogDateFormat: String { return self._s[3125]! } - public var WallpaperColors_SetCustomColor: String { return self._s[3126]! } - public var Notifications_InAppNotifications: String { return self._s[3127]! } + public var Conversation_ClousStorageInfo_Description2: String { return self._s[3140]! } + public var WebSearch_RecentClearConfirmation: String { return self._s[3141]! } + public var Notification_CallOutgoing: String { return self._s[3142]! } + public var PrivacySettings_PasscodeAndFaceId: String { return self._s[3143]! } + public var Channel_DiscussionGroup_MakeHistoryPublic: String { return self._s[3144]! } + public var Call_RecordingDisabledMessage: String { return self._s[3145]! } + public var Message_Game: String { return self._s[3146]! } + public var Conversation_PressVolumeButtonForSound: String { return self._s[3147]! } + public var PrivacyLastSeenSettings_CustomHelp: String { return self._s[3148]! } + public var Channel_DiscussionGroup_PrivateGroup: String { return self._s[3149]! } + public var Channel_EditAdmin_PermissionAddAdmins: String { return self._s[3150]! } + public var Date_DialogDateFormat: String { return self._s[3152]! } + public var WallpaperColors_SetCustomColor: String { return self._s[3153]! } + public var Notifications_InAppNotifications: String { return self._s[3154]! } public func Channel_Management_RemovedBy(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3128]!, self._r[3128]!, [_0]) + return formatWithArgumentRanges(self._s[3155]!, self._r[3155]!, [_0]) } public func Settings_ApplyProxyAlert(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3129]!, self._r[3129]!, [_1, _2]) + return formatWithArgumentRanges(self._s[3156]!, self._r[3156]!, [_1, _2]) } - public var NewContact_Title: String { return self._s[3130]! } + public var NewContact_Title: String { return self._s[3157]! } public func AutoDownloadSettings_UpToForAll(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3131]!, self._r[3131]!, [_0]) - } - public var Conversation_ViewContactDetails: String { return self._s[3132]! } - public func PUSH_CHANNEL_MESSAGE_CONTACT(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3134]!, self._r[3134]!, [_1]) - } - public var Checkout_NewCard_CardholderNameTitle: String { return self._s[3135]! } - public var Passport_Identity_ExpiryDateNone: String { return self._s[3136]! } - public var PrivacySettings_Title: String { return self._s[3137]! } - public var Conversation_SilentBroadcastTooltipOff: String { return self._s[3140]! } - public var GroupRemoved_UsersSectionTitle: String { return self._s[3141]! } - public var VoiceOver_Chat_ContactEmail: String { return self._s[3142]! } - public var Contacts_PhoneNumber: String { return self._s[3143]! } - public var TwoFactorSetup_Password_PlaceholderConfirmPassword: String { return self._s[3145]! } - public var Map_ShowPlaces: String { return self._s[3146]! } - public var ChatAdmins_Title: String { return self._s[3147]! } - public var InstantPage_Reference: String { return self._s[3149]! } - public var Wallet_Info_Updating: String { return self._s[3150]! } - public var ReportGroupLocation_Text: String { return self._s[3151]! } - public func PUSH_CHAT_MESSAGE_FWD(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3152]!, self._r[3152]!, [_1, _2]) - } - public var Camera_FlashOff: String { return self._s[3153]! } - public var Watch_UserInfo_Block: String { return self._s[3154]! } - public var ChatSettings_Stickers: String { return self._s[3155]! } - public var ChatSettings_DownloadInBackground: String { return self._s[3156]! } - public var Appearance_ThemeCarouselTintedNight: String { return self._s[3157]! } - public func UserInfo_BlockConfirmation(_ _0: String) -> (String, [(Int, NSRange)]) { return formatWithArgumentRanges(self._s[3158]!, self._r[3158]!, [_0]) } - public var Settings_ViewPhoto: String { return self._s[3159]! } - public var Login_CheckOtherSessionMessages: String { return self._s[3160]! } - public var AutoDownloadSettings_Cellular: String { return self._s[3161]! } - public var Wallet_Created_ExportErrorTitle: String { return self._s[3162]! } - public var SettingsSearch_Synonyms_Notifications_GroupNotificationsExceptions: String { return self._s[3163]! } - public var VoiceOver_MessageContextShare: String { return self._s[3164]! } + public var Conversation_ViewContactDetails: String { return self._s[3159]! } + public func PUSH_CHANNEL_MESSAGE_CONTACT(_ _1: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[3161]!, self._r[3161]!, [_1]) + } + public var Checkout_NewCard_CardholderNameTitle: String { return self._s[3162]! } + public var Passport_Identity_ExpiryDateNone: String { return self._s[3163]! } + public var PrivacySettings_Title: String { return self._s[3164]! } + public var Conversation_SilentBroadcastTooltipOff: String { return self._s[3167]! } + public var GroupRemoved_UsersSectionTitle: String { return self._s[3168]! } + public var VoiceOver_Chat_ContactEmail: String { return self._s[3169]! } + public var Contacts_PhoneNumber: String { return self._s[3170]! } + public var TwoFactorSetup_Password_PlaceholderConfirmPassword: String { return self._s[3172]! } + public var Map_ShowPlaces: String { return self._s[3173]! } + public var ChatAdmins_Title: String { return self._s[3174]! } + public var InstantPage_Reference: String { return self._s[3176]! } + public var Wallet_Info_Updating: String { return self._s[3177]! } + public var ReportGroupLocation_Text: String { return self._s[3178]! } + public func PUSH_CHAT_MESSAGE_FWD(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[3179]!, self._r[3179]!, [_1, _2]) + } + public var Camera_FlashOff: String { return self._s[3180]! } + public var Watch_UserInfo_Block: String { return self._s[3181]! } + public var ChatSettings_Stickers: String { return self._s[3182]! } + public var ChatSettings_DownloadInBackground: String { return self._s[3183]! } + public var Appearance_ThemeCarouselTintedNight: String { return self._s[3184]! } + public func UserInfo_BlockConfirmation(_ _0: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[3185]!, self._r[3185]!, [_0]) + } + public var Settings_ViewPhoto: String { return self._s[3186]! } + public var Login_CheckOtherSessionMessages: String { return self._s[3187]! } + public var AutoDownloadSettings_Cellular: String { return self._s[3188]! } + public var Wallet_Created_ExportErrorTitle: String { return self._s[3189]! } + public var SettingsSearch_Synonyms_Notifications_GroupNotificationsExceptions: String { return self._s[3190]! } + public var VoiceOver_MessageContextShare: String { return self._s[3191]! } public func Target_InviteToGroupConfirmation(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3166]!, self._r[3166]!, [_0]) + return formatWithArgumentRanges(self._s[3193]!, self._r[3193]!, [_0]) } - public var Privacy_DeleteDrafts: String { return self._s[3167]! } - public var Wallpaper_SetCustomBackgroundInfo: String { return self._s[3168]! } + public var Privacy_DeleteDrafts: String { return self._s[3194]! } + public var Wallpaper_SetCustomBackgroundInfo: String { return self._s[3195]! } public func LastSeen_AtDate(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3169]!, self._r[3169]!, [_0]) + return formatWithArgumentRanges(self._s[3196]!, self._r[3196]!, [_0]) } - public var DialogList_SavedMessagesHelp: String { return self._s[3170]! } - public var Wallet_SecureStorageNotAvailable_Title: String { return self._s[3171]! } - public var DialogList_SavedMessages: String { return self._s[3172]! } - public var GroupInfo_UpgradeButton: String { return self._s[3173]! } - public var Appearance_ThemePreview_ChatList_3_Text: String { return self._s[3175]! } - public var DialogList_Pin: String { return self._s[3176]! } + public var DialogList_SavedMessagesHelp: String { return self._s[3197]! } + public var Wallet_SecureStorageNotAvailable_Title: String { return self._s[3198]! } + public var DialogList_SavedMessages: String { return self._s[3199]! } + public var GroupInfo_UpgradeButton: String { return self._s[3200]! } + public var Appearance_ThemePreview_ChatList_3_Text: String { return self._s[3202]! } + public var DialogList_Pin: String { return self._s[3203]! } public func ForwardedAuthors2(_ _0: String, _ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3177]!, self._r[3177]!, [_0, _1]) + return formatWithArgumentRanges(self._s[3204]!, self._r[3204]!, [_0, _1]) } public func Login_PhoneGenericEmailSubject(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3178]!, self._r[3178]!, [_0]) + return formatWithArgumentRanges(self._s[3205]!, self._r[3205]!, [_0]) } - public var Notification_Exceptions_AlwaysOn: String { return self._s[3179]! } - public var UserInfo_NotificationsDisable: String { return self._s[3180]! } - public var Conversation_ContextMenuCancelEditing: String { return self._s[3181]! } - public var Paint_Outlined: String { return self._s[3182]! } - public var Activity_PlayingGame: String { return self._s[3183]! } - public var SearchImages_NoImagesFound: String { return self._s[3184]! } - public var SocksProxySetup_ProxyType: String { return self._s[3185]! } - public var AppleWatch_ReplyPresetsHelp: String { return self._s[3187]! } - public var Conversation_ContextMenuCancelSending: String { return self._s[3188]! } - public var Settings_AppLanguage: String { return self._s[3189]! } - public var TwoStepAuth_ResetAccountHelp: String { return self._s[3190]! } - public var Common_ChoosePhoto: String { return self._s[3191]! } - public var AuthSessions_AddDevice_InvalidQRCode: String { return self._s[3192]! } - public var CallFeedback_ReasonEcho: String { return self._s[3193]! } + public var Notification_Exceptions_AlwaysOn: String { return self._s[3206]! } + public var UserInfo_NotificationsDisable: String { return self._s[3207]! } + public var Conversation_ContextMenuCancelEditing: String { return self._s[3208]! } + public var Paint_Outlined: String { return self._s[3209]! } + public var Activity_PlayingGame: String { return self._s[3210]! } + public var SearchImages_NoImagesFound: String { return self._s[3211]! } + public var SocksProxySetup_ProxyType: String { return self._s[3212]! } + public var AppleWatch_ReplyPresetsHelp: String { return self._s[3214]! } + public var Conversation_ContextMenuCancelSending: String { return self._s[3215]! } + public var Settings_AppLanguage: String { return self._s[3216]! } + public var TwoStepAuth_ResetAccountHelp: String { return self._s[3217]! } + public var Common_ChoosePhoto: String { return self._s[3218]! } + public var AuthSessions_AddDevice_InvalidQRCode: String { return self._s[3219]! } + public var CallFeedback_ReasonEcho: String { return self._s[3220]! } public func PUSH_PINNED_AUDIO(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3194]!, self._r[3194]!, [_1]) + return formatWithArgumentRanges(self._s[3221]!, self._r[3221]!, [_1]) } - public var Privacy_Calls_AlwaysAllow: String { return self._s[3195]! } - public var Activity_UploadingVideo: String { return self._s[3196]! } - public var Conversation_WalletRequiredNotNow: String { return self._s[3197]! } - public var ChannelInfo_DeleteChannelConfirmation: String { return self._s[3198]! } - public var NetworkUsageSettings_Wifi: String { return self._s[3199]! } - public var VoiceOver_Editing_ClearText: String { return self._s[3200]! } - public var PUSH_SENDER_YOU: String { return self._s[3201]! } - public var Channel_BanUser_PermissionReadMessages: String { return self._s[3202]! } - public var Checkout_PayWithTouchId: String { return self._s[3203]! } - public var Wallpaper_ResetWallpapersConfirmation: String { return self._s[3204]! } + public var Privacy_Calls_AlwaysAllow: String { return self._s[3222]! } + public var PollResults_Collapse: String { return self._s[3223]! } + public var Activity_UploadingVideo: String { return self._s[3224]! } + public var Conversation_WalletRequiredNotNow: String { return self._s[3225]! } + public var ChannelInfo_DeleteChannelConfirmation: String { return self._s[3226]! } + public var NetworkUsageSettings_Wifi: String { return self._s[3227]! } + public var VoiceOver_Editing_ClearText: String { return self._s[3228]! } + public var PUSH_SENDER_YOU: String { return self._s[3229]! } + public var Channel_BanUser_PermissionReadMessages: String { return self._s[3230]! } + public var Checkout_PayWithTouchId: String { return self._s[3231]! } + public var Wallpaper_ResetWallpapersConfirmation: String { return self._s[3232]! } public func PUSH_LOCKED_MESSAGE(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3206]!, self._r[3206]!, [_1]) + return formatWithArgumentRanges(self._s[3234]!, self._r[3234]!, [_1]) } - public var Notifications_ExceptionsNone: String { return self._s[3207]! } + public var Notifications_ExceptionsNone: String { return self._s[3235]! } public func Message_ForwardedMessageShort(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3208]!, self._r[3208]!, [_0]) + return formatWithArgumentRanges(self._s[3236]!, self._r[3236]!, [_0]) } public func PUSH_PINNED_GEO(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3209]!, self._r[3209]!, [_1]) + return formatWithArgumentRanges(self._s[3237]!, self._r[3237]!, [_1]) } - public var AuthSessions_IncompleteAttempts: String { return self._s[3211]! } - public var Passport_Address_Region: String { return self._s[3214]! } - public var ChatList_DeleteChat: String { return self._s[3215]! } - public var LogoutOptions_ClearCacheTitle: String { return self._s[3216]! } - public var PhotoEditor_TiltShift: String { return self._s[3217]! } - public var Settings_FAQ_URL: String { return self._s[3218]! } - public var TwoFactorSetup_EmailVerification_ChangeAction: String { return self._s[3219]! } - public var Passport_Language_sl: String { return self._s[3220]! } - public var Settings_PrivacySettings: String { return self._s[3222]! } - public var SharedMedia_TitleLink: String { return self._s[3223]! } - public var Passport_Identity_TypePassportUploadScan: String { return self._s[3224]! } - public var Settings_SetProfilePhoto: String { return self._s[3225]! } - public var Channel_About_Help: String { return self._s[3226]! } - public var Contacts_PermissionsEnable: String { return self._s[3227]! } - public var Wallet_Sending_Title: String { return self._s[3228]! } - public var SettingsSearch_Synonyms_Notifications_GroupNotificationsAlert: String { return self._s[3229]! } - public var AttachmentMenu_SendAsFiles: String { return self._s[3230]! } - public var CallFeedback_ReasonInterruption: String { return self._s[3232]! } - public var Passport_Address_AddTemporaryRegistration: String { return self._s[3233]! } - public var AutoDownloadSettings_AutodownloadVideos: String { return self._s[3234]! } - public var ChatSettings_AutoDownloadSettings_Delimeter: String { return self._s[3235]! } - public var OldChannels_Title: String { return self._s[3236]! } - public var PrivacySettings_DeleteAccountTitle: String { return self._s[3237]! } - public var AccessDenied_VideoMessageCamera: String { return self._s[3239]! } - public var Map_OpenInYandexMaps: String { return self._s[3241]! } - public var CreateGroup_ErrorLocatedGroupsTooMuch: String { return self._s[3242]! } - public var VoiceOver_MessageContextReply: String { return self._s[3243]! } - public var PhotoEditor_SaturationTool: String { return self._s[3245]! } + public var AuthSessions_IncompleteAttempts: String { return self._s[3239]! } + public var Passport_Address_Region: String { return self._s[3242]! } + public var ChatList_DeleteChat: String { return self._s[3243]! } + public var LogoutOptions_ClearCacheTitle: String { return self._s[3244]! } + public var PhotoEditor_TiltShift: String { return self._s[3245]! } + public var Settings_FAQ_URL: String { return self._s[3246]! } + public var TwoFactorSetup_EmailVerification_ChangeAction: String { return self._s[3247]! } + public var Passport_Language_sl: String { return self._s[3248]! } + public var Settings_PrivacySettings: String { return self._s[3250]! } + public var SharedMedia_TitleLink: String { return self._s[3251]! } + public var Passport_Identity_TypePassportUploadScan: String { return self._s[3252]! } + public var Settings_SetProfilePhoto: String { return self._s[3253]! } + public var Channel_About_Help: String { return self._s[3254]! } + public var Contacts_PermissionsEnable: String { return self._s[3255]! } + public var Wallet_Sending_Title: String { return self._s[3256]! } + public var SettingsSearch_Synonyms_Notifications_GroupNotificationsAlert: String { return self._s[3257]! } + public var AttachmentMenu_SendAsFiles: String { return self._s[3258]! } + public var CallFeedback_ReasonInterruption: String { return self._s[3260]! } + public var Passport_Address_AddTemporaryRegistration: String { return self._s[3261]! } + public var AutoDownloadSettings_AutodownloadVideos: String { return self._s[3262]! } + public var ChatSettings_AutoDownloadSettings_Delimeter: String { return self._s[3263]! } + public var OldChannels_Title: String { return self._s[3264]! } + public var PrivacySettings_DeleteAccountTitle: String { return self._s[3265]! } + public var AccessDenied_VideoMessageCamera: String { return self._s[3267]! } + public var Map_OpenInYandexMaps: String { return self._s[3269]! } + public var CreateGroup_ErrorLocatedGroupsTooMuch: String { return self._s[3270]! } + public var VoiceOver_MessageContextReply: String { return self._s[3271]! } + public var PhotoEditor_SaturationTool: String { return self._s[3273]! } public func PUSH_MESSAGE_STICKER(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3246]!, self._r[3246]!, [_1, _2]) + return formatWithArgumentRanges(self._s[3274]!, self._r[3274]!, [_1, _2]) } - public var PrivacyPhoneNumberSettings_CustomHelp: String { return self._s[3247]! } - public var Notification_Exceptions_NewException_NotificationHeader: String { return self._s[3248]! } - public var Group_OwnershipTransfer_ErrorLocatedGroupsTooMuch: String { return self._s[3249]! } + public var PrivacyPhoneNumberSettings_CustomHelp: String { return self._s[3275]! } + public var Notification_Exceptions_NewException_NotificationHeader: String { return self._s[3276]! } + public var Group_OwnershipTransfer_ErrorLocatedGroupsTooMuch: String { return self._s[3277]! } public func LOCAL_MESSAGE_FWDS(_ _1: String, _ _2: Int) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3250]!, self._r[3250]!, [_1, "\(_2)"]) + return formatWithArgumentRanges(self._s[3278]!, self._r[3278]!, [_1, "\(_2)"]) } - public var Appearance_ThemePreview_ChatList_2_Text: String { return self._s[3251]! } - public var Channel_Username_InvalidTooShort: String { return self._s[3253]! } - public var SettingsSearch_Synonyms_Wallet: String { return self._s[3254]! } + public var Appearance_ThemePreview_ChatList_2_Text: String { return self._s[3279]! } + public var Channel_Username_InvalidTooShort: String { return self._s[3281]! } + public var SettingsSearch_Synonyms_Wallet: String { return self._s[3282]! } public func Group_OwnershipTransfer_DescriptionInfo(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3255]!, self._r[3255]!, [_1, _2]) + return formatWithArgumentRanges(self._s[3283]!, self._r[3283]!, [_1, _2]) } + public var Forward_ErrorPublicPollDisabledInChannels: String { return self._s[3284]! } public func PUSH_CHAT_MESSAGE_GAME(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3256]!, self._r[3256]!, [_1, _2, _3]) + return formatWithArgumentRanges(self._s[3285]!, self._r[3285]!, [_1, _2, _3]) } - public var WallpaperPreview_PatternTitle: String { return self._s[3257]! } - public var GroupInfo_PublicLinkAdd: String { return self._s[3258]! } - public var Passport_PassportInformation: String { return self._s[3261]! } - public var Theme_Unsupported: String { return self._s[3262]! } - public var WatchRemote_AlertTitle: String { return self._s[3263]! } - public var Privacy_GroupsAndChannels_NeverAllow: String { return self._s[3264]! } - public var ConvertToSupergroup_HelpText: String { return self._s[3266]! } + public var WallpaperPreview_PatternTitle: String { return self._s[3286]! } + public var GroupInfo_PublicLinkAdd: String { return self._s[3287]! } + public var Passport_PassportInformation: String { return self._s[3290]! } + public var Theme_Unsupported: String { return self._s[3291]! } + public var WatchRemote_AlertTitle: String { return self._s[3292]! } + public var Privacy_GroupsAndChannels_NeverAllow: String { return self._s[3293]! } + public var ConvertToSupergroup_HelpText: String { return self._s[3295]! } public func Time_MonthOfYear_m7(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3267]!, self._r[3267]!, [_0]) + return formatWithArgumentRanges(self._s[3296]!, self._r[3296]!, [_0]) } public func PUSH_PHONE_CALL_REQUEST(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3268]!, self._r[3268]!, [_1]) + return formatWithArgumentRanges(self._s[3297]!, self._r[3297]!, [_1]) } - public var Privacy_GroupsAndChannels_CustomHelp: String { return self._s[3269]! } - public var Wallet_Navigation_Done: String { return self._s[3271]! } - public var TwoStepAuth_RecoveryCodeInvalid: String { return self._s[3272]! } - public var AccessDenied_CameraDisabled: String { return self._s[3273]! } + public var Privacy_GroupsAndChannels_CustomHelp: String { return self._s[3298]! } + public var Wallet_Navigation_Done: String { return self._s[3300]! } + public var TwoStepAuth_RecoveryCodeInvalid: String { return self._s[3301]! } + public var AccessDenied_CameraDisabled: String { return self._s[3302]! } public func Channel_Username_UsernameIsAvailable(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3274]!, self._r[3274]!, [_0]) + return formatWithArgumentRanges(self._s[3303]!, self._r[3303]!, [_0]) } - public var ClearCache_Forever: String { return self._s[3275]! } - public var AuthSessions_AddDeviceIntro_Title: String { return self._s[3276]! } - public var PhotoEditor_ContrastTool: String { return self._s[3279]! } + public var ClearCache_Forever: String { return self._s[3304]! } + public var AuthSessions_AddDeviceIntro_Title: String { return self._s[3305]! } + public var CreatePoll_Quiz: String { return self._s[3306]! } + public var PhotoEditor_ContrastTool: String { return self._s[3309]! } public func PUSH_PINNED_DOC(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3280]!, self._r[3280]!, [_1]) + return formatWithArgumentRanges(self._s[3310]!, self._r[3310]!, [_1]) } - public var DialogList_Draft: String { return self._s[3281]! } - public var Wallet_Configuration_BlockchainIdInfo: String { return self._s[3282]! } - public var Privacy_TopPeersDelete: String { return self._s[3284]! } - public var LoginPassword_PasswordPlaceholder: String { return self._s[3285]! } - public var Passport_Identity_TypeIdentityCardUploadScan: String { return self._s[3286]! } - public var WebSearch_RecentSectionClear: String { return self._s[3287]! } - public var EditTheme_ErrorInvalidCharacters: String { return self._s[3288]! } - public var Watch_ChatList_NoConversationsTitle: String { return self._s[3290]! } - public var Common_Done: String { return self._s[3292]! } - public var Shortcut_SwitchAccount: String { return self._s[3293]! } - public var AuthSessions_EmptyText: String { return self._s[3294]! } - public var Wallet_Configuration_BlockchainNameChangedTitle: String { return self._s[3295]! } - public var Conversation_ShareBotContactConfirmation: String { return self._s[3296]! } - public var Tour_Title5: String { return self._s[3297]! } - public var Wallet_Settings_Title: String { return self._s[3298]! } + public var DialogList_Draft: String { return self._s[3311]! } + public var Wallet_Configuration_BlockchainIdInfo: String { return self._s[3312]! } + public var Privacy_TopPeersDelete: String { return self._s[3314]! } + public var LoginPassword_PasswordPlaceholder: String { return self._s[3315]! } + public var Passport_Identity_TypeIdentityCardUploadScan: String { return self._s[3316]! } + public var WebSearch_RecentSectionClear: String { return self._s[3317]! } + public var EditTheme_ErrorInvalidCharacters: String { return self._s[3318]! } + public var Watch_ChatList_NoConversationsTitle: String { return self._s[3320]! } + public var Common_Done: String { return self._s[3322]! } + public var Shortcut_SwitchAccount: String { return self._s[3323]! } + public var AuthSessions_EmptyText: String { return self._s[3324]! } + public var Wallet_Configuration_BlockchainNameChangedTitle: String { return self._s[3325]! } + public var Conversation_ShareBotContactConfirmation: String { return self._s[3326]! } + public var Tour_Title5: String { return self._s[3327]! } + public var Wallet_Settings_Title: String { return self._s[3328]! } public func Map_DirectionsDriveEta(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3299]!, self._r[3299]!, [_0]) + return formatWithArgumentRanges(self._s[3329]!, self._r[3329]!, [_0]) } - public var ApplyLanguage_UnsufficientDataTitle: String { return self._s[3300]! } - public var Conversation_LinkDialogSave: String { return self._s[3301]! } - public var GroupInfo_ActionRestrict: String { return self._s[3302]! } - public var Checkout_Title: String { return self._s[3303]! } - public var Channel_DiscussionGroup_HeaderLabel: String { return self._s[3305]! } - public var Channel_AdminLog_CanChangeInfo: String { return self._s[3307]! } - public var Notification_RenamedGroup: String { return self._s[3308]! } - public var PeopleNearby_Groups: String { return self._s[3309]! } - public var Checkout_PayWithFaceId: String { return self._s[3310]! } - public var Channel_BanList_BlockedTitle: String { return self._s[3311]! } - public var SettingsSearch_Synonyms_Notifications_InAppNotificationsSound: String { return self._s[3313]! } - public var Checkout_WebConfirmation_Title: String { return self._s[3314]! } - public var Notifications_MessageNotificationsAlert: String { return self._s[3315]! } + public var ApplyLanguage_UnsufficientDataTitle: String { return self._s[3330]! } + public var Conversation_LinkDialogSave: String { return self._s[3331]! } + public var GroupInfo_ActionRestrict: String { return self._s[3332]! } + public var Checkout_Title: String { return self._s[3333]! } + public var Channel_DiscussionGroup_HeaderLabel: String { return self._s[3335]! } + public var Channel_AdminLog_CanChangeInfo: String { return self._s[3337]! } + public var Notification_RenamedGroup: String { return self._s[3338]! } + public var PeopleNearby_Groups: String { return self._s[3339]! } + public var Checkout_PayWithFaceId: String { return self._s[3340]! } + public var Channel_BanList_BlockedTitle: String { return self._s[3341]! } + public var SettingsSearch_Synonyms_Notifications_InAppNotificationsSound: String { return self._s[3343]! } + public var Checkout_WebConfirmation_Title: String { return self._s[3344]! } + public var Notifications_MessageNotificationsAlert: String { return self._s[3345]! } public func Activity_RemindAboutGroup(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3316]!, self._r[3316]!, [_0]) + return formatWithArgumentRanges(self._s[3346]!, self._r[3346]!, [_0]) } - public var Profile_AddToExisting: String { return self._s[3318]! } + public var Profile_AddToExisting: String { return self._s[3348]! } public func Profile_CreateEncryptedChatOutdatedError(_ _0: String, _ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3319]!, self._r[3319]!, [_0, _1]) + return formatWithArgumentRanges(self._s[3349]!, self._r[3349]!, [_0, _1]) } - public var Cache_Files: String { return self._s[3321]! } - public var Permissions_PrivacyPolicy: String { return self._s[3322]! } - public var SocksProxySetup_ConnectAndSave: String { return self._s[3323]! } - public var UserInfo_NotificationsDefaultDisabled: String { return self._s[3324]! } - public var AutoDownloadSettings_TypeContacts: String { return self._s[3326]! } - public var Appearance_ThemePreview_ChatList_1_Text: String { return self._s[3328]! } - public var Calls_NoCallsPlaceholder: String { return self._s[3329]! } + public var Cache_Files: String { return self._s[3351]! } + public var Permissions_PrivacyPolicy: String { return self._s[3352]! } + public var SocksProxySetup_ConnectAndSave: String { return self._s[3353]! } + public var UserInfo_NotificationsDefaultDisabled: String { return self._s[3354]! } + public var AutoDownloadSettings_TypeContacts: String { return self._s[3356]! } + public var Appearance_ThemePreview_ChatList_1_Text: String { return self._s[3358]! } + public var Calls_NoCallsPlaceholder: String { return self._s[3359]! } public func Wallet_Receive_ShareInvoiceUrlInfo(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3330]!, self._r[3330]!, [_0]) + return formatWithArgumentRanges(self._s[3360]!, self._r[3360]!, [_0]) } - public var Channel_Username_RevokeExistingUsernamesInfo: String { return self._s[3331]! } - public var VoiceOver_AttachMedia: String { return self._s[3334]! } - public var Notifications_ExceptionsGroupPlaceholder: String { return self._s[3335]! } + public var Channel_Username_RevokeExistingUsernamesInfo: String { return self._s[3361]! } + public var VoiceOver_AttachMedia: String { return self._s[3364]! } + public var Notifications_ExceptionsGroupPlaceholder: String { return self._s[3365]! } public func PUSH_CHAT_MESSAGE_INVOICE(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3336]!, self._r[3336]!, [_1, _2, _3]) + return formatWithArgumentRanges(self._s[3366]!, self._r[3366]!, [_1, _2, _3]) } - public var SettingsSearch_Synonyms_Notifications_GroupNotificationsSound: String { return self._s[3337]! } - public var Conversation_SetReminder_Title: String { return self._s[3338]! } - public var Passport_FieldAddressHelp: String { return self._s[3339]! } - public var Privacy_GroupsAndChannels_InviteToChannelMultipleError: String { return self._s[3340]! } - public var PUSH_REMINDER_TITLE: String { return self._s[3341]! } + public var SettingsSearch_Synonyms_Notifications_GroupNotificationsSound: String { return self._s[3367]! } + public var Conversation_SetReminder_Title: String { return self._s[3368]! } + public var Passport_FieldAddressHelp: String { return self._s[3369]! } + public var Privacy_GroupsAndChannels_InviteToChannelMultipleError: String { return self._s[3370]! } + public var PUSH_REMINDER_TITLE: String { return self._s[3371]! } public func Login_TermsOfService_ProceedBot(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3342]!, self._r[3342]!, [_0]) + return formatWithArgumentRanges(self._s[3372]!, self._r[3372]!, [_0]) } - public var Channel_AdminLog_EmptyTitle: String { return self._s[3343]! } - public var Privacy_Calls_NeverAllow_Title: String { return self._s[3344]! } - public var Login_UnknownError: String { return self._s[3345]! } - public var Group_UpgradeNoticeText2: String { return self._s[3348]! } - public var Watch_Compose_AddContact: String { return self._s[3349]! } - public var ClearCache_StorageServiceFiles: String { return self._s[3350]! } - public var Web_Error: String { return self._s[3351]! } - public var Gif_Search: String { return self._s[3352]! } - public var Profile_MessageLifetime1h: String { return self._s[3353]! } - public var CheckoutInfo_ReceiverInfoEmailPlaceholder: String { return self._s[3354]! } - public var Channel_Username_CheckingUsername: String { return self._s[3355]! } - public var CallFeedback_ReasonSilentRemote: String { return self._s[3356]! } - public var AutoDownloadSettings_TypeChannels: String { return self._s[3357]! } - public var Channel_AboutItem: String { return self._s[3358]! } - public var Privacy_GroupsAndChannels_AlwaysAllow_Placeholder: String { return self._s[3360]! } - public var VoiceOver_Chat_VoiceMessage: String { return self._s[3361]! } - public var GroupInfo_SharedMedia: String { return self._s[3362]! } + public var Channel_AdminLog_EmptyTitle: String { return self._s[3373]! } + public var Privacy_Calls_NeverAllow_Title: String { return self._s[3374]! } + public var Login_UnknownError: String { return self._s[3375]! } + public var Group_UpgradeNoticeText2: String { return self._s[3378]! } + public var Watch_Compose_AddContact: String { return self._s[3379]! } + public var ClearCache_StorageServiceFiles: String { return self._s[3380]! } + public var Web_Error: String { return self._s[3381]! } + public var Gif_Search: String { return self._s[3382]! } + public var Profile_MessageLifetime1h: String { return self._s[3383]! } + public var CheckoutInfo_ReceiverInfoEmailPlaceholder: String { return self._s[3384]! } + public var Channel_Username_CheckingUsername: String { return self._s[3385]! } + public var CallFeedback_ReasonSilentRemote: String { return self._s[3386]! } + public var AutoDownloadSettings_TypeChannels: String { return self._s[3387]! } + public var Channel_AboutItem: String { return self._s[3388]! } + public var Privacy_GroupsAndChannels_AlwaysAllow_Placeholder: String { return self._s[3390]! } + public var VoiceOver_Chat_VoiceMessage: String { return self._s[3391]! } + public var GroupInfo_SharedMedia: String { return self._s[3392]! } public func Channel_AdminLog_MessagePromotedName(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3363]!, self._r[3363]!, [_1]) + return formatWithArgumentRanges(self._s[3393]!, self._r[3393]!, [_1]) } - public var Call_PhoneCallInProgressMessage: String { return self._s[3364]! } + public var Call_PhoneCallInProgressMessage: String { return self._s[3394]! } public func PUSH_CHANNEL_ALBUM(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3365]!, self._r[3365]!, [_1]) + return formatWithArgumentRanges(self._s[3395]!, self._r[3395]!, [_1]) } - public var ChatList_UndoArchiveRevealedText: String { return self._s[3366]! } - public var GroupInfo_InviteLink_RevokeAlert_Text: String { return self._s[3367]! } - public var Conversation_SearchByName_Placeholder: String { return self._s[3368]! } - public var CreatePoll_AddOption: String { return self._s[3369]! } - public var GroupInfo_Permissions_SearchPlaceholder: String { return self._s[3370]! } - public var Group_UpgradeNoticeHeader: String { return self._s[3371]! } - public var Channel_Management_AddModerator: String { return self._s[3372]! } - public var AutoDownloadSettings_MaxFileSize: String { return self._s[3373]! } - public var StickerPacksSettings_ShowStickersButton: String { return self._s[3374]! } - public var Wallet_Info_RefreshErrorNetworkText: String { return self._s[3375]! } - public var Theme_Colors_Background: String { return self._s[3376]! } - public var NotificationsSound_Hello: String { return self._s[3378]! } - public var SocksProxySetup_SavedProxies: String { return self._s[3379]! } - public var Channel_Stickers_Placeholder: String { return self._s[3381]! } + public var ChatList_UndoArchiveRevealedText: String { return self._s[3396]! } + public var GroupInfo_InviteLink_RevokeAlert_Text: String { return self._s[3397]! } + public var Conversation_SearchByName_Placeholder: String { return self._s[3398]! } + public var CreatePoll_AddOption: String { return self._s[3399]! } + public var GroupInfo_Permissions_SearchPlaceholder: String { return self._s[3400]! } + public var Group_UpgradeNoticeHeader: String { return self._s[3401]! } + public var Channel_Management_AddModerator: String { return self._s[3402]! } + public var AutoDownloadSettings_MaxFileSize: String { return self._s[3403]! } + public var StickerPacksSettings_ShowStickersButton: String { return self._s[3404]! } + public var Wallet_Info_RefreshErrorNetworkText: String { return self._s[3405]! } + public var Theme_Colors_Background: String { return self._s[3406]! } + public var NotificationsSound_Hello: String { return self._s[3408]! } + public var SocksProxySetup_SavedProxies: String { return self._s[3409]! } + public var Channel_Stickers_Placeholder: String { return self._s[3411]! } public func Login_EmailCodeBody(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3382]!, self._r[3382]!, [_0]) + return formatWithArgumentRanges(self._s[3412]!, self._r[3412]!, [_0]) } - public var PrivacyPolicy_DeclineDeclineAndDelete: String { return self._s[3383]! } - public var Channel_Management_AddModeratorHelp: String { return self._s[3384]! } - public var ContactInfo_BirthdayLabel: String { return self._s[3385]! } - public var ChangePhoneNumberCode_RequestingACall: String { return self._s[3386]! } - public var AutoDownloadSettings_Channels: String { return self._s[3387]! } - public var Passport_Language_mn: String { return self._s[3388]! } - public var Notifications_ResetAllNotificationsHelp: String { return self._s[3391]! } - public var GroupInfo_Permissions_SlowmodeValue_Off: String { return self._s[3392]! } - public var Passport_Language_ja: String { return self._s[3394]! } - public var Settings_About_Title: String { return self._s[3395]! } - public var Settings_NotificationsAndSounds: String { return self._s[3396]! } - public var ChannelInfo_DeleteGroup: String { return self._s[3397]! } - public var Settings_BlockedUsers: String { return self._s[3398]! } + public var PrivacyPolicy_DeclineDeclineAndDelete: String { return self._s[3413]! } + public var Channel_Management_AddModeratorHelp: String { return self._s[3414]! } + public var ContactInfo_BirthdayLabel: String { return self._s[3415]! } + public var ChangePhoneNumberCode_RequestingACall: String { return self._s[3416]! } + public var AutoDownloadSettings_Channels: String { return self._s[3417]! } + public var Passport_Language_mn: String { return self._s[3418]! } + public var Notifications_ResetAllNotificationsHelp: String { return self._s[3421]! } + public var GroupInfo_Permissions_SlowmodeValue_Off: String { return self._s[3422]! } + public var Passport_Language_ja: String { return self._s[3424]! } + public var Settings_About_Title: String { return self._s[3425]! } + public var Settings_NotificationsAndSounds: String { return self._s[3426]! } + public var ChannelInfo_DeleteGroup: String { return self._s[3427]! } + public var Settings_BlockedUsers: String { return self._s[3428]! } public func Time_MonthOfYear_m4(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3399]!, self._r[3399]!, [_0]) + return formatWithArgumentRanges(self._s[3429]!, self._r[3429]!, [_0]) } - public var EditTheme_Create_Preview_OutgoingText: String { return self._s[3400]! } - public var Wallet_Weekday_Today: String { return self._s[3401]! } - public var AutoDownloadSettings_PreloadVideo: String { return self._s[3402]! } - public var Widget_ApplicationLocked: String { return self._s[3403]! } - public var Passport_Address_AddResidentialAddress: String { return self._s[3404]! } - public var Channel_Username_Title: String { return self._s[3405]! } + public var EditTheme_Create_Preview_OutgoingText: String { return self._s[3430]! } + public var Wallet_Weekday_Today: String { return self._s[3431]! } + public var AutoDownloadSettings_PreloadVideo: String { return self._s[3432]! } + public var Widget_ApplicationLocked: String { return self._s[3433]! } + public var Passport_Address_AddResidentialAddress: String { return self._s[3434]! } + public var Channel_Username_Title: String { return self._s[3435]! } public func Notification_RemovedGroupPhoto(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3406]!, self._r[3406]!, [_0]) + return formatWithArgumentRanges(self._s[3436]!, self._r[3436]!, [_0]) } - public var AttachmentMenu_File: String { return self._s[3408]! } - public var AppleWatch_Title: String { return self._s[3409]! } - public var Activity_RecordingVideoMessage: String { return self._s[3410]! } + public var AttachmentMenu_File: String { return self._s[3438]! } + public var AppleWatch_Title: String { return self._s[3439]! } + public var Activity_RecordingVideoMessage: String { return self._s[3440]! } public func Channel_DiscussionGroup_PublicChannelLink(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3411]!, self._r[3411]!, [_1, _2]) + return formatWithArgumentRanges(self._s[3441]!, self._r[3441]!, [_1, _2]) } - public var Theme_Colors_Messages: String { return self._s[3412]! } - public var Weekday_Saturday: String { return self._s[3413]! } - public var WallpaperPreview_SwipeColorsTopText: String { return self._s[3414]! } - public var Profile_CreateEncryptedChatError: String { return self._s[3415]! } - public var Common_Next: String { return self._s[3417]! } - public var Channel_Stickers_YourStickers: String { return self._s[3419]! } - public var Message_Theme: String { return self._s[3420]! } - public var Call_AudioRouteHeadphones: String { return self._s[3421]! } - public var TwoStepAuth_EnterPasswordForgot: String { return self._s[3423]! } - public var Watch_Contacts_NoResults: String { return self._s[3425]! } - public var PhotoEditor_TintTool: String { return self._s[3428]! } - public var LoginPassword_ResetAccount: String { return self._s[3430]! } - public var Settings_SavedMessages: String { return self._s[3431]! } - public var SettingsSearch_Synonyms_Appearance_Animations: String { return self._s[3432]! } - public var Bot_GenericSupportStatus: String { return self._s[3433]! } - public var StickerPack_Add: String { return self._s[3434]! } - public var Checkout_TotalAmount: String { return self._s[3435]! } - public var Your_cards_number_is_invalid: String { return self._s[3436]! } - public var SettingsSearch_Synonyms_Appearance_AutoNightTheme: String { return self._s[3437]! } - public var VoiceOver_Chat_VideoMessage: String { return self._s[3438]! } + public var Theme_Colors_Messages: String { return self._s[3442]! } + public var Weekday_Saturday: String { return self._s[3443]! } + public var WallpaperPreview_SwipeColorsTopText: String { return self._s[3444]! } + public var Profile_CreateEncryptedChatError: String { return self._s[3445]! } + public var Common_Next: String { return self._s[3447]! } + public var Channel_Stickers_YourStickers: String { return self._s[3449]! } + public var Message_Theme: String { return self._s[3450]! } + public var Call_AudioRouteHeadphones: String { return self._s[3451]! } + public var TwoStepAuth_EnterPasswordForgot: String { return self._s[3453]! } + public var Watch_Contacts_NoResults: String { return self._s[3455]! } + public var PhotoEditor_TintTool: String { return self._s[3458]! } + public var LoginPassword_ResetAccount: String { return self._s[3460]! } + public var Settings_SavedMessages: String { return self._s[3461]! } + public var SettingsSearch_Synonyms_Appearance_Animations: String { return self._s[3462]! } + public var Bot_GenericSupportStatus: String { return self._s[3463]! } + public var StickerPack_Add: String { return self._s[3464]! } + public var Checkout_TotalAmount: String { return self._s[3465]! } + public var Your_cards_number_is_invalid: String { return self._s[3466]! } + public var SettingsSearch_Synonyms_Appearance_AutoNightTheme: String { return self._s[3467]! } + public var VoiceOver_Chat_VideoMessage: String { return self._s[3468]! } public func ChangePhoneNumberCode_CallTimer(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3439]!, self._r[3439]!, [_0]) + return formatWithArgumentRanges(self._s[3469]!, self._r[3469]!, [_0]) } public func GroupPermission_AddedInfo(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3440]!, self._r[3440]!, [_1, _2]) + return formatWithArgumentRanges(self._s[3470]!, self._r[3470]!, [_1, _2]) } - public var ChatSettings_ConnectionType_UseSocks5: String { return self._s[3441]! } + public var ChatSettings_ConnectionType_UseSocks5: String { return self._s[3471]! } public func PUSH_CHAT_PHOTO_EDITED(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3443]!, self._r[3443]!, [_1, _2]) + return formatWithArgumentRanges(self._s[3473]!, self._r[3473]!, [_1, _2]) } public func Conversation_RestrictedTextTimed(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3444]!, self._r[3444]!, [_0]) + return formatWithArgumentRanges(self._s[3474]!, self._r[3474]!, [_0]) } - public var GroupInfo_InviteLink_ShareLink: String { return self._s[3445]! } - public var StickerPack_Share: String { return self._s[3446]! } - public var Passport_DeleteAddress: String { return self._s[3447]! } - public var Settings_Passport: String { return self._s[3448]! } - public var SharedMedia_EmptyFilesText: String { return self._s[3449]! } - public var Conversation_DeleteMessagesForMe: String { return self._s[3450]! } - public var PasscodeSettings_AutoLock_IfAwayFor_1hour: String { return self._s[3451]! } - public var Contacts_PermissionsText: String { return self._s[3452]! } - public var Group_Setup_HistoryVisible: String { return self._s[3453]! } - public var Wallet_Month_ShortDecember: String { return self._s[3455]! } - public var Channel_EditAdmin_PermissionEnabledByDefault: String { return self._s[3456]! } - public var Passport_Address_AddRentalAgreement: String { return self._s[3457]! } - public var SocksProxySetup_Title: String { return self._s[3458]! } - public var Notification_Mute1h: String { return self._s[3459]! } + public var GroupInfo_InviteLink_ShareLink: String { return self._s[3475]! } + public var StickerPack_Share: String { return self._s[3476]! } + public var Passport_DeleteAddress: String { return self._s[3477]! } + public var Settings_Passport: String { return self._s[3478]! } + public var SharedMedia_EmptyFilesText: String { return self._s[3479]! } + public var Conversation_DeleteMessagesForMe: String { return self._s[3480]! } + public var PasscodeSettings_AutoLock_IfAwayFor_1hour: String { return self._s[3481]! } + public var Contacts_PermissionsText: String { return self._s[3482]! } + public var Group_Setup_HistoryVisible: String { return self._s[3483]! } + public var Wallet_Month_ShortDecember: String { return self._s[3485]! } + public var Channel_EditAdmin_PermissionEnabledByDefault: String { return self._s[3486]! } + public var Passport_Address_AddRentalAgreement: String { return self._s[3487]! } + public var SocksProxySetup_Title: String { return self._s[3488]! } + public var Notification_Mute1h: String { return self._s[3489]! } public func Passport_Email_CodeHelp(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3460]!, self._r[3460]!, [_0]) + return formatWithArgumentRanges(self._s[3490]!, self._r[3490]!, [_0]) } - public var NotificationSettings_ShowNotificationsAllAccountsInfoOff: String { return self._s[3461]! } + public var NotificationSettings_ShowNotificationsAllAccountsInfoOff: String { return self._s[3491]! } public func PUSH_PINNED_GEOLIVE(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3462]!, self._r[3462]!, [_1]) + return formatWithArgumentRanges(self._s[3492]!, self._r[3492]!, [_1]) } - public var FastTwoStepSetup_PasswordSection: String { return self._s[3463]! } - public var NetworkUsageSettings_ResetStatsConfirmation: String { return self._s[3466]! } - public var InfoPlist_NSFaceIDUsageDescription: String { return self._s[3468]! } - public var DialogList_NoMessagesText: String { return self._s[3469]! } - public var Privacy_ContactsResetConfirmation: String { return self._s[3470]! } - public var Privacy_Calls_P2PHelp: String { return self._s[3471]! } - public var Channel_DiscussionGroup_SearchPlaceholder: String { return self._s[3473]! } - public var Your_cards_expiration_year_is_invalid: String { return self._s[3474]! } - public var Common_TakePhotoOrVideo: String { return self._s[3475]! } - public var Wallet_Words_Text: String { return self._s[3476]! } - public var Call_StatusBusy: String { return self._s[3477]! } - public var Conversation_PinnedMessage: String { return self._s[3478]! } - public var AutoDownloadSettings_VoiceMessagesTitle: String { return self._s[3479]! } - public var Wallet_Configuration_BlockchainNameChangedProceed: String { return self._s[3480]! } - public var TwoStepAuth_SetupPasswordConfirmFailed: String { return self._s[3481]! } - public var Undo_ChatCleared: String { return self._s[3482]! } - public var AppleWatch_ReplyPresets: String { return self._s[3483]! } - public var Passport_DiscardMessageDescription: String { return self._s[3485]! } - public var Login_NetworkError: String { return self._s[3486]! } + public var FastTwoStepSetup_PasswordSection: String { return self._s[3493]! } + public var NetworkUsageSettings_ResetStatsConfirmation: String { return self._s[3496]! } + public var InfoPlist_NSFaceIDUsageDescription: String { return self._s[3498]! } + public var DialogList_NoMessagesText: String { return self._s[3499]! } + public var Privacy_ContactsResetConfirmation: String { return self._s[3500]! } + public var Privacy_Calls_P2PHelp: String { return self._s[3501]! } + public var Channel_DiscussionGroup_SearchPlaceholder: String { return self._s[3503]! } + public var Your_cards_expiration_year_is_invalid: String { return self._s[3504]! } + public var Common_TakePhotoOrVideo: String { return self._s[3505]! } + public var Wallet_Words_Text: String { return self._s[3506]! } + public var Call_StatusBusy: String { return self._s[3507]! } + public var Conversation_PinnedMessage: String { return self._s[3508]! } + public var AutoDownloadSettings_VoiceMessagesTitle: String { return self._s[3509]! } + public var Wallet_Configuration_BlockchainNameChangedProceed: String { return self._s[3510]! } + public var TwoStepAuth_SetupPasswordConfirmFailed: String { return self._s[3511]! } + public var Undo_ChatCleared: String { return self._s[3512]! } + public var AppleWatch_ReplyPresets: String { return self._s[3513]! } + public var Passport_DiscardMessageDescription: String { return self._s[3515]! } + public var Login_NetworkError: String { return self._s[3516]! } public func Notification_PinnedRoundMessage(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3487]!, self._r[3487]!, [_0]) + return formatWithArgumentRanges(self._s[3517]!, self._r[3517]!, [_0]) } public func Channel_AdminLog_MessageRemovedChannelUsername(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3488]!, self._r[3488]!, [_0]) + return formatWithArgumentRanges(self._s[3518]!, self._r[3518]!, [_0]) } - public var SocksProxySetup_PasswordPlaceholder: String { return self._s[3489]! } - public var Wallet_WordCheck_ViewWords: String { return self._s[3491]! } - public var Login_ResetAccountProtected_LimitExceeded: String { return self._s[3492]! } + public var SocksProxySetup_PasswordPlaceholder: String { return self._s[3519]! } + public var Wallet_WordCheck_ViewWords: String { return self._s[3521]! } + public var Login_ResetAccountProtected_LimitExceeded: String { return self._s[3522]! } public func Watch_LastSeen_YesterdayAt(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3494]!, self._r[3494]!, [_0]) + return formatWithArgumentRanges(self._s[3524]!, self._r[3524]!, [_0]) } - public var Call_ConnectionErrorMessage: String { return self._s[3495]! } - public var VoiceOver_Chat_Music: String { return self._s[3496]! } - public var SettingsSearch_Synonyms_Notifications_MessageNotificationsSound: String { return self._s[3497]! } - public var Compose_GroupTokenListPlaceholder: String { return self._s[3499]! } - public var ConversationMedia_Title: String { return self._s[3500]! } - public var EncryptionKey_Title: String { return self._s[3502]! } - public var TwoStepAuth_EnterPasswordTitle: String { return self._s[3503]! } - public var Notification_Exceptions_AddException: String { return self._s[3504]! } - public var PrivacySettings_BlockedPeersEmpty: String { return self._s[3505]! } - public var Profile_MessageLifetime1m: String { return self._s[3506]! } + public var Call_ConnectionErrorMessage: String { return self._s[3525]! } + public var VoiceOver_Chat_Music: String { return self._s[3526]! } + public var SettingsSearch_Synonyms_Notifications_MessageNotificationsSound: String { return self._s[3527]! } + public var Compose_GroupTokenListPlaceholder: String { return self._s[3529]! } + public var ConversationMedia_Title: String { return self._s[3530]! } + public var EncryptionKey_Title: String { return self._s[3532]! } + public var TwoStepAuth_EnterPasswordTitle: String { return self._s[3533]! } + public var Notification_Exceptions_AddException: String { return self._s[3534]! } + public var PrivacySettings_BlockedPeersEmpty: String { return self._s[3535]! } + public var Profile_MessageLifetime1m: String { return self._s[3536]! } public func Channel_AdminLog_MessageUnkickedName(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3507]!, self._r[3507]!, [_1]) + return formatWithArgumentRanges(self._s[3537]!, self._r[3537]!, [_1]) } - public var Month_GenMay: String { return self._s[3508]! } + public var Month_GenMay: String { return self._s[3538]! } public func LiveLocationUpdated_TodayAt(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3509]!, self._r[3509]!, [_0]) + return formatWithArgumentRanges(self._s[3539]!, self._r[3539]!, [_0]) } - public var PeopleNearby_Users: String { return self._s[3510]! } - public var Wallet_Send_AddressInfo: String { return self._s[3511]! } - public var ChannelMembers_WhoCanAddMembersAllHelp: String { return self._s[3512]! } - public var AutoDownloadSettings_ResetSettings: String { return self._s[3513]! } + public var PeopleNearby_Users: String { return self._s[3540]! } + public var Wallet_Send_AddressInfo: String { return self._s[3541]! } + public var ChannelMembers_WhoCanAddMembersAllHelp: String { return self._s[3542]! } + public var AutoDownloadSettings_ResetSettings: String { return self._s[3543]! } public func Wallet_Updated_AtDate(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3515]!, self._r[3515]!, [_0]) + return formatWithArgumentRanges(self._s[3545]!, self._r[3545]!, [_0]) } - public var Conversation_EmptyPlaceholder: String { return self._s[3516]! } - public var Passport_Address_AddPassportRegistration: String { return self._s[3517]! } - public var Notifications_ChannelNotificationsAlert: String { return self._s[3518]! } - public var ChatSettings_AutoDownloadUsingCellular: String { return self._s[3519]! } - public var Camera_TapAndHoldForVideo: String { return self._s[3520]! } - public var Channel_JoinChannel: String { return self._s[3522]! } - public var Appearance_Animations: String { return self._s[3525]! } + public var Conversation_EmptyPlaceholder: String { return self._s[3546]! } + public var Passport_Address_AddPassportRegistration: String { return self._s[3547]! } + public var Notifications_ChannelNotificationsAlert: String { return self._s[3548]! } + public var ChatSettings_AutoDownloadUsingCellular: String { return self._s[3549]! } + public var Camera_TapAndHoldForVideo: String { return self._s[3550]! } + public var Channel_JoinChannel: String { return self._s[3552]! } + public var Appearance_Animations: String { return self._s[3555]! } public func Notification_MessageLifetimeChanged(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3526]!, self._r[3526]!, [_1, _2]) + return formatWithArgumentRanges(self._s[3556]!, self._r[3556]!, [_1, _2]) } - public var Stickers_GroupStickers: String { return self._s[3528]! } - public var Appearance_ShareTheme: String { return self._s[3529]! } - public var TwoFactorSetup_Hint_Placeholder: String { return self._s[3530]! } - public var ConvertToSupergroup_HelpTitle: String { return self._s[3532]! } - public var StickerPackActionInfo_RemovedTitle: String { return self._s[3533]! } - public var Passport_Address_Street: String { return self._s[3534]! } - public var Conversation_AddContact: String { return self._s[3535]! } - public var Login_PhonePlaceholder: String { return self._s[3536]! } - public var Channel_Members_InviteLink: String { return self._s[3538]! } - public var Bot_Stop: String { return self._s[3539]! } - public var SettingsSearch_Synonyms_Proxy_UseForCalls: String { return self._s[3541]! } - public var Notification_PassportValueAddress: String { return self._s[3542]! } - public var Month_ShortJuly: String { return self._s[3543]! } - public var Passport_Address_TypeTemporaryRegistrationUploadScan: String { return self._s[3544]! } - public var Channel_AdminLog_BanSendMedia: String { return self._s[3545]! } - public var Passport_Identity_ReverseSide: String { return self._s[3546]! } - public var Watch_Stickers_Recents: String { return self._s[3549]! } - public var PrivacyLastSeenSettings_EmpryUsersPlaceholder: String { return self._s[3551]! } - public var Map_SendThisLocation: String { return self._s[3552]! } + public var Stickers_GroupStickers: String { return self._s[3558]! } + public var Appearance_ShareTheme: String { return self._s[3559]! } + public var TwoFactorSetup_Hint_Placeholder: String { return self._s[3560]! } + public var ConvertToSupergroup_HelpTitle: String { return self._s[3562]! } + public var StickerPackActionInfo_RemovedTitle: String { return self._s[3563]! } + public var Passport_Address_Street: String { return self._s[3564]! } + public var Conversation_AddContact: String { return self._s[3565]! } + public var Login_PhonePlaceholder: String { return self._s[3566]! } + public var Channel_Members_InviteLink: String { return self._s[3568]! } + public var Bot_Stop: String { return self._s[3569]! } + public var SettingsSearch_Synonyms_Proxy_UseForCalls: String { return self._s[3571]! } + public var Notification_PassportValueAddress: String { return self._s[3572]! } + public var Month_ShortJuly: String { return self._s[3573]! } + public var Passport_Address_TypeTemporaryRegistrationUploadScan: String { return self._s[3574]! } + public var Channel_AdminLog_BanSendMedia: String { return self._s[3575]! } + public var Passport_Identity_ReverseSide: String { return self._s[3576]! } + public var Watch_Stickers_Recents: String { return self._s[3579]! } + public var PrivacyLastSeenSettings_EmpryUsersPlaceholder: String { return self._s[3581]! } + public var Map_SendThisLocation: String { return self._s[3582]! } public func Time_MonthOfYear_m1(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3553]!, self._r[3553]!, [_0]) + return formatWithArgumentRanges(self._s[3583]!, self._r[3583]!, [_0]) } public func InviteText_SingleContact(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3554]!, self._r[3554]!, [_0]) + return formatWithArgumentRanges(self._s[3584]!, self._r[3584]!, [_0]) } - public var ConvertToSupergroup_Note: String { return self._s[3555]! } - public var Wallet_Intro_NotNow: String { return self._s[3556]! } + public var ConvertToSupergroup_Note: String { return self._s[3585]! } + public var Wallet_Intro_NotNow: String { return self._s[3586]! } public func FileSize_MB(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3557]!, self._r[3557]!, [_0]) + return formatWithArgumentRanges(self._s[3587]!, self._r[3587]!, [_0]) } - public var NetworkUsageSettings_GeneralDataSection: String { return self._s[3558]! } + public var NetworkUsageSettings_GeneralDataSection: String { return self._s[3588]! } public func Compatibility_SecretMediaVersionTooLow(_ _0: String, _ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3559]!, self._r[3559]!, [_0, _1]) + return formatWithArgumentRanges(self._s[3589]!, self._r[3589]!, [_0, _1]) } - public var Login_CallRequestState3: String { return self._s[3561]! } - public var Wallpaper_SearchShort: String { return self._s[3562]! } - public var SettingsSearch_Synonyms_Appearance_ColorTheme: String { return self._s[3564]! } - public var PasscodeSettings_UnlockWithFaceId: String { return self._s[3565]! } - public var Channel_BotDoesntSupportGroups: String { return self._s[3566]! } + public var Login_CallRequestState3: String { return self._s[3591]! } + public var Wallpaper_SearchShort: String { return self._s[3592]! } + public var SettingsSearch_Synonyms_Appearance_ColorTheme: String { return self._s[3594]! } + public var PasscodeSettings_UnlockWithFaceId: String { return self._s[3595]! } + public var Channel_BotDoesntSupportGroups: String { return self._s[3596]! } public func PUSH_CHAT_MESSAGE_GEOLIVE(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3567]!, self._r[3567]!, [_1, _2]) + return formatWithArgumentRanges(self._s[3597]!, self._r[3597]!, [_1, _2]) } - public var Channel_AdminLogFilter_Title: String { return self._s[3568]! } - public var Appearance_ThemePreview_Chat_4_Text: String { return self._s[3570]! } - public var Notifications_GroupNotificationsExceptions: String { return self._s[3573]! } + public var Channel_AdminLogFilter_Title: String { return self._s[3598]! } + public var Appearance_ThemePreview_Chat_4_Text: String { return self._s[3600]! } + public var Notifications_GroupNotificationsExceptions: String { return self._s[3603]! } public func FileSize_B(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3574]!, self._r[3574]!, [_0]) + return formatWithArgumentRanges(self._s[3604]!, self._r[3604]!, [_0]) } - public var Passport_CorrectErrors: String { return self._s[3575]! } - public var VoiceOver_Chat_YourAnonymousPoll: String { return self._s[3576]! } + public var Passport_CorrectErrors: String { return self._s[3605]! } + public var VoiceOver_Chat_YourAnonymousPoll: String { return self._s[3606]! } public func Channel_MessageTitleUpdated(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3577]!, self._r[3577]!, [_0]) + return formatWithArgumentRanges(self._s[3607]!, self._r[3607]!, [_0]) } - public var Map_SendMyCurrentLocation: String { return self._s[3578]! } - public var Channel_DiscussionGroup: String { return self._s[3579]! } - public var TwoFactorSetup_Email_SkipConfirmationSkip: String { return self._s[3580]! } + public var Map_SendMyCurrentLocation: String { return self._s[3608]! } + public var Channel_DiscussionGroup: String { return self._s[3609]! } + public var TwoFactorSetup_Email_SkipConfirmationSkip: String { return self._s[3610]! } public func PUSH_PINNED_CONTACT(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3581]!, self._r[3581]!, [_1, _2]) + return formatWithArgumentRanges(self._s[3611]!, self._r[3611]!, [_1, _2]) } - public var SharedMedia_SearchNoResults: String { return self._s[3582]! } - public var Permissions_NotificationsText_v0: String { return self._s[3583]! } - public var Channel_EditAdmin_PermissionDeleteMessagesOfOthers: String { return self._s[3584]! } - public var Appearance_AppIcon: String { return self._s[3585]! } - public var Appearance_ThemePreview_ChatList_3_AuthorName: String { return self._s[3586]! } - public var LoginPassword_FloodError: String { return self._s[3587]! } - public var Wallet_Send_OwnAddressAlertProceed: String { return self._s[3589]! } - public var Group_Setup_HistoryHiddenHelp: String { return self._s[3590]! } + public var SharedMedia_SearchNoResults: String { return self._s[3612]! } + public var Permissions_NotificationsText_v0: String { return self._s[3613]! } + public var Channel_EditAdmin_PermissionDeleteMessagesOfOthers: String { return self._s[3614]! } + public var Appearance_AppIcon: String { return self._s[3615]! } + public var Appearance_ThemePreview_ChatList_3_AuthorName: String { return self._s[3616]! } + public var LoginPassword_FloodError: String { return self._s[3617]! } + public var Wallet_Send_OwnAddressAlertProceed: String { return self._s[3619]! } + public var Group_Setup_HistoryHiddenHelp: String { return self._s[3620]! } public func TwoStepAuth_PendingEmailHelp(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3591]!, self._r[3591]!, [_0]) + return formatWithArgumentRanges(self._s[3621]!, self._r[3621]!, [_0]) } - public var Passport_Language_bn: String { return self._s[3592]! } + public var Passport_Language_bn: String { return self._s[3622]! } public func DialogList_SingleUploadingPhotoSuffix(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3593]!, self._r[3593]!, [_0]) + return formatWithArgumentRanges(self._s[3623]!, self._r[3623]!, [_0]) } - public var ChatList_Context_Pin: String { return self._s[3594]! } + public var ChatList_Context_Pin: String { return self._s[3624]! } public func Notification_PinnedAudioMessage(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3595]!, self._r[3595]!, [_0]) + return formatWithArgumentRanges(self._s[3625]!, self._r[3625]!, [_0]) } public func Channel_AdminLog_MessageChangedGroupStickerPack(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3596]!, self._r[3596]!, [_0]) + return formatWithArgumentRanges(self._s[3626]!, self._r[3626]!, [_0]) } - public var Wallet_Navigation_Close: String { return self._s[3597]! } - public var GroupInfo_InvitationLinkGroupFull: String { return self._s[3601]! } - public var Group_EditAdmin_PermissionChangeInfo: String { return self._s[3603]! } - public var Wallet_Month_GenDecember: String { return self._s[3604]! } - public var Contacts_PermissionsAllow: String { return self._s[3605]! } - public var ReportPeer_ReasonCopyright: String { return self._s[3606]! } - public var Channel_EditAdmin_PermissinAddAdminOn: String { return self._s[3607]! } - public var WallpaperPreview_Pattern: String { return self._s[3608]! } - public var Paint_Duplicate: String { return self._s[3609]! } - public var Passport_Address_Country: String { return self._s[3610]! } - public var Notification_RenamedChannel: String { return self._s[3612]! } - public var ChatList_Context_Unmute: String { return self._s[3613]! } - public var CheckoutInfo_ErrorPostcodeInvalid: String { return self._s[3614]! } - public var Group_MessagePhotoUpdated: String { return self._s[3615]! } - public var Channel_BanUser_PermissionSendMedia: String { return self._s[3616]! } - public var Conversation_ContextMenuBan: String { return self._s[3617]! } - public var TwoStepAuth_EmailSent: String { return self._s[3618]! } - public var MessagePoll_NoVotes: String { return self._s[3619]! } - public var Wallet_Send_ErrorNotEnoughFundsTitle: String { return self._s[3620]! } - public var Passport_Language_is: String { return self._s[3621]! } - public var PeopleNearby_UsersEmpty: String { return self._s[3623]! } - public var Tour_Text5: String { return self._s[3624]! } + public var Wallet_Navigation_Close: String { return self._s[3627]! } + public var GroupInfo_InvitationLinkGroupFull: String { return self._s[3631]! } + public var Group_EditAdmin_PermissionChangeInfo: String { return self._s[3633]! } + public var Wallet_Month_GenDecember: String { return self._s[3634]! } + public var Contacts_PermissionsAllow: String { return self._s[3635]! } + public var ReportPeer_ReasonCopyright: String { return self._s[3636]! } + public var Channel_EditAdmin_PermissinAddAdminOn: String { return self._s[3637]! } + public var WallpaperPreview_Pattern: String { return self._s[3638]! } + public var Paint_Duplicate: String { return self._s[3639]! } + public var Passport_Address_Country: String { return self._s[3640]! } + public var Notification_RenamedChannel: String { return self._s[3642]! } + public var ChatList_Context_Unmute: String { return self._s[3643]! } + public var CheckoutInfo_ErrorPostcodeInvalid: String { return self._s[3644]! } + public var Group_MessagePhotoUpdated: String { return self._s[3645]! } + public var Channel_BanUser_PermissionSendMedia: String { return self._s[3646]! } + public var Conversation_ContextMenuBan: String { return self._s[3647]! } + public var TwoStepAuth_EmailSent: String { return self._s[3648]! } + public var MessagePoll_NoVotes: String { return self._s[3649]! } + public var Wallet_Send_ErrorNotEnoughFundsTitle: String { return self._s[3650]! } + public var Passport_Language_is: String { return self._s[3652]! } + public var PeopleNearby_UsersEmpty: String { return self._s[3654]! } + public var Tour_Text5: String { return self._s[3655]! } public func Call_GroupFormat(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3626]!, self._r[3626]!, [_1, _2]) + return formatWithArgumentRanges(self._s[3658]!, self._r[3658]!, [_1, _2]) } - public var Undo_SecretChatDeleted: String { return self._s[3627]! } - public var SocksProxySetup_ShareQRCode: String { return self._s[3628]! } + public var Undo_SecretChatDeleted: String { return self._s[3659]! } + public var SocksProxySetup_ShareQRCode: String { return self._s[3660]! } public func VoiceOver_Chat_Size(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3629]!, self._r[3629]!, [_0]) + return formatWithArgumentRanges(self._s[3661]!, self._r[3661]!, [_0]) } - public var LogoutOptions_ChangePhoneNumberText: String { return self._s[3630]! } - public var Paint_Edit: String { return self._s[3632]! } - public var ScheduledMessages_ReminderNotification: String { return self._s[3634]! } - public var Undo_DeletedGroup: String { return self._s[3636]! } - public var LoginPassword_ForgotPassword: String { return self._s[3637]! } - public var Wallet_WordImport_IncorrectTitle: String { return self._s[3638]! } - public var GroupInfo_GroupNamePlaceholder: String { return self._s[3639]! } + public var Forward_ErrorDisabledForChat: String { return self._s[3662]! } + public var LogoutOptions_ChangePhoneNumberText: String { return self._s[3663]! } + public var Paint_Edit: String { return self._s[3665]! } + public var ScheduledMessages_ReminderNotification: String { return self._s[3667]! } + public var Undo_DeletedGroup: String { return self._s[3669]! } + public var LoginPassword_ForgotPassword: String { return self._s[3670]! } + public var Wallet_WordImport_IncorrectTitle: String { return self._s[3671]! } + public var GroupInfo_GroupNamePlaceholder: String { return self._s[3672]! } public func Notification_Kicked(_ _0: String, _ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3640]!, self._r[3640]!, [_0, _1]) + return formatWithArgumentRanges(self._s[3673]!, self._r[3673]!, [_0, _1]) } - public var AppWallet_TransactionInfo_FeeInfoURL: String { return self._s[3641]! } - public var Conversation_InputTextCaptionPlaceholder: String { return self._s[3642]! } - public var AutoDownloadSettings_VideoMessagesTitle: String { return self._s[3643]! } - public var Passport_Language_uz: String { return self._s[3644]! } - public var Conversation_PinMessageAlertGroup: String { return self._s[3645]! } - public var SettingsSearch_Synonyms_Privacy_GroupsAndChannels: String { return self._s[3646]! } - public var Map_StopLiveLocation: String { return self._s[3648]! } - public var VoiceOver_MessageContextSend: String { return self._s[3650]! } - public var PasscodeSettings_Help: String { return self._s[3651]! } - public var NotificationsSound_Input: String { return self._s[3652]! } - public var Share_Title: String { return self._s[3655]! } - public var LogoutOptions_Title: String { return self._s[3656]! } - public var Wallet_Send_AddressText: String { return self._s[3657]! } - public var Login_TermsOfServiceAgree: String { return self._s[3658]! } - public var Compose_NewEncryptedChatTitle: String { return self._s[3659]! } - public var Channel_AdminLog_TitleSelectedEvents: String { return self._s[3660]! } - public var Channel_EditAdmin_PermissionEditMessages: String { return self._s[3661]! } - public var EnterPasscode_EnterTitle: String { return self._s[3662]! } + public var AppWallet_TransactionInfo_FeeInfoURL: String { return self._s[3674]! } + public var Conversation_InputTextCaptionPlaceholder: String { return self._s[3675]! } + public var AutoDownloadSettings_VideoMessagesTitle: String { return self._s[3676]! } + public var Passport_Language_uz: String { return self._s[3677]! } + public var Conversation_PinMessageAlertGroup: String { return self._s[3678]! } + public var SettingsSearch_Synonyms_Privacy_GroupsAndChannels: String { return self._s[3679]! } + public var Map_StopLiveLocation: String { return self._s[3681]! } + public var VoiceOver_MessageContextSend: String { return self._s[3683]! } + public var PasscodeSettings_Help: String { return self._s[3684]! } + public var NotificationsSound_Input: String { return self._s[3685]! } + public var Share_Title: String { return self._s[3688]! } + public var LogoutOptions_Title: String { return self._s[3689]! } + public var Wallet_Send_AddressText: String { return self._s[3690]! } + public var Login_TermsOfServiceAgree: String { return self._s[3691]! } + public var Compose_NewEncryptedChatTitle: String { return self._s[3692]! } + public var Channel_AdminLog_TitleSelectedEvents: String { return self._s[3693]! } + public var Channel_EditAdmin_PermissionEditMessages: String { return self._s[3694]! } + public var EnterPasscode_EnterTitle: String { return self._s[3695]! } public func Call_PrivacyErrorMessage(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3663]!, self._r[3663]!, [_0]) + return formatWithArgumentRanges(self._s[3696]!, self._r[3696]!, [_0]) } - public var Settings_CopyPhoneNumber: String { return self._s[3664]! } - public var Conversation_AddToContacts: String { return self._s[3665]! } + public var Settings_CopyPhoneNumber: String { return self._s[3697]! } + public var Conversation_AddToContacts: String { return self._s[3698]! } public func VoiceOver_Chat_ReplyFrom(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3666]!, self._r[3666]!, [_0]) + return formatWithArgumentRanges(self._s[3699]!, self._r[3699]!, [_0]) } - public var NotificationsSound_Keys: String { return self._s[3667]! } + public var NotificationsSound_Keys: String { return self._s[3700]! } public func Call_ParticipantVersionOutdatedError(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3668]!, self._r[3668]!, [_0]) + return formatWithArgumentRanges(self._s[3701]!, self._r[3701]!, [_0]) } - public var Notification_MessageLifetime1w: String { return self._s[3669]! } - public var Message_Video: String { return self._s[3670]! } - public var AutoDownloadSettings_CellularTitle: String { return self._s[3671]! } + public var Notification_MessageLifetime1w: String { return self._s[3702]! } + public var Message_Video: String { return self._s[3703]! } + public var AutoDownloadSettings_CellularTitle: String { return self._s[3704]! } public func PUSH_CHANNEL_MESSAGE_PHOTO(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3672]!, self._r[3672]!, [_1]) + return formatWithArgumentRanges(self._s[3705]!, self._r[3705]!, [_1]) } - public var Wallet_Receive_AmountInfo: String { return self._s[3675]! } + public var Wallet_Receive_AmountInfo: String { return self._s[3708]! } public func Notification_JoinedChat(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3676]!, self._r[3676]!, [_0]) + return formatWithArgumentRanges(self._s[3709]!, self._r[3709]!, [_0]) } public func PrivacySettings_LastSeenContactsPlus(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3677]!, self._r[3677]!, [_0]) + return formatWithArgumentRanges(self._s[3710]!, self._r[3710]!, [_0]) } - public var Passport_Language_mk: String { return self._s[3678]! } + public var Passport_Language_mk: String { return self._s[3711]! } public func Wallet_Time_PreciseDate_m2(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3679]!, self._r[3679]!, [_1, _2, _3]) + return formatWithArgumentRanges(self._s[3712]!, self._r[3712]!, [_1, _2, _3]) } - public var CreatePoll_CancelConfirmation: String { return self._s[3680]! } - public var Conversation_SilentBroadcastTooltipOn: String { return self._s[3682]! } - public var PrivacyPolicy_Decline: String { return self._s[3683]! } - public var Passport_Identity_DoesNotExpire: String { return self._s[3684]! } - public var Channel_AdminLogFilter_EventsRestrictions: String { return self._s[3685]! } - public var AuthSessions_AddDeviceIntro_Action: String { return self._s[3686]! } - public var Permissions_SiriAllow_v0: String { return self._s[3688]! } - public var Wallet_Month_ShortAugust: String { return self._s[3689]! } - public var Appearance_ThemeCarouselNight: String { return self._s[3690]! } + public var CreatePoll_CancelConfirmation: String { return self._s[3713]! } + public var MessagePoll_LabelAnonymousQuiz: String { return self._s[3714]! } + public var Conversation_SilentBroadcastTooltipOn: String { return self._s[3716]! } + public var PrivacyPolicy_Decline: String { return self._s[3717]! } + public var Passport_Identity_DoesNotExpire: String { return self._s[3718]! } + public var Channel_AdminLogFilter_EventsRestrictions: String { return self._s[3719]! } + public var AuthSessions_AddDeviceIntro_Action: String { return self._s[3720]! } + public var Permissions_SiriAllow_v0: String { return self._s[3722]! } + public var Wallet_Month_ShortAugust: String { return self._s[3723]! } + public var Appearance_ThemeCarouselNight: String { return self._s[3724]! } public func LOCAL_CHAT_MESSAGE_FWDS(_ _1: String, _ _2: Int) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3691]!, self._r[3691]!, [_1, "\(_2)"]) + return formatWithArgumentRanges(self._s[3725]!, self._r[3725]!, [_1, "\(_2)"]) } public func Notification_RenamedChat(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3692]!, self._r[3692]!, [_0]) + return formatWithArgumentRanges(self._s[3726]!, self._r[3726]!, [_0]) } - public var Paint_Regular: String { return self._s[3693]! } - public var ChatSettings_AutoDownloadReset: String { return self._s[3694]! } - public var SocksProxySetup_ShareLink: String { return self._s[3695]! } - public var Wallet_Qr_Title: String { return self._s[3696]! } - public var BlockedUsers_SelectUserTitle: String { return self._s[3697]! } - public var VoiceOver_Chat_RecordModeVoiceMessage: String { return self._s[3699]! } - public var Wallet_Settings_Configuration: String { return self._s[3700]! } - public var GroupInfo_InviteByLink: String { return self._s[3701]! } - public var MessageTimer_Custom: String { return self._s[3702]! } - public var UserInfo_NotificationsDefaultEnabled: String { return self._s[3703]! } - public var Passport_Address_TypeTemporaryRegistration: String { return self._s[3705]! } - public var Conversation_SendMessage_SetReminder: String { return self._s[3706]! } - public var VoiceOver_Chat_Selected: String { return self._s[3707]! } - public var ChatSettings_AutoDownloadUsingWiFi: String { return self._s[3708]! } - public var Channel_Username_InvalidTaken: String { return self._s[3709]! } - public var Conversation_ClousStorageInfo_Description3: String { return self._s[3710]! } - public var Wallet_WordCheck_TryAgain: String { return self._s[3711]! } - public var Wallet_Info_TransactionPendingHeader: String { return self._s[3712]! } - public var Settings_ChatBackground: String { return self._s[3713]! } - public var Channel_Subscribers_Title: String { return self._s[3714]! } - public var Wallet_Receive_InvoiceUrlHeader: String { return self._s[3715]! } - public var ApplyLanguage_ChangeLanguageTitle: String { return self._s[3716]! } - public var Watch_ConnectionDescription: String { return self._s[3717]! } - public var OldChannels_NoticeText: String { return self._s[3720]! } - public var Wallet_Configuration_ApplyErrorTitle: String { return self._s[3721]! } - public var IntentsSettings_SuggestBy: String { return self._s[3723]! } - public var Theme_ThemeChangedText: String { return self._s[3724]! } - public var ChatList_ArchivedChatsTitle: String { return self._s[3725]! } - public var Wallpaper_ResetWallpapers: String { return self._s[3726]! } - public var Wallet_Send_TransactionInProgress: String { return self._s[3727]! } - public var EditProfile_Title: String { return self._s[3728]! } - public var NotificationsSound_Bamboo: String { return self._s[3730]! } - public var Channel_AdminLog_MessagePreviousMessage: String { return self._s[3732]! } - public var Login_SmsRequestState2: String { return self._s[3733]! } - public var Passport_Language_ar: String { return self._s[3734]! } + public var Paint_Regular: String { return self._s[3727]! } + public var ChatSettings_AutoDownloadReset: String { return self._s[3728]! } + public var SocksProxySetup_ShareLink: String { return self._s[3729]! } + public var Wallet_Qr_Title: String { return self._s[3730]! } + public var BlockedUsers_SelectUserTitle: String { return self._s[3731]! } + public var VoiceOver_Chat_RecordModeVoiceMessage: String { return self._s[3733]! } + public var Wallet_Settings_Configuration: String { return self._s[3734]! } + public var GroupInfo_InviteByLink: String { return self._s[3735]! } + public var MessageTimer_Custom: String { return self._s[3736]! } + public var UserInfo_NotificationsDefaultEnabled: String { return self._s[3737]! } + public var Conversation_StopQuizConfirmationTitle: String { return self._s[3738]! } + public var Passport_Address_TypeTemporaryRegistration: String { return self._s[3740]! } + public var Conversation_SendMessage_SetReminder: String { return self._s[3741]! } + public var VoiceOver_Chat_Selected: String { return self._s[3742]! } + public var ChatSettings_AutoDownloadUsingWiFi: String { return self._s[3743]! } + public var Channel_Username_InvalidTaken: String { return self._s[3744]! } + public var Conversation_ClousStorageInfo_Description3: String { return self._s[3745]! } + public var Wallet_WordCheck_TryAgain: String { return self._s[3746]! } + public var Wallet_Info_TransactionPendingHeader: String { return self._s[3747]! } + public var Settings_ChatBackground: String { return self._s[3748]! } + public var Channel_Subscribers_Title: String { return self._s[3749]! } + public var Wallet_Receive_InvoiceUrlHeader: String { return self._s[3750]! } + public var ApplyLanguage_ChangeLanguageTitle: String { return self._s[3751]! } + public var Watch_ConnectionDescription: String { return self._s[3752]! } + public var OldChannels_NoticeText: String { return self._s[3755]! } + public var Wallet_Configuration_ApplyErrorTitle: String { return self._s[3756]! } + public var IntentsSettings_SuggestBy: String { return self._s[3758]! } + public var Theme_ThemeChangedText: String { return self._s[3759]! } + public var ChatList_ArchivedChatsTitle: String { return self._s[3760]! } + public var Wallpaper_ResetWallpapers: String { return self._s[3761]! } + public var Wallet_Send_TransactionInProgress: String { return self._s[3762]! } + public var EditProfile_Title: String { return self._s[3763]! } + public var NotificationsSound_Bamboo: String { return self._s[3765]! } + public var Channel_AdminLog_MessagePreviousMessage: String { return self._s[3767]! } + public var Login_SmsRequestState2: String { return self._s[3768]! } + public var Passport_Language_ar: String { return self._s[3769]! } public func Message_AuthorPinnedGame(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3735]!, self._r[3735]!, [_0]) + return formatWithArgumentRanges(self._s[3770]!, self._r[3770]!, [_0]) } - public var SettingsSearch_Synonyms_EditProfile_Title: String { return self._s[3736]! } - public var Wallet_Created_Text: String { return self._s[3737]! } - public var Conversation_MessageDialogEdit: String { return self._s[3739]! } - public var Wallet_Created_Proceed: String { return self._s[3740]! } - public var Wallet_Words_Done: String { return self._s[3741]! } - public var VoiceOver_Media_PlaybackPause: String { return self._s[3742]! } + public var SettingsSearch_Synonyms_EditProfile_Title: String { return self._s[3771]! } + public var Wallet_Created_Text: String { return self._s[3772]! } + public var Conversation_MessageDialogEdit: String { return self._s[3774]! } + public var Wallet_Created_Proceed: String { return self._s[3775]! } + public var Wallet_Words_Done: String { return self._s[3776]! } + public var VoiceOver_Media_PlaybackPause: String { return self._s[3777]! } public func PUSH_AUTH_UNKNOWN(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3743]!, self._r[3743]!, [_1]) + return formatWithArgumentRanges(self._s[3778]!, self._r[3778]!, [_1]) } - public var Common_Close: String { return self._s[3744]! } - public var GroupInfo_PublicLink: String { return self._s[3745]! } - public var Channel_OwnershipTransfer_ErrorPrivacyRestricted: String { return self._s[3746]! } - public var SettingsSearch_Synonyms_Notifications_GroupNotificationsPreview: String { return self._s[3747]! } + public var Common_Close: String { return self._s[3779]! } + public var GroupInfo_PublicLink: String { return self._s[3780]! } + public var Channel_OwnershipTransfer_ErrorPrivacyRestricted: String { return self._s[3781]! } + public var SettingsSearch_Synonyms_Notifications_GroupNotificationsPreview: String { return self._s[3782]! } public func Channel_AdminLog_MessageToggleInvitesOff(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3751]!, self._r[3751]!, [_0]) + return formatWithArgumentRanges(self._s[3786]!, self._r[3786]!, [_0]) } - public var UserInfo_About_Placeholder: String { return self._s[3752]! } + public var UserInfo_About_Placeholder: String { return self._s[3787]! } public func Conversation_FileHowToText(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3753]!, self._r[3753]!, [_0]) + return formatWithArgumentRanges(self._s[3788]!, self._r[3788]!, [_0]) } - public var GroupInfo_Permissions_SectionTitle: String { return self._s[3754]! } - public var Channel_Info_Banned: String { return self._s[3756]! } + public var GroupInfo_Permissions_SectionTitle: String { return self._s[3789]! } + public var Channel_Info_Banned: String { return self._s[3791]! } public func Time_MonthOfYear_m11(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3757]!, self._r[3757]!, [_0]) + return formatWithArgumentRanges(self._s[3792]!, self._r[3792]!, [_0]) } - public var Appearance_Other: String { return self._s[3758]! } - public var Passport_Language_my: String { return self._s[3759]! } - public var Group_Setup_BasicHistoryHiddenHelp: String { return self._s[3760]! } + public var Appearance_Other: String { return self._s[3793]! } + public var Passport_Language_my: String { return self._s[3794]! } + public var Group_Setup_BasicHistoryHiddenHelp: String { return self._s[3795]! } public func Time_PreciseDate_m9(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3761]!, self._r[3761]!, [_1, _2, _3]) + return formatWithArgumentRanges(self._s[3796]!, self._r[3796]!, [_1, _2, _3]) } - public var SettingsSearch_Synonyms_Privacy_PasscodeAndFaceId: String { return self._s[3762]! } - public var IntentsSettings_SuggestedAndSpotlightChatsInfo: String { return self._s[3763]! } - public var Preview_CopyAddress: String { return self._s[3764]! } + public var SettingsSearch_Synonyms_Privacy_PasscodeAndFaceId: String { return self._s[3797]! } + public var IntentsSettings_SuggestedAndSpotlightChatsInfo: String { return self._s[3798]! } + public var Preview_CopyAddress: String { return self._s[3799]! } public func DialogList_SinglePlayingGameSuffix(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3765]!, self._r[3765]!, [_0]) - } - public var KeyCommand_JumpToPreviousChat: String { return self._s[3766]! } - public var UserInfo_BotSettings: String { return self._s[3767]! } - public var LiveLocation_MenuStopAll: String { return self._s[3769]! } - public var Passport_PasswordCreate: String { return self._s[3770]! } - public var StickerSettings_MaskContextInfo: String { return self._s[3771]! } - public var Message_PinnedLocationMessage: String { return self._s[3772]! } - public var Map_Satellite: String { return self._s[3773]! } - public var Watch_Message_Unsupported: String { return self._s[3774]! } - public var Username_TooManyPublicUsernamesError: String { return self._s[3775]! } - public var TwoStepAuth_EnterPasswordInvalid: String { return self._s[3776]! } - public func Notification_PinnedTextMessage(_ _0: String, _ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3777]!, self._r[3777]!, [_0, _1]) - } - public func Conversation_OpenBotLinkText(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3778]!, self._r[3778]!, [_0]) - } - public var Wallet_WordImport_Continue: String { return self._s[3779]! } - public func TwoFactorSetup_EmailVerification_Text(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3780]!, self._r[3780]!, [_0]) - } - public var Notifications_ChannelNotificationsHelp: String { return self._s[3781]! } - public var Privacy_Calls_P2PContacts: String { return self._s[3782]! } - public var NotificationsSound_None: String { return self._s[3783]! } - public var Wallet_TransactionInfo_StorageFeeHeader: String { return self._s[3784]! } - public var Channel_DiscussionGroup_UnlinkGroup: String { return self._s[3786]! } - public var AccessDenied_VoiceMicrophone: String { return self._s[3787]! } - public func ApplyLanguage_ChangeLanguageAlreadyActive(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3788]!, self._r[3788]!, [_1]) - } - public var Cache_Indexing: String { return self._s[3789]! } - public var DialogList_RecentTitlePeople: String { return self._s[3791]! } - public var DialogList_EncryptionRejected: String { return self._s[3792]! } - public var GroupInfo_Administrators: String { return self._s[3793]! } - public var Passport_ScanPassportHelp: String { return self._s[3794]! } - public var Application_Name: String { return self._s[3795]! } - public var Channel_AdminLogFilter_ChannelEventsInfo: String { return self._s[3796]! } - public var Appearance_ThemeCarouselDay: String { return self._s[3798]! } - public var Passport_Identity_TranslationHelp: String { return self._s[3799]! } - public func VoiceOver_Chat_VideoMessageFrom(_ _0: String) -> (String, [(Int, NSRange)]) { return formatWithArgumentRanges(self._s[3800]!, self._r[3800]!, [_0]) } + public var KeyCommand_JumpToPreviousChat: String { return self._s[3801]! } + public var UserInfo_BotSettings: String { return self._s[3802]! } + public var LiveLocation_MenuStopAll: String { return self._s[3804]! } + public var Passport_PasswordCreate: String { return self._s[3805]! } + public var StickerSettings_MaskContextInfo: String { return self._s[3806]! } + public var Message_PinnedLocationMessage: String { return self._s[3807]! } + public var Map_Satellite: String { return self._s[3808]! } + public var Watch_Message_Unsupported: String { return self._s[3809]! } + public var Username_TooManyPublicUsernamesError: String { return self._s[3810]! } + public var TwoStepAuth_EnterPasswordInvalid: String { return self._s[3811]! } + public func Notification_PinnedTextMessage(_ _0: String, _ _1: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[3812]!, self._r[3812]!, [_0, _1]) + } + public func Conversation_OpenBotLinkText(_ _0: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[3813]!, self._r[3813]!, [_0]) + } + public var Wallet_WordImport_Continue: String { return self._s[3814]! } + public func TwoFactorSetup_EmailVerification_Text(_ _0: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[3815]!, self._r[3815]!, [_0]) + } + public var Notifications_ChannelNotificationsHelp: String { return self._s[3816]! } + public var Privacy_Calls_P2PContacts: String { return self._s[3817]! } + public var NotificationsSound_None: String { return self._s[3818]! } + public var Wallet_TransactionInfo_StorageFeeHeader: String { return self._s[3819]! } + public var Channel_DiscussionGroup_UnlinkGroup: String { return self._s[3821]! } + public var AccessDenied_VoiceMicrophone: String { return self._s[3822]! } + public func ApplyLanguage_ChangeLanguageAlreadyActive(_ _1: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[3823]!, self._r[3823]!, [_1]) + } + public var Cache_Indexing: String { return self._s[3824]! } + public var DialogList_RecentTitlePeople: String { return self._s[3826]! } + public var DialogList_EncryptionRejected: String { return self._s[3827]! } + public var GroupInfo_Administrators: String { return self._s[3828]! } + public var Passport_ScanPassportHelp: String { return self._s[3829]! } + public var Application_Name: String { return self._s[3830]! } + public var Channel_AdminLogFilter_ChannelEventsInfo: String { return self._s[3831]! } + public var Appearance_ThemeCarouselDay: String { return self._s[3833]! } + public var Passport_Identity_TranslationHelp: String { return self._s[3834]! } + public func VoiceOver_Chat_VideoMessageFrom(_ _0: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[3835]!, self._r[3835]!, [_0]) + } public func Notification_JoinedGroupByLink(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3801]!, self._r[3801]!, [_0]) + return formatWithArgumentRanges(self._s[3836]!, self._r[3836]!, [_0]) } public func DialogList_EncryptedChatStartedOutgoing(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3802]!, self._r[3802]!, [_0]) + return formatWithArgumentRanges(self._s[3837]!, self._r[3837]!, [_0]) } - public var Channel_EditAdmin_PermissionDeleteMessages: String { return self._s[3803]! } - public var Privacy_ChatsTitle: String { return self._s[3804]! } - public var DialogList_ClearHistoryConfirmation: String { return self._s[3805]! } - public var SettingsSearch_Synonyms_Data_Storage_ClearCache: String { return self._s[3806]! } - public var Watch_Suggestion_HoldOn: String { return self._s[3807]! } - public var Group_EditAdmin_TransferOwnership: String { return self._s[3808]! } - public var WebBrowser_Title: String { return self._s[3809]! } - public var Group_LinkedChannel: String { return self._s[3810]! } - public var VoiceOver_Chat_SeenByRecipient: String { return self._s[3811]! } - public var SocksProxySetup_RequiredCredentials: String { return self._s[3812]! } - public var Passport_Address_TypeRentalAgreementUploadScan: String { return self._s[3813]! } - public var Appearance_TextSize_UseSystem: String { return self._s[3814]! } - public var TwoStepAuth_EmailSkipAlert: String { return self._s[3815]! } - public var ScheduledMessages_RemindersTitle: String { return self._s[3817]! } - public var Channel_Setup_TypePublic: String { return self._s[3819]! } + public var Channel_EditAdmin_PermissionDeleteMessages: String { return self._s[3838]! } + public var Privacy_ChatsTitle: String { return self._s[3839]! } + public var DialogList_ClearHistoryConfirmation: String { return self._s[3840]! } + public var SettingsSearch_Synonyms_Data_Storage_ClearCache: String { return self._s[3841]! } + public var Watch_Suggestion_HoldOn: String { return self._s[3842]! } + public var Group_EditAdmin_TransferOwnership: String { return self._s[3843]! } + public var WebBrowser_Title: String { return self._s[3844]! } + public var Group_LinkedChannel: String { return self._s[3845]! } + public var VoiceOver_Chat_SeenByRecipient: String { return self._s[3846]! } + public var SocksProxySetup_RequiredCredentials: String { return self._s[3847]! } + public var Passport_Address_TypeRentalAgreementUploadScan: String { return self._s[3848]! } + public var Appearance_TextSize_UseSystem: String { return self._s[3849]! } + public var TwoStepAuth_EmailSkipAlert: String { return self._s[3850]! } + public var ScheduledMessages_RemindersTitle: String { return self._s[3852]! } + public var Channel_Setup_TypePublic: String { return self._s[3854]! } public func Channel_AdminLog_MessageToggleInvitesOn(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3820]!, self._r[3820]!, [_0]) + return formatWithArgumentRanges(self._s[3855]!, self._r[3855]!, [_0]) } - public var Channel_TypeSetup_Title: String { return self._s[3822]! } - public var Map_OpenInMaps: String { return self._s[3824]! } + public var Channel_TypeSetup_Title: String { return self._s[3857]! } + public var MessagePoll_ViewResults: String { return self._s[3858]! } + public var Map_OpenInMaps: String { return self._s[3860]! } public func PUSH_PINNED_NOTEXT(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3825]!, self._r[3825]!, [_1]) + return formatWithArgumentRanges(self._s[3861]!, self._r[3861]!, [_1]) } - public var NotificationsSound_Tremolo: String { return self._s[3827]! } + public var NotificationsSound_Tremolo: String { return self._s[3863]! } public func Date_ChatDateHeaderYear(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3828]!, self._r[3828]!, [_1, _2, _3]) + return formatWithArgumentRanges(self._s[3864]!, self._r[3864]!, [_1, _2, _3]) } - public var ConversationProfile_UnknownAddMemberError: String { return self._s[3829]! } - public var Channel_OwnershipTransfer_PasswordPlaceholder: String { return self._s[3830]! } - public var Passport_PasswordHelp: String { return self._s[3831]! } - public var Login_CodeExpiredError: String { return self._s[3832]! } - public var Channel_EditAdmin_PermissionChangeInfo: String { return self._s[3833]! } - public var Conversation_TitleUnmute: String { return self._s[3834]! } - public var Passport_Identity_ScansHelp: String { return self._s[3835]! } - public var Passport_Language_lo: String { return self._s[3836]! } - public var Camera_FlashAuto: String { return self._s[3837]! } - public var Conversation_OpenBotLinkOpen: String { return self._s[3838]! } - public var Common_Cancel: String { return self._s[3839]! } - public var DialogList_SavedMessagesTooltip: String { return self._s[3840]! } - public var TwoStepAuth_SetupPasswordTitle: String { return self._s[3841]! } - public var Appearance_TintAllColors: String { return self._s[3842]! } + public var ConversationProfile_UnknownAddMemberError: String { return self._s[3865]! } + public var Channel_OwnershipTransfer_PasswordPlaceholder: String { return self._s[3866]! } + public var Passport_PasswordHelp: String { return self._s[3867]! } + public var Login_CodeExpiredError: String { return self._s[3868]! } + public var Channel_EditAdmin_PermissionChangeInfo: String { return self._s[3869]! } + public var Conversation_TitleUnmute: String { return self._s[3870]! } + public var Passport_Identity_ScansHelp: String { return self._s[3871]! } + public var Passport_Language_lo: String { return self._s[3872]! } + public var Camera_FlashAuto: String { return self._s[3873]! } + public var Conversation_OpenBotLinkOpen: String { return self._s[3874]! } + public var Common_Cancel: String { return self._s[3875]! } + public var DialogList_SavedMessagesTooltip: String { return self._s[3876]! } + public var TwoStepAuth_SetupPasswordTitle: String { return self._s[3877]! } + public var Appearance_TintAllColors: String { return self._s[3878]! } public func PUSH_MESSAGE_FWD(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3843]!, self._r[3843]!, [_1]) + return formatWithArgumentRanges(self._s[3879]!, self._r[3879]!, [_1]) } - public var Conversation_ReportSpamConfirmation: String { return self._s[3844]! } - public var ChatSettings_Title: String { return self._s[3846]! } - public var Passport_PasswordReset: String { return self._s[3847]! } - public var SocksProxySetup_TypeNone: String { return self._s[3848]! } - public var EditTheme_Title: String { return self._s[3850]! } - public var PhoneNumberHelp_Help: String { return self._s[3851]! } - public var Checkout_EnterPassword: String { return self._s[3852]! } - public var Share_AuthTitle: String { return self._s[3854]! } - public var Activity_UploadingDocument: String { return self._s[3855]! } - public var State_Connecting: String { return self._s[3856]! } - public var Profile_MessageLifetime1w: String { return self._s[3857]! } - public var Conversation_ContextMenuReport: String { return self._s[3858]! } - public var CheckoutInfo_ReceiverInfoPhone: String { return self._s[3859]! } - public var AutoNightTheme_ScheduledTo: String { return self._s[3860]! } + public var Conversation_ReportSpamConfirmation: String { return self._s[3880]! } + public var ChatSettings_Title: String { return self._s[3882]! } + public var Passport_PasswordReset: String { return self._s[3883]! } + public var SocksProxySetup_TypeNone: String { return self._s[3884]! } + public var EditTheme_Title: String { return self._s[3887]! } + public var PhoneNumberHelp_Help: String { return self._s[3888]! } + public var Checkout_EnterPassword: String { return self._s[3889]! } + public var Activity_UploadingDocument: String { return self._s[3891]! } + public var Share_AuthTitle: String { return self._s[3892]! } + public var State_Connecting: String { return self._s[3893]! } + public var Profile_MessageLifetime1w: String { return self._s[3894]! } + public var Conversation_ContextMenuReport: String { return self._s[3895]! } + public var CheckoutInfo_ReceiverInfoPhone: String { return self._s[3896]! } + public var AutoNightTheme_ScheduledTo: String { return self._s[3897]! } public func VoiceOver_Chat_AnonymousPollFrom(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3861]!, self._r[3861]!, [_0]) + return formatWithArgumentRanges(self._s[3898]!, self._r[3898]!, [_0]) } - public var AuthSessions_Terminate: String { return self._s[3862]! } - public var Wallet_WordImport_CanNotRemember: String { return self._s[3863]! } - public var Checkout_NewCard_CardholderNamePlaceholder: String { return self._s[3865]! } - public var KeyCommand_JumpToPreviousUnreadChat: String { return self._s[3866]! } - public var PhotoEditor_Set: String { return self._s[3867]! } - public var EmptyGroupInfo_Title: String { return self._s[3868]! } - public var Login_PadPhoneHelp: String { return self._s[3869]! } - public var AutoDownloadSettings_TypeGroupChats: String { return self._s[3871]! } - public var PrivacyPolicy_DeclineLastWarning: String { return self._s[3873]! } - public var NotificationsSound_Complete: String { return self._s[3874]! } - public var SettingsSearch_Synonyms_Privacy_Data_Title: String { return self._s[3875]! } - public var Group_Info_AdminLog: String { return self._s[3876]! } - public var GroupPermission_NotAvailableInPublicGroups: String { return self._s[3877]! } + public var AuthSessions_Terminate: String { return self._s[3899]! } + public var Wallet_WordImport_CanNotRemember: String { return self._s[3900]! } + public var Checkout_NewCard_CardholderNamePlaceholder: String { return self._s[3902]! } + public var KeyCommand_JumpToPreviousUnreadChat: String { return self._s[3903]! } + public var PhotoEditor_Set: String { return self._s[3904]! } + public var EmptyGroupInfo_Title: String { return self._s[3905]! } + public var Login_PadPhoneHelp: String { return self._s[3906]! } + public var AutoDownloadSettings_TypeGroupChats: String { return self._s[3908]! } + public var PrivacyPolicy_DeclineLastWarning: String { return self._s[3910]! } + public var NotificationsSound_Complete: String { return self._s[3911]! } + public var SettingsSearch_Synonyms_Privacy_Data_Title: String { return self._s[3912]! } + public var Group_Info_AdminLog: String { return self._s[3913]! } + public var GroupPermission_NotAvailableInPublicGroups: String { return self._s[3914]! } public func Wallet_Time_PreciseDate_m11(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3878]!, self._r[3878]!, [_1, _2, _3]) + return formatWithArgumentRanges(self._s[3915]!, self._r[3915]!, [_1, _2, _3]) } - public var Channel_AdminLog_InfoPanelAlertText: String { return self._s[3879]! } - public var Group_Location_CreateInThisPlace: String { return self._s[3881]! } - public var Conversation_Admin: String { return self._s[3882]! } - public var Conversation_GifTooltip: String { return self._s[3883]! } - public var Passport_NotLoggedInMessage: String { return self._s[3884]! } + public var Channel_AdminLog_InfoPanelAlertText: String { return self._s[3916]! } + public var Group_Location_CreateInThisPlace: String { return self._s[3918]! } + public var Conversation_Admin: String { return self._s[3919]! } + public var Conversation_GifTooltip: String { return self._s[3920]! } + public var Passport_NotLoggedInMessage: String { return self._s[3921]! } public func AutoDownloadSettings_OnFor(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3886]!, self._r[3886]!, [_0]) + return formatWithArgumentRanges(self._s[3923]!, self._r[3923]!, [_0]) } - public var Profile_MessageLifetimeForever: String { return self._s[3887]! } - public var SharedMedia_EmptyTitle: String { return self._s[3889]! } - public var Channel_Edit_PrivatePublicLinkAlert: String { return self._s[3891]! } - public var Username_Help: String { return self._s[3892]! } - public var DialogList_LanguageTooltip: String { return self._s[3894]! } - public var Map_LoadError: String { return self._s[3895]! } - public var Login_PhoneNumberAlreadyAuthorized: String { return self._s[3896]! } - public var Channel_AdminLog_AddMembers: String { return self._s[3897]! } - public var ArchivedChats_IntroTitle2: String { return self._s[3898]! } - public var Notification_Exceptions_NewException: String { return self._s[3899]! } - public var TwoStepAuth_EmailTitle: String { return self._s[3900]! } - public var WatchRemote_AlertText: String { return self._s[3901]! } + public var Profile_MessageLifetimeForever: String { return self._s[3924]! } + public var SharedMedia_EmptyTitle: String { return self._s[3926]! } + public var Channel_Edit_PrivatePublicLinkAlert: String { return self._s[3928]! } + public var Username_Help: String { return self._s[3929]! } + public var DialogList_LanguageTooltip: String { return self._s[3931]! } + public var Map_LoadError: String { return self._s[3932]! } + public var Login_PhoneNumberAlreadyAuthorized: String { return self._s[3933]! } + public var Channel_AdminLog_AddMembers: String { return self._s[3934]! } + public var ArchivedChats_IntroTitle2: String { return self._s[3935]! } + public var Notification_Exceptions_NewException: String { return self._s[3936]! } + public var TwoStepAuth_EmailTitle: String { return self._s[3937]! } + public var WatchRemote_AlertText: String { return self._s[3938]! } public func Wallet_Send_ConfirmationText(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3902]!, self._r[3902]!, [_1, _2, _3]) + return formatWithArgumentRanges(self._s[3939]!, self._r[3939]!, [_1, _2, _3]) + } + public var ChatSettings_ConnectionType_Title: String { return self._s[3943]! } + public func PUSH_PINNED_QUIZ(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[3944]!, self._r[3944]!, [_1, _2]) } - public var ChatSettings_ConnectionType_Title: String { return self._s[3906]! } - public var WebBrowser_DefaultBrowser: String { return self._s[3907]! } public func Settings_CheckPhoneNumberTitle(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3908]!, self._r[3908]!, [_0]) + return formatWithArgumentRanges(self._s[3945]!, self._r[3945]!, [_0]) } - public var SettingsSearch_Synonyms_Calls_CallTab: String { return self._s[3909]! } - public var Passport_Address_CountryPlaceholder: String { return self._s[3910]! } + public var SettingsSearch_Synonyms_Calls_CallTab: String { return self._s[3946]! } + public var WebBrowser_DefaultBrowser: String { return self._s[3947]! } + public var Passport_Address_CountryPlaceholder: String { return self._s[3948]! } public func DialogList_AwaitingEncryption(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3911]!, self._r[3911]!, [_0]) + return formatWithArgumentRanges(self._s[3949]!, self._r[3949]!, [_0]) } public func Time_PreciseDate_m6(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3912]!, self._r[3912]!, [_1, _2, _3]) + return formatWithArgumentRanges(self._s[3950]!, self._r[3950]!, [_1, _2, _3]) } - public var Group_AdminLog_EmptyText: String { return self._s[3913]! } - public var SettingsSearch_Synonyms_Appearance_Title: String { return self._s[3914]! } - public var Conversation_PrivateChannelTooltip: String { return self._s[3916]! } - public var Wallet_Created_ExportErrorText: String { return self._s[3917]! } - public var ChatList_UndoArchiveText1: String { return self._s[3918]! } - public var AccessDenied_VideoMicrophone: String { return self._s[3919]! } - public var Conversation_ContextMenuStickerPackAdd: String { return self._s[3920]! } - public var Cache_ClearNone: String { return self._s[3921]! } - public var SocksProxySetup_FailedToConnect: String { return self._s[3922]! } - public var Permissions_NotificationsTitle_v0: String { return self._s[3923]! } + public var Group_AdminLog_EmptyText: String { return self._s[3951]! } + public var SettingsSearch_Synonyms_Appearance_Title: String { return self._s[3952]! } + public var Conversation_PrivateChannelTooltip: String { return self._s[3954]! } + public var Wallet_Created_ExportErrorText: String { return self._s[3955]! } + public var ChatList_UndoArchiveText1: String { return self._s[3956]! } + public var AccessDenied_VideoMicrophone: String { return self._s[3957]! } + public var Conversation_ContextMenuStickerPackAdd: String { return self._s[3958]! } + public var Cache_ClearNone: String { return self._s[3959]! } + public var SocksProxySetup_FailedToConnect: String { return self._s[3960]! } + public var Permissions_NotificationsTitle_v0: String { return self._s[3961]! } public func Channel_AdminLog_MessageEdited(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3924]!, self._r[3924]!, [_0]) - } - public var Passport_Identity_Country: String { return self._s[3925]! } - public func ChatSettings_AutoDownloadSettings_TypeFile(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3926]!, self._r[3926]!, [_0]) - } - public func Notification_CreatedChat(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3927]!, self._r[3927]!, [_0]) - } - public var Exceptions_AddToExceptions: String { return self._s[3928]! } - public var AccessDenied_Settings: String { return self._s[3929]! } - public var Passport_Address_TypeUtilityBillUploadScan: String { return self._s[3930]! } - public var Month_ShortMay: String { return self._s[3931]! } - public var Compose_NewGroup: String { return self._s[3933]! } - public var Group_Setup_TypePrivate: String { return self._s[3935]! } - public var Login_PadPhoneHelpTitle: String { return self._s[3937]! } - public var Appearance_ThemeDayClassic: String { return self._s[3938]! } - public var Channel_AdminLog_MessagePreviousCaption: String { return self._s[3939]! } - public var AutoDownloadSettings_OffForAll: String { return self._s[3940]! } - public var Privacy_GroupsAndChannels_WhoCanAddMe: String { return self._s[3941]! } - public var Conversation_typing: String { return self._s[3943]! } - public var Undo_ScheduledMessagesCleared: String { return self._s[3944]! } - public var Paint_Masks: String { return self._s[3945]! } - public var Contacts_DeselectAll: String { return self._s[3946]! } - public func Wallet_Updated_YesterdayAt(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3947]!, self._r[3947]!, [_0]) - } - public var Username_InvalidTaken: String { return self._s[3948]! } - public var Call_StatusNoAnswer: String { return self._s[3949]! } - public var TwoStepAuth_EmailAddSuccess: String { return self._s[3950]! } - public var SettingsSearch_Synonyms_Privacy_BlockedUsers: String { return self._s[3951]! } - public var Passport_Identity_Selfie: String { return self._s[3952]! } - public var Login_InfoLastNamePlaceholder: String { return self._s[3953]! } - public var Privacy_SecretChatsLinkPreviewsHelp: String { return self._s[3954]! } - public var Conversation_ClearSecretHistory: String { return self._s[3955]! } - public var PeopleNearby_Description: String { return self._s[3957]! } - public var NetworkUsageSettings_Title: String { return self._s[3958]! } - public var Your_cards_security_code_is_invalid: String { return self._s[3960]! } - public func Notification_LeftChannel(_ _0: String) -> (String, [(Int, NSRange)]) { return formatWithArgumentRanges(self._s[3962]!, self._r[3962]!, [_0]) } + public var Passport_Identity_Country: String { return self._s[3963]! } + public func ChatSettings_AutoDownloadSettings_TypeFile(_ _0: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[3964]!, self._r[3964]!, [_0]) + } + public func Notification_CreatedChat(_ _0: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[3965]!, self._r[3965]!, [_0]) + } + public var Exceptions_AddToExceptions: String { return self._s[3966]! } + public var AccessDenied_Settings: String { return self._s[3967]! } + public var Passport_Address_TypeUtilityBillUploadScan: String { return self._s[3968]! } + public var Month_ShortMay: String { return self._s[3969]! } + public var Compose_NewGroup: String { return self._s[3971]! } + public var Group_Setup_TypePrivate: String { return self._s[3973]! } + public var Login_PadPhoneHelpTitle: String { return self._s[3975]! } + public var Appearance_ThemeDayClassic: String { return self._s[3976]! } + public var Channel_AdminLog_MessagePreviousCaption: String { return self._s[3977]! } + public var AutoDownloadSettings_OffForAll: String { return self._s[3978]! } + public var Privacy_GroupsAndChannels_WhoCanAddMe: String { return self._s[3979]! } + public var Conversation_typing: String { return self._s[3981]! } + public var Undo_ScheduledMessagesCleared: String { return self._s[3982]! } + public var Paint_Masks: String { return self._s[3983]! } + public var Contacts_DeselectAll: String { return self._s[3984]! } + public func Wallet_Updated_YesterdayAt(_ _0: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[3985]!, self._r[3985]!, [_0]) + } + public var CreatePoll_MultipleChoiceQuizAlert: String { return self._s[3986]! } + public var Username_InvalidTaken: String { return self._s[3987]! } + public var Call_StatusNoAnswer: String { return self._s[3988]! } + public var TwoStepAuth_EmailAddSuccess: String { return self._s[3989]! } + public var SettingsSearch_Synonyms_Privacy_BlockedUsers: String { return self._s[3990]! } + public var Passport_Identity_Selfie: String { return self._s[3991]! } + public var Login_InfoLastNamePlaceholder: String { return self._s[3992]! } + public var Privacy_SecretChatsLinkPreviewsHelp: String { return self._s[3993]! } + public var Conversation_ClearSecretHistory: String { return self._s[3994]! } + public var PeopleNearby_Description: String { return self._s[3996]! } + public var NetworkUsageSettings_Title: String { return self._s[3997]! } + public var Your_cards_security_code_is_invalid: String { return self._s[3999]! } + public func Notification_LeftChannel(_ _0: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[4001]!, self._r[4001]!, [_0]) + } public func Call_CallInProgressMessage(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3963]!, self._r[3963]!, [_1, _2]) + return formatWithArgumentRanges(self._s[4002]!, self._r[4002]!, [_1, _2]) } - public var SaveIncomingPhotosSettings_From: String { return self._s[3965]! } - public var VoiceOver_Navigation_Search: String { return self._s[3966]! } - public var Map_LiveLocationTitle: String { return self._s[3967]! } - public var Login_InfoAvatarAdd: String { return self._s[3968]! } - public var Passport_Identity_FilesView: String { return self._s[3969]! } - public var UserInfo_GenericPhoneLabel: String { return self._s[3970]! } - public var Privacy_Calls_NeverAllow: String { return self._s[3971]! } - public var VoiceOver_Chat_File: String { return self._s[3972]! } - public var Wallet_Settings_DeleteWalletInfo: String { return self._s[3973]! } + public var SaveIncomingPhotosSettings_From: String { return self._s[4004]! } + public var VoiceOver_Navigation_Search: String { return self._s[4005]! } + public var Map_LiveLocationTitle: String { return self._s[4006]! } + public var Login_InfoAvatarAdd: String { return self._s[4007]! } + public var Passport_Identity_FilesView: String { return self._s[4008]! } + public var UserInfo_GenericPhoneLabel: String { return self._s[4009]! } + public var Privacy_Calls_NeverAllow: String { return self._s[4010]! } + public var VoiceOver_Chat_File: String { return self._s[4011]! } + public var Wallet_Settings_DeleteWalletInfo: String { return self._s[4012]! } public func Contacts_AddPhoneNumber(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3974]!, self._r[3974]!, [_0]) - } - public var ContactInfo_PhoneNumberHidden: String { return self._s[3975]! } - public var TwoStepAuth_ConfirmationText: String { return self._s[3976]! } - public var ChatSettings_AutomaticVideoMessageDownload: String { return self._s[3977]! } - public func PUSH_CHAT_MESSAGE_VIDEOS(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3978]!, self._r[3978]!, [_1, _2, _3]) - } - public var Channel_AdminLogFilter_AdminsAll: String { return self._s[3979]! } - public var Wallet_Intro_CreateErrorText: String { return self._s[3980]! } - public var Tour_Title2: String { return self._s[3981]! } - public var Wallet_Sent_ViewWallet: String { return self._s[3982]! } - public var Conversation_FileOpenIn: String { return self._s[3983]! } - public var Checkout_ErrorPrecheckoutFailed: String { return self._s[3984]! } - public var Wallet_Send_ErrorInvalidAddress: String { return self._s[3985]! } - public var Wallpaper_Set: String { return self._s[3986]! } - public var Passport_Identity_Translations: String { return self._s[3988]! } - public func Channel_AdminLog_MessageChangedChannelAbout(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3989]!, self._r[3989]!, [_0]) - } - public var Channel_LeaveChannel: String { return self._s[3990]! } - public func PINNED_INVOICE(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[3991]!, self._r[3991]!, [_1]) - } - public var SettingsSearch_Synonyms_Proxy_AddProxy: String { return self._s[3993]! } - public var PhotoEditor_HighlightsTint: String { return self._s[3994]! } - public var Passport_Email_Delete: String { return self._s[3995]! } - public var Conversation_Mute: String { return self._s[3997]! } - public var Channel_AddBotAsAdmin: String { return self._s[3998]! } - public var Channel_AdminLog_CanSendMessages: String { return self._s[4000]! } - public var Wallet_Configuration_BlockchainNameChangedText: String { return self._s[4001]! } - public var ChatSettings_IntentsSettings: String { return self._s[4003]! } - public var Channel_Management_LabelOwner: String { return self._s[4004]! } - public func Notification_PassportValuesSentMessage(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[4005]!, self._r[4005]!, [_1, _2]) - } - public var Calls_CallTabDescription: String { return self._s[4006]! } - public var Passport_Identity_NativeNameHelp: String { return self._s[4007]! } - public var Common_No: String { return self._s[4008]! } - public var Weekday_Sunday: String { return self._s[4009]! } - public var Notification_Reply: String { return self._s[4010]! } - public var Conversation_ViewMessage: String { return self._s[4011]! } - public func Checkout_SavePasswordTimeoutAndFaceId(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[4012]!, self._r[4012]!, [_0]) - } - public func Map_LiveLocationPrivateDescription(_ _0: String) -> (String, [(Int, NSRange)]) { return formatWithArgumentRanges(self._s[4013]!, self._r[4013]!, [_0]) } - public func Wallet_Time_PreciseDate_m7(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[4014]!, self._r[4014]!, [_1, _2, _3]) + public var ContactInfo_PhoneNumberHidden: String { return self._s[4014]! } + public var TwoStepAuth_ConfirmationText: String { return self._s[4015]! } + public var ChatSettings_AutomaticVideoMessageDownload: String { return self._s[4016]! } + public func PUSH_CHAT_MESSAGE_VIDEOS(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[4017]!, self._r[4017]!, [_1, _2, _3]) } - public var SettingsSearch_Synonyms_EditProfile_AddAccount: String { return self._s[4015]! } - public var Wallet_Send_Title: String { return self._s[4016]! } - public var Message_PinnedDocumentMessage: String { return self._s[4017]! } - public var Wallet_Info_RefreshErrorText: String { return self._s[4018]! } - public var DialogList_TabTitle: String { return self._s[4020]! } - public var ChatSettings_AutoPlayTitle: String { return self._s[4021]! } - public var Passport_FieldEmail: String { return self._s[4022]! } - public var Conversation_UnpinMessageAlert: String { return self._s[4023]! } - public var Passport_Address_TypeBankStatement: String { return self._s[4024]! } - public var Wallet_SecureStorageReset_Title: String { return self._s[4025]! } - public var Passport_Identity_ExpiryDate: String { return self._s[4026]! } - public var Privacy_Calls_P2P: String { return self._s[4027]! } - public func CancelResetAccount_Success(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[4029]!, self._r[4029]!, [_0]) + public var Channel_AdminLogFilter_AdminsAll: String { return self._s[4018]! } + public var Wallet_Intro_CreateErrorText: String { return self._s[4019]! } + public var Tour_Title2: String { return self._s[4020]! } + public var Wallet_Sent_ViewWallet: String { return self._s[4021]! } + public var Conversation_FileOpenIn: String { return self._s[4022]! } + public var Checkout_ErrorPrecheckoutFailed: String { return self._s[4023]! } + public var Wallet_Send_ErrorInvalidAddress: String { return self._s[4024]! } + public var Wallpaper_Set: String { return self._s[4025]! } + public var Passport_Identity_Translations: String { return self._s[4027]! } + public func Channel_AdminLog_MessageChangedChannelAbout(_ _0: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[4028]!, self._r[4028]!, [_0]) } - public var SocksProxySetup_UseForCallsHelp: String { return self._s[4030]! } - public func PUSH_CHAT_ALBUM(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[4031]!, self._r[4031]!, [_1, _2]) + public var Channel_LeaveChannel: String { return self._s[4029]! } + public func PINNED_INVOICE(_ _1: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[4030]!, self._r[4030]!, [_1]) } - public var Stickers_ClearRecent: String { return self._s[4032]! } - public var EnterPasscode_ChangeTitle: String { return self._s[4033]! } - public var TwoFactorSetup_Email_Title: String { return self._s[4034]! } - public var Passport_InfoText: String { return self._s[4035]! } - public var Checkout_NewCard_SaveInfoEnableHelp: String { return self._s[4036]! } - public func Login_InvalidPhoneEmailSubject(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[4037]!, self._r[4037]!, [_0]) + public var SettingsSearch_Synonyms_Proxy_AddProxy: String { return self._s[4032]! } + public var PhotoEditor_HighlightsTint: String { return self._s[4033]! } + public var MessagePoll_LabelPoll: String { return self._s[4034]! } + public var Passport_Email_Delete: String { return self._s[4035]! } + public var Conversation_Mute: String { return self._s[4037]! } + public var Channel_AddBotAsAdmin: String { return self._s[4038]! } + public var Channel_AdminLog_CanSendMessages: String { return self._s[4040]! } + public var Wallet_Configuration_BlockchainNameChangedText: String { return self._s[4041]! } + public var ChatSettings_IntentsSettings: String { return self._s[4043]! } + public var Channel_Management_LabelOwner: String { return self._s[4044]! } + public func Notification_PassportValuesSentMessage(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[4045]!, self._r[4045]!, [_1, _2]) } - public func Time_PreciseDate_m3(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[4038]!, self._r[4038]!, [_1, _2, _3]) - } - public var SettingsSearch_Synonyms_Notifications_BadgeIncludeMutedChannels: String { return self._s[4039]! } - public var ScheduledMessages_PollUnavailable: String { return self._s[4040]! } - public var VoiceOver_Navigation_Compose: String { return self._s[4041]! } - public var Passport_Identity_EditDriversLicense: String { return self._s[4042]! } - public var Conversation_TapAndHoldToRecord: String { return self._s[4044]! } - public var SettingsSearch_Synonyms_Notifications_BadgeIncludeMutedChats: String { return self._s[4045]! } - public func Notification_CallTimeFormat(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[4046]!, self._r[4046]!, [_1, _2]) - } - public var Channel_EditAdmin_PermissionInviteViaLink: String { return self._s[4048]! } - public var ChatSettings_OpenLinksIn: String { return self._s[4049]! } - public var Map_HomeAndWorkTitle: String { return self._s[4050]! } - public func Generic_OpenHiddenLinkAlert(_ _0: String) -> (String, [(Int, NSRange)]) { + public var Calls_CallTabDescription: String { return self._s[4046]! } + public var Passport_Identity_NativeNameHelp: String { return self._s[4047]! } + public var Common_No: String { return self._s[4048]! } + public var Weekday_Sunday: String { return self._s[4049]! } + public var Notification_Reply: String { return self._s[4050]! } + public var Conversation_ViewMessage: String { return self._s[4051]! } + public func Checkout_SavePasswordTimeoutAndFaceId(_ _0: String) -> (String, [(Int, NSRange)]) { return formatWithArgumentRanges(self._s[4052]!, self._r[4052]!, [_0]) } - public var DialogList_Unread: String { return self._s[4053]! } + public func Map_LiveLocationPrivateDescription(_ _0: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[4053]!, self._r[4053]!, [_0]) + } + public func Wallet_Time_PreciseDate_m7(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[4054]!, self._r[4054]!, [_1, _2, _3]) + } + public var SettingsSearch_Synonyms_EditProfile_AddAccount: String { return self._s[4055]! } + public var Wallet_Send_Title: String { return self._s[4056]! } + public var Message_PinnedDocumentMessage: String { return self._s[4057]! } + public var Wallet_Info_RefreshErrorText: String { return self._s[4058]! } + public var DialogList_TabTitle: String { return self._s[4060]! } + public var ChatSettings_AutoPlayTitle: String { return self._s[4061]! } + public var Passport_FieldEmail: String { return self._s[4062]! } + public var Conversation_UnpinMessageAlert: String { return self._s[4063]! } + public var Passport_Address_TypeBankStatement: String { return self._s[4064]! } + public var Wallet_SecureStorageReset_Title: String { return self._s[4065]! } + public var Passport_Identity_ExpiryDate: String { return self._s[4066]! } + public var Privacy_Calls_P2P: String { return self._s[4067]! } + public func CancelResetAccount_Success(_ _0: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[4069]!, self._r[4069]!, [_0]) + } + public var SocksProxySetup_UseForCallsHelp: String { return self._s[4070]! } + public func PUSH_CHAT_ALBUM(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[4071]!, self._r[4071]!, [_1, _2]) + } + public var Stickers_ClearRecent: String { return self._s[4072]! } + public var EnterPasscode_ChangeTitle: String { return self._s[4073]! } + public var TwoFactorSetup_Email_Title: String { return self._s[4074]! } + public var Passport_InfoText: String { return self._s[4075]! } + public var Checkout_NewCard_SaveInfoEnableHelp: String { return self._s[4076]! } + public func Login_InvalidPhoneEmailSubject(_ _0: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[4077]!, self._r[4077]!, [_0]) + } + public func Time_PreciseDate_m3(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[4078]!, self._r[4078]!, [_1, _2, _3]) + } + public var SettingsSearch_Synonyms_Notifications_BadgeIncludeMutedChannels: String { return self._s[4079]! } + public var ScheduledMessages_PollUnavailable: String { return self._s[4080]! } + public var VoiceOver_Navigation_Compose: String { return self._s[4081]! } + public var Passport_Identity_EditDriversLicense: String { return self._s[4082]! } + public var Conversation_TapAndHoldToRecord: String { return self._s[4084]! } + public var SettingsSearch_Synonyms_Notifications_BadgeIncludeMutedChats: String { return self._s[4085]! } + public func Notification_CallTimeFormat(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[4086]!, self._r[4086]!, [_1, _2]) + } + public var Channel_EditAdmin_PermissionInviteViaLink: String { return self._s[4088]! } + public var ChatSettings_OpenLinksIn: String { return self._s[4089]! } + public var Map_HomeAndWorkTitle: String { return self._s[4090]! } + public func Generic_OpenHiddenLinkAlert(_ _0: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[4092]!, self._r[4092]!, [_0]) + } + public var DialogList_Unread: String { return self._s[4093]! } public func PUSH_CHAT_MESSAGE_GIF(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[4054]!, self._r[4054]!, [_1, _2]) + return formatWithArgumentRanges(self._s[4094]!, self._r[4094]!, [_1, _2]) } - public var User_DeletedAccount: String { return self._s[4055]! } - public var OwnershipTransfer_SetupTwoStepAuth: String { return self._s[4056]! } + public var User_DeletedAccount: String { return self._s[4095]! } + public var OwnershipTransfer_SetupTwoStepAuth: String { return self._s[4096]! } public func Watch_Time_ShortYesterdayAt(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[4057]!, self._r[4057]!, [_0]) + return formatWithArgumentRanges(self._s[4097]!, self._r[4097]!, [_0]) } - public var UserInfo_NotificationsDefault: String { return self._s[4058]! } - public var SharedMedia_CategoryMedia: String { return self._s[4059]! } - public var SocksProxySetup_ProxyStatusUnavailable: String { return self._s[4060]! } - public var Channel_AdminLog_MessageRestrictedForever: String { return self._s[4061]! } - public var Watch_ChatList_Compose: String { return self._s[4062]! } - public var Notifications_MessageNotificationsExceptionsHelp: String { return self._s[4063]! } - public var AutoDownloadSettings_Delimeter: String { return self._s[4064]! } - public var Watch_Microphone_Access: String { return self._s[4065]! } - public var Group_Setup_HistoryHeader: String { return self._s[4066]! } - public var Map_SetThisLocation: String { return self._s[4067]! } - public var Appearance_ThemePreview_Chat_2_ReplyName: String { return self._s[4068]! } - public var Activity_UploadingPhoto: String { return self._s[4069]! } - public var Conversation_Edit: String { return self._s[4071]! } - public var Group_ErrorSendRestrictedMedia: String { return self._s[4072]! } - public var Login_TermsOfServiceDecline: String { return self._s[4073]! } - public var Message_PinnedContactMessage: String { return self._s[4074]! } + public var UserInfo_NotificationsDefault: String { return self._s[4098]! } + public var SharedMedia_CategoryMedia: String { return self._s[4099]! } + public var SocksProxySetup_ProxyStatusUnavailable: String { return self._s[4100]! } + public var Channel_AdminLog_MessageRestrictedForever: String { return self._s[4101]! } + public var Watch_ChatList_Compose: String { return self._s[4102]! } + public var Notifications_MessageNotificationsExceptionsHelp: String { return self._s[4103]! } + public var AutoDownloadSettings_Delimeter: String { return self._s[4104]! } + public var Watch_Microphone_Access: String { return self._s[4105]! } + public var Group_Setup_HistoryHeader: String { return self._s[4106]! } + public var Map_SetThisLocation: String { return self._s[4107]! } + public var Appearance_ThemePreview_Chat_2_ReplyName: String { return self._s[4108]! } + public var Activity_UploadingPhoto: String { return self._s[4109]! } + public var Conversation_Edit: String { return self._s[4111]! } + public var Group_ErrorSendRestrictedMedia: String { return self._s[4112]! } + public var Login_TermsOfServiceDecline: String { return self._s[4113]! } + public var Message_PinnedContactMessage: String { return self._s[4114]! } public func Channel_AdminLog_MessageRestrictedNameUsername(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[4075]!, self._r[4075]!, [_1, _2]) + return formatWithArgumentRanges(self._s[4115]!, self._r[4115]!, [_1, _2]) } public func Login_PhoneBannedEmailBody(_ _1: String, _ _2: String, _ _3: String, _ _4: String, _ _5: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[4076]!, self._r[4076]!, [_1, _2, _3, _4, _5]) + return formatWithArgumentRanges(self._s[4116]!, self._r[4116]!, [_1, _2, _3, _4, _5]) } - public var Appearance_LargeEmoji: String { return self._s[4077]! } - public var TwoStepAuth_AdditionalPassword: String { return self._s[4079]! } - public var EditTheme_Edit_Preview_IncomingReplyText: String { return self._s[4080]! } + public var Appearance_LargeEmoji: String { return self._s[4117]! } + public var TwoStepAuth_AdditionalPassword: String { return self._s[4119]! } + public var EditTheme_Edit_Preview_IncomingReplyText: String { return self._s[4120]! } public func PUSH_CHAT_DELETE_YOU(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[4081]!, self._r[4081]!, [_1, _2]) + return formatWithArgumentRanges(self._s[4121]!, self._r[4121]!, [_1, _2]) } - public var Passport_Phone_EnterOtherNumber: String { return self._s[4082]! } - public var Message_PinnedPhotoMessage: String { return self._s[4083]! } - public var Passport_FieldPhone: String { return self._s[4084]! } - public var TwoStepAuth_RecoveryEmailAddDescription: String { return self._s[4085]! } - public var ChatSettings_AutoPlayGifs: String { return self._s[4086]! } - public var InfoPlist_NSCameraUsageDescription: String { return self._s[4088]! } - public var Conversation_Call: String { return self._s[4089]! } - public var Common_TakePhoto: String { return self._s[4091]! } - public var Group_EditAdmin_RankTitle: String { return self._s[4092]! } - public var Wallet_Receive_CommentHeader: String { return self._s[4093]! } - public var Channel_NotificationLoading: String { return self._s[4094]! } + public var Passport_Phone_EnterOtherNumber: String { return self._s[4122]! } + public var Message_PinnedPhotoMessage: String { return self._s[4123]! } + public var Passport_FieldPhone: String { return self._s[4124]! } + public var TwoStepAuth_RecoveryEmailAddDescription: String { return self._s[4125]! } + public var ChatSettings_AutoPlayGifs: String { return self._s[4126]! } + public var InfoPlist_NSCameraUsageDescription: String { return self._s[4128]! } + public var Conversation_Call: String { return self._s[4129]! } + public var Common_TakePhoto: String { return self._s[4131]! } + public var Group_EditAdmin_RankTitle: String { return self._s[4132]! } + public var Wallet_Receive_CommentHeader: String { return self._s[4133]! } + public var Channel_NotificationLoading: String { return self._s[4134]! } public func Notification_Exceptions_Sound(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[4095]!, self._r[4095]!, [_0]) + return formatWithArgumentRanges(self._s[4135]!, self._r[4135]!, [_0]) } public func ScheduledMessages_ScheduledDate(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[4096]!, self._r[4096]!, [_0]) + return formatWithArgumentRanges(self._s[4136]!, self._r[4136]!, [_0]) } public func PUSH_CHANNEL_MESSAGE_VIDEO(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[4097]!, self._r[4097]!, [_1]) + return formatWithArgumentRanges(self._s[4137]!, self._r[4137]!, [_1]) } - public var Permissions_SiriTitle_v0: String { return self._s[4098]! } + public var Permissions_SiriTitle_v0: String { return self._s[4138]! } public func VoiceOver_Chat_VoiceMessageFrom(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[4099]!, self._r[4099]!, [_0]) + return formatWithArgumentRanges(self._s[4139]!, self._r[4139]!, [_0]) } public func Login_ResetAccountProtected_Text(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[4100]!, self._r[4100]!, [_0]) + return formatWithArgumentRanges(self._s[4140]!, self._r[4140]!, [_0]) } - public var Channel_MessagePhotoRemoved: String { return self._s[4101]! } - public var Wallet_Info_ReceiveGrams: String { return self._s[4102]! } - public var ClearCache_FreeSpace: String { return self._s[4103]! } - public var Common_edit: String { return self._s[4104]! } - public var PrivacySettings_AuthSessions: String { return self._s[4105]! } - public var Month_ShortJune: String { return self._s[4106]! } - public var PrivacyLastSeenSettings_AlwaysShareWith_Placeholder: String { return self._s[4107]! } - public var Call_ReportSend: String { return self._s[4108]! } - public var Watch_LastSeen_JustNow: String { return self._s[4109]! } - public var Notifications_MessageNotifications: String { return self._s[4110]! } - public var WallpaperSearch_ColorGreen: String { return self._s[4111]! } - public var BroadcastListInfo_AddRecipient: String { return self._s[4113]! } - public var Group_Status: String { return self._s[4114]! } + public var Channel_MessagePhotoRemoved: String { return self._s[4141]! } + public var Wallet_Info_ReceiveGrams: String { return self._s[4142]! } + public var ClearCache_FreeSpace: String { return self._s[4143]! } + public var Appearance_BubbleCorners_Apply: String { return self._s[4144]! } + public var Common_edit: String { return self._s[4145]! } + public var PrivacySettings_AuthSessions: String { return self._s[4146]! } + public var Month_ShortJune: String { return self._s[4147]! } + public var PrivacyLastSeenSettings_AlwaysShareWith_Placeholder: String { return self._s[4148]! } + public var Call_ReportSend: String { return self._s[4149]! } + public var Watch_LastSeen_JustNow: String { return self._s[4150]! } + public var Notifications_MessageNotifications: String { return self._s[4151]! } + public var WallpaperSearch_ColorGreen: String { return self._s[4152]! } + public var BroadcastListInfo_AddRecipient: String { return self._s[4154]! } + public var Group_Status: String { return self._s[4155]! } public func AutoNightTheme_LocationHelp(_ _0: String, _ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[4115]!, self._r[4115]!, [_0, _1]) + return formatWithArgumentRanges(self._s[4156]!, self._r[4156]!, [_0, _1]) } - public var TextFormat_AddLinkTitle: String { return self._s[4116]! } - public var ShareMenu_ShareTo: String { return self._s[4117]! } - public var Conversation_Moderate_Ban: String { return self._s[4118]! } + public var TextFormat_AddLinkTitle: String { return self._s[4157]! } + public var ShareMenu_ShareTo: String { return self._s[4158]! } + public var Conversation_Moderate_Ban: String { return self._s[4159]! } public func Conversation_DeleteMessagesFor(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[4119]!, self._r[4119]!, [_0]) + return formatWithArgumentRanges(self._s[4160]!, self._r[4160]!, [_0]) } - public var SharedMedia_ViewInChat: String { return self._s[4120]! } - public var Map_LiveLocationFor8Hours: String { return self._s[4121]! } + public var SharedMedia_ViewInChat: String { return self._s[4161]! } + public var Map_LiveLocationFor8Hours: String { return self._s[4162]! } public func PUSH_PINNED_PHOTO(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[4122]!, self._r[4122]!, [_1]) + return formatWithArgumentRanges(self._s[4163]!, self._r[4163]!, [_1]) } public func PUSH_PINNED_POLL(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[4123]!, self._r[4123]!, [_1, _2]) + return formatWithArgumentRanges(self._s[4164]!, self._r[4164]!, [_1, _2]) } public func Map_AccurateTo(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[4125]!, self._r[4125]!, [_0]) + return formatWithArgumentRanges(self._s[4166]!, self._r[4166]!, [_0]) } - public var Map_OpenInHereMaps: String { return self._s[4126]! } - public var Appearance_ReduceMotion: String { return self._s[4127]! } + public var Map_OpenInHereMaps: String { return self._s[4167]! } + public var Appearance_ReduceMotion: String { return self._s[4168]! } public func PUSH_MESSAGE_TEXT(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[4128]!, self._r[4128]!, [_1, _2]) + return formatWithArgumentRanges(self._s[4169]!, self._r[4169]!, [_1, _2]) } - public var Channel_Setup_TypePublicHelp: String { return self._s[4129]! } - public var Passport_Identity_EditInternalPassport: String { return self._s[4130]! } - public var PhotoEditor_Skip: String { return self._s[4131]! } - public func MuteFor_Hours(_ value: Int32) -> String { + public var Channel_Setup_TypePublicHelp: String { return self._s[4170]! } + public var Passport_Identity_EditInternalPassport: String { return self._s[4171]! } + public var PhotoEditor_Skip: String { return self._s[4172]! } + public func LiveLocationUpdated_MinutesAgo(_ value: Int32) -> String { let form = getPluralizationForm(self.lc, value) let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) return String(format: self._ps[0 * 6 + Int(form.rawValue)]!, stringValue) } - public func StickerPack_StickerCount(_ value: Int32) -> String { + public func LastSeen_MinutesAgo(_ value: Int32) -> String { let form = getPluralizationForm(self.lc, value) let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) return String(format: self._ps[1 * 6 + Int(form.rawValue)]!, stringValue) } - public func OldChannels_InactiveYear(_ value: Int32) -> String { + public func CreatePoll_AddMoreOptions(_ value: Int32) -> String { let form = getPluralizationForm(self.lc, value) let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) return String(format: self._ps[2 * 6 + Int(form.rawValue)]!, stringValue) } - public func PUSH_MESSAGES(_ selector: Int32, _ _1: String, _ _2: Int32) -> String { - let form = getPluralizationForm(self.lc, selector) - return String(format: self._ps[3 * 6 + Int(form.rawValue)]!, _1, _2) + public func ForwardedLocations(_ value: Int32) -> String { + let form = getPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[3 * 6 + Int(form.rawValue)]!, stringValue) } - public func ForwardedPolls(_ value: Int32) -> String { + public func MessageTimer_Weeks(_ value: Int32) -> String { let form = getPluralizationForm(self.lc, value) let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) return String(format: self._ps[4 * 6 + Int(form.rawValue)]!, stringValue) } - public func GroupInfo_ShowMoreMembers(_ value: Int32) -> String { + public func AttachmentMenu_SendVideo(_ value: Int32) -> String { let form = getPluralizationForm(self.lc, value) let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) return String(format: self._ps[5 * 6 + Int(form.rawValue)]!, stringValue) } - public func ChatList_DeletedChats(_ value: Int32) -> String { + public func SharedMedia_Photo(_ value: Int32) -> String { let form = getPluralizationForm(self.lc, value) let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) return String(format: self._ps[6 * 6 + Int(form.rawValue)]!, stringValue) } - public func PUSH_CHAT_MESSAGE_ROUNDS(_ selector: Int32, _ _2: String, _ _1: String, _ _3: Int32) -> String { + public func PUSH_CHANNEL_MESSAGES(_ selector: Int32, _ _1: String, _ _2: Int32) -> String { let form = getPluralizationForm(self.lc, selector) - return String(format: self._ps[7 * 6 + Int(form.rawValue)]!, _2, _1, _3) + return String(format: self._ps[7 * 6 + Int(form.rawValue)]!, _1, _2) } - public func ForwardedAudios(_ value: Int32) -> String { + public func Notification_GameScoreExtended(_ value: Int32) -> String { let form = getPluralizationForm(self.lc, value) let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) return String(format: self._ps[8 * 6 + Int(form.rawValue)]!, stringValue) } - public func Wallet_Updated_MinutesAgo(_ value: Int32) -> String { + public func MuteFor_Days(_ value: Int32) -> String { let form = getPluralizationForm(self.lc, value) let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) return String(format: self._ps[9 * 6 + Int(form.rawValue)]!, stringValue) } - public func PUSH_CHAT_MESSAGES(_ selector: Int32, _ _2: String, _ _1: String, _ _3: Int32) -> String { - let form = getPluralizationForm(self.lc, selector) - return String(format: self._ps[10 * 6 + Int(form.rawValue)]!, _2, _1, _3) + public func Wallpaper_DeleteConfirmation(_ value: Int32) -> String { + let form = getPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[10 * 6 + Int(form.rawValue)]!, stringValue) } - public func InviteText_ContactsCountText(_ value: Int32) -> String { + public func Conversation_LiveLocationMembersCount(_ value: Int32) -> String { let form = getPluralizationForm(self.lc, value) let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) return String(format: self._ps[11 * 6 + Int(form.rawValue)]!, stringValue) } - public func MessagePoll_VotedCount(_ value: Int32) -> String { + public func ForwardedVideoMessages(_ value: Int32) -> String { let form = getPluralizationForm(self.lc, value) let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) return String(format: self._ps[12 * 6 + Int(form.rawValue)]!, stringValue) @@ -4729,109 +4776,110 @@ public final class PresentationStrings: Equatable { let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) return String(format: self._ps[13 * 6 + Int(form.rawValue)]!, stringValue) } - public func Conversation_StatusOnline(_ value: Int32) -> String { + public func ForwardedMessages(_ value: Int32) -> String { let form = getPluralizationForm(self.lc, value) let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) return String(format: self._ps[14 * 6 + Int(form.rawValue)]!, stringValue) } - public func PUSH_MESSAGE_PHOTOS(_ selector: Int32, _ _1: String, _ _2: Int32) -> String { + public func PUSH_CHANNEL_MESSAGE_VIDEOS(_ selector: Int32, _ _1: String, _ _2: Int32) -> String { let form = getPluralizationForm(self.lc, selector) return String(format: self._ps[15 * 6 + Int(form.rawValue)]!, _1, _2) } - public func Call_ShortMinutes(_ value: Int32) -> String { + public func AttachmentMenu_SendItem(_ value: Int32) -> String { let form = getPluralizationForm(self.lc, value) let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) return String(format: self._ps[16 * 6 + Int(form.rawValue)]!, stringValue) } - public func VoiceOver_Chat_PollOptionCount(_ value: Int32) -> String { + public func ServiceMessage_GameScoreSelfExtended(_ value: Int32) -> String { let form = getPluralizationForm(self.lc, value) let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) return String(format: self._ps[17 * 6 + Int(form.rawValue)]!, stringValue) } - public func MessageTimer_Seconds(_ value: Int32) -> String { + public func Forward_ConfirmMultipleFiles(_ value: Int32) -> String { let form = getPluralizationForm(self.lc, value) let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) return String(format: self._ps[18 * 6 + Int(form.rawValue)]!, stringValue) } - public func PUSH_CHANNEL_MESSAGE_ROUNDS(_ selector: Int32, _ _1: String, _ _2: Int32) -> String { - let form = getPluralizationForm(self.lc, selector) - return String(format: self._ps[19 * 6 + Int(form.rawValue)]!, _1, _2) + public func PollResults_ShowMore(_ value: Int32) -> String { + let form = getPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[19 * 6 + Int(form.rawValue)]!, stringValue) } - public func MuteExpires_Minutes(_ value: Int32) -> String { + public func DialogList_LiveLocationChatsCount(_ value: Int32) -> String { let form = getPluralizationForm(self.lc, value) let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) return String(format: self._ps[20 * 6 + Int(form.rawValue)]!, stringValue) } - public func Chat_DeleteMessagesConfirmation(_ value: Int32) -> String { + public func Call_Minutes(_ value: Int32) -> String { let form = getPluralizationForm(self.lc, value) let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) return String(format: self._ps[21 * 6 + Int(form.rawValue)]!, stringValue) } - public func Watch_LastSeen_MinutesAgo(_ value: Int32) -> String { + public func Conversation_SelectedMessages(_ value: Int32) -> String { let form = getPluralizationForm(self.lc, value) let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) return String(format: self._ps[22 * 6 + Int(form.rawValue)]!, stringValue) } - public func ForwardedMessages(_ value: Int32) -> String { + public func Media_SharePhoto(_ value: Int32) -> String { let form = getPluralizationForm(self.lc, value) let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) return String(format: self._ps[23 * 6 + Int(form.rawValue)]!, stringValue) } - public func MessageTimer_Hours(_ value: Int32) -> String { + public func OldChannels_InactiveWeek(_ value: Int32) -> String { let form = getPluralizationForm(self.lc, value) let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) return String(format: self._ps[24 * 6 + Int(form.rawValue)]!, stringValue) } - public func AttachmentMenu_SendVideo(_ value: Int32) -> String { - let form = getPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[25 * 6 + Int(form.rawValue)]!, stringValue) + public func PUSH_CHAT_MESSAGE_VIDEOS(_ selector: Int32, _ _2: String, _ _1: String, _ _3: Int32) -> String { + let form = getPluralizationForm(self.lc, selector) + return String(format: self._ps[25 * 6 + Int(form.rawValue)]!, _2, _1, _3) } - public func Call_ShortSeconds(_ value: Int32) -> String { + public func MuteFor_Hours(_ value: Int32) -> String { let form = getPluralizationForm(self.lc, value) let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) return String(format: self._ps[26 * 6 + Int(form.rawValue)]!, stringValue) } - public func PUSH_CHANNEL_MESSAGE_FWDS(_ selector: Int32, _ _1: String, _ _2: Int32) -> String { - let form = getPluralizationForm(self.lc, selector) - return String(format: self._ps[27 * 6 + Int(form.rawValue)]!, _1, _2) + public func OldChannels_GroupFormat(_ value: Int32) -> String { + let form = getPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[27 * 6 + Int(form.rawValue)]!, stringValue) } - public func Conversation_SelectedMessages(_ value: Int32) -> String { + public func UserCount(_ value: Int32) -> String { let form = getPluralizationForm(self.lc, value) let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) return String(format: self._ps[28 * 6 + Int(form.rawValue)]!, stringValue) } - public func ForwardedFiles(_ value: Int32) -> String { + public func Notification_GameScoreSelfExtended(_ value: Int32) -> String { let form = getPluralizationForm(self.lc, value) let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) return String(format: self._ps[29 * 6 + Int(form.rawValue)]!, stringValue) } - public func StickerPack_RemoveMaskCount(_ value: Int32) -> String { + public func Watch_UserInfo_Mute(_ value: Int32) -> String { let form = getPluralizationForm(self.lc, value) let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) return String(format: self._ps[30 * 6 + Int(form.rawValue)]!, stringValue) } - public func GroupInfo_ParticipantCount(_ value: Int32) -> String { + public func AttachmentMenu_SendGif(_ value: Int32) -> String { let form = getPluralizationForm(self.lc, value) let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) return String(format: self._ps[31 * 6 + Int(form.rawValue)]!, stringValue) } - public func Forward_ConfirmMultipleFiles(_ value: Int32) -> String { + public func MuteExpires_Days(_ value: Int32) -> String { let form = getPluralizationForm(self.lc, value) let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) return String(format: self._ps[32 * 6 + Int(form.rawValue)]!, stringValue) } - public func MessageTimer_Days(_ value: Int32) -> String { + public func InviteText_ContactsCountText(_ value: Int32) -> String { let form = getPluralizationForm(self.lc, value) let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) return String(format: self._ps[33 * 6 + Int(form.rawValue)]!, stringValue) } - public func SharedMedia_DeleteItemsConfirmation(_ value: Int32) -> String { + public func StickerPack_StickerCount(_ value: Int32) -> String { let form = getPluralizationForm(self.lc, value) let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) return String(format: self._ps[34 * 6 + Int(form.rawValue)]!, stringValue) } - public func PUSH_MESSAGE_VIDEOS(_ selector: Int32, _ _1: String, _ _2: Int32) -> String { + public func PUSH_CHANNEL_MESSAGE_FWDS(_ selector: Int32, _ _1: String, _ _2: Int32) -> String { let form = getPluralizationForm(self.lc, selector) return String(format: self._ps[35 * 6 + Int(form.rawValue)]!, _1, _2) } @@ -4840,431 +4888,438 @@ public final class PresentationStrings: Equatable { let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) return String(format: self._ps[36 * 6 + Int(form.rawValue)]!, stringValue) } - public func Theme_UsersCount(_ value: Int32) -> String { + public func Map_ETAHours(_ value: Int32) -> String { let form = getPluralizationForm(self.lc, value) let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) return String(format: self._ps[37 * 6 + Int(form.rawValue)]!, stringValue) } - public func Conversation_StatusMembers(_ value: Int32) -> String { + public func ChatList_SelectedChats(_ value: Int32) -> String { let form = getPluralizationForm(self.lc, value) let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) return String(format: self._ps[38 * 6 + Int(form.rawValue)]!, stringValue) } - public func Notifications_ExceptionMuteExpires_Days(_ value: Int32) -> String { + public func Call_ShortMinutes(_ value: Int32) -> String { let form = getPluralizationForm(self.lc, value) let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) return String(format: self._ps[39 * 6 + Int(form.rawValue)]!, stringValue) } - public func Contacts_InviteContacts(_ value: Int32) -> String { + public func Media_ShareItem(_ value: Int32) -> String { let form = getPluralizationForm(self.lc, value) let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) return String(format: self._ps[40 * 6 + Int(form.rawValue)]!, stringValue) } - public func Notifications_Exceptions(_ value: Int32) -> String { + public func Media_ShareVideo(_ value: Int32) -> String { let form = getPluralizationForm(self.lc, value) let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) return String(format: self._ps[41 * 6 + Int(form.rawValue)]!, stringValue) } - public func QuickSend_Photos(_ value: Int32) -> String { + public func OldChannels_InactiveYear(_ value: Int32) -> String { let form = getPluralizationForm(self.lc, value) let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) return String(format: self._ps[42 * 6 + Int(form.rawValue)]!, stringValue) } - public func Notification_GameScoreExtended(_ value: Int32) -> String { + public func VoiceOver_Chat_PollOptionCount(_ value: Int32) -> String { let form = getPluralizationForm(self.lc, value) let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) return String(format: self._ps[43 * 6 + Int(form.rawValue)]!, stringValue) } - public func Watch_UserInfo_Mute(_ value: Int32) -> String { + public func MessagePoll_VotedCount(_ value: Int32) -> String { let form = getPluralizationForm(self.lc, value) let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) return String(format: self._ps[44 * 6 + Int(form.rawValue)]!, stringValue) } - public func DialogList_LiveLocationChatsCount(_ value: Int32) -> String { - let form = getPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[45 * 6 + Int(form.rawValue)]!, stringValue) - } - public func LastSeen_HoursAgo(_ value: Int32) -> String { - let form = getPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[46 * 6 + Int(form.rawValue)]!, stringValue) - } - public func ForwardedAuthorsOthers(_ selector: Int32, _ _0: String, _ _1: String) -> String { - let form = getPluralizationForm(self.lc, selector) - return String(format: self._ps[47 * 6 + Int(form.rawValue)]!, _0, _1) - } - public func ForwardedVideoMessages(_ value: Int32) -> String { - let form = getPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[48 * 6 + Int(form.rawValue)]!, stringValue) - } - public func PUSH_CHANNEL_MESSAGE_PHOTOS(_ selector: Int32, _ _1: String, _ _2: Int32) -> String { - let form = getPluralizationForm(self.lc, selector) - return String(format: self._ps[49 * 6 + Int(form.rawValue)]!, _1, _2) - } - public func OldChannels_InactiveWeek(_ value: Int32) -> String { - let form = getPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[50 * 6 + Int(form.rawValue)]!, stringValue) - } - public func CreatePoll_AddMoreOptions(_ value: Int32) -> String { - let form = getPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[51 * 6 + Int(form.rawValue)]!, stringValue) - } - public func MuteFor_Days(_ value: Int32) -> String { - let form = getPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[52 * 6 + Int(form.rawValue)]!, stringValue) - } - public func VoiceOver_Chat_ContactEmailCount(_ value: Int32) -> String { - let form = getPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[53 * 6 + Int(form.rawValue)]!, stringValue) - } - public func Invitation_Members(_ value: Int32) -> String { - let form = getPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[54 * 6 + Int(form.rawValue)]!, stringValue) - } - public func MessageTimer_ShortDays(_ value: Int32) -> String { - let form = getPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[55 * 6 + Int(form.rawValue)]!, stringValue) - } - public func AttachmentMenu_SendItem(_ value: Int32) -> String { - let form = getPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[56 * 6 + Int(form.rawValue)]!, stringValue) - } public func PUSH_MESSAGE_ROUNDS(_ selector: Int32, _ _1: String, _ _2: Int32) -> String { let form = getPluralizationForm(self.lc, selector) - return String(format: self._ps[57 * 6 + Int(form.rawValue)]!, _1, _2) + return String(format: self._ps[45 * 6 + Int(form.rawValue)]!, _1, _2) } - public func PUSH_CHAT_MESSAGE_FWDS(_ selector: Int32, _ _2: String, _ _1: String, _ _3: Int32) -> String { + public func PUSH_CHAT_MESSAGES(_ selector: Int32, _ _2: String, _ _1: String, _ _3: Int32) -> String { let form = getPluralizationForm(self.lc, selector) - return String(format: self._ps[58 * 6 + Int(form.rawValue)]!, _2, _1, _3) - } - public func SharedMedia_Link(_ value: Int32) -> String { - let form = getPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[59 * 6 + Int(form.rawValue)]!, stringValue) - } - public func ForwardedVideos(_ value: Int32) -> String { - let form = getPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[60 * 6 + Int(form.rawValue)]!, stringValue) - } - public func MessageTimer_Months(_ value: Int32) -> String { - let form = getPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[61 * 6 + Int(form.rawValue)]!, stringValue) - } - public func MessageTimer_ShortHours(_ value: Int32) -> String { - let form = getPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[62 * 6 + Int(form.rawValue)]!, stringValue) - } - public func SharedMedia_Photo(_ value: Int32) -> String { - let form = getPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[63 * 6 + Int(form.rawValue)]!, stringValue) - } - public func MessageTimer_Minutes(_ value: Int32) -> String { - let form = getPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[64 * 6 + Int(form.rawValue)]!, stringValue) - } - public func ForwardedLocations(_ value: Int32) -> String { - let form = getPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[65 * 6 + Int(form.rawValue)]!, stringValue) - } - public func PUSH_MESSAGE_FWDS(_ selector: Int32, _ _1: String, _ _2: Int32) -> String { - let form = getPluralizationForm(self.lc, selector) - return String(format: self._ps[66 * 6 + Int(form.rawValue)]!, _1, _2) - } - public func MessageTimer_ShortMinutes(_ value: Int32) -> String { - let form = getPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[67 * 6 + Int(form.rawValue)]!, stringValue) - } - public func OldChannels_GroupFormat(_ value: Int32) -> String { - let form = getPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[68 * 6 + Int(form.rawValue)]!, stringValue) - } - public func AttachmentMenu_SendPhoto(_ value: Int32) -> String { - let form = getPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[69 * 6 + Int(form.rawValue)]!, stringValue) - } - public func Wallet_Updated_HoursAgo(_ value: Int32) -> String { - let form = getPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[70 * 6 + Int(form.rawValue)]!, stringValue) - } - public func Wallpaper_DeleteConfirmation(_ value: Int32) -> String { - let form = getPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[71 * 6 + Int(form.rawValue)]!, stringValue) - } - public func ServiceMessage_GameScoreSimple(_ value: Int32) -> String { - let form = getPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[72 * 6 + Int(form.rawValue)]!, stringValue) - } - public func UserCount(_ value: Int32) -> String { - let form = getPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[73 * 6 + Int(form.rawValue)]!, stringValue) - } - public func ForwardedPhotos(_ value: Int32) -> String { - let form = getPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[74 * 6 + Int(form.rawValue)]!, stringValue) - } - public func AttachmentMenu_SendGif(_ value: Int32) -> String { - let form = getPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[75 * 6 + Int(form.rawValue)]!, stringValue) - } - public func Watch_LastSeen_HoursAgo(_ value: Int32) -> String { - let form = getPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[76 * 6 + Int(form.rawValue)]!, stringValue) - } - public func OldChannels_Leave(_ value: Int32) -> String { - let form = getPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[77 * 6 + Int(form.rawValue)]!, stringValue) - } - public func LiveLocationUpdated_MinutesAgo(_ value: Int32) -> String { - let form = getPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[78 * 6 + Int(form.rawValue)]!, stringValue) - } - public func MuteExpires_Days(_ value: Int32) -> String { - let form = getPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[79 * 6 + Int(form.rawValue)]!, stringValue) - } - public func Notification_GameScoreSelfSimple(_ value: Int32) -> String { - let form = getPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[80 * 6 + Int(form.rawValue)]!, stringValue) - } - public func PUSH_CHAT_MESSAGE_VIDEOS(_ selector: Int32, _ _2: String, _ _1: String, _ _3: Int32) -> String { - let form = getPluralizationForm(self.lc, selector) - return String(format: self._ps[81 * 6 + Int(form.rawValue)]!, _2, _1, _3) - } - public func PasscodeSettings_FailedAttempts(_ value: Int32) -> String { - let form = getPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[82 * 6 + Int(form.rawValue)]!, stringValue) - } - public func ServiceMessage_GameScoreSelfSimple(_ value: Int32) -> String { - let form = getPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[83 * 6 + Int(form.rawValue)]!, stringValue) - } - public func ChatList_DeleteConfirmation(_ value: Int32) -> String { - let form = getPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[84 * 6 + Int(form.rawValue)]!, stringValue) - } - public func Conversation_StatusSubscribers(_ value: Int32) -> String { - let form = getPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[85 * 6 + Int(form.rawValue)]!, stringValue) - } - public func Passport_Scans(_ value: Int32) -> String { - let form = getPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[86 * 6 + Int(form.rawValue)]!, stringValue) + return String(format: self._ps[46 * 6 + Int(form.rawValue)]!, _2, _1, _3) } public func MessageTimer_ShortSeconds(_ value: Int32) -> String { let form = getPluralizationForm(self.lc, value) let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[87 * 6 + Int(form.rawValue)]!, stringValue) + return String(format: self._ps[47 * 6 + Int(form.rawValue)]!, stringValue) } - public func Notification_GameScoreSelfExtended(_ value: Int32) -> String { + public func ServiceMessage_GameScoreSimple(_ value: Int32) -> String { let form = getPluralizationForm(self.lc, value) let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[88 * 6 + Int(form.rawValue)]!, stringValue) - } - public func LastSeen_MinutesAgo(_ value: Int32) -> String { - let form = getPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[89 * 6 + Int(form.rawValue)]!, stringValue) - } - public func PUSH_CHANNEL_MESSAGES(_ selector: Int32, _ _1: String, _ _2: Int32) -> String { - let form = getPluralizationForm(self.lc, selector) - return String(format: self._ps[90 * 6 + Int(form.rawValue)]!, _1, _2) - } - public func PrivacyLastSeenSettings_AddUsers(_ value: Int32) -> String { - let form = getPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[91 * 6 + Int(form.rawValue)]!, stringValue) - } - public func Conversation_LiveLocationMembersCount(_ value: Int32) -> String { - let form = getPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[92 * 6 + Int(form.rawValue)]!, stringValue) - } - public func PUSH_CHAT_MESSAGE_PHOTOS(_ selector: Int32, _ _2: String, _ _1: String, _ _3: Int32) -> String { - let form = getPluralizationForm(self.lc, selector) - return String(format: self._ps[93 * 6 + Int(form.rawValue)]!, _2, _1, _3) - } - public func Notification_GameScoreSimple(_ value: Int32) -> String { - let form = getPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[94 * 6 + Int(form.rawValue)]!, stringValue) - } - public func ServiceMessage_GameScoreExtended(_ value: Int32) -> String { - let form = getPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[95 * 6 + Int(form.rawValue)]!, stringValue) - } - public func MuteExpires_Hours(_ value: Int32) -> String { - let form = getPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[96 * 6 + Int(form.rawValue)]!, stringValue) - } - public func Call_Seconds(_ value: Int32) -> String { - let form = getPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[97 * 6 + Int(form.rawValue)]!, stringValue) - } - public func MessageTimer_ShortWeeks(_ value: Int32) -> String { - let form = getPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[98 * 6 + Int(form.rawValue)]!, stringValue) - } - public func StickerPack_AddStickerCount(_ value: Int32) -> String { - let form = getPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[99 * 6 + Int(form.rawValue)]!, stringValue) - } - public func Map_ETAHours(_ value: Int32) -> String { - let form = getPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[100 * 6 + Int(form.rawValue)]!, stringValue) - } - public func Notifications_ExceptionMuteExpires_Hours(_ value: Int32) -> String { - let form = getPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[101 * 6 + Int(form.rawValue)]!, stringValue) - } - public func ForwardedContacts(_ value: Int32) -> String { - let form = getPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[102 * 6 + Int(form.rawValue)]!, stringValue) - } - public func OldChannels_InactiveMonth(_ value: Int32) -> String { - let form = getPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[103 * 6 + Int(form.rawValue)]!, stringValue) - } - public func Media_SharePhoto(_ value: Int32) -> String { - let form = getPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[104 * 6 + Int(form.rawValue)]!, stringValue) - } - public func Notifications_ExceptionMuteExpires_Minutes(_ value: Int32) -> String { - let form = getPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[105 * 6 + Int(form.rawValue)]!, stringValue) - } - public func SharedMedia_Video(_ value: Int32) -> String { - let form = getPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[106 * 6 + Int(form.rawValue)]!, stringValue) - } - public func Map_ETAMinutes(_ value: Int32) -> String { - let form = getPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[107 * 6 + Int(form.rawValue)]!, stringValue) - } - public func ChatList_SelectedChats(_ value: Int32) -> String { - let form = getPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[108 * 6 + Int(form.rawValue)]!, stringValue) - } - public func MessageTimer_Weeks(_ value: Int32) -> String { - let form = getPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[109 * 6 + Int(form.rawValue)]!, stringValue) - } - public func MessageTimer_Years(_ value: Int32) -> String { - let form = getPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[110 * 6 + Int(form.rawValue)]!, stringValue) - } - public func ForwardedStickers(_ value: Int32) -> String { - let form = getPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[111 * 6 + Int(form.rawValue)]!, stringValue) - } - public func PUSH_CHANNEL_MESSAGE_VIDEOS(_ selector: Int32, _ _1: String, _ _2: Int32) -> String { - let form = getPluralizationForm(self.lc, selector) - return String(format: self._ps[112 * 6 + Int(form.rawValue)]!, _1, _2) - } - public func StickerPack_AddMaskCount(_ value: Int32) -> String { - let form = getPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[113 * 6 + Int(form.rawValue)]!, stringValue) - } - public func LiveLocation_MenuChatsCount(_ value: Int32) -> String { - let form = getPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[114 * 6 + Int(form.rawValue)]!, stringValue) - } - public func VoiceOver_Chat_ContactPhoneNumberCount(_ value: Int32) -> String { - let form = getPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[115 * 6 + Int(form.rawValue)]!, stringValue) - } - public func Media_ShareVideo(_ value: Int32) -> String { - let form = getPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[116 * 6 + Int(form.rawValue)]!, stringValue) - } - public func Call_Minutes(_ value: Int32) -> String { - let form = getPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[117 * 6 + Int(form.rawValue)]!, stringValue) - } - public func StickerPack_RemoveStickerCount(_ value: Int32) -> String { - let form = getPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[118 * 6 + Int(form.rawValue)]!, stringValue) - } - public func ServiceMessage_GameScoreSelfExtended(_ value: Int32) -> String { - let form = getPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[119 * 6 + Int(form.rawValue)]!, stringValue) - } - public func VoiceOver_Chat_PollVotes(_ value: Int32) -> String { - let form = getPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[120 * 6 + Int(form.rawValue)]!, stringValue) - } - public func SharedMedia_File(_ value: Int32) -> String { - let form = getPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[121 * 6 + Int(form.rawValue)]!, stringValue) - } - public func Media_ShareItem(_ value: Int32) -> String { - let form = getPluralizationForm(self.lc, value) - let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[122 * 6 + Int(form.rawValue)]!, stringValue) + return String(format: self._ps[48 * 6 + Int(form.rawValue)]!, stringValue) } public func SharedMedia_Generic(_ value: Int32) -> String { let form = getPluralizationForm(self.lc, value) let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) - return String(format: self._ps[123 * 6 + Int(form.rawValue)]!, stringValue) + return String(format: self._ps[49 * 6 + Int(form.rawValue)]!, stringValue) + } + public func MessageTimer_Days(_ value: Int32) -> String { + let form = getPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[50 * 6 + Int(form.rawValue)]!, stringValue) + } + public func Conversation_StatusMembers(_ value: Int32) -> String { + let form = getPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[51 * 6 + Int(form.rawValue)]!, stringValue) + } + public func MessageTimer_ShortMinutes(_ value: Int32) -> String { + let form = getPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[52 * 6 + Int(form.rawValue)]!, stringValue) + } + public func MessageTimer_Seconds(_ value: Int32) -> String { + let form = getPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[53 * 6 + Int(form.rawValue)]!, stringValue) + } + public func GroupInfo_ShowMoreMembers(_ value: Int32) -> String { + let form = getPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[54 * 6 + Int(form.rawValue)]!, stringValue) + } + public func Notification_GameScoreSimple(_ value: Int32) -> String { + let form = getPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[55 * 6 + Int(form.rawValue)]!, stringValue) + } + public func OldChannels_Leave(_ value: Int32) -> String { + let form = getPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[56 * 6 + Int(form.rawValue)]!, stringValue) + } + public func PUSH_CHAT_MESSAGE_PHOTOS(_ selector: Int32, _ _2: String, _ _1: String, _ _3: Int32) -> String { + let form = getPluralizationForm(self.lc, selector) + return String(format: self._ps[57 * 6 + Int(form.rawValue)]!, _2, _1, _3) + } + public func VoiceOver_Chat_ContactEmailCount(_ value: Int32) -> String { + let form = getPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[58 * 6 + Int(form.rawValue)]!, stringValue) + } + public func MessageTimer_Years(_ value: Int32) -> String { + let form = getPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[59 * 6 + Int(form.rawValue)]!, stringValue) + } + public func MessagePoll_QuizCount(_ value: Int32) -> String { + let form = getPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[60 * 6 + Int(form.rawValue)]!, stringValue) + } + public func PUSH_MESSAGE_VIDEOS(_ selector: Int32, _ _1: String, _ _2: Int32) -> String { + let form = getPluralizationForm(self.lc, selector) + return String(format: self._ps[61 * 6 + Int(form.rawValue)]!, _1, _2) + } + public func SharedMedia_Link(_ value: Int32) -> String { + let form = getPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[62 * 6 + Int(form.rawValue)]!, stringValue) + } + public func StickerPack_RemoveMaskCount(_ value: Int32) -> String { + let form = getPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[63 * 6 + Int(form.rawValue)]!, stringValue) + } + public func Notifications_ExceptionMuteExpires_Hours(_ value: Int32) -> String { + let form = getPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[64 * 6 + Int(form.rawValue)]!, stringValue) + } + public func Theme_UsersCount(_ value: Int32) -> String { + let form = getPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[65 * 6 + Int(form.rawValue)]!, stringValue) + } + public func ForwardedAuthorsOthers(_ selector: Int32, _ _0: String, _ _1: String) -> String { + let form = getPluralizationForm(self.lc, selector) + return String(format: self._ps[66 * 6 + Int(form.rawValue)]!, _0, _1) + } + public func Watch_LastSeen_MinutesAgo(_ value: Int32) -> String { + let form = getPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[67 * 6 + Int(form.rawValue)]!, stringValue) + } + public func QuickSend_Photos(_ value: Int32) -> String { + let form = getPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[68 * 6 + Int(form.rawValue)]!, stringValue) + } + public func MessageTimer_ShortDays(_ value: Int32) -> String { + let form = getPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[69 * 6 + Int(form.rawValue)]!, stringValue) + } + public func MessageTimer_ShortWeeks(_ value: Int32) -> String { + let form = getPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[70 * 6 + Int(form.rawValue)]!, stringValue) + } + public func Watch_LastSeen_HoursAgo(_ value: Int32) -> String { + let form = getPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[71 * 6 + Int(form.rawValue)]!, stringValue) + } + public func Chat_DeleteMessagesConfirmation(_ value: Int32) -> String { + let form = getPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[72 * 6 + Int(form.rawValue)]!, stringValue) + } + public func PasscodeSettings_FailedAttempts(_ value: Int32) -> String { + let form = getPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[73 * 6 + Int(form.rawValue)]!, stringValue) + } + public func Call_Seconds(_ value: Int32) -> String { + let form = getPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[74 * 6 + Int(form.rawValue)]!, stringValue) + } + public func MuteExpires_Hours(_ value: Int32) -> String { + let form = getPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[75 * 6 + Int(form.rawValue)]!, stringValue) + } + public func Contacts_InviteContacts(_ value: Int32) -> String { + let form = getPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[76 * 6 + Int(form.rawValue)]!, stringValue) + } + public func ForwardedFiles(_ value: Int32) -> String { + let form = getPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[77 * 6 + Int(form.rawValue)]!, stringValue) + } + public func PUSH_CHANNEL_MESSAGE_PHOTOS(_ selector: Int32, _ _1: String, _ _2: Int32) -> String { + let form = getPluralizationForm(self.lc, selector) + return String(format: self._ps[78 * 6 + Int(form.rawValue)]!, _1, _2) + } + public func Notifications_ExceptionMuteExpires_Days(_ value: Int32) -> String { + let form = getPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[79 * 6 + Int(form.rawValue)]!, stringValue) + } + public func Wallet_Updated_HoursAgo(_ value: Int32) -> String { + let form = getPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[80 * 6 + Int(form.rawValue)]!, stringValue) + } + public func StickerPack_AddStickerCount(_ value: Int32) -> String { + let form = getPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[81 * 6 + Int(form.rawValue)]!, stringValue) + } + public func ForwardedContacts(_ value: Int32) -> String { + let form = getPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[82 * 6 + Int(form.rawValue)]!, stringValue) + } + public func ForwardedAudios(_ value: Int32) -> String { + let form = getPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[83 * 6 + Int(form.rawValue)]!, stringValue) + } + public func Notification_GameScoreSelfSimple(_ value: Int32) -> String { + let form = getPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[84 * 6 + Int(form.rawValue)]!, stringValue) + } + public func ServiceMessage_GameScoreExtended(_ value: Int32) -> String { + let form = getPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[85 * 6 + Int(form.rawValue)]!, stringValue) + } + public func ServiceMessage_GameScoreSelfSimple(_ value: Int32) -> String { + let form = getPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[86 * 6 + Int(form.rawValue)]!, stringValue) + } + public func PrivacyLastSeenSettings_AddUsers(_ value: Int32) -> String { + let form = getPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[87 * 6 + Int(form.rawValue)]!, stringValue) + } + public func VoiceOver_Chat_PollVotes(_ value: Int32) -> String { + let form = getPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[88 * 6 + Int(form.rawValue)]!, stringValue) + } + public func ForwardedStickers(_ value: Int32) -> String { + let form = getPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[89 * 6 + Int(form.rawValue)]!, stringValue) + } + public func ForwardedPolls(_ value: Int32) -> String { + let form = getPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[90 * 6 + Int(form.rawValue)]!, stringValue) + } + public func AttachmentMenu_SendPhoto(_ value: Int32) -> String { + let form = getPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[91 * 6 + Int(form.rawValue)]!, stringValue) + } + public func MessageTimer_ShortHours(_ value: Int32) -> String { + let form = getPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[92 * 6 + Int(form.rawValue)]!, stringValue) + } + public func LiveLocation_MenuChatsCount(_ value: Int32) -> String { + let form = getPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[93 * 6 + Int(form.rawValue)]!, stringValue) + } + public func VoiceOver_Chat_ContactPhoneNumberCount(_ value: Int32) -> String { + let form = getPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[94 * 6 + Int(form.rawValue)]!, stringValue) + } + public func LastSeen_HoursAgo(_ value: Int32) -> String { + let form = getPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[95 * 6 + Int(form.rawValue)]!, stringValue) + } + public func ChatList_DeleteConfirmation(_ value: Int32) -> String { + let form = getPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[96 * 6 + Int(form.rawValue)]!, stringValue) + } + public func OldChannels_InactiveMonth(_ value: Int32) -> String { + let form = getPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[97 * 6 + Int(form.rawValue)]!, stringValue) + } + public func ForwardedVideos(_ value: Int32) -> String { + let form = getPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[98 * 6 + Int(form.rawValue)]!, stringValue) + } + public func SharedMedia_Video(_ value: Int32) -> String { + let form = getPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[99 * 6 + Int(form.rawValue)]!, stringValue) + } + public func StickerPack_AddMaskCount(_ value: Int32) -> String { + let form = getPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[100 * 6 + Int(form.rawValue)]!, stringValue) + } + public func Map_ETAMinutes(_ value: Int32) -> String { + let form = getPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[101 * 6 + Int(form.rawValue)]!, stringValue) + } + public func SharedMedia_File(_ value: Int32) -> String { + let form = getPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[102 * 6 + Int(form.rawValue)]!, stringValue) + } + public func MessageTimer_Minutes(_ value: Int32) -> String { + let form = getPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[103 * 6 + Int(form.rawValue)]!, stringValue) + } + public func Invitation_Members(_ value: Int32) -> String { + let form = getPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[104 * 6 + Int(form.rawValue)]!, stringValue) + } + public func StickerPack_RemoveStickerCount(_ value: Int32) -> String { + let form = getPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[105 * 6 + Int(form.rawValue)]!, stringValue) + } + public func ChatList_DeletedChats(_ value: Int32) -> String { + let form = getPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[106 * 6 + Int(form.rawValue)]!, stringValue) + } + public func PUSH_CHAT_MESSAGE_ROUNDS(_ selector: Int32, _ _2: String, _ _1: String, _ _3: Int32) -> String { + let form = getPluralizationForm(self.lc, selector) + return String(format: self._ps[107 * 6 + Int(form.rawValue)]!, _2, _1, _3) + } + public func MessageTimer_Hours(_ value: Int32) -> String { + let form = getPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[108 * 6 + Int(form.rawValue)]!, stringValue) + } + public func Notifications_ExceptionMuteExpires_Minutes(_ value: Int32) -> String { + let form = getPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[109 * 6 + Int(form.rawValue)]!, stringValue) + } + public func SharedMedia_DeleteItemsConfirmation(_ value: Int32) -> String { + let form = getPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[110 * 6 + Int(form.rawValue)]!, stringValue) + } + public func PUSH_MESSAGE_FWDS(_ selector: Int32, _ _1: String, _ _2: Int32) -> String { + let form = getPluralizationForm(self.lc, selector) + return String(format: self._ps[111 * 6 + Int(form.rawValue)]!, _1, _2) + } + public func Passport_Scans(_ value: Int32) -> String { + let form = getPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[112 * 6 + Int(form.rawValue)]!, stringValue) + } + public func PUSH_CHANNEL_MESSAGE_ROUNDS(_ selector: Int32, _ _1: String, _ _2: Int32) -> String { + let form = getPluralizationForm(self.lc, selector) + return String(format: self._ps[113 * 6 + Int(form.rawValue)]!, _1, _2) + } + public func MessageTimer_Months(_ value: Int32) -> String { + let form = getPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[114 * 6 + Int(form.rawValue)]!, stringValue) + } + public func PUSH_CHAT_MESSAGE_FWDS(_ selector: Int32, _ _2: String, _ _1: String, _ _3: Int32) -> String { + let form = getPluralizationForm(self.lc, selector) + return String(format: self._ps[115 * 6 + Int(form.rawValue)]!, _2, _1, _3) + } + public func MuteExpires_Minutes(_ value: Int32) -> String { + let form = getPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[116 * 6 + Int(form.rawValue)]!, stringValue) + } + public func Wallet_Updated_MinutesAgo(_ value: Int32) -> String { + let form = getPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[117 * 6 + Int(form.rawValue)]!, stringValue) + } + public func Notifications_Exceptions(_ value: Int32) -> String { + let form = getPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[118 * 6 + Int(form.rawValue)]!, stringValue) + } + public func Call_ShortSeconds(_ value: Int32) -> String { + let form = getPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[119 * 6 + Int(form.rawValue)]!, stringValue) + } + public func GroupInfo_ParticipantCount(_ value: Int32) -> String { + let form = getPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[120 * 6 + Int(form.rawValue)]!, stringValue) + } + public func Conversation_StatusSubscribers(_ value: Int32) -> String { + let form = getPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[121 * 6 + Int(form.rawValue)]!, stringValue) + } + public func PUSH_MESSAGE_PHOTOS(_ selector: Int32, _ _1: String, _ _2: Int32) -> String { + let form = getPluralizationForm(self.lc, selector) + return String(format: self._ps[122 * 6 + Int(form.rawValue)]!, _1, _2) + } + public func PUSH_MESSAGES(_ selector: Int32, _ _1: String, _ _2: Int32) -> String { + let form = getPluralizationForm(self.lc, selector) + return String(format: self._ps[123 * 6 + Int(form.rawValue)]!, _1, _2) + } + public func Conversation_StatusOnline(_ value: Int32) -> String { + let form = getPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[124 * 6 + Int(form.rawValue)]!, stringValue) + } + public func ForwardedPhotos(_ value: Int32) -> String { + let form = getPluralizationForm(self.lc, value) + let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator) + return String(format: self._ps[125 * 6 + Int(form.rawValue)]!, stringValue) } public init(primaryComponent: PresentationStringsComponent, secondaryComponent: PresentationStringsComponent?, groupingSeparator: String) { diff --git a/submodules/TelegramPresentationData/Sources/PresentationTheme.swift b/submodules/TelegramPresentationData/Sources/PresentationTheme.swift index 66a2ba6ba1..e9ab5e256a 100644 --- a/submodules/TelegramPresentationData/Sources/PresentationTheme.swift +++ b/submodules/TelegramPresentationData/Sources/PresentationTheme.swift @@ -631,17 +631,23 @@ public final class PresentationThemeChatBubblePolls { public let highlight: UIColor public let separator: UIColor public let bar: UIColor + public let barIconForeground: UIColor + public let barPositive: UIColor + public let barNegative: UIColor - public init(radioButton: UIColor, radioProgress: UIColor, highlight: UIColor, separator: UIColor, bar: UIColor) { + public init(radioButton: UIColor, radioProgress: UIColor, highlight: UIColor, separator: UIColor, bar: UIColor, barIconForeground: UIColor, barPositive: UIColor, barNegative: UIColor) { self.radioButton = radioButton self.radioProgress = radioProgress self.highlight = highlight self.separator = separator self.bar = bar + self.barIconForeground = barIconForeground + self.barPositive = barPositive + self.barNegative = barNegative } - public func withUpdated(radioButton: UIColor? = nil, radioProgress: UIColor? = nil, highlight: UIColor? = nil, separator: UIColor? = nil, bar: UIColor? = nil) -> PresentationThemeChatBubblePolls { - return PresentationThemeChatBubblePolls(radioButton: radioButton ?? self.radioButton, radioProgress: radioProgress ?? self.radioProgress, highlight: highlight ?? self.highlight, separator: separator ?? self.separator, bar: bar ?? self.bar) + public func withUpdated(radioButton: UIColor? = nil, radioProgress: UIColor? = nil, highlight: UIColor? = nil, separator: UIColor? = nil, bar: UIColor? = nil, barIconForeground: UIColor? = nil, barPositive: UIColor? = nil, barNegative: UIColor? = nil) -> PresentationThemeChatBubblePolls { + return PresentationThemeChatBubblePolls(radioButton: radioButton ?? self.radioButton, radioProgress: radioProgress ?? self.radioProgress, highlight: highlight ?? self.highlight, separator: separator ?? self.separator, bar: bar ?? self.bar, barIconForeground: barIconForeground ?? self.barIconForeground, barPositive: barPositive ?? self.barPositive, barNegative: barNegative ?? self.barNegative) } } @@ -655,6 +661,7 @@ public final class PresentationThemePartedColors { public let textHighlightColor: UIColor public let accentTextColor: UIColor public let accentControlColor: UIColor + public let accentControlDisabledColor: UIColor public let mediaActiveControlColor: UIColor public let mediaInactiveControlColor: UIColor public let mediaControlInnerBackgroundColor: UIColor @@ -670,7 +677,7 @@ public final class PresentationThemePartedColors { public let textSelectionColor: UIColor public let textSelectionKnobColor: UIColor - public init(bubble: PresentationThemeBubbleColor, primaryTextColor: UIColor, secondaryTextColor: UIColor, linkTextColor: UIColor, linkHighlightColor: UIColor, scamColor: UIColor, textHighlightColor: UIColor, accentTextColor: UIColor, accentControlColor: UIColor, mediaActiveControlColor: UIColor, mediaInactiveControlColor: UIColor, mediaControlInnerBackgroundColor: UIColor, pendingActivityColor: UIColor, fileTitleColor: UIColor, fileDescriptionColor: UIColor, fileDurationColor: UIColor, mediaPlaceholderColor: UIColor, polls: PresentationThemeChatBubblePolls, actionButtonsFillColor: PresentationThemeVariableColor, actionButtonsStrokeColor: PresentationThemeVariableColor, actionButtonsTextColor: PresentationThemeVariableColor, textSelectionColor: UIColor, textSelectionKnobColor: UIColor) { + public init(bubble: PresentationThemeBubbleColor, primaryTextColor: UIColor, secondaryTextColor: UIColor, linkTextColor: UIColor, linkHighlightColor: UIColor, scamColor: UIColor, textHighlightColor: UIColor, accentTextColor: UIColor, accentControlColor: UIColor, accentControlDisabledColor: UIColor, mediaActiveControlColor: UIColor, mediaInactiveControlColor: UIColor, mediaControlInnerBackgroundColor: UIColor, pendingActivityColor: UIColor, fileTitleColor: UIColor, fileDescriptionColor: UIColor, fileDurationColor: UIColor, mediaPlaceholderColor: UIColor, polls: PresentationThemeChatBubblePolls, actionButtonsFillColor: PresentationThemeVariableColor, actionButtonsStrokeColor: PresentationThemeVariableColor, actionButtonsTextColor: PresentationThemeVariableColor, textSelectionColor: UIColor, textSelectionKnobColor: UIColor) { self.bubble = bubble self.primaryTextColor = primaryTextColor self.secondaryTextColor = secondaryTextColor @@ -680,6 +687,7 @@ public final class PresentationThemePartedColors { self.textHighlightColor = textHighlightColor self.accentTextColor = accentTextColor self.accentControlColor = accentControlColor + self.accentControlDisabledColor = accentControlDisabledColor self.mediaActiveControlColor = mediaActiveControlColor self.mediaInactiveControlColor = mediaInactiveControlColor self.mediaControlInnerBackgroundColor = mediaControlInnerBackgroundColor @@ -696,8 +704,8 @@ public final class PresentationThemePartedColors { self.textSelectionKnobColor = textSelectionKnobColor } - public func withUpdated(bubble: PresentationThemeBubbleColor? = nil, primaryTextColor: UIColor? = nil, secondaryTextColor: UIColor? = nil, linkTextColor: UIColor? = nil, linkHighlightColor: UIColor? = nil, scamColor: UIColor? = nil, textHighlightColor: UIColor? = nil, accentTextColor: UIColor? = nil, accentControlColor: UIColor? = nil, mediaActiveControlColor: UIColor? = nil, mediaInactiveControlColor: UIColor? = nil, mediaControlInnerBackgroundColor: UIColor? = nil, pendingActivityColor: UIColor? = nil, fileTitleColor: UIColor? = nil, fileDescriptionColor: UIColor? = nil, fileDurationColor: UIColor? = nil, mediaPlaceholderColor: UIColor? = nil, polls: PresentationThemeChatBubblePolls? = nil, actionButtonsFillColor: PresentationThemeVariableColor? = nil, actionButtonsStrokeColor: PresentationThemeVariableColor? = nil, actionButtonsTextColor: PresentationThemeVariableColor? = nil, textSelectionColor: UIColor? = nil, textSelectionKnobColor: UIColor? = nil) -> PresentationThemePartedColors { - return PresentationThemePartedColors(bubble: bubble ?? self.bubble, primaryTextColor: primaryTextColor ?? self.primaryTextColor, secondaryTextColor: secondaryTextColor ?? self.secondaryTextColor, linkTextColor: linkTextColor ?? self.linkTextColor, linkHighlightColor: linkHighlightColor ?? self.linkHighlightColor, scamColor: scamColor ?? self.scamColor, textHighlightColor: textHighlightColor ?? self.textHighlightColor, accentTextColor: accentTextColor ?? self.accentTextColor, accentControlColor: accentControlColor ?? self.accentControlColor, mediaActiveControlColor: mediaActiveControlColor ?? self.mediaActiveControlColor, mediaInactiveControlColor: mediaInactiveControlColor ?? self.mediaInactiveControlColor, mediaControlInnerBackgroundColor: mediaControlInnerBackgroundColor ?? self.mediaControlInnerBackgroundColor, pendingActivityColor: pendingActivityColor ?? self.pendingActivityColor, fileTitleColor: fileTitleColor ?? self.fileTitleColor, fileDescriptionColor: fileDescriptionColor ?? self.fileDescriptionColor, fileDurationColor: fileDurationColor ?? self.fileDurationColor, mediaPlaceholderColor: mediaPlaceholderColor ?? self.mediaPlaceholderColor, polls: polls ?? self.polls, actionButtonsFillColor: actionButtonsFillColor ?? self.actionButtonsFillColor, actionButtonsStrokeColor: actionButtonsStrokeColor ?? self.actionButtonsStrokeColor, actionButtonsTextColor: actionButtonsTextColor ?? self.actionButtonsTextColor, textSelectionColor: textSelectionColor ?? self.textSelectionColor, textSelectionKnobColor: textSelectionKnobColor ?? self.textSelectionKnobColor) + public func withUpdated(bubble: PresentationThemeBubbleColor? = nil, primaryTextColor: UIColor? = nil, secondaryTextColor: UIColor? = nil, linkTextColor: UIColor? = nil, linkHighlightColor: UIColor? = nil, scamColor: UIColor? = nil, textHighlightColor: UIColor? = nil, accentTextColor: UIColor? = nil, accentControlColor: UIColor? = nil, accentControlDisabledColor: UIColor? = nil, mediaActiveControlColor: UIColor? = nil, mediaInactiveControlColor: UIColor? = nil, mediaControlInnerBackgroundColor: UIColor? = nil, pendingActivityColor: UIColor? = nil, fileTitleColor: UIColor? = nil, fileDescriptionColor: UIColor? = nil, fileDurationColor: UIColor? = nil, mediaPlaceholderColor: UIColor? = nil, polls: PresentationThemeChatBubblePolls? = nil, actionButtonsFillColor: PresentationThemeVariableColor? = nil, actionButtonsStrokeColor: PresentationThemeVariableColor? = nil, actionButtonsTextColor: PresentationThemeVariableColor? = nil, textSelectionColor: UIColor? = nil, textSelectionKnobColor: UIColor? = nil) -> PresentationThemePartedColors { + return PresentationThemePartedColors(bubble: bubble ?? self.bubble, primaryTextColor: primaryTextColor ?? self.primaryTextColor, secondaryTextColor: secondaryTextColor ?? self.secondaryTextColor, linkTextColor: linkTextColor ?? self.linkTextColor, linkHighlightColor: linkHighlightColor ?? self.linkHighlightColor, scamColor: scamColor ?? self.scamColor, textHighlightColor: textHighlightColor ?? self.textHighlightColor, accentTextColor: accentTextColor ?? self.accentTextColor, accentControlColor: accentControlColor ?? self.accentControlColor, accentControlDisabledColor: accentControlDisabledColor ?? self.accentControlDisabledColor, mediaActiveControlColor: mediaActiveControlColor ?? self.mediaActiveControlColor, mediaInactiveControlColor: mediaInactiveControlColor ?? self.mediaInactiveControlColor, mediaControlInnerBackgroundColor: mediaControlInnerBackgroundColor ?? self.mediaControlInnerBackgroundColor, pendingActivityColor: pendingActivityColor ?? self.pendingActivityColor, fileTitleColor: fileTitleColor ?? self.fileTitleColor, fileDescriptionColor: fileDescriptionColor ?? self.fileDescriptionColor, fileDurationColor: fileDurationColor ?? self.fileDurationColor, mediaPlaceholderColor: mediaPlaceholderColor ?? self.mediaPlaceholderColor, polls: polls ?? self.polls, actionButtonsFillColor: actionButtonsFillColor ?? self.actionButtonsFillColor, actionButtonsStrokeColor: actionButtonsStrokeColor ?? self.actionButtonsStrokeColor, actionButtonsTextColor: actionButtonsTextColor ?? self.actionButtonsTextColor, textSelectionColor: textSelectionColor ?? self.textSelectionColor, textSelectionKnobColor: textSelectionKnobColor ?? self.textSelectionKnobColor) } } @@ -1174,6 +1182,10 @@ public final class PresentationTheme: Equatable { return self.resourceCache.object(key, self, generate) } + public func object(_ key: PresentationResourceParameterKey, _ generate: (PresentationTheme) -> AnyObject?) -> AnyObject? { + return self.resourceCache.parameterObject(key, self, generate) + } + public static func ==(lhs: PresentationTheme, rhs: PresentationTheme) -> Bool { return lhs === rhs } diff --git a/submodules/TelegramPresentationData/Sources/PresentationThemeCodable.swift b/submodules/TelegramPresentationData/Sources/PresentationThemeCodable.swift index cff496f675..876a2f188c 100644 --- a/submodules/TelegramPresentationData/Sources/PresentationThemeCodable.swift +++ b/submodules/TelegramPresentationData/Sources/PresentationThemeCodable.swift @@ -55,7 +55,6 @@ extension TelegramWallpaper: Codable { } if components.count >= 2 && components.count <= 5 && [6, 8].contains(components[0].count) && !optionKeys.contains(components[0]) && [6, 8].contains(components[1].count) && !optionKeys.contains(components[1]), let topColor = UIColor(hexString: components[0]), let bottomColor = UIColor(hexString: components[1]) { - var rotation: Int32? if components.count > 2, components[2].count <= 3, let value = Int32(components[2]) { if value >= 0 && value < 360 { @@ -1051,15 +1050,24 @@ extension PresentationThemeChatBubblePolls: Codable { case highlight case separator case bar + case barIconForeground + case barPositive + case barNegative } public convenience init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) - self.init(radioButton: try decodeColor(values, .radioButton), - radioProgress: try decodeColor(values, .radioProgress), - highlight: try decodeColor(values, .highlight), - separator: try decodeColor(values, .separator), - bar: try decodeColor(values, .bar)) + let bar = try decodeColor(values, .bar) + self.init( + radioButton: try decodeColor(values, .radioButton), + radioProgress: try decodeColor(values, .radioProgress), + highlight: try decodeColor(values, .highlight), + separator: try decodeColor(values, .separator), + bar: bar, + barIconForeground: (try? decodeColor(values, .barIconForeground)) ?? .clear, + barPositive: (try? decodeColor(values, .barPositive)) ?? bar, + barNegative: (try? decodeColor(values, .barNegative)) ?? bar + ) } public func encode(to encoder: Encoder) throws { @@ -1069,6 +1077,9 @@ extension PresentationThemeChatBubblePolls: Codable { try encodeColor(&values, self.highlight, .highlight) try encodeColor(&values, self.separator, .separator) try encodeColor(&values, self.bar, .bar) + try encodeColor(&values, self.barIconForeground, .barIconForeground) + try encodeColor(&values, self.barPositive, .barPositive) + try encodeColor(&values, self.barNegative, .barNegative) } } @@ -1097,11 +1108,13 @@ extension PresentationThemePartedColors: Codable { case actionButtonsText case textSelection case textSelectionKnob + case accentControlDisabled } public convenience init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) let codingPath = decoder.codingPath.map { $0.stringValue }.joined(separator: ".") + let accentControlColor = try decodeColor(values, .accentControl) self.init( bubble: try values.decode(PresentationThemeBubbleColor.self, forKey: .bubble), primaryTextColor: try decodeColor(values, .primaryText), @@ -1111,7 +1124,8 @@ extension PresentationThemePartedColors: Codable { scamColor: try decodeColor(values, .scam), textHighlightColor: try decodeColor(values, .textHighlight), accentTextColor: try decodeColor(values, .accentText), - accentControlColor: try decodeColor(values, .accentControl), + accentControlColor: accentControlColor, + accentControlDisabledColor: (try? decodeColor(values, .accentControlDisabled)) ?? accentControlColor.withAlphaComponent(0.5), mediaActiveControlColor: try decodeColor(values, .mediaActiveControl), mediaInactiveControlColor: try decodeColor(values, .mediaInactiveControl), mediaControlInnerBackgroundColor: try decodeColor(values, .mediaControlInnerBg, decoder: decoder, fallbackKey: codingPath + ".bubble.withWp.bg"), diff --git a/submodules/TelegramPresentationData/Sources/PresentationThemeEssentialGraphics.swift b/submodules/TelegramPresentationData/Sources/PresentationThemeEssentialGraphics.swift index dd1618f965..3374f03b51 100644 --- a/submodules/TelegramPresentationData/Sources/PresentationThemeEssentialGraphics.swift +++ b/submodules/TelegramPresentationData/Sources/PresentationThemeEssentialGraphics.swift @@ -160,7 +160,7 @@ public final class PrincipalThemeEssentialGraphics { public let incomingBubbleGradientImage: UIImage? public let outgoingBubbleGradientImage: UIImage? - init(mediaBox: MediaBox, presentationTheme: PresentationTheme, wallpaper initialWallpaper: TelegramWallpaper, preview: Bool = false, knockoutMode: Bool) { + init(mediaBox: MediaBox, presentationTheme: PresentationTheme, wallpaper initialWallpaper: TelegramWallpaper, preview: Bool = false, knockoutMode: Bool, bubbleCorners: PresentationChatBubbleCorners) { let theme = presentationTheme.chat var wallpaper = initialWallpaper @@ -215,28 +215,31 @@ public final class PrincipalThemeEssentialGraphics { let serviceColor = serviceMessageColorComponents(chatTheme: theme, wallpaper: wallpaper) + let maxCornerRadius = bubbleCorners.mainRadius + let minCornerRadius = (bubbleCorners.mergeBubbleCorners && maxCornerRadius >= 10.0) ? bubbleCorners.auxiliaryRadius : bubbleCorners.mainRadius + let emptyImage = UIImage() if preview { - self.chatMessageBackgroundIncomingMaskImage = messageBubbleImage(incoming: true, fillColor: UIColor.black, strokeColor: UIColor.clear, neighbors: .none, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true) - self.chatMessageBackgroundIncomingImage = messageBubbleImage(incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true) - self.chatMessageBackgroundIncomingOutlineImage = messageBubbleImage(incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true, onlyOutline: true) - self.chatMessageBackgroundOutgoingMaskImage = messageBubbleImage(incoming: false, fillColor: .black, strokeColor: .clear, neighbors: .none, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true) - self.chatMessageBackgroundOutgoingImage = messageBubbleImage(incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true) - self.chatMessageBackgroundOutgoingOutlineImage = messageBubbleImage(incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyOutline: true) + self.chatMessageBackgroundIncomingMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: UIColor.black, strokeColor: UIColor.clear, neighbors: .none, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true) + self.chatMessageBackgroundIncomingImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true) + self.chatMessageBackgroundIncomingOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true, onlyOutline: true) + self.chatMessageBackgroundOutgoingMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: .black, strokeColor: .clear, neighbors: .none, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true) + self.chatMessageBackgroundOutgoingImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true) + self.chatMessageBackgroundOutgoingOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyOutline: true) self.checkBubbleFullImage = generateCheckImage(partial: false, color: theme.message.outgoingCheckColor, width: 11.0)! self.checkBubblePartialImage = generateCheckImage(partial: true, color: theme.message.outgoingCheckColor, width: 11.0)! self.chatMessageBackgroundIncomingHighlightedImage = emptyImage self.chatMessageBackgroundIncomingMergedTopMaskImage = emptyImage - self.chatMessageBackgroundIncomingMergedTopImage = messageBubbleImage(incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .top(side: false), theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true) - self.chatMessageBackgroundIncomingMergedTopOutlineImage = messageBubbleImage(incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .top(side: false), theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true, onlyOutline: true) + self.chatMessageBackgroundIncomingMergedTopImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .top(side: false), theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true) + self.chatMessageBackgroundIncomingMergedTopOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .top(side: false), theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true, onlyOutline: true) self.chatMessageBackgroundIncomingMergedTopHighlightedImage = emptyImage self.chatMessageBackgroundIncomingMergedTopSideMaskImage = emptyImage self.chatMessageBackgroundIncomingMergedTopSideImage = emptyImage self.chatMessageBackgroundIncomingMergedTopSideOutlineImage = emptyImage self.chatMessageBackgroundIncomingMergedTopSideHighlightedImage = emptyImage self.chatMessageBackgroundIncomingMergedBottomMaskImage = emptyImage - self.chatMessageBackgroundIncomingMergedBottomImage = messageBubbleImage(incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .bottom, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true) - self.chatMessageBackgroundIncomingMergedBottomOutlineImage = messageBubbleImage(incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .bottom, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true, onlyOutline: true) + self.chatMessageBackgroundIncomingMergedBottomImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .bottom, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true) + self.chatMessageBackgroundIncomingMergedBottomOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .bottom, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true, onlyOutline: true) self.chatMessageBackgroundIncomingMergedBottomHighlightedImage = emptyImage self.chatMessageBackgroundIncomingMergedBothMaskImage = emptyImage self.chatMessageBackgroundIncomingMergedBothImage = emptyImage @@ -247,21 +250,21 @@ public final class PrincipalThemeEssentialGraphics { self.chatMessageBackgroundIncomingMergedSideOutlineImage = emptyImage self.chatMessageBackgroundIncomingMergedSideHighlightedImage = emptyImage self.chatMessageBackgroundOutgoingHighlightedImage = emptyImage - self.chatMessageBackgroundOutgoingMergedTopMaskImage = messageBubbleImage(incoming: false, fillColor: .black, strokeColor: .clear, neighbors: .top(side: false), theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true) - self.chatMessageBackgroundOutgoingMergedTopImage = messageBubbleImage(incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .top(side: false), theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true) - self.chatMessageBackgroundOutgoingMergedTopOutlineImage = messageBubbleImage(incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .top(side: false), theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyOutline: true) + self.chatMessageBackgroundOutgoingMergedTopMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: .black, strokeColor: .clear, neighbors: .top(side: false), theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true) + self.chatMessageBackgroundOutgoingMergedTopImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .top(side: false), theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true) + self.chatMessageBackgroundOutgoingMergedTopOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .top(side: false), theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyOutline: true) self.chatMessageBackgroundOutgoingMergedTopHighlightedImage = emptyImage self.chatMessageBackgroundOutgoingMergedTopSideMaskImage = emptyImage self.chatMessageBackgroundOutgoingMergedTopSideImage = emptyImage self.chatMessageBackgroundOutgoingMergedTopSideOutlineImage = emptyImage self.chatMessageBackgroundOutgoingMergedTopSideHighlightedImage = emptyImage - self.chatMessageBackgroundOutgoingMergedBottomMaskImage = messageBubbleImage(incoming: false, fillColor: .black, strokeColor: .clear, neighbors: .bottom, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true) - self.chatMessageBackgroundOutgoingMergedBottomImage = messageBubbleImage(incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .bottom, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true) - self.chatMessageBackgroundOutgoingMergedBottomOutlineImage = messageBubbleImage(incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .bottom, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyOutline: true) + self.chatMessageBackgroundOutgoingMergedBottomMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: .black, strokeColor: .clear, neighbors: .bottom, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true) + self.chatMessageBackgroundOutgoingMergedBottomImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .bottom, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true) + self.chatMessageBackgroundOutgoingMergedBottomOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .bottom, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyOutline: true) self.chatMessageBackgroundOutgoingMergedBottomHighlightedImage = emptyImage - self.chatMessageBackgroundOutgoingMergedBothMaskImage = messageBubbleImage(incoming: false, fillColor: .black, strokeColor: .clear, neighbors: .both, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true) - self.chatMessageBackgroundOutgoingMergedBothImage = messageBubbleImage(incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .both, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true) - self.chatMessageBackgroundOutgoingMergedBothOutlineImage = messageBubbleImage(incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .both, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyOutline: true) + self.chatMessageBackgroundOutgoingMergedBothMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: .black, strokeColor: .clear, neighbors: .both, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true) + self.chatMessageBackgroundOutgoingMergedBothImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .both, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true) + self.chatMessageBackgroundOutgoingMergedBothOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .both, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyOutline: true) self.chatMessageBackgroundOutgoingMergedBothHighlightedImage = emptyImage self.chatMessageBackgroundOutgoingMergedSideMaskImage = emptyImage self.chatMessageBackgroundOutgoingMergedSideImage = emptyImage @@ -288,56 +291,56 @@ public final class PrincipalThemeEssentialGraphics { self.radialIndicatorFileIconIncoming = emptyImage self.radialIndicatorFileIconOutgoing = emptyImage } else { - self.chatMessageBackgroundIncomingMaskImage = messageBubbleImage(incoming: true, fillColor: .black, strokeColor: .clear, neighbors: .none, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true) - self.chatMessageBackgroundIncomingImage = messageBubbleImage(incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true) - self.chatMessageBackgroundIncomingOutlineImage = messageBubbleImage(incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true, onlyOutline: true) - self.chatMessageBackgroundIncomingHighlightedImage = messageBubbleImage(incoming: true, fillColor: incoming.highlightedFill, strokeColor: incoming.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true) - self.chatMessageBackgroundIncomingMergedTopMaskImage = messageBubbleImage(incoming: true, fillColor: .black, strokeColor: .clear, neighbors: .top(side: false), theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true) - self.chatMessageBackgroundIncomingMergedTopImage = messageBubbleImage(incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .top(side: false), theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true) - self.chatMessageBackgroundIncomingMergedTopOutlineImage = messageBubbleImage(incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .top(side: false), theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true, onlyOutline: true) - self.chatMessageBackgroundIncomingMergedTopHighlightedImage = messageBubbleImage(incoming: true, fillColor: incoming.highlightedFill, strokeColor: incoming.stroke, neighbors: .top(side: false), theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true) - self.chatMessageBackgroundIncomingMergedTopSideMaskImage = messageBubbleImage(incoming: true, fillColor: .black, strokeColor: .clear, neighbors: .top(side: true), theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true) - self.chatMessageBackgroundIncomingMergedTopSideImage = messageBubbleImage(incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .top(side: true), theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true) - self.chatMessageBackgroundIncomingMergedTopSideOutlineImage = messageBubbleImage(incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .top(side: true), theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true, onlyOutline: true) - self.chatMessageBackgroundIncomingMergedTopSideHighlightedImage = messageBubbleImage(incoming: true, fillColor: incoming.highlightedFill, strokeColor: incoming.stroke, neighbors: .top(side: true), theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true) - self.chatMessageBackgroundIncomingMergedBottomMaskImage = messageBubbleImage(incoming: true, fillColor: .black, strokeColor: .clear, neighbors: .bottom, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true) - self.chatMessageBackgroundIncomingMergedBottomImage = messageBubbleImage(incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .bottom, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true) - self.chatMessageBackgroundIncomingMergedBottomOutlineImage = messageBubbleImage(incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .bottom, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true, onlyOutline: true) - self.chatMessageBackgroundIncomingMergedBottomHighlightedImage = messageBubbleImage(incoming: true, fillColor: incoming.highlightedFill, strokeColor: incoming.stroke, neighbors: .bottom, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true) - self.chatMessageBackgroundIncomingMergedBothMaskImage = messageBubbleImage(incoming: true, fillColor: .black, strokeColor: .clear, neighbors: .both, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true) - self.chatMessageBackgroundIncomingMergedBothImage = messageBubbleImage(incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .both, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true) - self.chatMessageBackgroundIncomingMergedBothOutlineImage = messageBubbleImage(incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .both, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true, onlyOutline: true) - self.chatMessageBackgroundIncomingMergedBothHighlightedImage = messageBubbleImage(incoming: true, fillColor: incoming.highlightedFill, strokeColor: incoming.stroke, neighbors: .both, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true) + self.chatMessageBackgroundIncomingMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: .black, strokeColor: .clear, neighbors: .none, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true) + self.chatMessageBackgroundIncomingImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true) + self.chatMessageBackgroundIncomingOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true, onlyOutline: true) + self.chatMessageBackgroundIncomingHighlightedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.highlightedFill, strokeColor: incoming.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true) + self.chatMessageBackgroundIncomingMergedTopMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: .black, strokeColor: .clear, neighbors: .top(side: false), theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true) + self.chatMessageBackgroundIncomingMergedTopImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .top(side: false), theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true) + self.chatMessageBackgroundIncomingMergedTopOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .top(side: false), theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true, onlyOutline: true) + self.chatMessageBackgroundIncomingMergedTopHighlightedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.highlightedFill, strokeColor: incoming.stroke, neighbors: .top(side: false), theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true) + self.chatMessageBackgroundIncomingMergedTopSideMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: .black, strokeColor: .clear, neighbors: .top(side: true), theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true) + self.chatMessageBackgroundIncomingMergedTopSideImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .top(side: true), theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true) + self.chatMessageBackgroundIncomingMergedTopSideOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .top(side: true), theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true, onlyOutline: true) + self.chatMessageBackgroundIncomingMergedTopSideHighlightedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.highlightedFill, strokeColor: incoming.stroke, neighbors: .top(side: true), theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true) + self.chatMessageBackgroundIncomingMergedBottomMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: .black, strokeColor: .clear, neighbors: .bottom, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true) + self.chatMessageBackgroundIncomingMergedBottomImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .bottom, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true) + self.chatMessageBackgroundIncomingMergedBottomOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .bottom, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true, onlyOutline: true) + self.chatMessageBackgroundIncomingMergedBottomHighlightedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.highlightedFill, strokeColor: incoming.stroke, neighbors: .bottom, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true) + self.chatMessageBackgroundIncomingMergedBothMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: .black, strokeColor: .clear, neighbors: .both, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true) + self.chatMessageBackgroundIncomingMergedBothImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .both, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true) + self.chatMessageBackgroundIncomingMergedBothOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .both, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true, onlyOutline: true) + self.chatMessageBackgroundIncomingMergedBothHighlightedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.highlightedFill, strokeColor: incoming.stroke, neighbors: .both, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true) - self.chatMessageBackgroundOutgoingMaskImage = messageBubbleImage(incoming: false, fillColor: .black, strokeColor: .clear, neighbors: .none, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true) - self.chatMessageBackgroundOutgoingImage = messageBubbleImage(incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true) - self.chatMessageBackgroundOutgoingOutlineImage = messageBubbleImage(incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyOutline: true) - self.chatMessageBackgroundOutgoingHighlightedImage = messageBubbleImage(incoming: false, fillColor: outgoing.highlightedFill, strokeColor: outgoing.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true) - self.chatMessageBackgroundOutgoingMergedTopMaskImage = messageBubbleImage(incoming: false, fillColor: .black, strokeColor: .clear, neighbors: .top(side: false), theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true) - self.chatMessageBackgroundOutgoingMergedTopImage = messageBubbleImage(incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .top(side: false), theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true) - self.chatMessageBackgroundOutgoingMergedTopOutlineImage = messageBubbleImage(incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .top(side: false), theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyOutline: true) - self.chatMessageBackgroundOutgoingMergedTopHighlightedImage = messageBubbleImage(incoming: false, fillColor: outgoing.highlightedFill, strokeColor: outgoing.stroke, neighbors: .top(side: false), theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true) - self.chatMessageBackgroundOutgoingMergedTopSideMaskImage = messageBubbleImage(incoming: false, fillColor: .black, strokeColor: .clear, neighbors: .top(side: true), theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true) - self.chatMessageBackgroundOutgoingMergedTopSideImage = messageBubbleImage(incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .top(side: true), theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true) - self.chatMessageBackgroundOutgoingMergedTopSideOutlineImage = messageBubbleImage(incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .top(side: true), theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyOutline: true) - self.chatMessageBackgroundOutgoingMergedTopSideHighlightedImage = messageBubbleImage(incoming: false, fillColor: outgoing.highlightedFill, strokeColor: outgoing.stroke, neighbors: .top(side: true), theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true) - self.chatMessageBackgroundOutgoingMergedBottomMaskImage = messageBubbleImage(incoming: false, fillColor: .black, strokeColor: .clear, neighbors: .bottom, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true) - self.chatMessageBackgroundOutgoingMergedBottomImage = messageBubbleImage(incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .bottom, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true) - self.chatMessageBackgroundOutgoingMergedBottomOutlineImage = messageBubbleImage(incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .bottom, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyOutline: true) - self.chatMessageBackgroundOutgoingMergedBottomHighlightedImage = messageBubbleImage(incoming: false, fillColor: outgoing.highlightedFill, strokeColor: outgoing.stroke, neighbors: .bottom, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true) - self.chatMessageBackgroundOutgoingMergedBothMaskImage = messageBubbleImage(incoming: false, fillColor: .black, strokeColor: .clear, neighbors: .both, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true) - self.chatMessageBackgroundOutgoingMergedBothImage = messageBubbleImage(incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .both, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true) - self.chatMessageBackgroundOutgoingMergedBothOutlineImage = messageBubbleImage(incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .both, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyOutline: true) - self.chatMessageBackgroundOutgoingMergedBothHighlightedImage = messageBubbleImage(incoming: false, fillColor: outgoing.highlightedFill, strokeColor: outgoing.stroke, neighbors: .both, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true) + self.chatMessageBackgroundOutgoingMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: .black, strokeColor: .clear, neighbors: .none, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true) + self.chatMessageBackgroundOutgoingImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true) + self.chatMessageBackgroundOutgoingOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyOutline: true) + self.chatMessageBackgroundOutgoingHighlightedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.highlightedFill, strokeColor: outgoing.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true) + self.chatMessageBackgroundOutgoingMergedTopMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: .black, strokeColor: .clear, neighbors: .top(side: false), theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true) + self.chatMessageBackgroundOutgoingMergedTopImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .top(side: false), theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true) + self.chatMessageBackgroundOutgoingMergedTopOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .top(side: false), theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyOutline: true) + self.chatMessageBackgroundOutgoingMergedTopHighlightedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.highlightedFill, strokeColor: outgoing.stroke, neighbors: .top(side: false), theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true) + self.chatMessageBackgroundOutgoingMergedTopSideMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: .black, strokeColor: .clear, neighbors: .top(side: true), theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true) + self.chatMessageBackgroundOutgoingMergedTopSideImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .top(side: true), theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true) + self.chatMessageBackgroundOutgoingMergedTopSideOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .top(side: true), theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyOutline: true) + self.chatMessageBackgroundOutgoingMergedTopSideHighlightedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.highlightedFill, strokeColor: outgoing.stroke, neighbors: .top(side: true), theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true) + self.chatMessageBackgroundOutgoingMergedBottomMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: .black, strokeColor: .clear, neighbors: .bottom, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true) + self.chatMessageBackgroundOutgoingMergedBottomImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .bottom, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true) + self.chatMessageBackgroundOutgoingMergedBottomOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .bottom, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyOutline: true) + self.chatMessageBackgroundOutgoingMergedBottomHighlightedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.highlightedFill, strokeColor: outgoing.stroke, neighbors: .bottom, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true) + self.chatMessageBackgroundOutgoingMergedBothMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: .black, strokeColor: .clear, neighbors: .both, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true) + self.chatMessageBackgroundOutgoingMergedBothImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .both, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true) + self.chatMessageBackgroundOutgoingMergedBothOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .both, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyOutline: true) + self.chatMessageBackgroundOutgoingMergedBothHighlightedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.highlightedFill, strokeColor: outgoing.stroke, neighbors: .both, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true) - self.chatMessageBackgroundIncomingMergedSideMaskImage = messageBubbleImage(incoming: true, fillColor: .black, strokeColor: .clear, neighbors: .side, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true) - self.chatMessageBackgroundIncomingMergedSideImage = messageBubbleImage(incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .side, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true) - self.chatMessageBackgroundIncomingMergedSideOutlineImage = messageBubbleImage(incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .side, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyOutline: true) - self.chatMessageBackgroundOutgoingMergedSideMaskImage = messageBubbleImage(incoming: false, fillColor: .black, strokeColor: .clear, neighbors: .side, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true) - self.chatMessageBackgroundOutgoingMergedSideImage = messageBubbleImage(incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .side, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true) - self.chatMessageBackgroundOutgoingMergedSideOutlineImage = messageBubbleImage(incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .side, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyOutline: true) - self.chatMessageBackgroundIncomingMergedSideHighlightedImage = messageBubbleImage(incoming: true, fillColor: incoming.highlightedFill, strokeColor: incoming.stroke, neighbors: .side, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true) - self.chatMessageBackgroundOutgoingMergedSideHighlightedImage = messageBubbleImage(incoming: false, fillColor: outgoing.highlightedFill, strokeColor: outgoing.stroke, neighbors: .side, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true) + self.chatMessageBackgroundIncomingMergedSideMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: .black, strokeColor: .clear, neighbors: .side, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true) + self.chatMessageBackgroundIncomingMergedSideImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .side, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true) + self.chatMessageBackgroundIncomingMergedSideOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .side, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyOutline: true) + self.chatMessageBackgroundOutgoingMergedSideMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: .black, strokeColor: .clear, neighbors: .side, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true) + self.chatMessageBackgroundOutgoingMergedSideImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .side, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true) + self.chatMessageBackgroundOutgoingMergedSideOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .side, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyOutline: true) + self.chatMessageBackgroundIncomingMergedSideHighlightedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.highlightedFill, strokeColor: incoming.stroke, neighbors: .side, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true) + self.chatMessageBackgroundOutgoingMergedSideHighlightedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.highlightedFill, strokeColor: outgoing.stroke, neighbors: .side, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true) self.checkBubbleFullImage = generateCheckImage(partial: false, color: theme.message.outgoingCheckColor, width: 11.0)! self.checkBubblePartialImage = generateCheckImage(partial: true, color: theme.message.outgoingCheckColor, width: 11.0)! @@ -420,7 +423,7 @@ public final class PrincipalThemeAdditionalGraphics { public let chatEmptyItemLockIcon: UIImage public let emptyChatListCheckIcon: UIImage - init(_ theme: PresentationThemeChat, wallpaper: TelegramWallpaper) { + init(_ theme: PresentationThemeChat, wallpaper: TelegramWallpaper, bubbleCorners: PresentationChatBubbleCorners) { let serviceColor = serviceMessageColorComponents(chatTheme: theme, wallpaper: wallpaper) self.chatServiceBubbleFillImage = generateImage(CGSize(width: 20.0, height: 20.0), contextGenerator: { size, context -> Void in context.clear(CGRect(origin: CGPoint(), size: size)) @@ -441,14 +444,14 @@ public final class PrincipalThemeAdditionalGraphics { self.chatBubbleShareButtonImage = chatBubbleActionButtonImage(fillColor: bubbleVariableColor(variableColor: theme.message.shareButtonFillColor, wallpaper: wallpaper), strokeColor: bubbleVariableColor(variableColor: theme.message.shareButtonStrokeColor, wallpaper: wallpaper), foregroundColor: bubbleVariableColor(variableColor: theme.message.shareButtonForegroundColor, wallpaper: wallpaper), image: UIImage(bundleImageName: "Chat/Message/ShareIcon"))! self.chatBubbleNavigateButtonImage = chatBubbleActionButtonImage(fillColor: bubbleVariableColor(variableColor: theme.message.shareButtonFillColor, wallpaper: wallpaper), strokeColor: bubbleVariableColor(variableColor: theme.message.shareButtonStrokeColor, wallpaper: wallpaper), foregroundColor: bubbleVariableColor(variableColor: theme.message.shareButtonForegroundColor, wallpaper: wallpaper), image: UIImage(bundleImageName: "Chat/Message/NavigateToMessageIcon"), iconOffset: CGPoint(x: 0.0, y: 1.0))! - self.chatBubbleActionButtonIncomingMiddleImage = messageBubbleActionButtonImage(color: bubbleVariableColor(variableColor: theme.message.incoming.actionButtonsFillColor, wallpaper: wallpaper), strokeColor: bubbleVariableColor(variableColor: theme.message.incoming.actionButtonsStrokeColor, wallpaper: wallpaper), position: .middle) - self.chatBubbleActionButtonIncomingBottomLeftImage = messageBubbleActionButtonImage(color: bubbleVariableColor(variableColor: theme.message.incoming.actionButtonsFillColor, wallpaper: wallpaper), strokeColor: bubbleVariableColor(variableColor: theme.message.incoming.actionButtonsStrokeColor, wallpaper: wallpaper), position: .bottomLeft) - self.chatBubbleActionButtonIncomingBottomRightImage = messageBubbleActionButtonImage(color: bubbleVariableColor(variableColor: theme.message.incoming.actionButtonsFillColor, wallpaper: wallpaper), strokeColor: bubbleVariableColor(variableColor: theme.message.incoming.actionButtonsStrokeColor, wallpaper: wallpaper), position: .bottomRight) - self.chatBubbleActionButtonIncomingBottomSingleImage = messageBubbleActionButtonImage(color: bubbleVariableColor(variableColor: theme.message.incoming.actionButtonsFillColor, wallpaper: wallpaper), strokeColor: bubbleVariableColor(variableColor: theme.message.incoming.actionButtonsStrokeColor, wallpaper: wallpaper), position: .bottomSingle) - self.chatBubbleActionButtonOutgoingMiddleImage = messageBubbleActionButtonImage(color: bubbleVariableColor(variableColor: theme.message.outgoing.actionButtonsFillColor, wallpaper: wallpaper), strokeColor: bubbleVariableColor(variableColor: theme.message.outgoing.actionButtonsStrokeColor, wallpaper: wallpaper), position: .middle) - self.chatBubbleActionButtonOutgoingBottomLeftImage = messageBubbleActionButtonImage(color: bubbleVariableColor(variableColor: theme.message.outgoing.actionButtonsFillColor, wallpaper: wallpaper), strokeColor: bubbleVariableColor(variableColor: theme.message.outgoing.actionButtonsStrokeColor, wallpaper: wallpaper), position: .bottomLeft) - self.chatBubbleActionButtonOutgoingBottomRightImage = messageBubbleActionButtonImage(color: bubbleVariableColor(variableColor: theme.message.outgoing.actionButtonsFillColor, wallpaper: wallpaper), strokeColor: bubbleVariableColor(variableColor: theme.message.outgoing.actionButtonsStrokeColor, wallpaper: wallpaper), position: .bottomRight) - self.chatBubbleActionButtonOutgoingBottomSingleImage = messageBubbleActionButtonImage(color: bubbleVariableColor(variableColor: theme.message.outgoing.actionButtonsFillColor, wallpaper: wallpaper), strokeColor: bubbleVariableColor(variableColor: theme.message.outgoing.actionButtonsStrokeColor, wallpaper: wallpaper), position: .bottomSingle) + self.chatBubbleActionButtonIncomingMiddleImage = messageBubbleActionButtonImage(color: bubbleVariableColor(variableColor: theme.message.incoming.actionButtonsFillColor, wallpaper: wallpaper), strokeColor: bubbleVariableColor(variableColor: theme.message.incoming.actionButtonsStrokeColor, wallpaper: wallpaper), position: .middle, bubbleCorners: bubbleCorners) + self.chatBubbleActionButtonIncomingBottomLeftImage = messageBubbleActionButtonImage(color: bubbleVariableColor(variableColor: theme.message.incoming.actionButtonsFillColor, wallpaper: wallpaper), strokeColor: bubbleVariableColor(variableColor: theme.message.incoming.actionButtonsStrokeColor, wallpaper: wallpaper), position: .bottomLeft, bubbleCorners: bubbleCorners) + self.chatBubbleActionButtonIncomingBottomRightImage = messageBubbleActionButtonImage(color: bubbleVariableColor(variableColor: theme.message.incoming.actionButtonsFillColor, wallpaper: wallpaper), strokeColor: bubbleVariableColor(variableColor: theme.message.incoming.actionButtonsStrokeColor, wallpaper: wallpaper), position: .bottomRight, bubbleCorners: bubbleCorners) + self.chatBubbleActionButtonIncomingBottomSingleImage = messageBubbleActionButtonImage(color: bubbleVariableColor(variableColor: theme.message.incoming.actionButtonsFillColor, wallpaper: wallpaper), strokeColor: bubbleVariableColor(variableColor: theme.message.incoming.actionButtonsStrokeColor, wallpaper: wallpaper), position: .bottomSingle, bubbleCorners: bubbleCorners) + self.chatBubbleActionButtonOutgoingMiddleImage = messageBubbleActionButtonImage(color: bubbleVariableColor(variableColor: theme.message.outgoing.actionButtonsFillColor, wallpaper: wallpaper), strokeColor: bubbleVariableColor(variableColor: theme.message.outgoing.actionButtonsStrokeColor, wallpaper: wallpaper), position: .middle, bubbleCorners: bubbleCorners) + self.chatBubbleActionButtonOutgoingBottomLeftImage = messageBubbleActionButtonImage(color: bubbleVariableColor(variableColor: theme.message.outgoing.actionButtonsFillColor, wallpaper: wallpaper), strokeColor: bubbleVariableColor(variableColor: theme.message.outgoing.actionButtonsStrokeColor, wallpaper: wallpaper), position: .bottomLeft, bubbleCorners: bubbleCorners) + self.chatBubbleActionButtonOutgoingBottomRightImage = messageBubbleActionButtonImage(color: bubbleVariableColor(variableColor: theme.message.outgoing.actionButtonsFillColor, wallpaper: wallpaper), strokeColor: bubbleVariableColor(variableColor: theme.message.outgoing.actionButtonsStrokeColor, wallpaper: wallpaper), position: .bottomRight, bubbleCorners: bubbleCorners) + self.chatBubbleActionButtonOutgoingBottomSingleImage = messageBubbleActionButtonImage(color: bubbleVariableColor(variableColor: theme.message.outgoing.actionButtonsFillColor, wallpaper: wallpaper), strokeColor: bubbleVariableColor(variableColor: theme.message.outgoing.actionButtonsStrokeColor, wallpaper: wallpaper), position: .bottomSingle, bubbleCorners: bubbleCorners) self.chatBubbleActionButtonIncomingMessageIconImage = generateTintedImage(image: UIImage(bundleImageName: "Chat/Message/BotMessage"), color: bubbleVariableColor(variableColor: theme.message.incoming.actionButtonsTextColor, wallpaper: wallpaper))! self.chatBubbleActionButtonIncomingLinkIconImage = generateTintedImage(image: UIImage(bundleImageName: "Chat/Message/BotLink"), color: bubbleVariableColor(variableColor: theme.message.incoming.actionButtonsTextColor, wallpaper: wallpaper))! self.chatBubbleActionButtonIncomingShareIconImage = generateTintedImage(image: UIImage(bundleImageName: "Chat/Message/BotShare"), color: bubbleVariableColor(variableColor: theme.message.incoming.actionButtonsTextColor, wallpaper: wallpaper))! diff --git a/submodules/TelegramPresentationData/Sources/PresentationsResourceCache.swift b/submodules/TelegramPresentationData/Sources/PresentationsResourceCache.swift index a241cbfa43..2d59cc9eec 100644 --- a/submodules/TelegramPresentationData/Sources/PresentationsResourceCache.swift +++ b/submodules/TelegramPresentationData/Sources/PresentationsResourceCache.swift @@ -9,6 +9,7 @@ private final class PresentationsResourceCacheHolder { private final class PresentationsResourceAnyCacheHolder { var objects: [Int32: AnyObject] = [:] + var parameterObjects: [PresentationResourceParameterKey: AnyObject] = [:] } public final class PresentationsResourceCache { @@ -68,4 +69,22 @@ public final class PresentationsResourceCache { } } } + + public func parameterObject(_ key: PresentationResourceParameterKey, _ theme: PresentationTheme, _ generate: (PresentationTheme) -> AnyObject?) -> AnyObject? { + let result = self.objectCache.with { holder -> AnyObject? in + return holder.parameterObjects[key] + } + if let result = result { + return result + } else { + if let object = generate(theme) { + self.objectCache.with { holder -> Void in + holder.parameterObjects[key] = object + } + return object + } else { + return nil + } + } + } } diff --git a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourceKey.swift b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourceKey.swift index a1b0744fad..0c11f4342c 100644 --- a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourceKey.swift +++ b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourceKey.swift @@ -78,12 +78,6 @@ public enum PresentationResourceKey: Int32 { case chatTitleLockIcon case chatTitleMuteIcon - case chatPrincipalThemeEssentialGraphicsWithWallpaper - case chatPrincipalThemeEssentialGraphicsWithoutWallpaper - - case chatPrincipalThemeAdditionalGraphicsWithCustomWallpaper - case chatPrincipalThemeAdditionalGraphicsWithDefaultWallpaper - case chatBubbleVerticalLineIncomingImage case chatBubbleVerticalLineOutgoingImage @@ -234,4 +228,9 @@ public enum PresentationResourceParameterKey: Hashable { case chatListBadgeBackgroundMention(CGFloat) case chatListBadgeBackgroundInactiveMention(CGFloat) case chatListBadgeBackgroundPinned(CGFloat) + + case chatBubbleMediaCorner(incoming: Bool, mainRadius: CGFloat, inset: CGFloat) + + case chatPrincipalThemeEssentialGraphics(hasWallpaper: Bool, bubbleCorners: PresentationChatBubbleCorners) + case chatPrincipalThemeAdditionalGraphics(isCustomWallpaper: Bool, bubbleCorners: PresentationChatBubbleCorners) } diff --git a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesChat.swift b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesChat.swift index e3c4799b12..c639d5bfa1 100644 --- a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesChat.swift +++ b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesChat.swift @@ -69,18 +69,17 @@ public struct PresentationResourcesChat { }) } - public static func principalGraphics(mediaBox: MediaBox, knockoutWallpaper: Bool, theme: PresentationTheme, wallpaper: TelegramWallpaper) -> PrincipalThemeEssentialGraphics { + public static func principalGraphics(mediaBox: MediaBox, knockoutWallpaper: Bool, theme: PresentationTheme, wallpaper: TelegramWallpaper, bubbleCorners: PresentationChatBubbleCorners) -> PrincipalThemeEssentialGraphics { let hasWallpaper = !wallpaper.isEmpty - let key: PresentationResourceKey = !hasWallpaper ? PresentationResourceKey.chatPrincipalThemeEssentialGraphicsWithoutWallpaper : PresentationResourceKey.chatPrincipalThemeEssentialGraphicsWithWallpaper - return theme.object(key.rawValue, { theme in - return PrincipalThemeEssentialGraphics(mediaBox: mediaBox, presentationTheme: theme, wallpaper: wallpaper, preview: theme.preview, knockoutMode: knockoutWallpaper) + return theme.object(PresentationResourceParameterKey.chatPrincipalThemeEssentialGraphics(hasWallpaper: hasWallpaper, bubbleCorners: bubbleCorners), { theme in + return PrincipalThemeEssentialGraphics(mediaBox: mediaBox, presentationTheme: theme, wallpaper: wallpaper, preview: theme.preview, knockoutMode: knockoutWallpaper, bubbleCorners: bubbleCorners) }) as! PrincipalThemeEssentialGraphics } - public static func additionalGraphics(_ theme: PresentationTheme, wallpaper: TelegramWallpaper) -> PrincipalThemeAdditionalGraphics { - let key: PresentationResourceKey = wallpaper.isBuiltin ? PresentationResourceKey.chatPrincipalThemeAdditionalGraphicsWithDefaultWallpaper : PresentationResourceKey.chatPrincipalThemeAdditionalGraphicsWithCustomWallpaper - return theme.object(key.rawValue, { theme in - return PrincipalThemeAdditionalGraphics(theme.chat, wallpaper: wallpaper) + public static func additionalGraphics(_ theme: PresentationTheme, wallpaper: TelegramWallpaper, bubbleCorners: PresentationChatBubbleCorners) -> PrincipalThemeAdditionalGraphics { + let key: PresentationResourceParameterKey = .chatPrincipalThemeAdditionalGraphics(isCustomWallpaper: !wallpaper.isBuiltin, bubbleCorners: bubbleCorners) + return theme.object(key, { theme in + return PrincipalThemeAdditionalGraphics(theme.chat, wallpaper: wallpaper, bubbleCorners: bubbleCorners) }) as! PrincipalThemeAdditionalGraphics } @@ -950,4 +949,10 @@ public struct PresentationResourcesChat { return generateCheckImage(partial: true, color: color, width: size) }) } + + public static func chatBubbleMediaCorner(_ theme: PresentationTheme, incoming: Bool, mainRadius: CGFloat, inset: CGFloat) -> UIImage? { + return theme.image(PresentationResourceParameterKey.chatBubbleMediaCorner(incoming: incoming, mainRadius: mainRadius, inset: inset), { _ in + return mediaBubbleCornerImage(incoming: incoming, radius: mainRadius, inset: inset) + }) + } } diff --git a/submodules/TelegramStringFormatting/Sources/ServiceMessageStrings.swift b/submodules/TelegramStringFormatting/Sources/ServiceMessageStrings.swift index 4483315d8d..d634e990e1 100644 --- a/submodules/TelegramStringFormatting/Sources/ServiceMessageStrings.swift +++ b/submodules/TelegramStringFormatting/Sources/ServiceMessageStrings.swift @@ -132,7 +132,7 @@ public func universalServiceMessageString(presentationData: (PresentationTheme, case sticker case location case contact - case poll + case poll(TelegramMediaPollKind) case deleted } @@ -188,8 +188,8 @@ public func universalServiceMessageString(presentationData: (PresentationTheme, type = .location } else if let _ = media as? TelegramMediaContact { type = .contact - } else if let _ = media as? TelegramMediaPoll { - type = .poll + } else if let poll = media as? TelegramMediaPoll { + type = .poll(poll.kind) } } } else { @@ -229,8 +229,13 @@ public func universalServiceMessageString(presentationData: (PresentationTheme, attributedString = addAttributesToStringWithRanges(strings.Notification_PinnedLocationMessage(authorName), body: bodyAttributes, argumentAttributes: peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: [(0, message.author?.id)])) case .contact: attributedString = addAttributesToStringWithRanges(strings.Notification_PinnedContactMessage(authorName), body: bodyAttributes, argumentAttributes: peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: [(0, message.author?.id)])) - case .poll: - attributedString = addAttributesToStringWithRanges(strings.Notification_PinnedPollMessage(authorName), body: bodyAttributes, argumentAttributes: peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: [(0, message.author?.id)])) + case let .poll(kind): + switch kind { + case .poll: + attributedString = addAttributesToStringWithRanges(strings.Notification_PinnedPollMessage(authorName), body: bodyAttributes, argumentAttributes: peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: [(0, message.author?.id)])) + case .quiz: + attributedString = addAttributesToStringWithRanges(strings.Notification_PinnedQuizMessage(authorName), body: bodyAttributes, argumentAttributes: peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: [(0, message.author?.id)])) + } case .deleted: attributedString = addAttributesToStringWithRanges(strings.PUSH_PINNED_NOTEXT(authorName), body: bodyAttributes, argumentAttributes: peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: [(0, message.author?.id)])) } diff --git a/submodules/TelegramUI/TelegramUI/AppDelegate.swift b/submodules/TelegramUI/TelegramUI/AppDelegate.swift index d40a654516..5fd24a884e 100644 --- a/submodules/TelegramUI/TelegramUI/AppDelegate.swift +++ b/submodules/TelegramUI/TelegramUI/AppDelegate.swift @@ -816,12 +816,12 @@ final class SharedApplicationContext { return } var exists = false - strongSelf.mainWindow.forEachViewController { controller in + strongSelf.mainWindow.forEachViewController({ controller in if controller is ThemeSettingsCrossfadeController || controller is ThemeSettingsController { exists = true } return true - } + }) if !exists { strongSelf.mainWindow.present(ThemeSettingsCrossfadeController(), on: .root) @@ -1156,6 +1156,8 @@ final class SharedApplicationContext { self.registerForNotifications(context: context.context, authorize: authorizeNotifications) self.resetIntentsIfNeeded(context: context.context) + + let _ = storeCurrentCallListTabDefaultValue(accountManager: context.context.sharedContext.accountManager).start() })) } else { self.mainWindow.viewController = nil @@ -1380,8 +1382,7 @@ final class SharedApplicationContext { UIApplication.shared.setStatusBarHidden(false, with: .none) } - #if canImport(BackgroundTasks) - if #available(iOS 13.0, *) { + /*if #available(iOS 13.0, *) { BGTaskScheduler.shared.register(forTaskWithIdentifier: baseAppBundleId + ".refresh", using: nil, launchHandler: { task in let _ = (self.sharedContextPromise.get() |> take(1) @@ -1400,8 +1401,7 @@ final class SharedApplicationContext { }) }) }) - } - #endif + }*/ return true } @@ -1425,12 +1425,12 @@ final class SharedApplicationContext { } } } - self.mainWindow.forEachViewController { controller in + self.mainWindow.forEachViewController({ controller in if let controller = controller as? UndoOverlayController { controller.dismissWithCommitAction() } return true - } + }) } func applicationDidEnterBackground(_ application: UIApplication) { diff --git a/submodules/TelegramUI/TelegramUI/ApplicationContext.swift b/submodules/TelegramUI/TelegramUI/ApplicationContext.swift index f8aaeef786..eeaa5b2e6d 100644 --- a/submodules/TelegramUI/TelegramUI/ApplicationContext.swift +++ b/submodules/TelegramUI/TelegramUI/ApplicationContext.swift @@ -333,7 +333,7 @@ final class AuthorizedApplicationContext { return false } return true - }) + }, excludeNavigationSubControllers: true) if foundOverlay { return true @@ -362,7 +362,7 @@ final class AuthorizedApplicationContext { return false }, expandAction: { expandData in if let strongSelf = self { - let chatController = ChatControllerImpl(context: strongSelf.context, chatLocation: .peer(firstMessage.id.peerId), mode: .overlay) + let chatController = ChatControllerImpl(context: strongSelf.context, chatLocation: .peer(firstMessage.id.peerId), mode: .overlay(strongSelf.rootController)) //chatController.navigation_setNavigationController(strongSelf.rootController) chatController.presentationArguments = ChatControllerOverlayPresentationData(expandData: expandData()) //strongSelf.rootController.pushViewController(chatController) @@ -659,7 +659,7 @@ final class AuthorizedApplicationContext { let showCallsTabSignal = context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.callListSettings]) |> map { sharedData -> Bool in - var value = true + var value = CallListSettings.defaultSettings.showTab if let settings = sharedData.entries[ApplicationSpecificSharedDataKeys.callListSettings] as? CallListSettings { value = settings.showTab } diff --git a/submodules/TelegramUI/TelegramUI/ChatBotInfoItem.swift b/submodules/TelegramUI/TelegramUI/ChatBotInfoItem.swift index 36e83fd4a7..be5f243176 100644 --- a/submodules/TelegramUI/TelegramUI/ChatBotInfoItem.swift +++ b/submodules/TelegramUI/TelegramUI/ChatBotInfoItem.swift @@ -111,7 +111,7 @@ final class ChatBotInfoItemNode: ListViewItemNode { let recognizer = TapLongTapOrDoubleTapGestureRecognizer(target: self, action: #selector(self.tapLongTapOrDoubleTapGesture(_:))) recognizer.tapActionAtPoint = { [weak self] point in if let strongSelf = self { - let tapAction = strongSelf.tapActionAtPoint(point, gesture: .tap) + let tapAction = strongSelf.tapActionAtPoint(point, gesture: .tap, isEstimating: true) switch tapAction { case .none: break @@ -264,7 +264,7 @@ final class ChatBotInfoItemNode: ListViewItemNode { } } - func tapActionAtPoint(_ point: CGPoint, gesture: TapLongTapOrDoubleTapGesture) -> ChatMessageBubbleContentTapAction { + func tapActionAtPoint(_ point: CGPoint, gesture: TapLongTapOrDoubleTapGesture, isEstimating: Bool) -> ChatMessageBubbleContentTapAction { let textNodeFrame = self.textNode.frame if let (index, attributes) = self.textNode.attributesAtPoint(CGPoint(x: point.x - self.offsetContainer.frame.minX - textNodeFrame.minX, y: point.y - self.offsetContainer.frame.minY - textNodeFrame.minY)) { if let url = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] as? String { @@ -295,7 +295,7 @@ final class ChatBotInfoItemNode: ListViewItemNode { if let (gesture, location) = recognizer.lastRecognizedGestureAndLocation { switch gesture { case .tap: - let tapAction = self.tapActionAtPoint(location, gesture: gesture) + let tapAction = self.tapActionAtPoint(location, gesture: gesture, isEstimating: false) switch tapAction { case .none, .ignore: break @@ -314,7 +314,7 @@ final class ChatBotInfoItemNode: ListViewItemNode { } case .longTap, .doubleTap: if let item = self.item, self.backgroundNode.frame.contains(location) { - let tapAction = self.tapActionAtPoint(location, gesture: gesture) + let tapAction = self.tapActionAtPoint(location, gesture: gesture, isEstimating: false) switch tapAction { case .none, .ignore: break diff --git a/submodules/TelegramUI/TelegramUI/ChatButtonKeyboardInputNode.swift b/submodules/TelegramUI/TelegramUI/ChatButtonKeyboardInputNode.swift index 7fac23527b..d5ae6da4f6 100644 --- a/submodules/TelegramUI/TelegramUI/ChatButtonKeyboardInputNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatButtonKeyboardInputNode.swift @@ -211,6 +211,8 @@ final class ChatButtonKeyboardInputNode: ChatInputNode { if let message = self.message { self.controllerInteraction.requestMessageActionUrlAuth(url, message.id, buttonId) } + case let .setupPoll(isQuiz): + self.controllerInteraction.openPollCreation(isQuiz) } if dismissIfOnce { if let message = self.message { diff --git a/submodules/TelegramUI/TelegramUI/ChatController.swift b/submodules/TelegramUI/TelegramUI/ChatController.swift index 8c7b7879a7..8e92f38297 100644 --- a/submodules/TelegramUI/TelegramUI/ChatController.swift +++ b/submodules/TelegramUI/TelegramUI/ChatController.swift @@ -289,7 +289,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G private weak var sendMessageActionsController: ChatSendMessageActionSheetController? private var searchResultsController: ChatSearchResultsController? - private var screenCaptureEventsDisposable: Disposable? + private var screenCaptureManager: ScreenCaptureDetectionManager? private let chatAdditionalDataDisposable = MetaDisposable() private var reportIrrelvantGeoNoticePromise = Promise() @@ -346,7 +346,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G isScheduledMessages = true } - self.presentationInterfaceState = ChatPresentationInterfaceState(chatWallpaper: self.presentationData.chatWallpaper, theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameDisplayOrder: self.presentationData.nameDisplayOrder, limitsConfiguration: context.currentLimitsConfiguration.with { $0 }, fontSize: self.presentationData.chatFontSize, accountPeerId: context.account.peerId, mode: mode, chatLocation: chatLocation, isScheduledMessages: isScheduledMessages) + self.presentationInterfaceState = ChatPresentationInterfaceState(chatWallpaper: self.presentationData.chatWallpaper, theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameDisplayOrder: self.presentationData.nameDisplayOrder, limitsConfiguration: context.currentLimitsConfiguration.with { $0 }, fontSize: self.presentationData.chatFontSize, bubbleCorners: self.presentationData.chatBubbleCorners, accountPeerId: context.account.peerId, mode: mode, chatLocation: chatLocation, isScheduledMessages: isScheduledMessages) var mediaAccessoryPanelVisibility = MediaAccessoryPanelVisibility.none if case .standard = mode { @@ -1534,16 +1534,42 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G strongSelf.chatDisplayNode.dismissInput() strongSelf.present(controller, in: .window(.root)) } - }, requestSelectMessagePollOption: { [weak self] id, opaqueIdentifier in + }, requestSelectMessagePollOptions: { [weak self] id, opaqueIdentifiers in guard let strongSelf = self, let controllerInteraction = strongSelf.controllerInteraction else { return } + guard !strongSelf.presentationInterfaceState.isScheduledMessages else { strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: strongSelf.presentationData.strings.ScheduledMessages_PollUnavailable, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) return } if controllerInteraction.pollActionState.pollMessageIdsInProgress[id] == nil { - controllerInteraction.pollActionState.pollMessageIdsInProgress[id] = opaqueIdentifier + #if DEBUG + if false { + var found = false + strongSelf.chatDisplayNode.historyNode.forEachVisibleItemNode { itemNode in + if !found, let itemNode = itemNode as? ChatMessageBubbleItemNode, itemNode.item?.message.id == id { + found = true + if strongSelf.selectPollOptionFeedback == nil { + strongSelf.selectPollOptionFeedback = HapticFeedback() + } + strongSelf.selectPollOptionFeedback?.error() + itemNode.animateQuizInvalidOptionSelected() + } + } + return; + } + if false { + if strongSelf.selectPollOptionFeedback == nil { + strongSelf.selectPollOptionFeedback = HapticFeedback() + } + strongSelf.selectPollOptionFeedback?.success() + strongSelf.chatDisplayNode.animateQuizCorrectOptionSelected() + return; + } + #endif + + controllerInteraction.pollActionState.pollMessageIdsInProgress[id] = opaqueIdentifiers strongSelf.chatDisplayNode.historyNode.requestMessageUpdate(id) let disposables: DisposableDict if let current = strongSelf.selectMessagePollOptionDisposables { @@ -1552,9 +1578,53 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G disposables = DisposableDict() strongSelf.selectMessagePollOptionDisposables = disposables } - let signal = requestMessageSelectPollOption(account: strongSelf.context.account, messageId: id, opaqueIdentifier: opaqueIdentifier) + let signal = requestMessageSelectPollOption(account: strongSelf.context.account, messageId: id, opaqueIdentifiers: opaqueIdentifiers) disposables.set((signal - |> deliverOnMainQueue).start(error: { _ in + |> deliverOnMainQueue).start(next: { resultPoll in + guard let strongSelf = self, let resultPoll = resultPoll else { + return + } + guard let message = strongSelf.chatDisplayNode.historyNode.messageInCurrentHistoryView(id) else { + return + } + + switch resultPoll.kind { + case .poll: + if strongSelf.selectPollOptionFeedback == nil { + strongSelf.selectPollOptionFeedback = HapticFeedback() + } + strongSelf.selectPollOptionFeedback?.success() + case .quiz: + if let voters = resultPoll.results.voters { + for voter in voters { + if voter.selected { + if voter.isCorrect { + if strongSelf.selectPollOptionFeedback == nil { + strongSelf.selectPollOptionFeedback = HapticFeedback() + } + strongSelf.selectPollOptionFeedback?.success() + + strongSelf.chatDisplayNode.animateQuizCorrectOptionSelected() + } else { + var found = false + strongSelf.chatDisplayNode.historyNode.forEachVisibleItemNode { itemNode in + if !found, let itemNode = itemNode as? ChatMessageBubbleItemNode, itemNode.item?.message.id == id { + found = true + if strongSelf.selectPollOptionFeedback == nil { + strongSelf.selectPollOptionFeedback = HapticFeedback() + } + strongSelf.selectPollOptionFeedback?.error() + + itemNode.animateQuizInvalidOptionSelected() + } + } + } + break + } + } + } + } + }, error: { _ in guard let strongSelf = self, let controllerInteraction = strongSelf.controllerInteraction else { return } @@ -1566,14 +1636,31 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G return } if controllerInteraction.pollActionState.pollMessageIdsInProgress.removeValue(forKey: id) != nil { - strongSelf.chatDisplayNode.historyNode.requestMessageUpdate(id) + Queue.mainQueue().after(1.0, { + + strongSelf.chatDisplayNode.historyNode.requestMessageUpdate(id) + }) } - if strongSelf.selectPollOptionFeedback == nil { - strongSelf.selectPollOptionFeedback = HapticFeedback() - } - strongSelf.selectPollOptionFeedback?.success() }), forKey: id) } + }, requestOpenMessagePollResults: { [weak self] messageId, pollId in + guard let strongSelf = self, pollId.namespace == Namespaces.Media.CloudPoll else { + return + } + let _ = (strongSelf.context.account.postbox.transaction { transaction -> Message? in + return transaction.getMessage(messageId) + } + |> deliverOnMainQueue).start(next: { message in + guard let message = message else { + return + } + for media in message.media { + if let poll = media as? TelegramMediaPoll, poll.pollId == pollId { + strongSelf.push(pollResultsController(context: strongSelf.context, messageId: messageId, poll: poll)) + break + } + } + }) }, openAppStorePage: { [weak self] in if let strongSelf = self { strongSelf.context.sharedContext.applicationBindings.openAppStorePage() @@ -1750,6 +1837,29 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G }) }) }) + }, openMessagePollResults: { [weak self] messageId, optionOpaqueIdentifier in + guard let strongSelf = self else { + return + } + let _ = (strongSelf.context.account.postbox.transaction { transaction -> Message? in + return transaction.getMessage(messageId) + } + |> deliverOnMainQueue).start(next: { message in + guard let message = message else { + return + } + for media in message.media { + if let poll = media as? TelegramMediaPoll, poll.pollId.namespace == Namespaces.Media.CloudPoll { + strongSelf.push(pollResultsController(context: strongSelf.context, messageId: messageId, poll: poll, focusOnOptionWithOpaqueIdentifier: optionOpaqueIdentifier)) + break + } + } + }) + }, openPollCreation: { [weak self] isQuiz in + guard let strongSelf = self else { + return + } + strongSelf.presentPollCreation(isQuiz: isQuiz) }, requestMessageUpdate: { [weak self] id in if let strongSelf = self { strongSelf.chatDisplayNode.historyNode.requestMessageUpdate(id) @@ -2386,7 +2496,6 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G self.applicationInForegroundDisposable?.dispose() self.canReadHistoryDisposable?.dispose() self.networkStateDisposable?.dispose() - self.screenCaptureEventsDisposable?.dispose() self.chatAdditionalDataDisposable.dispose() self.shareStatusDisposable?.dispose() self.context.sharedContext.mediaManager.galleryHiddenMediaManager.removeTarget(self) @@ -2428,6 +2537,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G state = state.updatedStrings(self.presentationData.strings) state = state.updatedDateTimeFormat(self.presentationData.dateTimeFormat) state = state.updatedChatWallpaper(self.presentationData.chatWallpaper) + state = state.updatedBubbleCorners(self.presentationData.chatBubbleCorners) return state }) @@ -3957,7 +4067,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } let controller = OverlayStatusController(theme: strongSelf.presentationData.theme, type: .loading(cancelled: nil)) strongSelf.present(controller, in: .window(.root)) - let signal = requestMessageSelectPollOption(account: strongSelf.context.account, messageId: id, opaqueIdentifier: nil) + let signal = requestMessageSelectPollOption(account: strongSelf.context.account, messageId: id, opaqueIdentifiers: []) |> afterDisposed { [weak controller] in Queue.mainQueue().async { controller?.dismiss() @@ -3975,14 +4085,37 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G strongSelf.selectPollOptionFeedback?.success() }), forKey: id) }, requestStopPollInMessage: { [weak self] id in - guard let strongSelf = self else { + guard let strongSelf = self, let message = strongSelf.chatDisplayNode.historyNode.messageInCurrentHistoryView(id) else { return } + var maybePoll: TelegramMediaPoll? + for media in message.media { + if let poll = media as? TelegramMediaPoll { + maybePoll = poll + break + } + } + + guard let poll = maybePoll else { + return + } + + let actionTitle: String + let actionButtonText: String + switch poll.kind { + case .poll: + actionTitle = strongSelf.presentationData.strings.Conversation_StopPollConfirmationTitle + actionButtonText = strongSelf.presentationData.strings.Conversation_StopPollConfirmation + case .quiz: + actionTitle = strongSelf.presentationData.strings.Conversation_StopQuizConfirmationTitle + actionButtonText = strongSelf.presentationData.strings.Conversation_StopQuizConfirmation + } + let actionSheet = ActionSheetController(presentationData: strongSelf.presentationData) actionSheet.setItemGroups([ActionSheetItemGroup(items: [ - ActionSheetTextItem(title: strongSelf.presentationData.strings.Conversation_StopPollConfirmationTitle), - ActionSheetButtonItem(title: strongSelf.presentationData.strings.Conversation_StopPollConfirmation, color: .destructive, action: { [weak self, weak actionSheet] in + ActionSheetTextItem(title: actionTitle), + ActionSheetButtonItem(title: actionButtonText, color: .destructive, action: { [weak self, weak actionSheet] in actionSheet?.dismissAnimated() guard let strongSelf = self else { return @@ -4505,10 +4638,13 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G if !self.checkedPeerChatServiceActions { self.checkedPeerChatServiceActions = true - if case let .peer(peerId) = self.chatLocation, peerId.namespace == Namespaces.Peer.SecretChat { - self.screenCaptureEventsDisposable = screenCaptureEvents().start(next: { [weak self] _ in + if case let .peer(peerId) = self.chatLocation, peerId.namespace == Namespaces.Peer.SecretChat, self.screenCaptureManager == nil { + self.screenCaptureManager = ScreenCaptureDetectionManager(check: { [weak self] in if let strongSelf = self, strongSelf.canReadHistoryValue, strongSelf.traceVisibility() { let _ = addSecretChatMessageScreenshot(account: strongSelf.context.account, peerId: peerId).start() + return true + } else { + return false } }) } @@ -5422,6 +5558,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } return state }) + self.interfaceInteraction?.editMessage() } } @@ -5989,9 +6126,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G })) } - private func presentPollCreation() { - if case let .peer(peerId) = self.chatLocation { - self.effectiveNavigationController?.pushViewController(createPollController(context: self.context, peerId: peerId, completion: { [weak self] message in + private func presentPollCreation(isQuiz: Bool? = nil) { + if case .peer = self.chatLocation, let peer = self.presentationInterfaceState.renderedPeer?.peer { + self.effectiveNavigationController?.pushViewController(createPollController(context: self.context, peer: peer, isQuiz: isQuiz, completion: { [weak self] message in guard let strongSelf = self else { return } @@ -6577,7 +6714,13 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } public func navigateToMessage(messageLocation: NavigateToMessageLocation, animated: Bool, forceInCurrentChat: Bool = false, completion: (() -> Void)? = nil, customPresentProgress: ((ViewController, Any?) -> Void)? = nil) { - self.navigateToMessage(from: nil, to: messageLocation, rememberInStack: false, forceInCurrentChat: forceInCurrentChat, animated: animated, completion: completion, customPresentProgress: customPresentProgress) + let scrollPosition: ListViewScrollPosition + if case .upperBound = messageLocation { + scrollPosition = .top(0.0) + } else { + scrollPosition = .center(.bottom) + } + self.navigateToMessage(from: nil, to: messageLocation, scrollPosition: scrollPosition, rememberInStack: false, forceInCurrentChat: forceInCurrentChat, animated: animated, completion: completion, customPresentProgress: customPresentProgress) } private func navigateToMessage(from fromId: MessageId?, to messageLocation: NavigateToMessageLocation, scrollPosition: ListViewScrollPosition = .center(.bottom), rememberInStack: Bool = true, forceInCurrentChat: Bool = false, animated: Bool = true, completion: (() -> Void)? = nil, customPresentProgress: ((ViewController, Any?) -> Void)? = nil) { @@ -6760,7 +6903,48 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } private func forwardMessages(messageIds: [MessageId], resetCurrent: Bool = false) { - let controller = self.context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: self.context, filter: [.onlyWriteable, .excludeDisabled, .includeSavedMessages])) + let _ = (self.context.account.postbox.transaction { transaction -> [Message] in + return messageIds.compactMap(transaction.getMessage) + } + |> deliverOnMainQueue).start(next: { [weak self] messages in + self?.forwardMessages(messages: messages, resetCurrent: resetCurrent) + }) + } + + private func forwardMessages(messages: [Message], resetCurrent: Bool) { + var filter: ChatListNodePeersFilter = [.onlyWriteable, .includeSavedMessages, .excludeDisabled] + var hasPublicPolls = false + var hasPublicQuiz = false + for message in messages { + for media in message.media { + if let poll = media as? TelegramMediaPoll, case .public = poll.publicity { + hasPublicPolls = true + if case .quiz = poll.kind { + hasPublicQuiz = true + } + filter.insert(.excludeChannels) + break + } + } + } + var attemptSelectionImpl: ((Peer) -> Void)? + let controller = self.context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: self.context, filter: filter, attemptSelection: { peer in + attemptSelectionImpl?(peer) + })) + let context = self.context + attemptSelectionImpl = { [weak controller] peer in + guard let controller = controller else { + return + } + let presentationData = context.sharedContext.currentPresentationData.with { $0 } + if hasPublicPolls { + if let channel = peer as? TelegramChannel, case .broadcast = channel.info { + controller.present(textAlertController(context: context, title: nil, text: hasPublicQuiz ? presentationData.strings.Forward_ErrorPublicQuizDisabledInChannels : presentationData.strings.Forward_ErrorPublicPollDisabledInChannels, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + return + } + } + controller.present(textAlertController(context: context, title: nil, text: presentationData.strings.Forward_ErrorDisabledForChat, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + } controller.peerSelected = { [weak self, weak controller] peerId in guard let strongSelf = self, let strongController = controller else { return @@ -6771,11 +6955,11 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } if case .peer(peerId) = strongSelf.chatLocation, strongSelf.parentController == nil { - strongSelf.updateChatPresentationInterfaceState(animated: false, interactive: true, { $0.updatedInterfaceState({ $0.withUpdatedForwardMessageIds(messageIds).withoutSelectionState() }) }) + strongSelf.updateChatPresentationInterfaceState(animated: false, interactive: true, { $0.updatedInterfaceState({ $0.withUpdatedForwardMessageIds(messages.map { $0.id }).withoutSelectionState() }) }) strongController.dismiss() } else if peerId == strongSelf.context.account.peerId { - let _ = (enqueueMessages(account: strongSelf.context.account, peerId: peerId, messages: messageIds.map { id -> EnqueueMessage in - return .forward(source: id, grouping: .auto, attributes: []) + let _ = (enqueueMessages(account: strongSelf.context.account, peerId: peerId, messages: messages.map { message -> EnqueueMessage in + return .forward(source: message.id, grouping: .auto, attributes: []) }) |> deliverOnMainQueue).start(next: { messageIds in if let strongSelf = self { @@ -6811,9 +6995,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G let _ = (strongSelf.context.account.postbox.transaction({ transaction -> Void in transaction.updatePeerChatInterfaceState(peerId, update: { currentState in if let currentState = currentState as? ChatInterfaceState { - return currentState.withUpdatedForwardMessageIds(messageIds) + return currentState.withUpdatedForwardMessageIds(messages.map { $0.id }) } else { - return ChatInterfaceState().withUpdatedForwardMessageIds(messageIds) + return ChatInterfaceState().withUpdatedForwardMessageIds(messages.map { $0.id }) } }) }) |> deliverOnMainQueue).start(completed: { @@ -8255,6 +8439,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G return navigationController } else if case let .inline(navigationController) = self.presentationInterfaceState.mode { return navigationController + } else if case let .overlay(navigationController) = self.presentationInterfaceState.mode { + return navigationController } else { return nil } diff --git a/submodules/TelegramUI/TelegramUI/ChatControllerInteraction.swift b/submodules/TelegramUI/TelegramUI/ChatControllerInteraction.swift index 4a8b0f8122..fdae414c0a 100644 --- a/submodules/TelegramUI/TelegramUI/ChatControllerInteraction.swift +++ b/submodules/TelegramUI/TelegramUI/ChatControllerInteraction.swift @@ -45,7 +45,7 @@ public enum ChatControllerInteractionLongTapAction { } struct ChatInterfacePollActionState: Equatable { - var pollMessageIdsInProgress: [MessageId: Data] = [:] + var pollMessageIdsInProgress: [MessageId: [Data]] = [:] } public final class ChatControllerInteraction { @@ -91,7 +91,8 @@ public final class ChatControllerInteraction { let requestRedeliveryOfFailedMessages: (MessageId) -> Void let addContact: (String) -> Void let rateCall: (Message, CallId) -> Void - let requestSelectMessagePollOption: (MessageId, Data) -> Void + let requestSelectMessagePollOptions: (MessageId, [Data]) -> Void + let requestOpenMessagePollResults: (MessageId, MediaId) -> Void let openAppStorePage: () -> Void let displayMessageTooltip: (MessageId, String, ASDisplayNode?, CGRect?) -> Void let seekToTimecode: (Message, Double, Bool) -> Void @@ -103,6 +104,8 @@ public final class ChatControllerInteraction { let openMessageReactions: (MessageId) -> Void let displaySwipeToReplyHint: () -> Void let dismissReplyMarkupMessage: (Message) -> Void + let openMessagePollResults: (MessageId, Data) -> Void + let openPollCreation: (Bool?) -> Void let requestMessageUpdate: (MessageId) -> Void let cancelInteractiveKeyboardGestures: () -> Void @@ -117,7 +120,7 @@ public final class ChatControllerInteraction { var searchTextHighightState: (String, [MessageIndex])? var seenOneTimeAnimatedMedia = Set() - init(openMessage: @escaping (Message, ChatControllerInteractionOpenMessageMode) -> Bool, openPeer: @escaping (PeerId?, ChatControllerInteractionNavigateToPeer, Message?) -> Void, openPeerMention: @escaping (String) -> Void, openMessageContextMenu: @escaping (Message, Bool, ASDisplayNode, CGRect, TapLongTapOrDoubleTapGestureRecognizer?) -> Void, openMessageContextActions: @escaping (Message, ASDisplayNode, CGRect, ContextGesture?) -> Void, navigateToMessage: @escaping (MessageId, MessageId) -> Void, tapMessage: ((Message) -> Void)?, clickThroughMessage: @escaping () -> Void, toggleMessagesSelection: @escaping ([MessageId], Bool) -> Void, sendCurrentMessage: @escaping (Bool) -> Void, sendMessage: @escaping (String) -> Void, sendSticker: @escaping (FileMediaReference, Bool, ASDisplayNode, CGRect) -> Bool, sendGif: @escaping (FileMediaReference, ASDisplayNode, CGRect) -> Bool, requestMessageActionCallback: @escaping (MessageId, MemoryBuffer?, Bool) -> Void, requestMessageActionUrlAuth: @escaping (String, MessageId, Int32) -> Void, activateSwitchInline: @escaping (PeerId?, String) -> Void, openUrl: @escaping (String, Bool, Bool?, Message?) -> Void, shareCurrentLocation: @escaping () -> Void, shareAccountContact: @escaping () -> Void, sendBotCommand: @escaping (MessageId?, String) -> Void, openInstantPage: @escaping (Message, ChatMessageItemAssociatedData?) -> Void, openWallpaper: @escaping (Message) -> Void, openTheme: @escaping (Message) -> Void, openHashtag: @escaping (String?, String) -> Void, updateInputState: @escaping ((ChatTextInputState) -> ChatTextInputState) -> Void, updateInputMode: @escaping ((ChatInputMode) -> ChatInputMode) -> Void, openMessageShareMenu: @escaping (MessageId) -> Void, presentController: @escaping (ViewController, Any?) -> Void, navigationController: @escaping () -> NavigationController?, chatControllerNode: @escaping () -> ASDisplayNode?, reactionContainerNode: @escaping () -> ReactionSelectionParentNode?, presentGlobalOverlayController: @escaping (ViewController, Any?) -> Void, callPeer: @escaping (PeerId) -> Void, longTap: @escaping (ChatControllerInteractionLongTapAction, Message?) -> Void, openCheckoutOrReceipt: @escaping (MessageId) -> Void, openSearch: @escaping () -> Void, setupReply: @escaping (MessageId) -> Void, canSetupReply: @escaping (Message) -> Bool, navigateToFirstDateMessage: @escaping(Int32) ->Void, requestRedeliveryOfFailedMessages: @escaping (MessageId) -> Void, addContact: @escaping (String) -> Void, rateCall: @escaping (Message, CallId) -> Void, requestSelectMessagePollOption: @escaping (MessageId, Data) -> Void, openAppStorePage: @escaping () -> Void, displayMessageTooltip: @escaping (MessageId, String, ASDisplayNode?, CGRect?) -> Void, seekToTimecode: @escaping (Message, Double, Bool) -> Void, scheduleCurrentMessage: @escaping () -> Void, sendScheduledMessagesNow: @escaping ([MessageId]) -> Void, editScheduledMessagesTime: @escaping ([MessageId]) -> Void, performTextSelectionAction: @escaping (UInt32, String, TextSelectionAction) -> Void, updateMessageReaction: @escaping (MessageId, String?) -> Void, openMessageReactions: @escaping (MessageId) -> Void, displaySwipeToReplyHint: @escaping () -> Void, dismissReplyMarkupMessage: @escaping (Message) -> Void, requestMessageUpdate: @escaping (MessageId) -> Void, cancelInteractiveKeyboardGestures: @escaping () -> Void, automaticMediaDownloadSettings: MediaAutoDownloadSettings, pollActionState: ChatInterfacePollActionState, stickerSettings: ChatInterfaceStickerSettings) { + init(openMessage: @escaping (Message, ChatControllerInteractionOpenMessageMode) -> Bool, openPeer: @escaping (PeerId?, ChatControllerInteractionNavigateToPeer, Message?) -> Void, openPeerMention: @escaping (String) -> Void, openMessageContextMenu: @escaping (Message, Bool, ASDisplayNode, CGRect, TapLongTapOrDoubleTapGestureRecognizer?) -> Void, openMessageContextActions: @escaping (Message, ASDisplayNode, CGRect, ContextGesture?) -> Void, navigateToMessage: @escaping (MessageId, MessageId) -> Void, tapMessage: ((Message) -> Void)?, clickThroughMessage: @escaping () -> Void, toggleMessagesSelection: @escaping ([MessageId], Bool) -> Void, sendCurrentMessage: @escaping (Bool) -> Void, sendMessage: @escaping (String) -> Void, sendSticker: @escaping (FileMediaReference, Bool, ASDisplayNode, CGRect) -> Bool, sendGif: @escaping (FileMediaReference, ASDisplayNode, CGRect) -> Bool, requestMessageActionCallback: @escaping (MessageId, MemoryBuffer?, Bool) -> Void, requestMessageActionUrlAuth: @escaping (String, MessageId, Int32) -> Void, activateSwitchInline: @escaping (PeerId?, String) -> Void, openUrl: @escaping (String, Bool, Bool?, Message?) -> Void, shareCurrentLocation: @escaping () -> Void, shareAccountContact: @escaping () -> Void, sendBotCommand: @escaping (MessageId?, String) -> Void, openInstantPage: @escaping (Message, ChatMessageItemAssociatedData?) -> Void, openWallpaper: @escaping (Message) -> Void, openTheme: @escaping (Message) -> Void, openHashtag: @escaping (String?, String) -> Void, updateInputState: @escaping ((ChatTextInputState) -> ChatTextInputState) -> Void, updateInputMode: @escaping ((ChatInputMode) -> ChatInputMode) -> Void, openMessageShareMenu: @escaping (MessageId) -> Void, presentController: @escaping (ViewController, Any?) -> Void, navigationController: @escaping () -> NavigationController?, chatControllerNode: @escaping () -> ASDisplayNode?, reactionContainerNode: @escaping () -> ReactionSelectionParentNode?, presentGlobalOverlayController: @escaping (ViewController, Any?) -> Void, callPeer: @escaping (PeerId) -> Void, longTap: @escaping (ChatControllerInteractionLongTapAction, Message?) -> Void, openCheckoutOrReceipt: @escaping (MessageId) -> Void, openSearch: @escaping () -> Void, setupReply: @escaping (MessageId) -> Void, canSetupReply: @escaping (Message) -> Bool, navigateToFirstDateMessage: @escaping(Int32) ->Void, requestRedeliveryOfFailedMessages: @escaping (MessageId) -> Void, addContact: @escaping (String) -> Void, rateCall: @escaping (Message, CallId) -> Void, requestSelectMessagePollOptions: @escaping (MessageId, [Data]) -> Void, requestOpenMessagePollResults: @escaping (MessageId, MediaId) -> Void, openAppStorePage: @escaping () -> Void, displayMessageTooltip: @escaping (MessageId, String, ASDisplayNode?, CGRect?) -> Void, seekToTimecode: @escaping (Message, Double, Bool) -> Void, scheduleCurrentMessage: @escaping () -> Void, sendScheduledMessagesNow: @escaping ([MessageId]) -> Void, editScheduledMessagesTime: @escaping ([MessageId]) -> Void, performTextSelectionAction: @escaping (UInt32, String, TextSelectionAction) -> Void, updateMessageReaction: @escaping (MessageId, String?) -> Void, openMessageReactions: @escaping (MessageId) -> Void, displaySwipeToReplyHint: @escaping () -> Void, dismissReplyMarkupMessage: @escaping (Message) -> Void, openMessagePollResults: @escaping (MessageId, Data) -> Void, openPollCreation: @escaping (Bool?) -> Void, requestMessageUpdate: @escaping (MessageId) -> Void, cancelInteractiveKeyboardGestures: @escaping () -> Void, automaticMediaDownloadSettings: MediaAutoDownloadSettings, pollActionState: ChatInterfacePollActionState, stickerSettings: ChatInterfaceStickerSettings) { self.openMessage = openMessage self.openPeer = openPeer self.openPeerMention = openPeerMention @@ -160,7 +163,9 @@ public final class ChatControllerInteraction { self.requestRedeliveryOfFailedMessages = requestRedeliveryOfFailedMessages self.addContact = addContact self.rateCall = rateCall - self.requestSelectMessagePollOption = requestSelectMessagePollOption + self.requestSelectMessagePollOptions = requestSelectMessagePollOptions + self.requestOpenMessagePollResults = requestOpenMessagePollResults + self.openPollCreation = openPollCreation self.openAppStorePage = openAppStorePage self.displayMessageTooltip = displayMessageTooltip self.seekToTimecode = seekToTimecode @@ -172,6 +177,7 @@ public final class ChatControllerInteraction { self.openMessageReactions = openMessageReactions self.displaySwipeToReplyHint = displaySwipeToReplyHint self.dismissReplyMarkupMessage = dismissReplyMarkupMessage + self.openMessagePollResults = openMessagePollResults self.requestMessageUpdate = requestMessageUpdate self.cancelInteractiveKeyboardGestures = cancelInteractiveKeyboardGestures @@ -198,7 +204,8 @@ public final class ChatControllerInteraction { }, requestRedeliveryOfFailedMessages: { _ in }, addContact: { _ in }, rateCall: { _, _ in - }, requestSelectMessagePollOption: { _, _ in + }, requestSelectMessagePollOptions: { _, _ in + }, requestOpenMessagePollResults: { _, _ in }, openAppStorePage: { }, displayMessageTooltip: { _, _, _, _ in }, seekToTimecode: { _, _, _ in @@ -210,6 +217,8 @@ public final class ChatControllerInteraction { }, openMessageReactions: { _ in }, displaySwipeToReplyHint: { }, dismissReplyMarkupMessage: { _ in + }, openMessagePollResults: { _, _ in + }, openPollCreation: { _ in }, requestMessageUpdate: { _ in }, cancelInteractiveKeyboardGestures: { }, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings, diff --git a/submodules/TelegramUI/TelegramUI/ChatControllerNode.swift b/submodules/TelegramUI/TelegramUI/ChatControllerNode.swift index 959684b84a..b4d9552cd5 100644 --- a/submodules/TelegramUI/TelegramUI/ChatControllerNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatControllerNode.swift @@ -77,6 +77,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { } let backgroundNode: WallpaperBackgroundNode + let backgroundDisposable = MetaDisposable() let historyNode: ChatHistoryListNode let reactionContainerNode: ReactionSelectionParentNode let historyNodeContainer: ASDisplayNode @@ -224,7 +225,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { self.reactionContainerNode = ReactionSelectionParentNode(account: context.account, theme: chatPresentationInterfaceState.theme) - self.loadingNode = ChatLoadingNode(theme: self.chatPresentationInterfaceState.theme, chatWallpaper: self.chatPresentationInterfaceState.chatWallpaper) + self.loadingNode = ChatLoadingNode(theme: self.chatPresentationInterfaceState.theme, chatWallpaper: self.chatPresentationInterfaceState.chatWallpaper, bubbleCorners: self.chatPresentationInterfaceState.bubbleCorners) self.inputPanelBackgroundNode = ASDisplayNode() if case let .color(color) = self.chatPresentationInterfaceState.chatWallpaper, UIColor(rgb: color).isEqual(self.chatPresentationInterfaceState.theme.chat.inputPanel.panelBackgroundColorNoWallpaper) { @@ -280,7 +281,11 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { } } - self.backgroundNode.image = chatControllerBackgroundImage(theme: chatPresentationInterfaceState.theme, wallpaper: chatPresentationInterfaceState.chatWallpaper, mediaBox: context.sharedContext.accountManager.mediaBox, knockoutMode: context.sharedContext.immediateExperimentalUISettings.knockoutWallpaper) + self.backgroundDisposable.set(chatControllerBackgroundImageSignal(wallpaper: chatPresentationInterfaceState.chatWallpaper, mediaBox: context.sharedContext.accountManager.mediaBox, accountMediaBox: context.account.postbox.mediaBox).start(next: { [weak self] image in + if let strongSelf = self, let (image, final) = image { + strongSelf.backgroundNode.image = image + } + })) if case .gradient = chatPresentationInterfaceState.chatWallpaper { self.backgroundNode.imageContentMode = .scaleToFill } else { @@ -496,7 +501,14 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { self.navigationBar?.isHidden = true } if self.overlayNavigationBar == nil { - let overlayNavigationBar = ChatOverlayNavigationBar(theme: self.chatPresentationInterfaceState.theme, strings: self.chatPresentationInterfaceState.strings, nameDisplayOrder: self.chatPresentationInterfaceState.nameDisplayOrder, close: { [weak self] in + let overlayNavigationBar = ChatOverlayNavigationBar(theme: self.chatPresentationInterfaceState.theme, strings: self.chatPresentationInterfaceState.strings, nameDisplayOrder: self.chatPresentationInterfaceState.nameDisplayOrder, tapped: { [weak self] in + if let strongSelf = self { + strongSelf.dismissAsOverlay() + if case let .peer(id) = strongSelf.chatPresentationInterfaceState.chatLocation { + strongSelf.interfaceInteraction?.navigateToChat(id) + } + } + }, close: { [weak self] in self?.dismissAsOverlay() }) overlayNavigationBar.peerView = self.peerView @@ -1436,6 +1448,11 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { let themeUpdated = self.chatPresentationInterfaceState.theme !== chatPresentationInterfaceState.theme if self.chatPresentationInterfaceState.chatWallpaper != chatPresentationInterfaceState.chatWallpaper { + self.backgroundDisposable.set(chatControllerBackgroundImageSignal(wallpaper: chatPresentationInterfaceState.chatWallpaper, mediaBox: self.context.sharedContext.accountManager.mediaBox, accountMediaBox: self.context.account.postbox.mediaBox).start(next: { [weak self] image in + if let strongSelf = self, let (image, final) = image { + strongSelf.backgroundNode.image = image + } + })) self.backgroundNode.image = chatControllerBackgroundImage(theme: chatPresentationInterfaceState.theme, wallpaper: chatPresentationInterfaceState.chatWallpaper, mediaBox: context.sharedContext.accountManager.mediaBox, knockoutMode: self.context.sharedContext.immediateExperimentalUISettings.knockoutWallpaper) if case .gradient = chatPresentationInterfaceState.chatWallpaper { self.backgroundNode.imageContentMode = .scaleToFill @@ -1495,7 +1512,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { if let restrictionText = restrictionText { if self.restrictedNode == nil { - let restrictedNode = ChatRecentActionsEmptyNode(theme: chatPresentationInterfaceState.theme, chatWallpaper: chatPresentationInterfaceState.chatWallpaper) + let restrictedNode = ChatRecentActionsEmptyNode(theme: chatPresentationInterfaceState.theme, chatWallpaper: chatPresentationInterfaceState.chatWallpaper, chatBubbleCorners: chatPresentationInterfaceState.bubbleCorners) self.historyNodeContainer.supernode?.insertSubnode(restrictedNode, aboveSubnode: self.historyNodeContainer) self.restrictedNode = restrictedNode } @@ -2317,4 +2334,126 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { transition.updateAlpha(node: self.inputPanelBackgroundSeparatorNode, alpha: resolvedValue, beginWithCurrentState: true) } } + + func animateQuizCorrectOptionSelected() { + self.view.insertSubview(ConfettiView(frame: self.view.bounds), aboveSubview: self.historyNode.view) + + /*class ConfettiView: UIView { + private let direction: Bool + private let confettiViewEmitterLayer = CAEmitterLayer() + private let confettiViewEmitterCell = CAEmitterCell() + + init(frame: CGRect, direction: Bool) { + self.direction = direction + + super.init(frame: frame) + + self.isUserInteractionEnabled = false + + self.setupConfettiEmitterLayer() + + self.confettiViewEmitterLayer.frame = self.bounds + self.confettiViewEmitterLayer.emitterCells = generateConfettiEmitterCells() + self.layer.addSublayer(self.confettiViewEmitterLayer) + + let animation = CAKeyframeAnimation(keyPath: #keyPath(CAEmitterLayer.birthRate)) + animation.duration = 0.5 + animation.timingFunction = CAMediaTimingFunction(name: .easeIn) + animation.fillMode = .forwards + animation.values = [1, 0, 0] + animation.keyTimes = [0, 0.5, 1] + animation.isRemovedOnCompletion = false + + self.confettiViewEmitterLayer.beginTime = CACurrentMediaTime() + self.confettiViewEmitterLayer.birthRate = 1.0 + + CATransaction.begin() + CATransaction.setCompletionBlock { [weak self] in + guard let strongSelf = self else { + return + } + strongSelf.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, delay: 1.0, removeOnCompletion: false, completion: { _ in + self?.removeFromSuperview() + }) + } + self.confettiViewEmitterLayer.add(animation, forKey: nil) + CATransaction.commit() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + private func setupConfettiEmitterLayer() { + let emitterWidth: CGFloat = self.bounds.width / 4.0 + self.confettiViewEmitterLayer.emitterSize = CGSize(width: emitterWidth, height: 2.0) + self.confettiViewEmitterLayer.emitterShape = .line + self.confettiViewEmitterLayer.emitterPosition = CGPoint(x: direction ? 0.0 : (self.bounds.width - emitterWidth * 0.0), y: self.bounds.height) + } + + private func generateConfettiEmitterCells() -> [CAEmitterCell] { + var cells = [CAEmitterCell]() + + let cellImageCircle = generateFilledCircleImage(diameter: 4.0, color: .white)!.cgImage! + let cellImageLine = generateImage(CGSize(width: 4.0, height: 10.0), opaque: false, rotatedContext: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + context.setFillColor(UIColor.white.cgColor) + context.fillEllipse(in: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: size.width))) + context.fillEllipse(in: CGRect(origin: CGPoint(x: 0.0, y: size.height - size.width), size: CGSize(width: size.width, height: size.width))) + context.fill(CGRect(origin: CGPoint(x: 0.0, y: size.width / 2.0), size: CGSize(width: size.width, height: size.height - size.width))) + })!.cgImage! + + for index in 0 ..< 4 { + let cell = CAEmitterCell() + cell.color = self.nextColor(i: index).cgColor + cell.contents = index % 2 == 0 ? cellImageCircle : cellImageLine + cell.birthRate = 60.0 + cell.lifetime = 14.0 + cell.lifetimeRange = 0 + if index % 2 == 0 { + cell.scale = 0.8 + cell.scaleRange = 0.4 + } else { + cell.scale = 0.5 + cell.scaleRange = 0.1 + } + cell.velocity = -self.randomVelocity + cell.velocityRange = abs(cell.velocity) * 0.3 + cell.yAcceleration = 3000.0 + cell.emissionLongitude = (self.direction ? -1.0 : 1.0) * (CGFloat.pi * 0.95) + cell.emissionRange = 0.2 + cell.spin = 5.5 + cell.spinRange = 1.0 + + cells.append(cell) + } + + return cells + } + + var randomNumber: Int { + let dimension = 4 + return Int(arc4random_uniform(UInt32(dimension))) + } + + var randomVelocity: CGFloat { + let velocities: [CGFloat] = [100.0, 120.0, 130.0, 140.0] + return velocities[self.randomNumber] * 12.0 + } + + private let colors: [UIColor] = ([ + 0x56CE6B, + 0xCD89D0, + 0x1E9AFF, + 0xFF8724 + ] as [UInt32]).map(UIColor.init(rgb:)) + + private func nextColor(i: Int) -> UIColor { + return self.colors[i % self.colors.count] + } + } + + self.view.insertSubview(ConfettiView(frame: self.view.bounds, direction: true), aboveSubview: self.historyNode.view) + self.view.insertSubview(ConfettiView(frame: self.view.bounds, direction: false), aboveSubview: self.historyNode.view)*/ + } } diff --git a/submodules/TelegramUI/TelegramUI/ChatEmptyNode.swift b/submodules/TelegramUI/TelegramUI/ChatEmptyNode.swift index 57b0fef12d..c0f8623b98 100644 --- a/submodules/TelegramUI/TelegramUI/ChatEmptyNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatEmptyNode.swift @@ -121,7 +121,7 @@ private final class ChatEmptyNodeSecretChatContent: ASDisplayNode, ChatEmptyNode let lines: [NSAttributedString] = strings.map { NSAttributedString(string: $0, font: messageFont, textColor: serviceColor.primaryText) } - let graphics = PresentationResourcesChat.additionalGraphics(interfaceState.theme, wallpaper: interfaceState.chatWallpaper) + let graphics = PresentationResourcesChat.additionalGraphics(interfaceState.theme, wallpaper: interfaceState.chatWallpaper, bubbleCorners: interfaceState.bubbleCorners) let lockIcon = graphics.chatEmptyItemLockIcon for i in 0 ..< lines.count { @@ -237,7 +237,7 @@ private final class ChatEmptyNodeGroupChatContent: ASDisplayNode, ChatEmptyNodeC let lines: [NSAttributedString] = strings.map { NSAttributedString(string: $0, font: messageFont, textColor: serviceColor.primaryText) } - let graphics = PresentationResourcesChat.additionalGraphics(interfaceState.theme, wallpaper: interfaceState.chatWallpaper) + let graphics = PresentationResourcesChat.additionalGraphics(interfaceState.theme, wallpaper: interfaceState.chatWallpaper, bubbleCorners: interfaceState.bubbleCorners) let lockIcon = graphics.emptyChatListCheckIcon for i in 0 ..< lines.count { @@ -453,7 +453,7 @@ final class ChatEmptyNode: ASDisplayNode { self.currentTheme = interfaceState.theme self.currentStrings = interfaceState.strings - let graphics = PresentationResourcesChat.additionalGraphics(interfaceState.theme, wallpaper: interfaceState.chatWallpaper) + let graphics = PresentationResourcesChat.additionalGraphics(interfaceState.theme, wallpaper: interfaceState.chatWallpaper, bubbleCorners: interfaceState.bubbleCorners) self.backgroundNode.image = graphics.chatEmptyItemBackgroundImage } diff --git a/submodules/TelegramUI/TelegramUI/ChatHistoryGridNode.swift b/submodules/TelegramUI/TelegramUI/ChatHistoryGridNode.swift index dfba0ca7ad..34f88f5cd1 100644 --- a/submodules/TelegramUI/TelegramUI/ChatHistoryGridNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatHistoryGridNode.swift @@ -251,7 +251,7 @@ public final class ChatHistoryGridNode: GridNode, ChatHistoryNode { self.chatPresentationDataPromise.set(context.sharedContext.presentationData |> map { presentationData in - return ChatPresentationData(theme: ChatPresentationThemeData(theme: presentationData.theme, wallpaper: presentationData.chatWallpaper), fontSize: presentationData.chatFontSize, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, disableAnimations: presentationData.disableAnimations, largeEmoji: presentationData.largeEmoji) + return ChatPresentationData(theme: ChatPresentationThemeData(theme: presentationData.theme, wallpaper: presentationData.chatWallpaper), fontSize: presentationData.chatFontSize, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, disableAnimations: presentationData.disableAnimations, largeEmoji: presentationData.largeEmoji, chatBubbleCorners: presentationData.chatBubbleCorners) }) self.floatingSections = true diff --git a/submodules/TelegramUI/TelegramUI/ChatHistoryListNode.swift b/submodules/TelegramUI/TelegramUI/ChatHistoryListNode.swift index ab032691e6..9e562d29e0 100644 --- a/submodules/TelegramUI/TelegramUI/ChatHistoryListNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatHistoryListNode.swift @@ -505,7 +505,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { self.mode = mode let presentationData = context.sharedContext.currentPresentationData.with { $0 } - self.currentPresentationData = ChatPresentationData(theme: ChatPresentationThemeData(theme: presentationData.theme, wallpaper: presentationData.chatWallpaper), fontSize: presentationData.chatFontSize, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, disableAnimations: presentationData.disableAnimations, largeEmoji: presentationData.largeEmoji, animatedEmojiScale: 1.0) + self.currentPresentationData = ChatPresentationData(theme: ChatPresentationThemeData(theme: presentationData.theme, wallpaper: presentationData.chatWallpaper), fontSize: presentationData.chatFontSize, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, disableAnimations: presentationData.disableAnimations, largeEmoji: presentationData.largeEmoji, chatBubbleCorners: presentationData.chatBubbleCorners, animatedEmojiScale: 1.0) self.chatPresentationDataPromise = Promise(self.currentPresentationData) @@ -880,7 +880,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { if previousTheme !== presentationData.theme || previousStrings !== presentationData.strings || previousWallpaper != presentationData.chatWallpaper || previousDisableAnimations != presentationData.disableAnimations || previousAnimatedEmojiScale != animatedEmojiConfig.scale { let themeData = ChatPresentationThemeData(theme: presentationData.theme, wallpaper: presentationData.chatWallpaper) - let chatPresentationData = ChatPresentationData(theme: themeData, fontSize: presentationData.chatFontSize, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, disableAnimations: presentationData.disableAnimations, largeEmoji: presentationData.largeEmoji, animatedEmojiScale: animatedEmojiConfig.scale) + let chatPresentationData = ChatPresentationData(theme: themeData, fontSize: presentationData.chatFontSize, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, disableAnimations: presentationData.disableAnimations, largeEmoji: presentationData.largeEmoji, chatBubbleCorners: presentationData.chatBubbleCorners, animatedEmojiScale: animatedEmojiConfig.scale) strongSelf.currentPresentationData = chatPresentationData strongSelf.dynamicBounceEnabled = !presentationData.disableAnimations diff --git a/submodules/TelegramUI/TelegramUI/ChatHoleItem.swift b/submodules/TelegramUI/TelegramUI/ChatHoleItem.swift index 59c80a38c7..b88cf37698 100644 --- a/submodules/TelegramUI/TelegramUI/ChatHoleItem.swift +++ b/submodules/TelegramUI/TelegramUI/ChatHoleItem.swift @@ -81,7 +81,7 @@ class ChatHoleItemNode: ListViewItemNode { return { item, params, dateAtBottom in var updatedBackground: UIImage? if item.presentationData.theme !== currentItem?.presentationData.theme { - let graphics = PresentationResourcesChat.additionalGraphics(item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper) + let graphics = PresentationResourcesChat.additionalGraphics(item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper, bubbleCorners: item.presentationData.chatBubbleCorners) updatedBackground = graphics.chatServiceBubbleFillImage } diff --git a/submodules/TelegramUI/TelegramUI/ChatInterfaceStateContextMenus.swift b/submodules/TelegramUI/TelegramUI/ChatInterfaceStateContextMenus.swift index af37f08b4d..166e5e84db 100644 --- a/submodules/TelegramUI/TelegramUI/ChatInterfaceStateContextMenus.swift +++ b/submodules/TelegramUI/TelegramUI/ChatInterfaceStateContextMenus.swift @@ -511,7 +511,7 @@ func contextMenuForChatPresentationIntefaceState(chatPresentationInterfaceState: hasSelected = true } } - if hasSelected { + if hasSelected, case .poll = activePoll.kind { actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_UnvotePoll, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Unvote"), color: theme.actionSheet.primaryTextColor) }, action: { _, f in @@ -539,7 +539,7 @@ func contextMenuForChatPresentationIntefaceState(chatPresentationInterfaceState: } } - if let _ = activePoll, messages[0].forwardInfo == nil { + if let activePoll = activePoll, messages[0].forwardInfo == nil { var canStopPoll = false if !messages[0].flags.contains(.Incoming) { canStopPoll = true @@ -565,7 +565,14 @@ func contextMenuForChatPresentationIntefaceState(chatPresentationInterfaceState: } if canStopPoll { - actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_StopPoll, icon: { theme in + let stopPollAction: String + switch activePoll.kind { + case .poll: + stopPollAction = chatPresentationInterfaceState.strings.Conversation_StopPoll + case .quiz: + stopPollAction = chatPresentationInterfaceState.strings.Conversation_StopQuiz + } + actions.append(.action(ContextMenuActionItem(text: stopPollAction, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/StopPoll"), color: theme.actionSheet.primaryTextColor) }, action: { _, f in interfaceInteraction.requestStopPollInMessage(messages[0].id) diff --git a/submodules/TelegramUI/TelegramUI/ChatLoadingNode.swift b/submodules/TelegramUI/TelegramUI/ChatLoadingNode.swift index e2280941f0..6cb7638a0b 100644 --- a/submodules/TelegramUI/TelegramUI/ChatLoadingNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatLoadingNode.swift @@ -12,13 +12,13 @@ final class ChatLoadingNode: ASDisplayNode { private let activityIndicator: ActivityIndicator private let offset: CGPoint - init(theme: PresentationTheme, chatWallpaper: TelegramWallpaper) { + init(theme: PresentationTheme, chatWallpaper: TelegramWallpaper, bubbleCorners: PresentationChatBubbleCorners) { self.backgroundNode = ASImageNode() self.backgroundNode.isLayerBacked = true self.backgroundNode.displayWithoutProcessing = true self.backgroundNode.displaysAsynchronously = false - let graphics = PresentationResourcesChat.additionalGraphics(theme, wallpaper: chatWallpaper) + let graphics = PresentationResourcesChat.additionalGraphics(theme, wallpaper: chatWallpaper, bubbleCorners: bubbleCorners) self.backgroundNode.image = graphics.chatLoadingIndicatorBackgroundImage let serviceColor = serviceMessageColorComponents(theme: theme, wallpaper: chatWallpaper) diff --git a/submodules/TelegramUI/TelegramUI/ChatMessageActionButtonsNode.swift b/submodules/TelegramUI/TelegramUI/ChatMessageActionButtonsNode.swift index af96c3b8f2..ab5189f89b 100644 --- a/submodules/TelegramUI/TelegramUI/ChatMessageActionButtonsNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatMessageActionButtonsNode.swift @@ -84,12 +84,12 @@ private final class ChatMessageActionButtonNode: ASDisplayNode { } } - class func asyncLayout(_ maybeNode: ChatMessageActionButtonNode?) -> (_ context: AccountContext, _ theme: ChatPresentationThemeData, _ strings: PresentationStrings, _ message: Message, _ button: ReplyMarkupButton, _ constrainedWidth: CGFloat, _ position: MessageBubbleActionButtonPosition) -> (minimumWidth: CGFloat, layout: ((CGFloat) -> (CGSize, () -> ChatMessageActionButtonNode))) { + class func asyncLayout(_ maybeNode: ChatMessageActionButtonNode?) -> (_ context: AccountContext, _ theme: ChatPresentationThemeData, _ bubbleCorners: PresentationChatBubbleCorners, _ strings: PresentationStrings, _ message: Message, _ button: ReplyMarkupButton, _ constrainedWidth: CGFloat, _ position: MessageBubbleActionButtonPosition) -> (minimumWidth: CGFloat, layout: ((CGFloat) -> (CGSize, () -> ChatMessageActionButtonNode))) { let titleLayout = TextNode.asyncLayout(maybeNode?.titleNode) - return { context, theme, strings, message, button, constrainedWidth, position in + return { context, theme, bubbleCorners, strings, message, button, constrainedWidth, position in let incoming = message.effectivelyIncoming(context.account.peerId) - let graphics = PresentationResourcesChat.additionalGraphics(theme.theme, wallpaper: theme.wallpaper) + let graphics = PresentationResourcesChat.additionalGraphics(theme.theme, wallpaper: theme.wallpaper, bubbleCorners: bubbleCorners) let iconImage: UIImage? switch button.action { @@ -216,10 +216,10 @@ final class ChatMessageActionButtonsNode: ASDisplayNode { } } - class func asyncLayout(_ maybeNode: ChatMessageActionButtonsNode?) -> (_ context: AccountContext, _ theme: ChatPresentationThemeData, _ strings: PresentationStrings, _ replyMarkup: ReplyMarkupMessageAttribute, _ message: Message, _ constrainedWidth: CGFloat) -> (minWidth: CGFloat, layout: (CGFloat) -> (CGSize, (_ animated: Bool) -> ChatMessageActionButtonsNode)) { + class func asyncLayout(_ maybeNode: ChatMessageActionButtonsNode?) -> (_ context: AccountContext, _ theme: ChatPresentationThemeData, _ chatBubbleCorners: PresentationChatBubbleCorners, _ strings: PresentationStrings, _ replyMarkup: ReplyMarkupMessageAttribute, _ message: Message, _ constrainedWidth: CGFloat) -> (minWidth: CGFloat, layout: (CGFloat) -> (CGSize, (_ animated: Bool) -> ChatMessageActionButtonsNode)) { let currentButtonLayouts = maybeNode?.buttonNodes.map { ChatMessageActionButtonNode.asyncLayout($0) } ?? [] - return { context, theme, strings, replyMarkup, message, constrainedWidth in + return { context, theme, chatBubbleCorners, strings, replyMarkup, message, constrainedWidth in let buttonHeight: CGFloat = 42.0 let buttonSpacing: CGFloat = 4.0 @@ -252,9 +252,9 @@ final class ChatMessageActionButtonsNode: ASDisplayNode { let prepareButtonLayout: (minimumWidth: CGFloat, layout: ((CGFloat) -> (CGSize, () -> ChatMessageActionButtonNode))) if buttonIndex < currentButtonLayouts.count { - prepareButtonLayout = currentButtonLayouts[buttonIndex](context, theme, strings, message, button, maximumButtonWidth, buttonPosition) + prepareButtonLayout = currentButtonLayouts[buttonIndex](context, theme, chatBubbleCorners, strings, message, button, maximumButtonWidth, buttonPosition) } else { - prepareButtonLayout = ChatMessageActionButtonNode.asyncLayout(nil)(context, theme, strings, message, button, maximumButtonWidth, buttonPosition) + prepareButtonLayout = ChatMessageActionButtonNode.asyncLayout(nil)(context, theme, chatBubbleCorners, strings, message, button, maximumButtonWidth, buttonPosition) } maximumRowButtonWidth = max(maximumRowButtonWidth, prepareButtonLayout.minimumWidth) diff --git a/submodules/TelegramUI/TelegramUI/ChatMessageActionItemNode.swift b/submodules/TelegramUI/TelegramUI/ChatMessageActionItemNode.swift index b09a154646..71a6d84dbb 100644 --- a/submodules/TelegramUI/TelegramUI/ChatMessageActionItemNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatMessageActionItemNode.swift @@ -244,7 +244,7 @@ class ChatMessageActionBubbleContentNode: ChatMessageBubbleContentNode { } } - override func tapActionAtPoint(_ point: CGPoint, gesture: TapLongTapOrDoubleTapGesture) -> ChatMessageBubbleContentTapAction { + override func tapActionAtPoint(_ point: CGPoint, gesture: TapLongTapOrDoubleTapGesture, isEstimating: Bool) -> ChatMessageBubbleContentTapAction { let textNodeFrame = self.labelNode.frame if let (index, attributes) = self.labelNode.attributesAtPoint(CGPoint(x: point.x - textNodeFrame.minX, y: point.y - textNodeFrame.minY - 10.0)), gesture == .tap { if let url = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] as? String { diff --git a/submodules/TelegramUI/TelegramUI/ChatMessageAnimatedStickerItemNode.swift b/submodules/TelegramUI/TelegramUI/ChatMessageAnimatedStickerItemNode.swift index 0cbef84d24..da8d51b1c8 100644 --- a/submodules/TelegramUI/TelegramUI/ChatMessageAnimatedStickerItemNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatMessageAnimatedStickerItemNode.swift @@ -349,7 +349,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { let currentItem = self.item return { item, params, mergedTop, mergedBottom, dateHeaderAtBottom in - let layoutConstants = chatMessageItemLayoutConstants(layoutConstants, params: params) + let layoutConstants = chatMessageItemLayoutConstants(layoutConstants, params: params, presentationData: item.presentationData) let incoming = item.message.effectivelyIncoming(item.context.account.peerId) var imageSize: CGSize = CGSize(width: 200.0, height: 200.0) var isEmoji = false @@ -560,7 +560,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { updatedReplyBackgroundNode = ASImageNode() } - let graphics = PresentationResourcesChat.additionalGraphics(item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper) + let graphics = PresentationResourcesChat.additionalGraphics(item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper, bubbleCorners: item.presentationData.chatBubbleCorners) replyBackgroundImage = graphics.chatFreeformContentAdditionalInfoBackgroundImage } @@ -571,7 +571,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { if currentShareButtonNode != nil { updatedShareButtonNode = currentShareButtonNode if item.presentationData.theme !== currentItem?.presentationData.theme { - let graphics = PresentationResourcesChat.additionalGraphics(item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper) + let graphics = PresentationResourcesChat.additionalGraphics(item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper, bubbleCorners: item.presentationData.chatBubbleCorners) if item.message.id.peerId == item.context.account.peerId { updatedShareButtonBackground = graphics.chatBubbleNavigateButtonImage } else { @@ -581,7 +581,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { } else { let buttonNode = HighlightableButtonNode() let buttonIcon: UIImage? - let graphics = PresentationResourcesChat.additionalGraphics(item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper) + let graphics = PresentationResourcesChat.additionalGraphics(item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper, bubbleCorners: item.presentationData.chatBubbleCorners) if item.message.id.peerId == item.context.account.peerId { buttonIcon = graphics.chatBubbleNavigateButtonImage } else { @@ -596,7 +596,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { var maxContentWidth = imageSize.width var actionButtonsFinalize: ((CGFloat) -> (CGSize, (_ animated: Bool) -> ChatMessageActionButtonsNode))? if let replyMarkup = replyMarkup { - let (minWidth, buttonsLayout) = actionButtonsLayout(item.context, item.presentationData.theme, item.presentationData.strings, replyMarkup, item.message, maxContentWidth) + let (minWidth, buttonsLayout) = actionButtonsLayout(item.context, item.presentationData.theme, item.presentationData.chatBubbleCorners, item.presentationData.strings, replyMarkup, item.message, maxContentWidth) maxContentWidth = max(maxContentWidth, minWidth) actionButtonsFinalize = buttonsLayout } diff --git a/submodules/TelegramUI/TelegramUI/ChatMessageAttachedContentNode.swift b/submodules/TelegramUI/TelegramUI/ChatMessageAttachedContentNode.swift index 1cb26d13db..0e565d40fb 100644 --- a/submodules/TelegramUI/TelegramUI/ChatMessageAttachedContentNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatMessageAttachedContentNode.swift @@ -983,7 +983,7 @@ final class ChatMessageAttachedContentNode: ASDisplayNode { return false } - func tapActionAtPoint(_ point: CGPoint, gesture: TapLongTapOrDoubleTapGesture) -> ChatMessageBubbleContentTapAction { + func tapActionAtPoint(_ point: CGPoint, gesture: TapLongTapOrDoubleTapGesture, isEstimating: Bool) -> ChatMessageBubbleContentTapAction { let textNodeFrame = self.textNode.frame if let (index, attributes) = self.textNode.attributesAtPoint(CGPoint(x: point.x - textNodeFrame.minX, y: point.y - textNodeFrame.minY)) { if let url = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] as? String { diff --git a/submodules/TelegramUI/TelegramUI/ChatMessageBackground.swift b/submodules/TelegramUI/TelegramUI/ChatMessageBackground.swift index 6af804330b..09ab5933d4 100644 --- a/submodules/TelegramUI/TelegramUI/ChatMessageBackground.swift +++ b/submodules/TelegramUI/TelegramUI/ChatMessageBackground.swift @@ -66,6 +66,10 @@ class ChatMessageBackground: ASDisplayNode { private let imageNode: ASImageNode private let outlineImageNode: ASImageNode + var hasImage: Bool { + self.imageNode.image != nil + } + override init() { self.imageNode = ASImageNode() self.imageNode.displaysAsynchronously = false @@ -78,8 +82,8 @@ class ChatMessageBackground: ASDisplayNode { super.init() self.isUserInteractionEnabled = false - self.addSubnode(self.outlineImageNode) self.addSubnode(self.imageNode) + self.addSubnode(self.outlineImageNode) } func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) { diff --git a/submodules/TelegramUI/TelegramUI/ChatMessageBubbleBackdrop.swift b/submodules/TelegramUI/TelegramUI/ChatMessageBubbleBackdrop.swift index aebaf0c11f..1f0b4a71ad 100644 --- a/submodules/TelegramUI/TelegramUI/ChatMessageBubbleBackdrop.swift +++ b/submodules/TelegramUI/TelegramUI/ChatMessageBubbleBackdrop.swift @@ -16,6 +16,10 @@ final class ChatMessageBubbleBackdrop: ASDisplayNode { private var maskView: UIImageView? + var hasImage: Bool { + return self.backgroundContent.contents != nil + } + override var frame: CGRect { didSet { if let maskView = self.maskView { @@ -87,7 +91,7 @@ final class ChatMessageBubbleBackdrop: ASDisplayNode { } func setType(type: ChatMessageBackgroundType, theme: ChatPresentationThemeData, mediaBox: MediaBox, essentialGraphics: PrincipalThemeEssentialGraphics, maskMode: Bool) { - if self.currentType != type || self.theme != theme || self.currentMaskMode != maskMode { + if self.currentType != type || self.theme != theme || self.currentMaskMode != maskMode || self.essentialGraphics !== essentialGraphics { self.currentType = type self.theme = theme self.essentialGraphics = essentialGraphics diff --git a/submodules/TelegramUI/TelegramUI/ChatMessageBubbleContentCalclulateImageCorners.swift b/submodules/TelegramUI/TelegramUI/ChatMessageBubbleContentCalclulateImageCorners.swift index f3c7671062..7d3fbc07f6 100644 --- a/submodules/TelegramUI/TelegramUI/ChatMessageBubbleContentCalclulateImageCorners.swift +++ b/submodules/TelegramUI/TelegramUI/ChatMessageBubbleContentCalclulateImageCorners.swift @@ -1,8 +1,9 @@ import Foundation import UIKit import Display +import TelegramPresentationData -func chatMessageBubbleImageContentCorners(relativeContentPosition position: ChatMessageBubbleContentPosition, normalRadius: CGFloat, mergedRadius: CGFloat, mergedWithAnotherContentRadius: CGFloat) -> ImageCorners { +func chatMessageBubbleImageContentCorners(relativeContentPosition position: ChatMessageBubbleContentPosition, normalRadius: CGFloat, mergedRadius: CGFloat, mergedWithAnotherContentRadius: CGFloat, layoutConstants: ChatMessageItemLayoutConstants, chatPresentationData: ChatPresentationData) -> ImageCorners { let topLeftCorner: ImageCorner let topRightCorner: ImageCorner @@ -12,6 +13,9 @@ func chatMessageBubbleImageContentCorners(relativeContentPosition position: Chat case .Neighbour: topLeftCorner = .Corner(mergedWithAnotherContentRadius) topRightCorner = .Corner(mergedWithAnotherContentRadius) + case .BubbleNeighbour: + topLeftCorner = .Corner(mergedRadius) + topRightCorner = .Corner(mergedRadius) case let .None(mergeStatus): switch mergeStatus { case .Left: @@ -31,12 +35,16 @@ func chatMessageBubbleImageContentCorners(relativeContentPosition position: Chat topLeftCorner = .Corner(normalRadius) case .merged: topLeftCorner = .Corner(mergedWithAnotherContentRadius) + case .mergedBubble: + topLeftCorner = .Corner(mergedRadius) } switch position.topRight { case .none: topRightCorner = .Corner(normalRadius) case .merged: topRightCorner = .Corner(mergedWithAnotherContentRadius) + case .mergedBubble: + topRightCorner = .Corner(mergedRadius) } } @@ -49,19 +57,42 @@ func chatMessageBubbleImageContentCorners(relativeContentPosition position: Chat case .Neighbour: bottomLeftCorner = .Corner(mergedWithAnotherContentRadius) bottomRightCorner = .Corner(mergedWithAnotherContentRadius) + case .BubbleNeighbour: + bottomLeftCorner = .Corner(mergedRadius) + bottomRightCorner = .Corner(mergedRadius) case let .None(mergeStatus): switch mergeStatus { case .Left: bottomLeftCorner = .Corner(mergedRadius) bottomRightCorner = .Corner(normalRadius) case let .None(status): + let bubbleInsets: UIEdgeInsets + if case .color = chatPresentationData.theme.wallpaper { + let colors: PresentationThemeBubbleColorComponents + switch status { + case .Incoming: + colors = chatPresentationData.theme.theme.chat.message.incoming.bubble.withoutWallpaper + case .Outgoing: + colors = chatPresentationData.theme.theme.chat.message.outgoing.bubble.withoutWallpaper + case .None: + colors = chatPresentationData.theme.theme.chat.message.incoming.bubble.withoutWallpaper + } + if colors.fill == colors.stroke || colors.stroke.alpha.isZero { + bubbleInsets = UIEdgeInsets(top: 1.0, left: 1.0, bottom: 1.0, right: 1.0) + } else { + bubbleInsets = layoutConstants.bubble.strokeInsets + } + } else { + bubbleInsets = layoutConstants.image.bubbleInsets + } + switch status { case .Incoming: - bottomLeftCorner = .Tail(normalRadius, true) + bottomLeftCorner = .Tail(normalRadius, PresentationResourcesChat.chatBubbleMediaCorner(chatPresentationData.theme.theme, incoming: true, mainRadius: normalRadius, inset: max(0.0, bubbleInsets.left - 1.0))!) bottomRightCorner = .Corner(normalRadius) case .Outgoing: bottomLeftCorner = .Corner(normalRadius) - bottomRightCorner = .Tail(normalRadius, true) + bottomRightCorner = .Tail(normalRadius, PresentationResourcesChat.chatBubbleMediaCorner(chatPresentationData.theme.theme, incoming: false, mainRadius: normalRadius, inset: max(0.0, bubbleInsets.right - 1.0))!) case .None: bottomLeftCorner = .Corner(normalRadius) bottomRightCorner = .Corner(normalRadius) @@ -75,22 +106,51 @@ func chatMessageBubbleImageContentCorners(relativeContentPosition position: Chat switch position.bottomLeft { case let .none(tail): if tail { - bottomLeftCorner = .Tail(normalRadius, true) + let bubbleInsets: UIEdgeInsets + if case .color = chatPresentationData.theme.wallpaper { + let colors: PresentationThemeBubbleColorComponents + colors = chatPresentationData.theme.theme.chat.message.incoming.bubble.withoutWallpaper + if colors.fill == colors.stroke || colors.stroke.alpha.isZero { + bubbleInsets = UIEdgeInsets(top: 1.0, left: 1.0, bottom: 1.0, right: 1.0) + } else { + bubbleInsets = layoutConstants.bubble.strokeInsets + } + } else { + bubbleInsets = layoutConstants.image.bubbleInsets + } + + bottomLeftCorner = .Tail(normalRadius, PresentationResourcesChat.chatBubbleMediaCorner(chatPresentationData.theme.theme, incoming: true, mainRadius: normalRadius, inset: max(0.0, bubbleInsets.left - 1.0))!) } else { bottomLeftCorner = .Corner(normalRadius) } case .merged: bottomLeftCorner = .Corner(mergedWithAnotherContentRadius) - } + case .mergedBubble: + bottomLeftCorner = .Corner(mergedRadius) + } switch position.bottomRight { case let .none(tail): if tail { - bottomRightCorner = .Tail(normalRadius, true) + let bubbleInsets: UIEdgeInsets + if case .color = chatPresentationData.theme.wallpaper { + let colors: PresentationThemeBubbleColorComponents + colors = chatPresentationData.theme.theme.chat.message.outgoing.bubble.withoutWallpaper + if colors.fill == colors.stroke || colors.stroke.alpha.isZero { + bubbleInsets = UIEdgeInsets(top: 1.0, left: 1.0, bottom: 1.0, right: 1.0) + } else { + bubbleInsets = layoutConstants.bubble.strokeInsets + } + } else { + bubbleInsets = layoutConstants.image.bubbleInsets + } + bottomRightCorner = .Tail(normalRadius, PresentationResourcesChat.chatBubbleMediaCorner(chatPresentationData.theme.theme, incoming: false, mainRadius: normalRadius, inset: max(0.0, bubbleInsets.right - 1.0))!) } else { bottomRightCorner = .Corner(normalRadius) } case .merged: bottomRightCorner = .Corner(mergedWithAnotherContentRadius) + case .mergedBubble: + bottomRightCorner = .Corner(mergedRadius) } } diff --git a/submodules/TelegramUI/TelegramUI/ChatMessageBubbleContentNode.swift b/submodules/TelegramUI/TelegramUI/ChatMessageBubbleContentNode.swift index 8a7925daf6..621473f04c 100644 --- a/submodules/TelegramUI/TelegramUI/ChatMessageBubbleContentNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatMessageBubbleContentNode.swift @@ -41,11 +41,13 @@ enum ChatMessageBubbleMergeStatus { enum ChatMessageBubbleRelativePosition { case None(ChatMessageBubbleMergeStatus) + case BubbleNeighbour case Neighbour } enum ChatMessageBubbleContentMosaicNeighbor { case merged + case mergedBubble case none(tail: Bool) } @@ -162,7 +164,7 @@ class ChatMessageBubbleContentNode: ASDisplayNode { return nil } - func tapActionAtPoint(_ point: CGPoint, gesture: TapLongTapOrDoubleTapGesture) -> ChatMessageBubbleContentTapAction { + func tapActionAtPoint(_ point: CGPoint, gesture: TapLongTapOrDoubleTapGesture, isEstimating: Bool) -> ChatMessageBubbleContentTapAction { return .none } diff --git a/submodules/TelegramUI/TelegramUI/ChatMessageBubbleItemNode.swift b/submodules/TelegramUI/TelegramUI/ChatMessageBubbleItemNode.swift index 90efe7d024..88a19d0bdb 100644 --- a/submodules/TelegramUI/TelegramUI/ChatMessageBubbleItemNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatMessageBubbleItemNode.swift @@ -76,7 +76,12 @@ private func contentNodeMessagesAndClassesForItem(_ item: ChatMessageItem) -> [( } } - if !message.text.isEmpty || isUnsupportedMedia { + var messageText = message.text + if let updatingMedia = itemAttributes.updatingMedia { + messageText = updatingMedia.text + } + + if !messageText.isEmpty || isUnsupportedMedia { if !skipText { if case .group = item.content { messageWithCaptionToAdd = (message, itemAttributes) @@ -271,14 +276,26 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode for node in subnodes { if let contextNode = node as? ContextExtractedContentContainingNode { if let contextSubnodes = contextNode.contentNode.subnodes { - for contextSubnode in contextSubnodes { + inner: for contextSubnode in contextSubnodes { if contextSubnode !== self.accessoryItemNode { - contextSubnode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + if contextSubnode == self.backgroundNode { + if self.backgroundNode.hasImage && self.backgroundWallpaperNode.hasImage { + continue inner + } + } + contextSubnode.layer.allowsGroupOpacity = true + contextSubnode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2, completion: { [weak contextSubnode] _ in + contextSubnode?.layer.allowsGroupOpacity = false + }) } } } } else if node !== self.accessoryItemNode { + node.layer.allowsGroupOpacity = true node.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + node.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2, completion: { [weak node] _ in + node?.layer.allowsGroupOpacity = false + }) } } } @@ -334,7 +351,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode return .waitForSingleTap } for contentNode in strongSelf.contentNodes { - let tapAction = contentNode.tapActionAtPoint(CGPoint(x: point.x - contentNode.frame.minX, y: point.y - contentNode.frame.minY), gesture: .tap) + let tapAction = contentNode.tapActionAtPoint(CGPoint(x: point.x - contentNode.frame.minX, y: point.y - contentNode.frame.minY), gesture: .tap, isEstimating: true) switch tapAction { case .none: if let _ = strongSelf.item?.controllerInteraction.tapMessage { @@ -603,7 +620,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode let weakSelf = Weak(self) return { item, params, mergedTop, mergedBottom, dateHeaderAtBottom in - let layoutConstants = chatMessageItemLayoutConstants(layoutConstants, params: params) + let layoutConstants = chatMessageItemLayoutConstants(layoutConstants, params: params, presentationData: item.presentationData) return ChatMessageBubbleItemNode.beginLayout(selfReference: weakSelf, item, params, mergedTop, mergedBottom, dateHeaderAtBottom, currentContentClassesPropertiesAndLayouts: currentContentClassesPropertiesAndLayouts, authorNameLayout: authorNameLayout, @@ -627,7 +644,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode adminBadgeLayout: (TextNodeLayoutArguments) -> (TextNodeLayout, () -> TextNode), forwardInfoLayout: (ChatPresentationData, PresentationStrings, ChatMessageForwardInfoType, Peer?, String?, CGSize) -> (CGSize, () -> ChatMessageForwardInfoNode), replyInfoLayout: (ChatPresentationData, PresentationStrings, AccountContext, ChatMessageReplyInfoType, Message, CGSize) -> (CGSize, () -> ChatMessageReplyInfoNode), - actionButtonsLayout: (AccountContext, ChatPresentationThemeData, PresentationStrings, ReplyMarkupMessageAttribute, Message, CGFloat) -> (minWidth: CGFloat, layout: (CGFloat) -> (CGSize, (Bool) -> ChatMessageActionButtonsNode)), + actionButtonsLayout: (AccountContext, ChatPresentationThemeData, PresentationChatBubbleCorners, PresentationStrings, ReplyMarkupMessageAttribute, Message, CGFloat) -> (minWidth: CGFloat, layout: (CGFloat) -> (CGSize, (Bool) -> ChatMessageActionButtonsNode)), mosaicStatusLayout: (AccountContext, ChatPresentationData, Bool, Int?, String, ChatMessageDateAndStatusType, CGSize, [MessageReaction]) -> (CGSize, (Bool) -> ChatMessageDateAndStatusNode), currentShareButtonNode: HighlightableButtonNode?, layoutConstants: ChatMessageItemLayoutConstants, @@ -1279,7 +1296,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode var actionButtonsFinalize: ((CGFloat) -> (CGSize, (_ animated: Bool) -> ChatMessageActionButtonsNode))? if let replyMarkup = replyMarkup { - let (minWidth, buttonsLayout) = actionButtonsLayout(item.context, item.presentationData.theme, item.presentationData.strings, replyMarkup, item.message, maximumNodeWidth) + let (minWidth, buttonsLayout) = actionButtonsLayout(item.context, item.presentationData.theme, item.presentationData.chatBubbleCorners, item.presentationData.strings, replyMarkup, item.message, maximumNodeWidth) maxContentWidth = max(maxContentWidth, minWidth) actionButtonsFinalize = buttonsLayout } @@ -1301,11 +1318,14 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode case .Neighbour: topLeft = .merged topRight = .merged + case .BubbleNeighbour: + topLeft = .mergedBubble + topRight = .mergedBubble case let .None(status): if position.contains(.top) && position.contains(.left) { switch status { case .Left: - topLeft = .merged + topLeft = .mergedBubble case .Right: topLeft = .none(tail: false) case .None: @@ -1320,7 +1340,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode case .Left: topRight = .none(tail: false) case .Right: - topRight = .merged + topRight = .mergedBubble case .None: topRight = .none(tail: false) } @@ -1344,11 +1364,14 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode case .Neighbour: bottomLeft = .merged bottomRight = .merged + case .BubbleNeighbour: + bottomLeft = .mergedBubble + bottomRight = .mergedBubble case let .None(status): if position.contains(.bottom) && position.contains(.left) { switch status { case .Left: - bottomLeft = .merged + bottomLeft = .mergedBubble case .Right: bottomLeft = .none(tail: false) case let .None(tailStatus): @@ -1367,7 +1390,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode case .Left: bottomRight = .none(tail: false) case .Right: - bottomRight = .merged + bottomRight = .mergedBubble case let .None(tailStatus): if case .Outgoing = tailStatus { bottomRight = .none(tail: true) @@ -1511,7 +1534,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode if currentShareButtonNode != nil { updatedShareButtonNode = currentShareButtonNode if item.presentationData.theme !== currentItem?.presentationData.theme { - let graphics = PresentationResourcesChat.additionalGraphics(item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper) + let graphics = PresentationResourcesChat.additionalGraphics(item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper, bubbleCorners: item.presentationData.chatBubbleCorners) if item.message.id.peerId == item.context.account.peerId { updatedShareButtonBackground = graphics.chatBubbleNavigateButtonImage } else { @@ -1521,7 +1544,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode } else { let buttonNode = HighlightableButtonNode() let buttonIcon: UIImage? - let graphics = PresentationResourcesChat.additionalGraphics(item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper) + let graphics = PresentationResourcesChat.additionalGraphics(item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper, bubbleCorners: item.presentationData.chatBubbleCorners) if item.message.id.peerId == item.context.account.peerId { buttonIcon = graphics.chatBubbleNavigateButtonImage } else { @@ -1534,7 +1557,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode let layout = ListViewItemNodeLayout(contentSize: layoutSize, insets: layoutInsets) - let graphics = PresentationResourcesChat.principalGraphics(mediaBox: item.context.account.postbox.mediaBox, knockoutWallpaper: item.context.sharedContext.immediateExperimentalUISettings.knockoutWallpaper, theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper) + let graphics = PresentationResourcesChat.principalGraphics(mediaBox: item.context.account.postbox.mediaBox, knockoutWallpaper: item.context.sharedContext.immediateExperimentalUISettings.knockoutWallpaper, theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper, bubbleCorners: item.presentationData.chatBubbleCorners) var updatedMergedTop = mergedBottom var updatedMergedBottom = mergedTop @@ -2305,7 +2328,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode } var foundTapAction = false loop: for contentNode in self.contentNodes { - let tapAction = contentNode.tapActionAtPoint(CGPoint(x: location.x - contentNode.frame.minX, y: location.y - contentNode.frame.minY), gesture: gesture) + let tapAction = contentNode.tapActionAtPoint(CGPoint(x: location.x - contentNode.frame.minX, y: location.y - contentNode.frame.minY), gesture: gesture, isEstimating: false) switch tapAction { case .none, .ignore: if let item = self.item, self.backgroundNode.frame.contains(CGPoint(x: self.frame.width - location.x, y: location.y)), let tapMessage = self.item?.controllerInteraction.tapMessage { @@ -2394,7 +2417,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode selectAll = false } tapMessage = contentNode.item?.message - let tapAction = contentNode.tapActionAtPoint(CGPoint(x: location.x - contentNode.frame.minX, y: location.y - contentNode.frame.minY), gesture: gesture) + let tapAction = contentNode.tapActionAtPoint(CGPoint(x: location.x - contentNode.frame.minX, y: location.y - contentNode.frame.minY), gesture: gesture, isEstimating: false) switch tapAction { case .none, .ignore: break @@ -2757,7 +2780,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode if self.highlightedState != highlighted { self.highlightedState = highlighted if let backgroundType = self.backgroundType { - let graphics = PresentationResourcesChat.principalGraphics(mediaBox: item.context.account.postbox.mediaBox, knockoutWallpaper: item.context.sharedContext.immediateExperimentalUISettings.knockoutWallpaper, theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper) + let graphics = PresentationResourcesChat.principalGraphics(mediaBox: item.context.account.postbox.mediaBox, knockoutWallpaper: item.context.sharedContext.immediateExperimentalUISettings.knockoutWallpaper, theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper, bubbleCorners: item.presentationData.chatBubbleCorners) let hasWallpaper = item.presentationData.theme.wallpaper.hasWallpaper self.backgroundNode.setType(type: backgroundType, highlighted: highlighted, graphics: graphics, maskMode: self.contextSourceNode.isExtractedToContextPreview, hasWallpaper: hasWallpaper, transition: animated ? .animated(duration: 0.3, curve: .easeInOut) : .immediate) @@ -2895,4 +2918,61 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode let isPreview = self.item?.presentationData.isPreview ?? false return self.contextSourceNode.isExtractedToContextPreview || hasWallpaper || isPreview } + + func animateQuizInvalidOptionSelected() { + if let supernode = self.supernode, let subnodes = supernode.subnodes { + for i in 0 ..< subnodes.count { + if subnodes[i] === self { + break + } + } + } + + let duration: Double = 0.5 + let minScale: CGFloat = -0.03 + let scaleAnimation0 = self.layer.makeAnimation(from: 0.0 as NSNumber, to: minScale as NSNumber, keyPath: "transform.scale", timingFunction: CAMediaTimingFunctionName.linear.rawValue, duration: duration / 2.0, removeOnCompletion: false, additive: true, completion: { [weak self] _ in + guard let strongSelf = self else { + return + } + let scaleAnimation1 = strongSelf.layer.makeAnimation(from: minScale as NSNumber, to: 0.0 as NSNumber, keyPath: "transform.scale", timingFunction: CAMediaTimingFunctionName.linear.rawValue, duration: duration / 2.0, additive: true) + strongSelf.layer.add(scaleAnimation1, forKey: "quizInvalidScale") + }) + self.layer.add(scaleAnimation0, forKey: "quizInvalidScale") + + let k = Float(UIView.animationDurationFactor()) + var speed: Float = 1.0 + if k != 0 && k != 1 { + speed = Float(1.0) / k + } + + let count = 4 + + let animation = CAKeyframeAnimation(keyPath: "transform.rotation.z") + var values: [CGFloat] = [] + values.append(0.0) + let rotationAmplitude: CGFloat = CGFloat.pi / 180.0 * 3.0 + for i in 0 ..< count { + let sign: CGFloat = (i % 2 == 0) ? 1.0 : -1.0 + let amplitude: CGFloat = rotationAmplitude + values.append(amplitude * sign) + } + values.append(0.0) + animation.values = values.map { ($0 as NSNumber) as AnyObject } + var keyTimes: [NSNumber] = [] + for i in 0 ..< values.count { + if i == 0 { + keyTimes.append(0.0) + } else if i == values.count - 1 { + keyTimes.append(1.0) + } else { + keyTimes.append((Double(i) / Double(values.count - 1)) as NSNumber) + } + } + animation.keyTimes = keyTimes + animation.speed = speed + animation.duration = duration + animation.isAdditive = true + + self.layer.add(animation, forKey: "quizInvalidRotation") + } } diff --git a/submodules/TelegramUI/TelegramUI/ChatMessageCallBubbleContentNode.swift b/submodules/TelegramUI/TelegramUI/ChatMessageCallBubbleContentNode.swift index 1a667442e5..b2c5038812 100644 --- a/submodules/TelegramUI/TelegramUI/ChatMessageCallBubbleContentNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatMessageCallBubbleContentNode.swift @@ -207,7 +207,7 @@ class ChatMessageCallBubbleContentNode: ChatMessageBubbleContentNode { } } - override func tapActionAtPoint(_ point: CGPoint, gesture: TapLongTapOrDoubleTapGesture) -> ChatMessageBubbleContentTapAction { + override func tapActionAtPoint(_ point: CGPoint, gesture: TapLongTapOrDoubleTapGesture, isEstimating: Bool) -> ChatMessageBubbleContentTapAction { if self.buttonNode.frame.contains(point) { return .ignore } else if self.bounds.contains(point), let item = self.item { diff --git a/submodules/TelegramUI/TelegramUI/ChatMessageContactBubbleContentNode.swift b/submodules/TelegramUI/TelegramUI/ChatMessageContactBubbleContentNode.swift index 4d5cbe76e5..c351ad160c 100644 --- a/submodules/TelegramUI/TelegramUI/ChatMessageContactBubbleContentNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatMessageContactBubbleContentNode.swift @@ -328,7 +328,7 @@ class ChatMessageContactBubbleContentNode: ChatMessageBubbleContentNode { self.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false) } - override func tapActionAtPoint(_ point: CGPoint, gesture: TapLongTapOrDoubleTapGesture) -> ChatMessageBubbleContentTapAction { + override func tapActionAtPoint(_ point: CGPoint, gesture: TapLongTapOrDoubleTapGesture, isEstimating: Bool) -> ChatMessageBubbleContentTapAction { if self.buttonNode.frame.contains(point) { return .openMessage } diff --git a/submodules/TelegramUI/TelegramUI/ChatMessageDateAndStatusNode.swift b/submodules/TelegramUI/TelegramUI/ChatMessageDateAndStatusNode.swift index 12b8e635f9..114cebebbd 100644 --- a/submodules/TelegramUI/TelegramUI/ChatMessageDateAndStatusNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatMessageDateAndStatusNode.swift @@ -187,7 +187,7 @@ class ChatMessageDateAndStatusNode: ASDisplayNode { let themeUpdated = presentationData.theme != currentTheme || type != currentType - let graphics = PresentationResourcesChat.principalGraphics(mediaBox: context.account.postbox.mediaBox, knockoutWallpaper: context.sharedContext.immediateExperimentalUISettings.knockoutWallpaper, theme: presentationData.theme.theme, wallpaper: presentationData.theme.wallpaper) + let graphics = PresentationResourcesChat.principalGraphics(mediaBox: context.account.postbox.mediaBox, knockoutWallpaper: context.sharedContext.immediateExperimentalUISettings.knockoutWallpaper, theme: presentationData.theme.theme, wallpaper: presentationData.theme.wallpaper, bubbleCorners: presentationData.chatBubbleCorners) let isDefaultWallpaper = serviceMessageColorHasDefaultWallpaper(presentationData.theme.wallpaper) let offset: CGFloat = -UIScreenPixel diff --git a/submodules/TelegramUI/TelegramUI/ChatMessageDateHeader.swift b/submodules/TelegramUI/TelegramUI/ChatMessageDateHeader.swift index 45efafcf67..97640a4d64 100644 --- a/submodules/TelegramUI/TelegramUI/ChatMessageDateHeader.swift +++ b/submodules/TelegramUI/TelegramUI/ChatMessageDateHeader.swift @@ -161,7 +161,7 @@ final class ChatMessageDateHeaderNode: ListViewItemHeaderNode { self.transform = CATransform3DMakeRotation(CGFloat.pi, 0.0, 0.0, 1.0) - let graphics = PresentationResourcesChat.principalGraphics(mediaBox: context.account.postbox.mediaBox, knockoutWallpaper: context.sharedContext.immediateExperimentalUISettings.knockoutWallpaper, theme: presentationData.theme.theme, wallpaper: presentationData.theme.wallpaper) + let graphics = PresentationResourcesChat.principalGraphics(mediaBox: context.account.postbox.mediaBox, knockoutWallpaper: context.sharedContext.immediateExperimentalUISettings.knockoutWallpaper, theme: presentationData.theme.theme, wallpaper: presentationData.theme.wallpaper, bubbleCorners: presentationData.chatBubbleCorners) self.backgroundNode.image = graphics.dateStaticBackground self.stickBackgroundNode.image = graphics.dateFloatingBackground @@ -190,7 +190,7 @@ final class ChatMessageDateHeaderNode: ListViewItemHeaderNode { let previousPresentationData = self.presentationData self.presentationData = presentationData - let graphics = PresentationResourcesChat.principalGraphics(mediaBox: context.account.postbox.mediaBox, knockoutWallpaper: context.sharedContext.immediateExperimentalUISettings.knockoutWallpaper, theme: presentationData.theme.theme, wallpaper: presentationData.theme.wallpaper) + let graphics = PresentationResourcesChat.principalGraphics(mediaBox: context.account.postbox.mediaBox, knockoutWallpaper: context.sharedContext.immediateExperimentalUISettings.knockoutWallpaper, theme: presentationData.theme.theme, wallpaper: presentationData.theme.wallpaper, bubbleCorners: presentationData.chatBubbleCorners) self.backgroundNode.image = graphics.dateStaticBackground self.stickBackgroundNode.image = graphics.dateFloatingBackground diff --git a/submodules/TelegramUI/TelegramUI/ChatMessageEventLogPreviousDescriptionContentNode.swift b/submodules/TelegramUI/TelegramUI/ChatMessageEventLogPreviousDescriptionContentNode.swift index f540c74239..9512d2b705 100644 --- a/submodules/TelegramUI/TelegramUI/ChatMessageEventLogPreviousDescriptionContentNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatMessageEventLogPreviousDescriptionContentNode.swift @@ -84,7 +84,7 @@ final class ChatMessageEventLogPreviousDescriptionContentNode: ChatMessageBubble self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25) } - override func tapActionAtPoint(_ point: CGPoint, gesture: TapLongTapOrDoubleTapGesture) -> ChatMessageBubbleContentTapAction { + override func tapActionAtPoint(_ point: CGPoint, gesture: TapLongTapOrDoubleTapGesture, isEstimating: Bool) -> ChatMessageBubbleContentTapAction { if self.bounds.contains(point) { /*if let webPage = self.webPage, case let .Loaded(content) = webPage.content { if content.instantPage != nil { diff --git a/submodules/TelegramUI/TelegramUI/ChatMessageEventLogPreviousLinkContentNode.swift b/submodules/TelegramUI/TelegramUI/ChatMessageEventLogPreviousLinkContentNode.swift index 9c76292417..cdefb5f241 100644 --- a/submodules/TelegramUI/TelegramUI/ChatMessageEventLogPreviousLinkContentNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatMessageEventLogPreviousLinkContentNode.swift @@ -79,7 +79,7 @@ final class ChatMessageEventLogPreviousLinkContentNode: ChatMessageBubbleContent self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25) } - override func tapActionAtPoint(_ point: CGPoint, gesture: TapLongTapOrDoubleTapGesture) -> ChatMessageBubbleContentTapAction { + override func tapActionAtPoint(_ point: CGPoint, gesture: TapLongTapOrDoubleTapGesture, isEstimating: Bool) -> ChatMessageBubbleContentTapAction { if self.bounds.contains(point) { /*if let webPage = self.webPage, case let .Loaded(content) = webPage.content { if content.instantPage != nil { diff --git a/submodules/TelegramUI/TelegramUI/ChatMessageEventLogPreviousMessageContentNode.swift b/submodules/TelegramUI/TelegramUI/ChatMessageEventLogPreviousMessageContentNode.swift index 9d05694c53..406fb699b0 100644 --- a/submodules/TelegramUI/TelegramUI/ChatMessageEventLogPreviousMessageContentNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatMessageEventLogPreviousMessageContentNode.swift @@ -84,10 +84,10 @@ final class ChatMessageEventLogPreviousMessageContentNode: ChatMessageBubbleCont self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25) } - override func tapActionAtPoint(_ point: CGPoint, gesture: TapLongTapOrDoubleTapGesture) -> ChatMessageBubbleContentTapAction { + override func tapActionAtPoint(_ point: CGPoint, gesture: TapLongTapOrDoubleTapGesture, isEstimating: Bool) -> ChatMessageBubbleContentTapAction { if self.bounds.contains(point) { let contentNodeFrame = self.contentNode.frame - return self.contentNode.tapActionAtPoint(point.offsetBy(dx: -contentNodeFrame.minX, dy: -contentNodeFrame.minY), gesture: gesture) + return self.contentNode.tapActionAtPoint(point.offsetBy(dx: -contentNodeFrame.minX, dy: -contentNodeFrame.minY), gesture: gesture, isEstimating: isEstimating) } return .none } diff --git a/submodules/TelegramUI/TelegramUI/ChatMessageGameBubbleContentNode.swift b/submodules/TelegramUI/TelegramUI/ChatMessageGameBubbleContentNode.swift index 04be9ee3d9..75fa0bed5c 100644 --- a/submodules/TelegramUI/TelegramUI/ChatMessageGameBubbleContentNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatMessageGameBubbleContentNode.swift @@ -112,7 +112,7 @@ final class ChatMessageGameBubbleContentNode: ChatMessageBubbleContentNode { self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25) } - override func tapActionAtPoint(_ point: CGPoint, gesture: TapLongTapOrDoubleTapGesture) -> ChatMessageBubbleContentTapAction { + override func tapActionAtPoint(_ point: CGPoint, gesture: TapLongTapOrDoubleTapGesture, isEstimating: Bool) -> ChatMessageBubbleContentTapAction { if self.bounds.contains(point) { /*if let webPage = self.webPage, case let .Loaded(content) = webPage.content { if content.instantPage != nil { diff --git a/submodules/TelegramUI/TelegramUI/ChatMessageInstantVideoItemNode.swift b/submodules/TelegramUI/TelegramUI/ChatMessageInstantVideoItemNode.swift index 77dbaa3cbe..7ef8e6201b 100644 --- a/submodules/TelegramUI/TelegramUI/ChatMessageInstantVideoItemNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatMessageInstantVideoItemNode.swift @@ -115,7 +115,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView { let currentForwardInfo = self.appliedForwardInfo return { item, params, mergedTop, mergedBottom, dateHeaderAtBottom in - let layoutConstants = chatMessageItemLayoutConstants(layoutConstants, params: params) + let layoutConstants = chatMessageItemLayoutConstants(layoutConstants, params: params, presentationData: item.presentationData) let incoming = item.message.effectivelyIncoming(item.context.account.peerId) let avatarInset: CGFloat @@ -295,7 +295,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView { updatedReplyBackgroundNode = ASImageNode() } - let graphics = PresentationResourcesChat.additionalGraphics(item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper) + let graphics = PresentationResourcesChat.additionalGraphics(item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper, bubbleCorners: item.presentationData.chatBubbleCorners) replyBackgroundImage = graphics.chatFreeformContentAdditionalInfoBackgroundImage } @@ -306,7 +306,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView { if currentShareButtonNode != nil { updatedShareButtonNode = currentShareButtonNode if item.presentationData.theme !== currentItem?.presentationData.theme { - let graphics = PresentationResourcesChat.additionalGraphics(item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper) + let graphics = PresentationResourcesChat.additionalGraphics(item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper, bubbleCorners: item.presentationData.chatBubbleCorners) if item.message.id.peerId == item.context.account.peerId { updatedShareButtonBackground = graphics.chatBubbleNavigateButtonImage } else { @@ -316,7 +316,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView { } else { let buttonNode = HighlightableButtonNode() let buttonIcon: UIImage? - let graphics = PresentationResourcesChat.additionalGraphics(item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper) + let graphics = PresentationResourcesChat.additionalGraphics(item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper, bubbleCorners: item.presentationData.chatBubbleCorners) if item.message.id.peerId == item.context.account.peerId { buttonIcon = graphics.chatBubbleNavigateButtonImage } else { @@ -364,14 +364,14 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView { updatedForwardBackgroundNode = ASImageNode() } - let graphics = PresentationResourcesChat.additionalGraphics(item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper) + let graphics = PresentationResourcesChat.additionalGraphics(item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper, bubbleCorners: item.presentationData.chatBubbleCorners) forwardBackgroundImage = graphics.chatServiceBubbleFillImage } var maxContentWidth = videoLayout.contentSize.width var actionButtonsFinalize: ((CGFloat) -> (CGSize, (_ animated: Bool) -> ChatMessageActionButtonsNode))? if let replyMarkup = replyMarkup { - let (minWidth, buttonsLayout) = actionButtonsLayout(item.context, item.presentationData.theme, item.presentationData.strings, replyMarkup, item.message, maxContentWidth) + let (minWidth, buttonsLayout) = actionButtonsLayout(item.context, item.presentationData.theme, item.presentationData.chatBubbleCorners, item.presentationData.strings, replyMarkup, item.message, maxContentWidth) maxContentWidth = max(maxContentWidth, minWidth) actionButtonsFinalize = buttonsLayout } diff --git a/submodules/TelegramUI/TelegramUI/ChatMessageInteractiveFileNode.swift b/submodules/TelegramUI/TelegramUI/ChatMessageInteractiveFileNode.swift index 758b2a4aa4..664a47f472 100644 --- a/submodules/TelegramUI/TelegramUI/ChatMessageInteractiveFileNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatMessageInteractiveFileNode.swift @@ -439,7 +439,7 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode { if hasThumbnail { fileIconImage = nil } else { - let principalGraphics = PresentationResourcesChat.principalGraphics(mediaBox: context.account.postbox.mediaBox, knockoutWallpaper: context.sharedContext.immediateExperimentalUISettings.knockoutWallpaper, theme: presentationData.theme.theme, wallpaper: presentationData.theme.wallpaper) + let principalGraphics = PresentationResourcesChat.principalGraphics(mediaBox: context.account.postbox.mediaBox, knockoutWallpaper: context.sharedContext.immediateExperimentalUISettings.knockoutWallpaper, theme: presentationData.theme.theme, wallpaper: presentationData.theme.wallpaper, bubbleCorners: presentationData.chatBubbleCorners) fileIconImage = incoming ? principalGraphics.radialIndicatorFileIconIncoming : principalGraphics.radialIndicatorFileIconOutgoing } @@ -831,6 +831,10 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode { if let statusNode = self.statusNode { if state == .none { self.statusNode = nil + if animated { + statusNode.layer.animateScale(from: 1.0, to: 0.1, duration: 0.2, removeOnCompletion: false) + statusNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false) + } } statusNode.transitionToState(state, animated: animated, synchronous: presentationData.theme.theme.preview, completion: { [weak statusNode] in if state == .none { diff --git a/submodules/TelegramUI/TelegramUI/ChatMessageInteractiveMediaNode.swift b/submodules/TelegramUI/TelegramUI/ChatMessageInteractiveMediaNode.swift index 90aaeaa6e8..42c76d580f 100644 --- a/submodules/TelegramUI/TelegramUI/ChatMessageInteractiveMediaNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatMessageInteractiveMediaNode.swift @@ -954,7 +954,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio } var progressRequired = false - if attributes.updatingMedia != nil { + if let updatingMedia = attributes.updatingMedia, case .update = updatingMedia.media { progressRequired = true } else if secretBeginTimeAndTimeout?.0 != nil { progressRequired = true diff --git a/submodules/TelegramUI/TelegramUI/ChatMessageInvoiceBubbleContentNode.swift b/submodules/TelegramUI/TelegramUI/ChatMessageInvoiceBubbleContentNode.swift index 92d81ffb0c..1b7f36074c 100644 --- a/submodules/TelegramUI/TelegramUI/ChatMessageInvoiceBubbleContentNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatMessageInvoiceBubbleContentNode.swift @@ -115,7 +115,7 @@ final class ChatMessageInvoiceBubbleContentNode: ChatMessageBubbleContentNode { self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25) } - override func tapActionAtPoint(_ point: CGPoint, gesture: TapLongTapOrDoubleTapGesture) -> ChatMessageBubbleContentTapAction { + override func tapActionAtPoint(_ point: CGPoint, gesture: TapLongTapOrDoubleTapGesture, isEstimating: Bool) -> ChatMessageBubbleContentTapAction { if self.bounds.contains(point) { /*if let webPage = self.webPage, case let .Loaded(content) = webPage.content { if content.instantPage != nil { diff --git a/submodules/TelegramUI/TelegramUI/ChatMessageItemView.swift b/submodules/TelegramUI/TelegramUI/ChatMessageItemView.swift index 8f84499aa4..96066bb31d 100644 --- a/submodules/TelegramUI/TelegramUI/ChatMessageItemView.swift +++ b/submodules/TelegramUI/TelegramUI/ChatMessageItemView.swift @@ -11,9 +11,9 @@ import ContextUI import ChatListUI struct ChatMessageItemWidthFill { - let compactInset: CGFloat - let compactWidthBoundary: CGFloat - let freeMaximumFillFactor: CGFloat + var compactInset: CGFloat + var compactWidthBoundary: CGFloat + var freeMaximumFillFactor: CGFloat func widthFor(_ width: CGFloat) -> CGFloat { if width <= self.compactWidthBoundary { @@ -25,68 +25,68 @@ struct ChatMessageItemWidthFill { } struct ChatMessageItemBubbleLayoutConstants { - let edgeInset: CGFloat - let defaultSpacing: CGFloat - let mergedSpacing: CGFloat - let maximumWidthFill: ChatMessageItemWidthFill - let minimumSize: CGSize - let contentInsets: UIEdgeInsets - let borderInset: CGFloat - let strokeInsets: UIEdgeInsets + var edgeInset: CGFloat + var defaultSpacing: CGFloat + var mergedSpacing: CGFloat + var maximumWidthFill: ChatMessageItemWidthFill + var minimumSize: CGSize + var contentInsets: UIEdgeInsets + var borderInset: CGFloat + var strokeInsets: UIEdgeInsets } struct ChatMessageItemTextLayoutConstants { - let bubbleInsets: UIEdgeInsets + var bubbleInsets: UIEdgeInsets } struct ChatMessageItemImageLayoutConstants { - let bubbleInsets: UIEdgeInsets - let statusInsets: UIEdgeInsets - let defaultCornerRadius: CGFloat - let mergedCornerRadius: CGFloat - let contentMergedCornerRadius: CGFloat - let maxDimensions: CGSize - let minDimensions: CGSize + var bubbleInsets: UIEdgeInsets + var statusInsets: UIEdgeInsets + var defaultCornerRadius: CGFloat + var mergedCornerRadius: CGFloat + var contentMergedCornerRadius: CGFloat + var maxDimensions: CGSize + var minDimensions: CGSize } struct ChatMessageItemVideoLayoutConstants { - let maxHorizontalHeight: CGFloat - let maxVerticalHeight: CGFloat + var maxHorizontalHeight: CGFloat + var maxVerticalHeight: CGFloat } struct ChatMessageItemInstantVideoConstants { - let insets: UIEdgeInsets - let dimensions: CGSize + var insets: UIEdgeInsets + var dimensions: CGSize } struct ChatMessageItemFileLayoutConstants { - let bubbleInsets: UIEdgeInsets + var bubbleInsets: UIEdgeInsets } struct ChatMessageItemWallpaperLayoutConstants { - let maxTextWidth: CGFloat + var maxTextWidth: CGFloat } struct ChatMessageItemLayoutConstants { - let avatarDiameter: CGFloat - let timestampHeaderHeight: CGFloat + var avatarDiameter: CGFloat + var timestampHeaderHeight: CGFloat - let bubble: ChatMessageItemBubbleLayoutConstants - let image: ChatMessageItemImageLayoutConstants - let video: ChatMessageItemVideoLayoutConstants - let text: ChatMessageItemTextLayoutConstants - let file: ChatMessageItemFileLayoutConstants - let instantVideo: ChatMessageItemInstantVideoConstants - let wallpapers: ChatMessageItemWallpaperLayoutConstants + var bubble: ChatMessageItemBubbleLayoutConstants + var image: ChatMessageItemImageLayoutConstants + var video: ChatMessageItemVideoLayoutConstants + var text: ChatMessageItemTextLayoutConstants + var file: ChatMessageItemFileLayoutConstants + var instantVideo: ChatMessageItemInstantVideoConstants + var wallpapers: ChatMessageItemWallpaperLayoutConstants static var `default`: ChatMessageItemLayoutConstants { return self.compact } fileprivate static var compact: ChatMessageItemLayoutConstants { - let bubble = ChatMessageItemBubbleLayoutConstants(edgeInset: 4.0, defaultSpacing: 2.0 + UIScreenPixel, mergedSpacing: 1.0, maximumWidthFill: ChatMessageItemWidthFill(compactInset: 36.0, compactWidthBoundary: 500.0, freeMaximumFillFactor: 0.85), minimumSize: CGSize(width: 40.0, height: 35.0), contentInsets: UIEdgeInsets(top: 0.0, left: 6.0, bottom: 0.0, right: 0.0), borderInset: UIScreenPixel, strokeInsets: UIEdgeInsets(top: 1.0 - UIScreenPixel, left: 1.0 - UIScreenPixel, bottom: 1.0 - UIScreenPixel, right: 1.0 - UIScreenPixel)) + let bubble = ChatMessageItemBubbleLayoutConstants(edgeInset: 4.0, defaultSpacing: 2.0 + UIScreenPixel, mergedSpacing: 1.0, maximumWidthFill: ChatMessageItemWidthFill(compactInset: 36.0, compactWidthBoundary: 500.0, freeMaximumFillFactor: 0.85), minimumSize: CGSize(width: 40.0, height: 35.0), contentInsets: UIEdgeInsets(top: 0.0, left: 6.0, bottom: 0.0, right: 0.0), borderInset: UIScreenPixel, strokeInsets: UIEdgeInsets(top: 1.0, left: 1.0, bottom: 1.0, right: 1.0)) let text = ChatMessageItemTextLayoutConstants(bubbleInsets: UIEdgeInsets(top: 6.0 + UIScreenPixel, left: 12.0, bottom: 6.0 - UIScreenPixel, right: 12.0)) - let image = ChatMessageItemImageLayoutConstants(bubbleInsets: UIEdgeInsets(top: 1.0 + UIScreenPixel, left: 1.0 + UIScreenPixel, bottom: 1.0 + UIScreenPixel, right: 1.0 + UIScreenPixel), statusInsets: UIEdgeInsets(top: 0.0, left: 0.0, bottom: 6.0, right: 6.0), defaultCornerRadius: 17.0, mergedCornerRadius: 5.0, contentMergedCornerRadius: 0.0, maxDimensions: CGSize(width: 300.0, height: 300.0), minDimensions: CGSize(width: 170.0, height: 74.0)) + let image = ChatMessageItemImageLayoutConstants(bubbleInsets: UIEdgeInsets(top: 2.0, left: 2.0, bottom: 2.0, right: 2.0), statusInsets: UIEdgeInsets(top: 0.0, left: 0.0, bottom: 6.0, right: 6.0), defaultCornerRadius: 16.0, mergedCornerRadius: 8.0, contentMergedCornerRadius: 0.0, maxDimensions: CGSize(width: 300.0, height: 300.0), minDimensions: CGSize(width: 170.0, height: 74.0)) let video = ChatMessageItemVideoLayoutConstants(maxHorizontalHeight: 250.0, maxVerticalHeight: 360.0) let file = ChatMessageItemFileLayoutConstants(bubbleInsets: UIEdgeInsets(top: 15.0, left: 9.0, bottom: 15.0, right: 12.0)) let instantVideo = ChatMessageItemInstantVideoConstants(insets: UIEdgeInsets(top: 4.0, left: 0.0, bottom: 4.0, right: 0.0), dimensions: CGSize(width: 212.0, height: 212.0)) @@ -96,9 +96,9 @@ struct ChatMessageItemLayoutConstants { } fileprivate static var regular: ChatMessageItemLayoutConstants { - let bubble = ChatMessageItemBubbleLayoutConstants(edgeInset: 4.0, defaultSpacing: 2.0 + UIScreenPixel, mergedSpacing: 1.0, maximumWidthFill: ChatMessageItemWidthFill(compactInset: 36.0, compactWidthBoundary: 500.0, freeMaximumFillFactor: 0.65), minimumSize: CGSize(width: 40.0, height: 35.0), contentInsets: UIEdgeInsets(top: 0.0, left: 6.0, bottom: 0.0, right: 0.0), borderInset: UIScreenPixel, strokeInsets: UIEdgeInsets(top: 1.0 - UIScreenPixel, left: 1.0 - UIScreenPixel, bottom: 1.0 - UIScreenPixel, right: 1.0 - UIScreenPixel)) + let bubble = ChatMessageItemBubbleLayoutConstants(edgeInset: 4.0, defaultSpacing: 2.0 + UIScreenPixel, mergedSpacing: 1.0, maximumWidthFill: ChatMessageItemWidthFill(compactInset: 36.0, compactWidthBoundary: 500.0, freeMaximumFillFactor: 0.65), minimumSize: CGSize(width: 40.0, height: 35.0), contentInsets: UIEdgeInsets(top: 0.0, left: 6.0, bottom: 0.0, right: 0.0), borderInset: UIScreenPixel, strokeInsets: UIEdgeInsets(top: 1.0, left: 1.0, bottom: 1.0, right: 1.0)) let text = ChatMessageItemTextLayoutConstants(bubbleInsets: UIEdgeInsets(top: 6.0 + UIScreenPixel, left: 12.0, bottom: 6.0 - UIScreenPixel, right: 12.0)) - let image = ChatMessageItemImageLayoutConstants(bubbleInsets: UIEdgeInsets(top: 1.0 + UIScreenPixel, left: 1.0 + UIScreenPixel, bottom: 1.0 + UIScreenPixel, right: 1.0 + UIScreenPixel), statusInsets: UIEdgeInsets(top: 0.0, left: 0.0, bottom: 6.0, right: 6.0), defaultCornerRadius: 17.0, mergedCornerRadius: 5.0, contentMergedCornerRadius: 5.0, maxDimensions: CGSize(width: 440.0, height: 440.0), minDimensions: CGSize(width: 170.0, height: 74.0)) + let image = ChatMessageItemImageLayoutConstants(bubbleInsets: UIEdgeInsets(top: 2.0, left: 2.0, bottom: 2.0, right: 2.0), statusInsets: UIEdgeInsets(top: 0.0, left: 0.0, bottom: 6.0, right: 6.0), defaultCornerRadius: 16.0, mergedCornerRadius: 8.0, contentMergedCornerRadius: 5.0, maxDimensions: CGSize(width: 440.0, height: 440.0), minDimensions: CGSize(width: 170.0, height: 74.0)) let video = ChatMessageItemVideoLayoutConstants(maxHorizontalHeight: 250.0, maxVerticalHeight: 360.0) let file = ChatMessageItemFileLayoutConstants(bubbleInsets: UIEdgeInsets(top: 15.0, left: 9.0, bottom: 15.0, right: 12.0)) let instantVideo = ChatMessageItemInstantVideoConstants(insets: UIEdgeInsets(top: 4.0, left: 0.0, bottom: 4.0, right: 0.0), dimensions: CGSize(width: 212.0, height: 212.0)) @@ -108,12 +108,24 @@ struct ChatMessageItemLayoutConstants { } } -func chatMessageItemLayoutConstants(_ constants: (ChatMessageItemLayoutConstants, ChatMessageItemLayoutConstants), params: ListViewItemLayoutParams) -> ChatMessageItemLayoutConstants { +func chatMessageItemLayoutConstants(_ constants: (ChatMessageItemLayoutConstants, ChatMessageItemLayoutConstants), params: ListViewItemLayoutParams, presentationData: ChatPresentationData) -> ChatMessageItemLayoutConstants { + var result: ChatMessageItemLayoutConstants if params.width > 680.0 { - return constants.1 + result = constants.1 } else { - return constants.0 + result = constants.0 } + result.image.defaultCornerRadius = presentationData.chatBubbleCorners.mainRadius + result.image.mergedCornerRadius = (presentationData.chatBubbleCorners.mergeBubbleCorners && result.image.defaultCornerRadius >= 10.0) ? presentationData.chatBubbleCorners.auxiliaryRadius : presentationData.chatBubbleCorners.mainRadius + let minRadius: CGFloat = 4.0 + let maxRadius: CGFloat = 16.0 + let radiusTransition = (presentationData.chatBubbleCorners.mainRadius - minRadius) / (maxRadius - minRadius) + let minInset: CGFloat = 9.0 + let maxInset: CGFloat = 12.0 + let textInset: CGFloat = min(maxInset, ceil(maxInset * radiusTransition + minInset * (1.0 - radiusTransition))) + result.text.bubbleInsets.left = textInset + result.text.bubbleInsets.right = textInset + return result } enum ChatMessageItemBottomNeighbor { @@ -799,6 +811,8 @@ public class ChatMessageItemView: ListViewItemNode { item.controllerInteraction.openCheckoutOrReceipt(item.message.id) case let .urlAuth(url, buttonId): item.controllerInteraction.requestMessageActionUrlAuth(url, item.message.id, buttonId) + case let .setupPoll(isQuiz): + break } } } diff --git a/submodules/TelegramUI/TelegramUI/ChatMessageMapBubbleContentNode.swift b/submodules/TelegramUI/TelegramUI/ChatMessageMapBubbleContentNode.swift index 08fea390f8..178685165d 100644 --- a/submodules/TelegramUI/TelegramUI/ChatMessageMapBubbleContentNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatMessageMapBubbleContentNode.swift @@ -159,13 +159,13 @@ class ChatMessageMapBubbleContentNode: ChatMessageBubbleContentNode { if case let .linear(top, _) = position { relativePosition = .linear(top: top, bottom: ChatMessageBubbleRelativePosition.Neighbour) } - imageCorners = chatMessageBubbleImageContentCorners(relativeContentPosition: relativePosition, normalRadius: layoutConstants.image.defaultCornerRadius, mergedRadius: layoutConstants.image.mergedCornerRadius, mergedWithAnotherContentRadius: layoutConstants.image.contentMergedCornerRadius) + imageCorners = chatMessageBubbleImageContentCorners(relativeContentPosition: relativePosition, normalRadius: layoutConstants.image.defaultCornerRadius, mergedRadius: layoutConstants.image.mergedCornerRadius, mergedWithAnotherContentRadius: layoutConstants.image.contentMergedCornerRadius, layoutConstants: layoutConstants, chatPresentationData: item.presentationData) maxTextWidth = constrainedSize.width - bubbleInsets.left + bubbleInsets.right - layoutConstants.text.bubbleInsets.left - layoutConstants.text.bubbleInsets.right - 40.0 } else { maxTextWidth = constrainedSize.width - imageSize.width - bubbleInsets.left + bubbleInsets.right - layoutConstants.text.bubbleInsets.right - imageCorners = chatMessageBubbleImageContentCorners(relativeContentPosition: position, normalRadius: layoutConstants.image.defaultCornerRadius, mergedRadius: layoutConstants.image.mergedCornerRadius, mergedWithAnotherContentRadius: layoutConstants.image.contentMergedCornerRadius) + imageCorners = chatMessageBubbleImageContentCorners(relativeContentPosition: position, normalRadius: layoutConstants.image.defaultCornerRadius, mergedRadius: layoutConstants.image.mergedCornerRadius, mergedWithAnotherContentRadius: layoutConstants.image.contentMergedCornerRadius, layoutConstants: layoutConstants, chatPresentationData: item.presentationData) } let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: titleString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: max(1.0, maxTextWidth), height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) @@ -460,7 +460,7 @@ class ChatMessageMapBubbleContentNode: ChatMessageBubbleContentNode { return mediaHidden } - override func tapActionAtPoint(_ point: CGPoint, gesture: TapLongTapOrDoubleTapGesture) -> ChatMessageBubbleContentTapAction { + override func tapActionAtPoint(_ point: CGPoint, gesture: TapLongTapOrDoubleTapGesture, isEstimating: Bool) -> ChatMessageBubbleContentTapAction { return .none } diff --git a/submodules/TelegramUI/TelegramUI/ChatMessageMediaBubbleContentNode.swift b/submodules/TelegramUI/TelegramUI/ChatMessageMediaBubbleContentNode.swift index 05bc4f997e..fec48009c9 100644 --- a/submodules/TelegramUI/TelegramUI/ChatMessageMediaBubbleContentNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatMessageMediaBubbleContentNode.swift @@ -128,8 +128,8 @@ class ChatMessageMediaBubbleContentNode: ChatMessageBubbleContentNode { } else { colors = item.presentationData.theme.theme.chat.message.outgoing.bubble.withoutWallpaper } - if colors.fill == colors.stroke { - bubbleInsets = UIEdgeInsets() + if colors.fill == colors.stroke || colors.stroke.alpha.isZero { + bubbleInsets = UIEdgeInsets(top: 1.0, left: 1.0, bottom: 1.0, right: 1.0) } else { bubbleInsets = layoutConstants.bubble.strokeInsets } @@ -159,10 +159,10 @@ class ChatMessageMediaBubbleContentNode: ChatMessageBubbleContentNode { if forceFullCorners, case .linear = updatedPosition { updatedPosition = .linear(top: .None(.None(.None)), bottom: .None(.None(.None))) } else if hasReplyMarkup, case let .linear(top, _) = updatedPosition { - updatedPosition = .linear(top: top, bottom: .Neighbour) + updatedPosition = .linear(top: top, bottom: .BubbleNeighbour) } - let imageCorners = chatMessageBubbleImageContentCorners(relativeContentPosition: updatedPosition, normalRadius: layoutConstants.image.defaultCornerRadius, mergedRadius: layoutConstants.image.mergedCornerRadius, mergedWithAnotherContentRadius: layoutConstants.image.contentMergedCornerRadius) + let imageCorners = chatMessageBubbleImageContentCorners(relativeContentPosition: updatedPosition, normalRadius: layoutConstants.image.defaultCornerRadius, mergedRadius: layoutConstants.image.mergedCornerRadius, mergedWithAnotherContentRadius: layoutConstants.image.contentMergedCornerRadius, layoutConstants: layoutConstants, chatPresentationData: item.presentationData) let (refinedWidth, finishLayout) = refineLayout(CGSize(width: constrainedSize.width - bubbleInsets.left - bubbleInsets.right, height: constrainedSize.height), automaticPlayback, wideLayout, imageCorners) @@ -355,7 +355,7 @@ class ChatMessageMediaBubbleContentNode: ChatMessageBubbleContentNode { return self.interactiveImageNode.playMediaWithSound() } - override func tapActionAtPoint(_ point: CGPoint, gesture: TapLongTapOrDoubleTapGesture) -> ChatMessageBubbleContentTapAction { + override func tapActionAtPoint(_ point: CGPoint, gesture: TapLongTapOrDoubleTapGesture, isEstimating: Bool) -> ChatMessageBubbleContentTapAction { return .none } diff --git a/submodules/TelegramUI/TelegramUI/ChatMessagePollBubbleContentNode.swift b/submodules/TelegramUI/TelegramUI/ChatMessagePollBubbleContentNode.swift index 4db3123a19..c1ba412885 100644 --- a/submodules/TelegramUI/TelegramUI/ChatMessagePollBubbleContentNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatMessagePollBubbleContentNode.swift @@ -7,8 +7,12 @@ import SyncCore import Postbox import TextFormat import UrlEscaping +import SwiftSignalKit +import AccountContext +import AvatarNode +import TelegramPresentationData -struct PercentCounterItem: Comparable { +private struct PercentCounterItem: Comparable { var index: Int = 0 var percent: Int = 0 var remainder: Int = 0 @@ -24,7 +28,7 @@ struct PercentCounterItem: Comparable { } -func adjustPercentCount(_ items: [PercentCounterItem], left: Int) -> [PercentCounterItem] { +private func adjustPercentCount(_ items: [PercentCounterItem], left: Int) -> [PercentCounterItem] { var left = left var items = items.sorted(by: <) var i:Int = 0 @@ -84,39 +88,80 @@ func countNicePercent(votes: [Int], total: Int) -> [Int] { } private final class ChatMessagePollOptionRadioNodeParameters: NSObject { + let timestamp: Double let staticColor: UIColor let animatedColor: UIColor + let fillColor: UIColor + let foregroundColor: UIColor let offset: Double? + let isChecked: Bool? + let checkTransition: ChatMessagePollOptionRadioNodeCheckTransition? - init(staticColor: UIColor, animatedColor: UIColor, offset: Double?) { + init(timestamp: Double, staticColor: UIColor, animatedColor: UIColor, fillColor: UIColor, foregroundColor: UIColor, offset: Double?, isChecked: Bool?, checkTransition: ChatMessagePollOptionRadioNodeCheckTransition?) { + self.timestamp = timestamp self.staticColor = staticColor self.animatedColor = animatedColor + self.fillColor = fillColor + self.foregroundColor = foregroundColor self.offset = offset + self.isChecked = isChecked + self.checkTransition = checkTransition super.init() } } +private final class ChatMessagePollOptionRadioNodeCheckTransition { + let startTime: Double + let duration: Double + let previousValue: Bool + let updatedValue: Bool + + init(startTime: Double, duration: Double, previousValue: Bool, updatedValue: Bool) { + self.startTime = startTime + self.duration = duration + self.previousValue = previousValue + self.updatedValue = updatedValue + } +} + private final class ChatMessagePollOptionRadioNode: ASDisplayNode { private(set) var staticColor: UIColor? private(set) var animatedColor: UIColor? + private(set) var fillColor: UIColor? + private(set) var foregroundColor: UIColor? private var isInHierarchyValue: Bool = false private(set) var isAnimating: Bool = false private var startTime: Double? + private var checkTransition: ChatMessagePollOptionRadioNodeCheckTransition? + private(set) var isChecked: Bool? - private var displayLink: CADisplayLink? + private var displayLink: ConstantDisplayLinkAnimator? private var shouldBeAnimating: Bool { - return self.isInHierarchyValue && self.isAnimating + return self.isInHierarchyValue && (self.isAnimating || self.checkTransition != nil) + } + + func updateIsChecked(_ value: Bool, animated: Bool) { + if let previousValue = self.isChecked, previousValue != value { + self.checkTransition = ChatMessagePollOptionRadioNodeCheckTransition(startTime: CACurrentMediaTime(), duration: 0.15, previousValue: previousValue, updatedValue: value) + self.isChecked = value + self.updateAnimating() + self.setNeedsDisplay() + } } override init() { super.init() - self.isLayerBacked = true + self.isUserInteractionEnabled = false self.isOpaque = false } + deinit { + self.displayLink?.isPaused = true + } + override func willEnterHierarchy() { super.willEnterHierarchy() @@ -139,8 +184,10 @@ private final class ChatMessagePollOptionRadioNode: ASDisplayNode { } } - func update(staticColor: UIColor, animatedColor: UIColor, isAnimating: Bool) { + func update(staticColor: UIColor, animatedColor: UIColor, fillColor: UIColor, foregroundColor: UIColor, isSelectable: Bool, isAnimating: Bool) { var updated = false + let shouldHaveBeenAnimating = self.shouldBeAnimating + let wasAnimating = self.isAnimating if !staticColor.isEqual(self.staticColor) { self.staticColor = staticColor updated = true @@ -149,11 +196,27 @@ private final class ChatMessagePollOptionRadioNode: ASDisplayNode { self.animatedColor = animatedColor updated = true } + if !fillColor.isEqual(self.fillColor) { + self.fillColor = fillColor + updated = true + } + if !foregroundColor.isEqual(self.foregroundColor) { + self.foregroundColor = foregroundColor + updated = true + } + if isSelectable != (self.isChecked != nil) { + if isSelectable { + self.isChecked = false + } else { + self.isChecked = nil + self.checkTransition = nil + } + updated = true + } if isAnimating != self.isAnimating { - let previous = self.shouldBeAnimating self.isAnimating = isAnimating let updated = self.shouldBeAnimating - if previous != updated { + if shouldHaveBeenAnimating != updated { self.updateAnimating() } } @@ -163,26 +226,25 @@ private final class ChatMessagePollOptionRadioNode: ASDisplayNode { } private func updateAnimating() { - if self.shouldBeAnimating { - self.startTime = CACurrentMediaTime() - if self.displayLink == nil { - class DisplayLinkProxy: NSObject { - var f: () -> Void - init(_ f: @escaping () -> Void) { - self.f = f - } - - @objc func displayLinkEvent() { - self.f() - } - } - let displayLink = CADisplayLink(target: DisplayLinkProxy({ [weak self] in - self?.setNeedsDisplay() - }), selector: #selector(DisplayLinkProxy.displayLinkEvent)) - displayLink.add(to: .main, forMode: .common) - self.displayLink = displayLink + let timestamp = CACurrentMediaTime() + if let checkTransition = self.checkTransition { + if checkTransition.startTime + checkTransition.duration <= timestamp { + self.checkTransition = nil + } + } + + if self.shouldBeAnimating { + if self.isAnimating && self.startTime == nil { + self.startTime = timestamp + } + if self.displayLink == nil { + self.displayLink = ConstantDisplayLinkAnimator(update: { [weak self] in + self?.updateAnimating() + self?.setNeedsDisplay() + }) + self.displayLink?.isPaused = false + self.setNeedsDisplay() } - self.setNeedsDisplay() } else if let displayLink = self.displayLink { self.startTime = nil displayLink.invalidate() @@ -192,12 +254,13 @@ private final class ChatMessagePollOptionRadioNode: ASDisplayNode { } override public func drawParameters(forAsyncLayer layer: _ASDisplayLayer) -> NSObjectProtocol? { - if let staticColor = self.staticColor, let animatedColor = self.animatedColor { + if let staticColor = self.staticColor, let animatedColor = self.animatedColor, let fillColor = self.fillColor, let foregroundColor = self.foregroundColor { + let timestamp = CACurrentMediaTime() var offset: Double? if let startTime = self.startTime { offset = CACurrentMediaTime() - startTime } - return ChatMessagePollOptionRadioNodeParameters(staticColor: staticColor, animatedColor: animatedColor, offset: offset) + return ChatMessagePollOptionRadioNodeParameters(timestamp: timestamp, staticColor: staticColor, animatedColor: animatedColor, fillColor: fillColor, foregroundColor: foregroundColor, offset: offset, isChecked: self.isChecked, checkTransition: self.checkTransition) } else { return nil } @@ -276,20 +339,93 @@ private final class ChatMessagePollOptionRadioNode: ASDisplayNode { } } } else { - context.setStrokeColor(parameters.staticColor.cgColor) - context.strokeEllipse(in: CGRect(origin: CGPoint(x: 0.5, y: 0.5), size: CGSize(width: bounds.width - 1.0, height: bounds.height - 1.0))) + if let isChecked = parameters.isChecked { + let checkedT: CGFloat + let fromValue: CGFloat + let toValue: CGFloat + let fromAlpha: CGFloat + let toAlpha: CGFloat + if let checkTransition = parameters.checkTransition { + checkedT = CGFloat(max(0.0, min(1.0, (parameters.timestamp - checkTransition.startTime) / checkTransition.duration))) + fromValue = checkTransition.previousValue ? bounds.width : 0.0 + fromAlpha = checkTransition.previousValue ? 1.0 : 0.0 + toValue = checkTransition.updatedValue ? bounds.width : 0.0 + toAlpha = checkTransition.updatedValue ? 1.0 : 0.0 + } else { + checkedT = 1.0 + fromValue = isChecked ? bounds.width : 0.0 + fromAlpha = isChecked ? 1.0 : 0.0 + toValue = isChecked ? bounds.width : 0.0 + toAlpha = isChecked ? 1.0 : 0.0 + } + + let diameter = fromValue * (1.0 - checkedT) + toValue * checkedT + let alpha = fromAlpha * (1.0 - checkedT) + toAlpha * checkedT + + if abs(diameter - 1.0) > CGFloat.ulpOfOne { + context.setStrokeColor(parameters.staticColor.cgColor) + context.strokeEllipse(in: CGRect(origin: CGPoint(x: 0.5, y: 0.5), size: CGSize(width: bounds.width - 1.0, height: bounds.height - 1.0))) + } + + if !diameter.isZero { + context.setFillColor(parameters.fillColor.withAlphaComponent(alpha).cgColor) + context.fillEllipse(in: CGRect(origin: CGPoint(x: (bounds.width - diameter) / 2.0, y: (bounds.width - diameter) / 2.0), size: CGSize(width: diameter, height: diameter))) + + context.setLineWidth(1.5) + context.setLineJoin(.round) + context.setLineCap(.round) + + context.setStrokeColor(parameters.foregroundColor.withAlphaComponent(alpha).cgColor) + if parameters.foregroundColor.alpha.isZero { + context.setBlendMode(.clear) + } + let startPoint = CGPoint(x: 6.0, y: 12.13) + let centerPoint = CGPoint(x: 9.28, y: 15.37) + let endPoint = CGPoint(x: 16.0, y: 8.0) + + let pathStartT: CGFloat = 0.15 + let pathT = max(0.0, (alpha - pathStartT) / (1.0 - pathStartT)) + let pathMiddleT: CGFloat = 0.4 + + context.move(to: startPoint) + if pathT >= pathMiddleT { + context.addLine(to: centerPoint) + + let pathEndT = (pathT - pathMiddleT) / (1.0 - pathMiddleT) + if pathEndT >= 1.0 { + context.addLine(to: endPoint) + } else { + context.addLine(to: CGPoint(x: (1.0 - pathEndT) * centerPoint.x + pathEndT * endPoint.x, y: (1.0 - pathEndT) * centerPoint.y + pathEndT * endPoint.y)) + } + } else { + context.addLine(to: CGPoint(x: (1.0 - pathT) * startPoint.x + pathT * centerPoint.x, y: (1.0 - pathT) * startPoint.y + pathT * centerPoint.y)) + } + context.strokePath() + context.setBlendMode(.normal) + } + } else { + context.setStrokeColor(parameters.staticColor.cgColor) + context.strokeEllipse(in: CGRect(origin: CGPoint(x: 0.5, y: 0.5), size: CGSize(width: bounds.width - 1.0, height: bounds.height - 1.0))) + } } } } private let percentageFont = Font.bold(14.5) +private let percentageSmallFont = Font.bold(12.5) -private func generatePercentageImage(presentationData: ChatPresentationData, incoming: Bool, value: Int) -> UIImage { +private func generatePercentageImage(presentationData: ChatPresentationData, incoming: Bool, value: Int, targetValue: Int) -> UIImage { return generateImage(CGSize(width: 42.0, height: 20.0), rotatedContext: { size, context in UIGraphicsPushContext(context) context.clear(CGRect(origin: CGPoint(), size: size)) - let string = NSAttributedString(string: "\(value)%", font: percentageFont, textColor: incoming ? presentationData.theme.theme.chat.message.incoming.primaryTextColor : presentationData.theme.theme.chat.message.outgoing.primaryTextColor, paragraphAlignment: .right) - string.draw(in: CGRect(origin: CGPoint(x: 0.0, y: 2.0), size: size)) + let font: UIFont + if targetValue == 100 { + font = percentageSmallFont + } else { + font = percentageFont + } + let string = NSAttributedString(string: "\(value)%", font: font, textColor: incoming ? presentationData.theme.theme.chat.message.incoming.primaryTextColor : presentationData.theme.theme.chat.message.outgoing.primaryTextColor, paragraphAlignment: .right) + string.draw(in: CGRect(origin: CGPoint(x: 0.0, y: targetValue == 100 ? 3.0 : 2.0), size: size)) UIGraphicsPopContext() })! } @@ -300,7 +436,7 @@ private func generatePercentageAnimationImages(presentationData: ChatPresentatio var images: [UIImage] = [] for i in 0 ..< numberOfFrames { let t = CGFloat(i) / CGFloat(numberOfFrames) - images.append(generatePercentageImage(presentationData: presentationData, incoming: incoming, value: Int((1.0 - t) * CGFloat(fromValue) + t * CGFloat(toValue)))) + images.append(generatePercentageImage(presentationData: presentationData, incoming: incoming, value: Int((1.0 - t) * CGFloat(fromValue) + t * CGFloat(toValue)), targetValue: toValue)) } return images } @@ -311,19 +447,27 @@ private struct ChatMessagePollOptionResult: Equatable { let count: Int32 } +private struct ChatMessagePollOptionSelection: Equatable { + var isSelected: Bool + var isCorrect: Bool +} + private final class ChatMessagePollOptionNode: ASDisplayNode { private let highlightedBackgroundNode: ASDisplayNode - private var radioNode: ChatMessagePollOptionRadioNode? + private(set) var radioNode: ChatMessagePollOptionRadioNode? private let percentageNode: ASDisplayNode private var percentageImage: UIImage? private var titleNode: TextNode? private let buttonNode: HighlightTrackingButtonNode private let separatorNode: ASDisplayNode private let resultBarNode: ASImageNode - + private let resultBarIconNode: ASImageNode var option: TelegramMediaPollOption? - public private(set) var currentResult: ChatMessagePollOptionResult? + private(set) var currentResult: ChatMessagePollOptionResult? + private(set) var currentSelection: ChatMessagePollOptionSelection? var pressed: (() -> Void)? + var selectionUpdated: (() -> Void)? + private var theme: PresentationTheme? override init() { self.highlightedBackgroundNode = ASDisplayNode() @@ -338,6 +482,9 @@ private final class ChatMessagePollOptionNode: ASDisplayNode { self.resultBarNode.isLayerBacked = true self.resultBarNode.alpha = 0.0 + self.resultBarIconNode = ASImageNode() + self.resultBarIconNode.isLayerBacked = true + self.percentageNode = ASDisplayNode() self.percentageNode.alpha = 0.0 self.percentageNode.isLayerBacked = true @@ -347,6 +494,7 @@ private final class ChatMessagePollOptionNode: ASDisplayNode { self.addSubnode(self.highlightedBackgroundNode) self.addSubnode(self.separatorNode) self.addSubnode(self.resultBarNode) + self.addSubnode(self.resultBarIconNode) self.addSubnode(self.percentageNode) self.addSubnode(self.buttonNode) @@ -365,14 +513,21 @@ private final class ChatMessagePollOptionNode: ASDisplayNode { } @objc private func buttonPressed() { - self.pressed?() + if let radioNode = self.radioNode, let isChecked = radioNode.isChecked { + radioNode.updateIsChecked(!isChecked, animated: true) + self.selectionUpdated?() + } else { + self.pressed?() + } } - static func asyncLayout(_ maybeNode: ChatMessagePollOptionNode?) -> (_ accountPeerId: PeerId, _ presentationData: ChatPresentationData, _ message: Message, _ option: TelegramMediaPollOption, _ optionResult: ChatMessagePollOptionResult?, _ constrainedWidth: CGFloat) -> (minimumWidth: CGFloat, layout: ((CGFloat) -> (CGSize, (Bool, Bool) -> ChatMessagePollOptionNode))) { + static func asyncLayout(_ maybeNode: ChatMessagePollOptionNode?) -> (_ accountPeerId: PeerId, _ presentationData: ChatPresentationData, _ message: Message, _ poll: TelegramMediaPoll, _ option: TelegramMediaPollOption, _ optionResult: ChatMessagePollOptionResult?, _ constrainedWidth: CGFloat) -> (minimumWidth: CGFloat, layout: ((CGFloat) -> (CGSize, (Bool, Bool) -> ChatMessagePollOptionNode))) { let makeTitleLayout = TextNode.asyncLayout(maybeNode?.titleNode) let currentResult = maybeNode?.currentResult + let currentSelection = maybeNode?.currentSelection + let currentTheme = maybeNode?.theme - return { accountPeerId, presentationData, message, option, optionResult, constrainedWidth in + return { accountPeerId, presentationData, message, poll, option, optionResult, constrainedWidth in let leftInset: CGFloat = 50.0 let rightInset: CGFloat = 12.0 @@ -383,10 +538,88 @@ private final class ChatMessagePollOptionNode: ASDisplayNode { let contentHeight: CGFloat = max(46.0, titleLayout.size.height + 22.0) let shouldHaveRadioNode = optionResult == nil + let isSelectable: Bool + if shouldHaveRadioNode, case .poll(multipleAnswers: true) = poll.kind, !Namespaces.Message.allScheduled.contains(message.id.namespace) { + isSelectable = true + } else { + isSelectable = false + } + + let themeUpdated = presentationData.theme.theme !== currentTheme var updatedPercentageImage: UIImage? - if currentResult != optionResult { - updatedPercentageImage = generatePercentageImage(presentationData: presentationData, incoming: incoming, value: optionResult?.percent ?? 0) + if currentResult != optionResult || themeUpdated { + let value = optionResult?.percent ?? 0 + updatedPercentageImage = generatePercentageImage(presentationData: presentationData, incoming: incoming, value: value, targetValue: value) + } + + var resultIcon: UIImage? + var updatedResultIcon = false + + var selection: ChatMessagePollOptionSelection? + if optionResult != nil { + if let voters = poll.results.voters { + for voter in voters { + if voter.opaqueIdentifier == option.opaqueIdentifier { + if voter.selected || voter.isCorrect { + selection = ChatMessagePollOptionSelection(isSelected: voter.selected, isCorrect: voter.isCorrect) + } + break + } + } + } + } + if selection != currentSelection || themeUpdated { + updatedResultIcon = true + if let selection = selection { + var isQuiz = false + if case .quiz = poll.kind { + isQuiz = true + } + resultIcon = generateImage(CGSize(width: 16.0, height: 16.0), rotatedContext: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + var isIncorrect = false + let fillColor: UIColor + if selection.isSelected { + if isQuiz { + if selection.isCorrect { + fillColor = incoming ? presentationData.theme.theme.chat.message.incoming.polls.barPositive : presentationData.theme.theme.chat.message.outgoing.polls.barPositive + } else { + fillColor = incoming ? presentationData.theme.theme.chat.message.incoming.polls.barNegative : presentationData.theme.theme.chat.message.outgoing.polls.barNegative + isIncorrect = true + } + } else { + fillColor = incoming ? presentationData.theme.theme.chat.message.incoming.polls.bar : presentationData.theme.theme.chat.message.outgoing.polls.bar + } + } else if isQuiz && selection.isCorrect { + fillColor = incoming ? presentationData.theme.theme.chat.message.incoming.polls.bar : presentationData.theme.theme.chat.message.outgoing.polls.bar + } else { + fillColor = incoming ? presentationData.theme.theme.chat.message.incoming.polls.bar : presentationData.theme.theme.chat.message.outgoing.polls.bar + } + context.setFillColor(fillColor.cgColor) + context.fillEllipse(in: CGRect(origin: CGPoint(), size: size)) + + let strokeColor = incoming ? presentationData.theme.theme.chat.message.incoming.polls.barIconForeground : presentationData.theme.theme.chat.message.outgoing.polls.barIconForeground + if strokeColor.alpha.isZero { + context.setBlendMode(.copy) + } + context.setStrokeColor(strokeColor.cgColor) + context.setLineWidth(1.5) + context.setLineJoin(.round) + context.setLineCap(.round) + if isIncorrect { + context.translateBy(x: 5.0, y: 5.0) + context.move(to: CGPoint(x: 0.0, y: 6.0)) + context.addLine(to: CGPoint(x: 6.0, y: 0.0)) + context.strokePath() + context.move(to: CGPoint(x: 0.0, y: 0.0)) + context.addLine(to: CGPoint(x: 6.0, y: 6.0)) + context.strokePath() + } else { + let _ = try? drawSvgPath(context, path: "M4,8.5 L6.44778395,10.9477839 C6.47662208,10.9766221 6.52452135,10.9754786 6.54754782,10.9524522 L12,5.5 S ") + } + }) + } } return (titleLayout.size.width + leftInset + rightInset, { width in @@ -401,6 +634,8 @@ private final class ChatMessagePollOptionNode: ASDisplayNode { node.option = option let previousResult = node.currentResult node.currentResult = optionResult + node.currentSelection = selection + node.theme = presentationData.theme.theme node.highlightedBackgroundNode.backgroundColor = incoming ? presentationData.theme.theme.chat.message.incoming.polls.highlight : presentationData.theme.theme.chat.message.outgoing.polls.highlight @@ -432,7 +667,7 @@ private final class ChatMessagePollOptionNode: ASDisplayNode { } let radioSize: CGFloat = 22.0 radioNode.frame = CGRect(origin: CGPoint(x: 12.0, y: 12.0), size: CGSize(width: radioSize, height: radioSize)) - radioNode.update(staticColor: incoming ? presentationData.theme.theme.chat.message.incoming.polls.radioButton : presentationData.theme.theme.chat.message.outgoing.polls.radioButton, animatedColor: incoming ? presentationData.theme.theme.chat.message.incoming.polls.radioProgress : presentationData.theme.theme.chat.message.outgoing.polls.radioProgress, isAnimating: inProgress) + radioNode.update(staticColor: incoming ? presentationData.theme.theme.chat.message.incoming.polls.radioButton : presentationData.theme.theme.chat.message.outgoing.polls.radioButton, animatedColor: incoming ? presentationData.theme.theme.chat.message.incoming.polls.radioProgress : presentationData.theme.theme.chat.message.outgoing.polls.radioProgress, fillColor: incoming ? presentationData.theme.theme.chat.message.incoming.polls.bar : presentationData.theme.theme.chat.message.outgoing.polls.bar, foregroundColor: incoming ? presentationData.theme.theme.chat.message.incoming.polls.barIconForeground : presentationData.theme.theme.chat.message.outgoing.polls.barIconForeground, isSelectable: isSelectable, isAnimating: inProgress) } else if let radioNode = node.radioNode { node.radioNode = nil if animated { @@ -469,26 +704,60 @@ private final class ChatMessagePollOptionNode: ASDisplayNode { node.separatorNode.backgroundColor = incoming ? presentationData.theme.theme.chat.message.incoming.polls.separator : presentationData.theme.theme.chat.message.outgoing.polls.separator node.separatorNode.frame = CGRect(origin: CGPoint(x: leftInset, y: contentHeight - UIScreenPixel), size: CGSize(width: width - leftInset, height: UIScreenPixel)) - if node.resultBarNode.image == nil { - node.resultBarNode.image = generateStretchableFilledCircleImage(diameter: 6.0, color: incoming ? presentationData.theme.theme.chat.message.incoming.polls.bar : presentationData.theme.theme.chat.message.outgoing.polls.bar) + if node.resultBarNode.image == nil || updatedResultIcon { + var isQuiz = false + if case .quiz = poll.kind { + isQuiz = true + } + let fillColor: UIColor + if let selection = selection { + if selection.isSelected { + if isQuiz { + if selection.isCorrect { + fillColor = incoming ? presentationData.theme.theme.chat.message.incoming.polls.barPositive : presentationData.theme.theme.chat.message.outgoing.polls.barPositive + } else { + fillColor = incoming ? presentationData.theme.theme.chat.message.incoming.polls.barNegative : presentationData.theme.theme.chat.message.outgoing.polls.barNegative + } + } else { + fillColor = incoming ? presentationData.theme.theme.chat.message.incoming.polls.bar : presentationData.theme.theme.chat.message.outgoing.polls.bar + } + } else if isQuiz && selection.isCorrect { + fillColor = incoming ? presentationData.theme.theme.chat.message.incoming.polls.bar : presentationData.theme.theme.chat.message.outgoing.polls.bar + } else { + fillColor = incoming ? presentationData.theme.theme.chat.message.incoming.polls.bar : presentationData.theme.theme.chat.message.outgoing.polls.bar + } + } else { + fillColor = incoming ? presentationData.theme.theme.chat.message.incoming.polls.bar : presentationData.theme.theme.chat.message.outgoing.polls.bar + } + + node.resultBarNode.image = generateStretchableFilledCircleImage(diameter: 6.0, color: fillColor) + } + + if updatedResultIcon { + node.resultBarIconNode.image = resultIcon } let minBarWidth: CGFloat = 6.0 let resultBarWidth = minBarWidth + floor((width - leftInset - rightInset - minBarWidth) * (optionResult?.normalized ?? 0.0)) - node.resultBarNode.frame = CGRect(origin: CGPoint(x: leftInset, y: contentHeight - 6.0 - 1.0), size: CGSize(width: resultBarWidth, height: 6.0)) + let barFrame = CGRect(origin: CGPoint(x: leftInset, y: contentHeight - 6.0 - 1.0), size: CGSize(width: resultBarWidth, height: 6.0)) + node.resultBarNode.frame = barFrame + node.resultBarIconNode.frame = CGRect(origin: CGPoint(x: barFrame.minX - 6.0 - 16.0, y: barFrame.minY + floor((barFrame.height - 16.0) / 2.0)), size: CGSize(width: 16.0, height: 16.0)) node.resultBarNode.alpha = optionResult != nil ? 1.0 : 0.0 node.percentageNode.alpha = optionResult != nil ? 1.0 : 0.0 node.separatorNode.alpha = optionResult == nil ? 1.0 : 0.0 + node.resultBarIconNode.alpha = optionResult != nil ? 1.0 : 0.0 if animated, currentResult != optionResult { if (currentResult != nil) != (optionResult != nil) { if optionResult != nil { node.resultBarNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.1) node.percentageNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) node.separatorNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.08) + node.resultBarIconNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) } else { node.resultBarNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.4) node.percentageNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2) node.separatorNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + node.resultBarIconNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2) } } @@ -513,10 +782,17 @@ private let labelsFont = Font.regular(14.0) class ChatMessagePollBubbleContentNode: ChatMessageBubbleContentNode { private let textNode: TextNode private let typeNode: TextNode + private let avatarsNode: MergedAvatarsNode private let votersNode: TextNode + private let buttonSubmitInactiveTextNode: TextNode + private let buttonSubmitActiveTextNode: TextNode + private let buttonViewResultsTextNode: TextNode + private let buttonNode: HighlightableButtonNode private let statusNode: ChatMessageDateAndStatusNode private var optionNodes: [ChatMessagePollOptionNode] = [] + private var poll: TelegramMediaPoll? + required init() { self.textNode = TextNode() self.textNode.isUserInteractionEnabled = false @@ -530,29 +806,106 @@ class ChatMessagePollBubbleContentNode: ChatMessageBubbleContentNode { self.typeNode.contentsScale = UIScreenScale self.typeNode.displaysAsynchronously = true + self.avatarsNode = MergedAvatarsNode() + self.votersNode = TextNode() self.votersNode.isUserInteractionEnabled = false self.votersNode.contentMode = .topLeft self.votersNode.contentsScale = UIScreenScale self.votersNode.displaysAsynchronously = true + self.buttonSubmitInactiveTextNode = TextNode() + self.buttonSubmitInactiveTextNode.isUserInteractionEnabled = false + self.buttonSubmitInactiveTextNode.contentMode = .topLeft + self.buttonSubmitInactiveTextNode.contentsScale = UIScreenScale + self.buttonSubmitInactiveTextNode.displaysAsynchronously = true + + self.buttonSubmitActiveTextNode = TextNode() + self.buttonSubmitActiveTextNode.isUserInteractionEnabled = false + self.buttonSubmitActiveTextNode.contentMode = .topLeft + self.buttonSubmitActiveTextNode.contentsScale = UIScreenScale + self.buttonSubmitActiveTextNode.displaysAsynchronously = true + + self.buttonViewResultsTextNode = TextNode() + self.buttonViewResultsTextNode.isUserInteractionEnabled = false + self.buttonViewResultsTextNode.contentMode = .topLeft + self.buttonViewResultsTextNode.contentsScale = UIScreenScale + self.buttonViewResultsTextNode.displaysAsynchronously = true + + self.buttonNode = HighlightableButtonNode() + self.statusNode = ChatMessageDateAndStatusNode() super.init() self.addSubnode(self.textNode) self.addSubnode(self.typeNode) + self.addSubnode(self.avatarsNode) self.addSubnode(self.votersNode) + self.addSubnode(self.buttonSubmitInactiveTextNode) + self.addSubnode(self.buttonSubmitActiveTextNode) + self.addSubnode(self.buttonViewResultsTextNode) + self.addSubnode(self.buttonNode) + + self.buttonNode.addTarget(self, action: #selector(self.buttonPressed), forControlEvents: .touchUpInside) + self.buttonNode.highligthedChanged = { [weak self] highlighted in + if let strongSelf = self { + if highlighted { + strongSelf.buttonSubmitActiveTextNode.layer.removeAnimation(forKey: "opacity") + strongSelf.buttonSubmitActiveTextNode.alpha = 0.6 + strongSelf.buttonViewResultsTextNode.layer.removeAnimation(forKey: "opacity") + strongSelf.buttonViewResultsTextNode.alpha = 0.6 + } else { + strongSelf.buttonSubmitActiveTextNode.alpha = 1.0 + strongSelf.buttonSubmitActiveTextNode.layer.animateAlpha(from: 0.6, to: 1.0, duration: 0.3) + strongSelf.buttonViewResultsTextNode.alpha = 1.0 + strongSelf.buttonViewResultsTextNode.layer.animateAlpha(from: 0.6, to: 1.0, duration: 0.3) + } + } + } + + self.avatarsNode.pressed = { [weak self] in + self?.buttonPressed() + } } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } + @objc private func buttonPressed() { + guard let item = self.item, let poll = self.poll, let pollId = poll.id else { + return + } + + var hasSelection = false + var selectedOpaqueIdentifiers: [Data] = [] + for optionNode in self.optionNodes { + if let option = optionNode.option { + if let isChecked = optionNode.radioNode?.isChecked { + hasSelection = true + if isChecked { + selectedOpaqueIdentifiers.append(option.opaqueIdentifier) + } + } + } + } + if !hasSelection { + if !Namespaces.Message.allScheduled.contains(item.message.id.namespace) { + item.controllerInteraction.requestOpenMessagePollResults(item.message.id, pollId) + } + } else if !selectedOpaqueIdentifiers.isEmpty { + item.controllerInteraction.requestSelectMessagePollOptions(item.message.id, selectedOpaqueIdentifiers) + } + } + override func asyncLayoutContent() -> (_ item: ChatMessageBubbleContentItem, _ layoutConstants: ChatMessageItemLayoutConstants, _ preparePosition: ChatMessageBubblePreparePosition, _ messageSelection: Bool?, _ constrainedSize: CGSize) -> (ChatMessageBubbleContentProperties, CGSize?, CGFloat, (CGSize, ChatMessageBubbleContentPosition) -> (CGFloat, (CGFloat) -> (CGSize, (ListViewItemUpdateAnimation, Bool) -> Void))) { let makeTextLayout = TextNode.asyncLayout(self.textNode) let makeTypeLayout = TextNode.asyncLayout(self.typeNode) let makeVotersLayout = TextNode.asyncLayout(self.votersNode) + let makeSubmitInactiveTextLayout = TextNode.asyncLayout(self.buttonSubmitInactiveTextNode) + let makeSubmitActiveTextLayout = TextNode.asyncLayout(self.buttonSubmitActiveTextNode) + let makeViewResultsTextLayout = TextNode.asyncLayout(self.buttonViewResultsTextNode) let statusLayout = self.statusNode.asyncLayout() var previousPoll: TelegramMediaPoll? @@ -564,11 +917,15 @@ class ChatMessagePollBubbleContentNode: ChatMessageBubbleContentNode { } } - var previousOptionNodeLayouts: [Data: (_ accountPeerId: PeerId, _ presentationData: ChatPresentationData, _ message: Message, _ option: TelegramMediaPollOption, _ optionResult: ChatMessagePollOptionResult?, _ constrainedWidth: CGFloat) -> (minimumWidth: CGFloat, layout: ((CGFloat) -> (CGSize, (Bool, Bool) -> ChatMessagePollOptionNode)))] = [:] + var previousOptionNodeLayouts: [Data: (_ accountPeerId: PeerId, _ presentationData: ChatPresentationData, _ message: Message, _ poll: TelegramMediaPoll, _ option: TelegramMediaPollOption, _ optionResult: ChatMessagePollOptionResult?, _ constrainedWidth: CGFloat) -> (minimumWidth: CGFloat, layout: ((CGFloat) -> (CGSize, (Bool, Bool) -> ChatMessagePollOptionNode)))] = [:] + var hasSelectedOptions = false for optionNode in self.optionNodes { if let option = optionNode.option { previousOptionNodeLayouts[option.opaqueIdentifier] = ChatMessagePollOptionNode.asyncLayout(optionNode) } + if let isChecked = optionNode.radioNode?.isChecked, isChecked { + hasSelectedOptions = true + } } return { item, layoutConstants, _, _, _ in @@ -653,25 +1010,65 @@ class ChatMessagePollBubbleContentNode: ChatMessageBubbleContentNode { let (textLayout, textApply) = makeTextLayout(TextNodeLayoutArguments(attributedString: attributedText, backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: textConstrainedSize, alignment: .natural, cutout: nil, insets: textInsets)) let typeText: String + + var avatarPeers: [Peer] = [] + if let poll = poll { + for peerId in poll.results.recentVoters { + if let peer = item.message.peers[peerId] { + avatarPeers.append(peer) + } + } + } + if let poll = poll, poll.isClosed { typeText = item.presentationData.strings.MessagePoll_LabelClosed + } else if let poll = poll { + switch poll.kind { + case .poll: + switch poll.publicity { + case .anonymous: + typeText = item.presentationData.strings.MessagePoll_LabelAnonymous + case .public: + typeText = item.presentationData.strings.MessagePoll_LabelPoll + } + case .quiz: + switch poll.publicity { + case .anonymous: + typeText = item.presentationData.strings.MessagePoll_LabelAnonymousQuiz + case .public: + typeText = item.presentationData.strings.MessagePoll_LabelQuiz + } + } } else { typeText = item.presentationData.strings.MessagePoll_LabelAnonymous } let (typeLayout, typeApply) = makeTypeLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: typeText, font: labelsFont, textColor: messageTheme.secondaryTextColor), backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: textConstrainedSize, alignment: .natural, cutout: nil, insets: UIEdgeInsets())) let votersString: String - if let totalVoters = poll?.results.totalVoters { - if totalVoters == 0 { - votersString = item.presentationData.strings.MessagePoll_NoVotes - } else { - votersString = item.presentationData.strings.MessagePoll_VotedCount(totalVoters) + if let poll = poll, let totalVoters = poll.results.totalVoters { + switch poll.kind { + case .poll: + if totalVoters == 0 { + votersString = item.presentationData.strings.MessagePoll_NoVotes + } else { + votersString = item.presentationData.strings.MessagePoll_VotedCount(totalVoters) + } + case .quiz: + if totalVoters == 0 { + votersString = item.presentationData.strings.MessagePoll_QuizNoUsers + } else { + votersString = item.presentationData.strings.MessagePoll_QuizCount(totalVoters) + } } } else { votersString = " " } let (votersLayout, votersApply) = makeVotersLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: votersString, font: labelsFont, textColor: messageTheme.secondaryTextColor), backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: textConstrainedSize, alignment: .natural, cutout: nil, insets: textInsets)) + let (buttonSubmitInactiveTextLayout, buttonSubmitInactiveTextApply) = makeSubmitInactiveTextLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.presentationData.strings.MessagePoll_SubmitVote, font: Font.regular(17.0), textColor: messageTheme.accentControlDisabledColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: textConstrainedSize, alignment: .natural, cutout: nil, insets: textInsets)) + let (buttonSubmitActiveTextLayout, buttonSubmitActiveTextApply) = makeSubmitActiveTextLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.presentationData.strings.MessagePoll_SubmitVote, font: Font.regular(17.0), textColor: messageTheme.polls.bar), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: textConstrainedSize, alignment: .natural, cutout: nil, insets: textInsets)) + let (buttonViewResultsTextLayout, buttonViewResultsTextApply) = makeViewResultsTextLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.presentationData.strings.MessagePoll_ViewResults, font: Font.regular(17.0), textColor: messageTheme.polls.bar), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: textConstrainedSize, alignment: .natural, cutout: nil, insets: textInsets)) + var textFrame = CGRect(origin: CGPoint(x: -textInsets.left, y: -textInsets.top), size: textLayout.size) var textFrameWithoutInsets = CGRect(origin: CGPoint(x: textFrame.origin.x + textInsets.left, y: textFrame.origin.y + textInsets.top), size: CGSize(width: textFrame.width - textInsets.left - textInsets.right, height: textFrame.height - textInsets.top - textInsets.bottom)) @@ -687,6 +1084,8 @@ class ChatMessagePollBubbleContentNode: ChatMessageBubbleContentNode { var boundingSize: CGSize = textFrameWithoutInsets.size boundingSize.width = max(boundingSize.width, typeLayout.size.width) boundingSize.width = max(boundingSize.width, votersLayout.size.width + 4.0 + (statusSize?.width ?? 0.0)) + boundingSize.width = max(boundingSize.width, buttonSubmitInactiveTextLayout.size.width + 4.0 + (statusSize?.width ?? 0.0)) + boundingSize.width = max(boundingSize.width, buttonViewResultsTextLayout.size.width + 4.0 + (statusSize?.width ?? 0.0)) boundingSize.width += layoutConstants.text.bubbleInsets.left + layoutConstants.text.bubbleInsets.right boundingSize.height += layoutConstants.text.bubbleInsets.top + layoutConstants.text.bubbleInsets.bottom @@ -733,7 +1132,7 @@ class ChatMessagePollBubbleContentNode: ChatMessageBubbleContentNode { for i in 0 ..< poll.options.count { let option = poll.options[i] - let makeLayout: (_ accountPeerId: PeerId, _ presentationData: ChatPresentationData, _ message: Message, _ option: TelegramMediaPollOption, _ optionResult: ChatMessagePollOptionResult?, _ constrainedWidth: CGFloat) -> (minimumWidth: CGFloat, layout: ((CGFloat) -> (CGSize, (Bool, Bool) -> ChatMessagePollOptionNode))) + let makeLayout: (_ accountPeerId: PeerId, _ presentationData: ChatPresentationData, _ message: Message, _ poll: TelegramMediaPoll, _ option: TelegramMediaPollOption, _ optionResult: ChatMessagePollOptionResult?, _ constrainedWidth: CGFloat) -> (minimumWidth: CGFloat, layout: ((CGFloat) -> (CGSize, (Bool, Bool) -> ChatMessagePollOptionNode))) if let previous = previousOptionNodeLayouts[option.opaqueIdentifier] { makeLayout = previous } else { @@ -749,7 +1148,7 @@ class ChatMessagePollBubbleContentNode: ChatMessageBubbleContentNode { } else if poll.isClosed { optionResult = ChatMessagePollOptionResult(normalized: 0, percent: 0, count: 0) } - let result = makeLayout(item.context.account.peerId, item.presentationData, item.message, option, optionResult, constrainedSize.width - layoutConstants.bubble.borderInset * 2.0) + let result = makeLayout(item.context.account.peerId, item.presentationData, item.message, poll, option, optionResult, constrainedSize.width - layoutConstants.bubble.borderInset * 2.0) boundingSize.width = max(boundingSize.width, result.minimumWidth + layoutConstants.bubble.borderInset * 2.0) pollOptionsFinalizeLayouts.append(result.1) } @@ -758,7 +1157,7 @@ class ChatMessagePollBubbleContentNode: ChatMessageBubbleContentNode { boundingSize.width = max(boundingSize.width, min(270.0, constrainedSize.width)) var canVote = false - if item.message.id.namespace == Namespaces.Message.Cloud, let poll = poll, poll.pollId.namespace == Namespaces.Media.CloudPoll, !poll.isClosed { + if (item.message.id.namespace == Namespaces.Message.Cloud || Namespaces.Message.allScheduled.contains(item.message.id.namespace)), let poll = poll, poll.pollId.namespace == Namespaces.Media.CloudPoll, !poll.isClosed { var hasVoted = false if let voters = poll.results.voters { for voter in voters { @@ -772,9 +1171,6 @@ class ChatMessagePollBubbleContentNode: ChatMessageBubbleContentNode { canVote = true } } - if Namespaces.Message.allScheduled.contains(item.message.id.namespace) { - canVote = true - } return (boundingSize.width, { boundingWidth in var resultSize = CGSize(width: max(boundingSize.width, boundingWidth), height: boundingSize.height) @@ -785,24 +1181,35 @@ class ChatMessagePollBubbleContentNode: ChatMessageBubbleContentNode { var optionNodesSizesAndApply: [(CGSize, (Bool, Bool) -> ChatMessagePollOptionNode)] = [] for finalizeLayout in pollOptionsFinalizeLayouts { - let result = finalizeLayout(boundingWidth - layoutConstants.bubble.borderInset * 2.0) + let result = finalizeLayout(boundingWidth - layoutConstants.bubble.borderInset * 2.0) resultSize.width = max(resultSize.width, result.0.width + layoutConstants.bubble.borderInset * 2.0) resultSize.height += result.0.height optionNodesSizesAndApply.append(result) } let optionsVotersSpacing: CGFloat = 11.0 - let votersBottomSpacing: CGFloat = 8.0 + let optionsButtonSpacing: CGFloat = 9.0 + let votersBottomSpacing: CGFloat = 11.0 resultSize.height += optionsVotersSpacing + votersLayout.size.height + votersBottomSpacing + let buttonSubmitInactiveTextFrame = CGRect(origin: CGPoint(x: floor((resultSize.width - buttonSubmitInactiveTextLayout.size.width) / 2.0), y: optionsButtonSpacing), size: buttonSubmitInactiveTextLayout.size) + let buttonSubmitActiveTextFrame = CGRect(origin: CGPoint(x: floor((resultSize.width - buttonSubmitActiveTextLayout.size.width) / 2.0), y: optionsButtonSpacing), size: buttonSubmitActiveTextLayout.size) + let buttonViewResultsTextFrame = CGRect(origin: CGPoint(x: floor((resultSize.width - buttonViewResultsTextLayout.size.width) / 2.0), y: optionsButtonSpacing), size: buttonViewResultsTextLayout.size) + var adjustedStatusFrame: CGRect? if let statusFrame = statusFrame { - adjustedStatusFrame = CGRect(origin: CGPoint(x: boundingWidth - statusFrame.size.width - layoutConstants.text.bubbleInsets.right, y: resultSize.height - statusFrame.size.height - 6.0), size: statusFrame.size) + var localStatusFrame = CGRect(origin: CGPoint(x: boundingWidth - statusFrame.size.width - layoutConstants.text.bubbleInsets.right, y: resultSize.height - statusFrame.size.height - 6.0), size: statusFrame.size) + if localStatusFrame.minX <= buttonViewResultsTextFrame.maxX || localStatusFrame.minX <= buttonSubmitActiveTextFrame.maxX { + localStatusFrame.origin.y += 10.0 + resultSize.height += 10.0 + } + adjustedStatusFrame = localStatusFrame } - return (resultSize, { [weak self] animation, _ in + return (resultSize, { [weak self] animation, synchronousLoad in if let strongSelf = self { strongSelf.item = item + strongSelf.poll = poll let cachedLayout = strongSelf.textNode.cachedLayout @@ -834,7 +1241,9 @@ class ChatMessagePollBubbleContentNode: ChatMessageBubbleContentNode { let (size, apply) = optionNodesSizesAndApply[i] var isRequesting = false if let poll = poll, i < poll.options.count { - isRequesting = item.controllerInteraction.pollActionState.pollMessageIdsInProgress[item.message.id] == poll.options[i].opaqueIdentifier + if let inProgressOpaqueIds = item.controllerInteraction.pollActionState.pollMessageIdsInProgress[item.message.id] { + isRequesting = inProgressOpaqueIds.contains(poll.options[i].opaqueIdentifier) + } } let optionNode = apply(animation.isAnimated, isRequesting) if optionNode.supernode !== strongSelf { @@ -845,7 +1254,13 @@ class ChatMessagePollBubbleContentNode: ChatMessageBubbleContentNode { return } - item.controllerInteraction.requestSelectMessagePollOption(item.message.id, option.opaqueIdentifier) + item.controllerInteraction.requestSelectMessagePollOptions(item.message.id, [option.opaqueIdentifier]) + } + optionNode.selectionUpdated = { + guard let strongSelf = self else { + return + } + strongSelf.updateSelection() } } optionNode.frame = CGRect(origin: CGPoint(x: layoutConstants.bubble.borderInset, y: verticalOffset), size: size) @@ -887,15 +1302,40 @@ class ChatMessagePollBubbleContentNode: ChatMessageBubbleContentNode { } else { strongSelf.textNode.frame = textFrame } - strongSelf.typeNode.frame = CGRect(origin: CGPoint(x: textFrame.minX, y: textFrame.maxY + titleTypeSpacing), size: typeLayout.size) + let typeFrame = CGRect(origin: CGPoint(x: textFrame.minX, y: textFrame.maxY + titleTypeSpacing), size: typeLayout.size) + strongSelf.typeNode.frame = typeFrame + let avatarsFrame = CGRect(origin: CGPoint(x: typeFrame.maxX + 6.0, y: typeFrame.minY + floor((typeFrame.height - mergedImageSize) / 2.0)), size: CGSize(width: mergedImageSize + mergedImageSpacing * 2.0, height: mergedImageSize)) + strongSelf.avatarsNode.frame = avatarsFrame + strongSelf.avatarsNode.updateLayout(size: avatarsFrame.size) + strongSelf.avatarsNode.update(context: item.context, peers: avatarPeers, synchronousLoad: synchronousLoad) + let alphaTransition: ContainedViewLayoutTransition + if animation.isAnimated { + alphaTransition = .animated(duration: 0.25, curve: .easeInOut) + alphaTransition.updateAlpha(node: strongSelf.avatarsNode, alpha: avatarPeers.isEmpty ? 0.0 : 1.0) + } else { + alphaTransition = .immediate + } let _ = votersApply() - strongSelf.votersNode.frame = CGRect(origin: CGPoint(x: textFrame.minX, y: verticalOffset + optionsVotersSpacing), size: votersLayout.size) + strongSelf.votersNode.frame = CGRect(origin: CGPoint(x: floor((resultSize.width - votersLayout.size.width) / 2.0), y: verticalOffset + optionsVotersSpacing), size: votersLayout.size) if animation.isAnimated, let previousPoll = previousPoll, let poll = poll { if previousPoll.results.totalVoters == nil && poll.results.totalVoters != nil { strongSelf.votersNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25) } } + + let _ = buttonSubmitInactiveTextApply() + strongSelf.buttonSubmitInactiveTextNode.frame = buttonSubmitInactiveTextFrame.offsetBy(dx: 0.0, dy: verticalOffset) + + let _ = buttonSubmitActiveTextApply() + strongSelf.buttonSubmitActiveTextNode.frame = buttonSubmitActiveTextFrame.offsetBy(dx: 0.0, dy: verticalOffset) + + let _ = buttonViewResultsTextApply() + strongSelf.buttonViewResultsTextNode.frame = buttonViewResultsTextFrame.offsetBy(dx: 0.0, dy: verticalOffset) + + strongSelf.buttonNode.frame = CGRect(origin: CGPoint(x: 0.0, y: verticalOffset), size: CGSize(width: resultSize.width, height: 44.0)) + + strongSelf.updateSelection() } }) }) @@ -903,6 +1343,81 @@ class ChatMessagePollBubbleContentNode: ChatMessageBubbleContentNode { } } + private func updateSelection() { + guard let item = self.item, let poll = self.poll else { + return + } + + let disableAllActions = false + + var hasSelection = false + switch poll.kind { + case .poll(true): + hasSelection = true + default: + break + } + + var hasSelectedOptions = false + for optionNode in self.optionNodes { + if let isChecked = optionNode.radioNode?.isChecked { + if isChecked { + hasSelectedOptions = true + } + } + } + + var hasResults = false + if poll.isClosed { + hasResults = true + hasSelection = false + if let totalVoters = poll.results.totalVoters, totalVoters == 0 { + hasResults = false + } + } else { + if let totalVoters = poll.results.totalVoters, totalVoters != 0 { + if let voters = poll.results.voters { + for voter in voters { + if voter.selected { + hasResults = true + break + } + } + } + } + } + + if !disableAllActions && hasSelection && !hasResults && poll.pollId.namespace == Namespaces.Media.CloudPoll { + self.votersNode.isHidden = true + self.buttonViewResultsTextNode.isHidden = true + self.buttonSubmitInactiveTextNode.isHidden = hasSelectedOptions + self.buttonSubmitActiveTextNode.isHidden = !hasSelectedOptions + self.buttonNode.isHidden = !hasSelectedOptions + self.buttonNode.isUserInteractionEnabled = true + } else { + if case .public = poll.publicity, hasResults, !disableAllActions { + self.votersNode.isHidden = true + self.buttonViewResultsTextNode.isHidden = false + self.buttonNode.isHidden = false + + if Namespaces.Message.allScheduled.contains(item.message.id.namespace) { + self.buttonNode.isUserInteractionEnabled = false + } else { + self.buttonNode.isUserInteractionEnabled = true + } + } else { + self.votersNode.isHidden = false + self.buttonViewResultsTextNode.isHidden = true + self.buttonNode.isHidden = true + self.buttonNode.isUserInteractionEnabled = true + } + self.buttonSubmitInactiveTextNode.isHidden = true + self.buttonSubmitActiveTextNode.isHidden = true + } + + self.avatarsNode.isUserInteractionEnabled = !self.buttonViewResultsTextNode.isHidden + } + override func animateInsertion(_ currentTimestamp: Double, duration: Double) { self.textNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) self.statusNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) @@ -918,7 +1433,7 @@ class ChatMessagePollBubbleContentNode: ChatMessageBubbleContentNode { self.statusNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false) } - override func tapActionAtPoint(_ point: CGPoint, gesture: TapLongTapOrDoubleTapGesture) -> ChatMessageBubbleContentTapAction { + override func tapActionAtPoint(_ point: CGPoint, gesture: TapLongTapOrDoubleTapGesture, isEstimating: Bool) -> ChatMessageBubbleContentTapAction { let textNodeFrame = self.textNode.frame if let (index, attributes) = self.textNode.attributesAtPoint(CGPoint(x: point.x - textNodeFrame.minX, y: point.y - textNodeFrame.minY)) { if let url = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] as? String { @@ -940,20 +1455,55 @@ class ChatMessagePollBubbleContentNode: ChatMessageBubbleContentNode { } } else { for optionNode in self.optionNodes { - if optionNode.frame.contains(point) { + if optionNode.frame.contains(point), case .tap = gesture { if optionNode.isUserInteractionEnabled { return .ignore - } else if let result = optionNode.currentResult, let item = self.item { - let string: String - if result.count == 0 { - string = item.presentationData.strings.MessagePoll_NoVotes - } else { - string = item.presentationData.strings.MessagePoll_VotedCount(result.count) + } else if let result = optionNode.currentResult, let item = self.item, !Namespaces.Message.allScheduled.contains(item.message.id.namespace), let poll = self.poll, let option = optionNode.option { + switch poll.publicity { + case .anonymous: + let string: String + switch poll.kind { + case .poll: + if result.count == 0 { + string = item.presentationData.strings.MessagePoll_NoVotes + } else { + string = item.presentationData.strings.MessagePoll_VotedCount(result.count) + } + case .quiz: + if result.count == 0 { + string = item.presentationData.strings.MessagePoll_QuizNoUsers + } else { + string = item.presentationData.strings.MessagePoll_QuizCount(result.count) + } + } + return .tooltip(string, optionNode, optionNode.bounds.offsetBy(dx: 0.0, dy: 10.0)) + case .public: + var hasNonZeroVoters = false + if let voters = poll.results.voters { + for voter in voters { + if voter.count != 0 { + hasNonZeroVoters = true + break + } + } + } + if hasNonZeroVoters { + if !isEstimating { + item.controllerInteraction.openMessagePollResults(item.message.id, option.opaqueIdentifier) + return .ignore + } + return .openMessage + } } - return .tooltip(string, optionNode, optionNode.bounds.offsetBy(dx: 0.0, dy: 10.0)) } } } + if self.buttonNode.isUserInteractionEnabled, !self.buttonNode.isHidden, self.buttonNode.frame.contains(point) { + return .ignore + } + if self.avatarsNode.isUserInteractionEnabled, !self.avatarsNode.isHidden, self.avatarsNode.frame.contains(point) { + return .ignore + } return .none } } @@ -965,3 +1515,186 @@ class ChatMessagePollBubbleContentNode: ChatMessageBubbleContentNode { return nil } } + +private enum PeerAvatarReference: Equatable { + case letters(PeerId, [String]) + case image(PeerReference, TelegramMediaImageRepresentation) + + var peerId: PeerId { + switch self { + case let .letters(value, _): + return value + case let .image(value, _): + return value.id + } + } +} + +private extension PeerAvatarReference { + init(peer: Peer) { + if let photo = peer.smallProfileImage, let peerReference = PeerReference(peer) { + self = .image(peerReference, photo) + } else { + self = .letters(peer.id, peer.displayLetters) + } + } +} + +private final class MergedAvatarsNodeArguments: NSObject { + let peers: [PeerAvatarReference] + let images: [PeerId: UIImage] + + init(peers: [PeerAvatarReference], images: [PeerId: UIImage]) { + self.peers = peers + self.images = images + } +} + +private let mergedImageSize: CGFloat = 16.0 +private let mergedImageSpacing: CGFloat = 15.0 + +private let avatarFont = avatarPlaceholderFont(size: 8.0) + +private final class MergedAvatarsNode: ASDisplayNode { + private var peers: [PeerAvatarReference] = [] + private var images: [PeerId: UIImage] = [:] + private var disposables: [PeerId: Disposable] = [:] + private let buttonNode: HighlightTrackingButtonNode + + var pressed: (() -> Void)? + + override init() { + self.buttonNode = HighlightTrackingButtonNode() + + super.init() + + self.isOpaque = false + self.displaysAsynchronously = true + self.buttonNode.addTarget(self, action: #selector(self.buttonPressed), forControlEvents: .touchUpInside) + self.addSubnode(self.buttonNode) + } + + deinit { + for (_, disposable) in self.disposables { + disposable.dispose() + } + } + + @objc private func buttonPressed() { + self.pressed?() + } + + func updateLayout(size: CGSize) { + self.buttonNode.frame = CGRect(origin: CGPoint(), size: size) + } + + func update(context: AccountContext, peers: [Peer], synchronousLoad: Bool) { + var filteredPeers = peers.map(PeerAvatarReference.init) + if filteredPeers.count > 3 { + filteredPeers.dropLast(filteredPeers.count - 3) + } + if filteredPeers != self.peers { + self.peers = filteredPeers + + var validImageIds: [PeerId] = [] + for peer in filteredPeers { + if case .image = peer { + validImageIds.append(peer.peerId) + } + } + + var removedImageIds: [PeerId] = [] + for (id, _) in self.images { + if !validImageIds.contains(id) { + removedImageIds.append(id) + } + } + var removedDisposableIds: [PeerId] = [] + for (id, disposable) in self.disposables { + if !validImageIds.contains(id) { + disposable.dispose() + removedDisposableIds.append(id) + } + } + for id in removedImageIds { + self.images.removeValue(forKey: id) + } + for id in removedDisposableIds { + self.disposables.removeValue(forKey: id) + } + for peer in filteredPeers { + switch peer { + case let .image(peerReference, representation): + if self.disposables[peer.peerId] == nil { + if let signal = peerAvatarImage(account: context.account, peerReference: peerReference, authorOfMessage: nil, representation: representation, displayDimensions: CGSize(width: mergedImageSize, height: mergedImageSize), synchronousLoad: synchronousLoad) { + let disposable = (signal + |> deliverOnMainQueue).start(next: { [weak self] image in + guard let strongSelf = self else { + return + } + if let image = image { + strongSelf.images[peer.peerId] = image + strongSelf.setNeedsDisplay() + } + }) + self.disposables[peer.peerId] = disposable + } + } + case .letters: + break + } + } + self.setNeedsDisplay() + } + } + + override func drawParameters(forAsyncLayer layer: _ASDisplayLayer) -> NSObjectProtocol { + return MergedAvatarsNodeArguments(peers: self.peers, images: self.images) + } + + @objc override class func draw(_ bounds: CGRect, withParameters parameters: Any?, isCancelled: () -> Bool, isRasterizing: Bool) { + assertNotOnMainThread() + + let context = UIGraphicsGetCurrentContext()! + + if !isRasterizing { + context.setBlendMode(.copy) + context.setFillColor(UIColor.clear.cgColor) + context.fill(bounds) + } + + guard let parameters = parameters as? MergedAvatarsNodeArguments else { + return + } + + let imageOverlaySpacing: CGFloat = 1.0 + context.setBlendMode(.copy) + + var currentX = mergedImageSize + mergedImageSpacing * CGFloat(parameters.peers.count - 1) - mergedImageSize + for i in (0 ..< parameters.peers.count).reversed() { + let imageRect = CGRect(origin: CGPoint(x: currentX, y: 0.0), size: CGSize(width: mergedImageSize, height: mergedImageSize)) + context.setFillColor(UIColor.clear.cgColor) + context.fillEllipse(in: imageRect.insetBy(dx: -1.0, dy: -1.0)) + + context.saveGState() + switch parameters.peers[i] { + case let .letters(peerId, letters): + context.translateBy(x: currentX, y: 0.0) + drawPeerAvatarLetters(context: context, size: CGSize(width: mergedImageSize, height: mergedImageSize), font: avatarFont, letters: letters, peerId: peerId) + context.translateBy(x: -currentX, y: 0.0) + case let .image(reference): + if let image = parameters.images[parameters.peers[i].peerId] { + context.translateBy(x: imageRect.midX, y: imageRect.midY) + context.scaleBy(x: 1.0, y: -1.0) + context.translateBy(x: -imageRect.midX, y: -imageRect.midY) + context.draw(image.cgImage!, in: imageRect) + } else { + context.setFillColor(UIColor.gray.cgColor) + context.fillEllipse(in: imageRect) + } + } + context.restoreGState() + currentX -= mergedImageSpacing + } + } +} diff --git a/submodules/TelegramUI/TelegramUI/ChatMessageReplyInfoNode.swift b/submodules/TelegramUI/TelegramUI/ChatMessageReplyInfoNode.swift index 6b17612beb..b546deccc4 100644 --- a/submodules/TelegramUI/TelegramUI/ChatMessageReplyInfoNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatMessageReplyInfoNode.swift @@ -76,7 +76,7 @@ class ChatMessageReplyInfoNode: ASDisplayNode { let serviceColor = serviceMessageColorComponents(theme: presentationData.theme.theme, wallpaper: presentationData.theme.wallpaper) titleColor = serviceColor.primaryText - let graphics = PresentationResourcesChat.additionalGraphics(presentationData.theme.theme, wallpaper: presentationData.theme.wallpaper) + let graphics = PresentationResourcesChat.additionalGraphics(presentationData.theme.theme, wallpaper: presentationData.theme.wallpaper, bubbleCorners: presentationData.chatBubbleCorners) lineImage = graphics.chatServiceVerticalLineImage textColor = titleColor } diff --git a/submodules/TelegramUI/TelegramUI/ChatMessageStickerItemNode.swift b/submodules/TelegramUI/TelegramUI/ChatMessageStickerItemNode.swift index d9c2e3c55d..d4b5f3b248 100644 --- a/submodules/TelegramUI/TelegramUI/ChatMessageStickerItemNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatMessageStickerItemNode.swift @@ -143,7 +143,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView { let currentItem = self.item return { item, params, mergedTop, mergedBottom, dateHeaderAtBottom in - let layoutConstants = chatMessageItemLayoutConstants(layoutConstants, params: params) + let layoutConstants = chatMessageItemLayoutConstants(layoutConstants, params: params, presentationData: item.presentationData) let incoming = item.message.effectivelyIncoming(item.context.account.peerId) var imageSize: CGSize = CGSize(width: 100.0, height: 100.0) if let telegramFile = telegramFile { @@ -353,7 +353,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView { updatedReplyBackgroundNode = ASImageNode() } - let graphics = PresentationResourcesChat.additionalGraphics(item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper) + let graphics = PresentationResourcesChat.additionalGraphics(item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper, bubbleCorners: item.presentationData.chatBubbleCorners) replyBackgroundImage = graphics.chatFreeformContentAdditionalInfoBackgroundImage } @@ -364,7 +364,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView { if currentShareButtonNode != nil { updatedShareButtonNode = currentShareButtonNode if item.presentationData.theme !== currentItem?.presentationData.theme { - let graphics = PresentationResourcesChat.additionalGraphics(item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper) + let graphics = PresentationResourcesChat.additionalGraphics(item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper, bubbleCorners: item.presentationData.chatBubbleCorners) if item.message.id.peerId == item.context.account.peerId { updatedShareButtonBackground = graphics.chatBubbleNavigateButtonImage } else { @@ -374,7 +374,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView { } else { let buttonNode = HighlightableButtonNode() let buttonIcon: UIImage? - let graphics = PresentationResourcesChat.additionalGraphics(item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper) + let graphics = PresentationResourcesChat.additionalGraphics(item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper, bubbleCorners: item.presentationData.chatBubbleCorners) if item.message.id.peerId == item.context.account.peerId { buttonIcon = graphics.chatBubbleNavigateButtonImage } else { @@ -389,7 +389,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView { var maxContentWidth = imageSize.width var actionButtonsFinalize: ((CGFloat) -> (CGSize, (_ animated: Bool) -> ChatMessageActionButtonsNode))? if let replyMarkup = replyMarkup { - let (minWidth, buttonsLayout) = actionButtonsLayout(item.context, item.presentationData.theme, item.presentationData.strings, replyMarkup, item.message, maxContentWidth) + let (minWidth, buttonsLayout) = actionButtonsLayout(item.context, item.presentationData.theme, item.presentationData.chatBubbleCorners, item.presentationData.strings, replyMarkup, item.message, maxContentWidth) maxContentWidth = max(maxContentWidth, minWidth) actionButtonsFinalize = buttonsLayout } diff --git a/submodules/TelegramUI/TelegramUI/ChatMessageTextBubbleContentNode.swift b/submodules/TelegramUI/TelegramUI/ChatMessageTextBubbleContentNode.swift index ef1007c97c..31b1d33df8 100644 --- a/submodules/TelegramUI/TelegramUI/ChatMessageTextBubbleContentNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatMessageTextBubbleContentNode.swift @@ -409,7 +409,7 @@ class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode { self.statusNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false) } - override func tapActionAtPoint(_ point: CGPoint, gesture: TapLongTapOrDoubleTapGesture) -> ChatMessageBubbleContentTapAction { + override func tapActionAtPoint(_ point: CGPoint, gesture: TapLongTapOrDoubleTapGesture, isEstimating: Bool) -> ChatMessageBubbleContentTapAction { let textNodeFrame = self.textNode.frame if let (index, attributes) = self.textNode.attributesAtPoint(CGPoint(x: point.x - textNodeFrame.minX, y: point.y - textNodeFrame.minY)) { if let url = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] as? String { diff --git a/submodules/TelegramUI/TelegramUI/ChatMessageUnsupportedBubbleContentNode.swift b/submodules/TelegramUI/TelegramUI/ChatMessageUnsupportedBubbleContentNode.swift index d5f3b64ed0..81c3423d01 100644 --- a/submodules/TelegramUI/TelegramUI/ChatMessageUnsupportedBubbleContentNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatMessageUnsupportedBubbleContentNode.swift @@ -100,7 +100,7 @@ final class ChatMessageUnsupportedBubbleContentNode: ChatMessageBubbleContentNod self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25) } - override func tapActionAtPoint(_ point: CGPoint, gesture: TapLongTapOrDoubleTapGesture) -> ChatMessageBubbleContentTapAction { + override func tapActionAtPoint(_ point: CGPoint, gesture: TapLongTapOrDoubleTapGesture, isEstimating: Bool) -> ChatMessageBubbleContentTapAction { if self.bounds.contains(point) { if self.buttonNode.frame.contains(point) { return .ignore diff --git a/submodules/TelegramUI/TelegramUI/ChatMessageWebpageBubbleContentNode.swift b/submodules/TelegramUI/TelegramUI/ChatMessageWebpageBubbleContentNode.swift index 9ab5159442..981c37021d 100644 --- a/submodules/TelegramUI/TelegramUI/ChatMessageWebpageBubbleContentNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatMessageWebpageBubbleContentNode.swift @@ -415,10 +415,10 @@ final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContentNode { return self.contentNode.playMediaWithSound() } - override func tapActionAtPoint(_ point: CGPoint, gesture: TapLongTapOrDoubleTapGesture) -> ChatMessageBubbleContentTapAction { + override func tapActionAtPoint(_ point: CGPoint, gesture: TapLongTapOrDoubleTapGesture, isEstimating: Bool) -> ChatMessageBubbleContentTapAction { if self.bounds.contains(point) { let contentNodeFrame = self.contentNode.frame - let result = self.contentNode.tapActionAtPoint(point.offsetBy(dx: -contentNodeFrame.minX, dy: -contentNodeFrame.minY), gesture: gesture) + let result = self.contentNode.tapActionAtPoint(point.offsetBy(dx: -contentNodeFrame.minX, dy: -contentNodeFrame.minY), gesture: gesture, isEstimating: isEstimating) switch result { case .none: break diff --git a/submodules/TelegramUI/TelegramUI/ChatOverlayNavigationBar.swift b/submodules/TelegramUI/TelegramUI/ChatOverlayNavigationBar.swift index a19a48aa66..720135e850 100644 --- a/submodules/TelegramUI/TelegramUI/ChatOverlayNavigationBar.swift +++ b/submodules/TelegramUI/TelegramUI/ChatOverlayNavigationBar.swift @@ -14,6 +14,7 @@ final class ChatOverlayNavigationBar: ASDisplayNode { private let theme: PresentationTheme private let strings: PresentationStrings private let nameDisplayOrder: PresentationPersonNameOrder + private let tapped: () -> Void private let close: () -> Void private let separatorNode: ASDisplayNode @@ -40,10 +41,11 @@ final class ChatOverlayNavigationBar: ASDisplayNode { } } - init(theme: PresentationTheme, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, close: @escaping () -> Void) { + init(theme: PresentationTheme, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, tapped: @escaping () -> Void, close: @escaping () -> Void) { self.theme = theme self.strings = strings self.nameDisplayOrder = nameDisplayOrder + self.tapped = tapped self.close = close self.separatorNode = ASDisplayNode() @@ -83,6 +85,13 @@ final class ChatOverlayNavigationBar: ASDisplayNode { self.closeButton.addTarget(self, action: #selector(self.closePressed), forControlEvents: [.touchUpInside]) } + override func didLoad() { + super.didLoad() + + let gestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.handleTap)) + self.view.addGestureRecognizer(gestureRecognizer) + } + func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) { transition.updateFrame(node: self.separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: size.height - UIScreenPixel), size: CGSize(width: size.width, height: UIScreenPixel))) @@ -93,11 +102,15 @@ final class ChatOverlayNavigationBar: ASDisplayNode { let _ = titleApply() transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(x: sideInset, y: floor((size.height - titleLayout.size.height) / 2.0)), size: titleLayout.size)) - let closeButtonSize = self.closeButton.measure(CGSize(width: 100.0, height: 100.0)) - transition.updateFrame(node: self.closeButton, frame: CGRect(origin: CGPoint(x: size.width - sideInset - closeButtonSize.width - 6.0, y: floor((size.height - closeButtonSize.height) / 2.0)), size: closeButtonSize)) + let closeButtonSize = CGSize(width: size.height, height: size.height) + transition.updateFrame(node: self.closeButton, frame: CGRect(origin: CGPoint(x: size.width - sideInset - closeButtonSize.width + 10.0, y: 0.0), size: closeButtonSize)) } - @objc func closePressed() { + @objc private func handleTap() { + self.tapped() + } + + @objc private func closePressed() { self.close() } } diff --git a/submodules/TelegramUI/TelegramUI/ChatPinnedMessageTitlePanelNode.swift b/submodules/TelegramUI/TelegramUI/ChatPinnedMessageTitlePanelNode.swift index 1df5cce3f6..96052f5d0b 100644 --- a/submodules/TelegramUI/TelegramUI/ChatPinnedMessageTitlePanelNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatPinnedMessageTitlePanelNode.swift @@ -13,6 +13,23 @@ import StickerResources import PhotoResources import TelegramStringFormatting +private func foldLineBreaks(_ text: String) -> String { + var lines = text.split { $0.isNewline } + var startedBothLines = false + var result = "" + for line in lines { + if line.isEmpty { + continue + } + if result.isEmpty { + result += line + } else { + result += " " + line + } + } + return result +} + final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode { private let context: AccountContext private let tapButton: HighlightTrackingButtonNode @@ -194,8 +211,13 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode { imageDimensions = representation.dimensions.cgSize } break - } else if let _ = media as? TelegramMediaPoll { - titleString = strings.Conversation_PinnedPoll + } else if let poll = media as? TelegramMediaPoll { + switch poll.kind { + case .poll: + titleString = strings.Conversation_PinnedPoll + case .quiz: + titleString = strings.Conversation_PinnedQuiz + } } } @@ -238,7 +260,7 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode { let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: titleString, font: Font.medium(15.0), textColor: theme.chat.inputPanel.panelControlAccentColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: width - textLineInset - contentLeftInset - rightInset - textRightInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets(top: 2.0, left: 0.0, bottom: 2.0, right: 0.0))) - let (textLayout, textApply) = makeTextLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: descriptionStringForMessage(contentSettings: context.currentContentSettings.with { $0 }, message: message, strings: strings, nameDisplayOrder: nameDisplayOrder, accountPeerId: accountPeerId).0, font: Font.regular(15.0), textColor: message.media.isEmpty || message.media.first is TelegramMediaWebpage ? theme.chat.inputPanel.primaryTextColor : theme.chat.inputPanel.secondaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: width - textLineInset - contentLeftInset - rightInset - textRightInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets(top: 2.0, left: 0.0, bottom: 2.0, right: 0.0))) + let (textLayout, textApply) = makeTextLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: foldLineBreaks(descriptionStringForMessage(contentSettings: context.currentContentSettings.with { $0 }, message: message, strings: strings, nameDisplayOrder: nameDisplayOrder, accountPeerId: accountPeerId).0), font: Font.regular(15.0), textColor: message.media.isEmpty || message.media.first is TelegramMediaWebpage ? theme.chat.inputPanel.primaryTextColor : theme.chat.inputPanel.secondaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: width - textLineInset - contentLeftInset - rightInset - textRightInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets(top: 2.0, left: 0.0, bottom: 2.0, right: 0.0))) Queue.mainQueue().async { if let strongSelf = self { diff --git a/submodules/TelegramUI/TelegramUI/ChatPresentationData.swift b/submodules/TelegramUI/TelegramUI/ChatPresentationData.swift index 0927b45710..253882e904 100644 --- a/submodules/TelegramUI/TelegramUI/ChatPresentationData.swift +++ b/submodules/TelegramUI/TelegramUI/ChatPresentationData.swift @@ -28,6 +28,7 @@ public final class ChatPresentationData { let nameDisplayOrder: PresentationPersonNameOrder let disableAnimations: Bool let largeEmoji: Bool + let chatBubbleCorners: PresentationChatBubbleCorners let animatedEmojiScale: CGFloat let isPreview: Bool @@ -39,13 +40,14 @@ public final class ChatPresentationData { let messageFixedFont: UIFont let messageBlockQuoteFont: UIFont - init(theme: ChatPresentationThemeData, fontSize: PresentationFontSize, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, disableAnimations: Bool, largeEmoji: Bool, animatedEmojiScale: CGFloat = 1.0, isPreview: Bool = false) { + init(theme: ChatPresentationThemeData, fontSize: PresentationFontSize, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, disableAnimations: Bool, largeEmoji: Bool, chatBubbleCorners: PresentationChatBubbleCorners, animatedEmojiScale: CGFloat = 1.0, isPreview: Bool = false) { self.theme = theme self.fontSize = fontSize self.strings = strings self.dateTimeFormat = dateTimeFormat self.nameDisplayOrder = nameDisplayOrder self.disableAnimations = disableAnimations + self.chatBubbleCorners = chatBubbleCorners self.largeEmoji = largeEmoji self.isPreview = isPreview diff --git a/submodules/TelegramUI/TelegramUI/ChatPresentationInterfaceState.swift b/submodules/TelegramUI/TelegramUI/ChatPresentationInterfaceState.swift index 60e1c60d6a..3697ec4e39 100644 --- a/submodules/TelegramUI/TelegramUI/ChatPresentationInterfaceState.swift +++ b/submodules/TelegramUI/TelegramUI/ChatPresentationInterfaceState.swift @@ -306,12 +306,13 @@ final class ChatPresentationInterfaceState: Equatable { let nameDisplayOrder: PresentationPersonNameOrder let limitsConfiguration: LimitsConfiguration let fontSize: PresentationFontSize + let bubbleCorners: PresentationChatBubbleCorners let accountPeerId: PeerId let mode: ChatControllerPresentationMode let hasScheduledMessages: Bool let isScheduledMessages: Bool - init(chatWallpaper: TelegramWallpaper, theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, limitsConfiguration: LimitsConfiguration, fontSize: PresentationFontSize, accountPeerId: PeerId, mode: ChatControllerPresentationMode, chatLocation: ChatLocation, isScheduledMessages: Bool) { + init(chatWallpaper: TelegramWallpaper, theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, limitsConfiguration: LimitsConfiguration, fontSize: PresentationFontSize, bubbleCorners: PresentationChatBubbleCorners, accountPeerId: PeerId, mode: ChatControllerPresentationMode, chatLocation: ChatLocation, isScheduledMessages: Bool) { self.interfaceState = ChatInterfaceState() self.inputTextPanelState = ChatTextInputPanelState() self.editMessageState = nil @@ -349,13 +350,14 @@ final class ChatPresentationInterfaceState: Equatable { self.nameDisplayOrder = nameDisplayOrder self.limitsConfiguration = limitsConfiguration self.fontSize = fontSize + self.bubbleCorners = bubbleCorners self.accountPeerId = accountPeerId self.mode = mode self.hasScheduledMessages = false self.isScheduledMessages = isScheduledMessages } - init(interfaceState: ChatInterfaceState, chatLocation: ChatLocation, renderedPeer: RenderedPeer?, isNotAccessible: Bool, explicitelyCanPinMessages: Bool, contactStatus: ChatContactStatus?, hasBots: Bool, isArchived: Bool, inputTextPanelState: ChatTextInputPanelState, editMessageState: ChatEditInterfaceMessageState?, recordedMediaPreview: ChatRecordedMediaPreview?, inputQueryResults: [ChatPresentationInputQueryKind: ChatPresentationInputQueryResult], inputMode: ChatInputMode, titlePanelContexts: [ChatTitlePanelContext], keyboardButtonsMessage: Message?, pinnedMessageId: MessageId?, pinnedMessage: Message?, peerIsBlocked: Bool, peerIsMuted: Bool, peerDiscussionId: PeerId?, peerGeoLocation: PeerGeoLocation?, callsAvailable: Bool, callsPrivate: Bool, slowmodeState: ChatSlowmodeState?, chatHistoryState: ChatHistoryNodeHistoryState?, botStartPayload: String?, urlPreview: (String, TelegramMediaWebpage)?, editingUrlPreview: (String, TelegramMediaWebpage)?, search: ChatSearchData?, searchQuerySuggestionResult: ChatPresentationInputQueryResult?, chatWallpaper: TelegramWallpaper, theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, limitsConfiguration: LimitsConfiguration, fontSize: PresentationFontSize, accountPeerId: PeerId, mode: ChatControllerPresentationMode, hasScheduledMessages: Bool, isScheduledMessages: Bool) { + init(interfaceState: ChatInterfaceState, chatLocation: ChatLocation, renderedPeer: RenderedPeer?, isNotAccessible: Bool, explicitelyCanPinMessages: Bool, contactStatus: ChatContactStatus?, hasBots: Bool, isArchived: Bool, inputTextPanelState: ChatTextInputPanelState, editMessageState: ChatEditInterfaceMessageState?, recordedMediaPreview: ChatRecordedMediaPreview?, inputQueryResults: [ChatPresentationInputQueryKind: ChatPresentationInputQueryResult], inputMode: ChatInputMode, titlePanelContexts: [ChatTitlePanelContext], keyboardButtonsMessage: Message?, pinnedMessageId: MessageId?, pinnedMessage: Message?, peerIsBlocked: Bool, peerIsMuted: Bool, peerDiscussionId: PeerId?, peerGeoLocation: PeerGeoLocation?, callsAvailable: Bool, callsPrivate: Bool, slowmodeState: ChatSlowmodeState?, chatHistoryState: ChatHistoryNodeHistoryState?, botStartPayload: String?, urlPreview: (String, TelegramMediaWebpage)?, editingUrlPreview: (String, TelegramMediaWebpage)?, search: ChatSearchData?, searchQuerySuggestionResult: ChatPresentationInputQueryResult?, chatWallpaper: TelegramWallpaper, theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, limitsConfiguration: LimitsConfiguration, fontSize: PresentationFontSize, bubbleCorners: PresentationChatBubbleCorners, accountPeerId: PeerId, mode: ChatControllerPresentationMode, hasScheduledMessages: Bool, isScheduledMessages: Bool) { self.interfaceState = interfaceState self.chatLocation = chatLocation self.renderedPeer = renderedPeer @@ -393,6 +395,7 @@ final class ChatPresentationInterfaceState: Equatable { self.nameDisplayOrder = nameDisplayOrder self.limitsConfiguration = limitsConfiguration self.fontSize = fontSize + self.bubbleCorners = bubbleCorners self.accountPeerId = accountPeerId self.mode = mode self.hasScheduledMessages = hasScheduledMessages @@ -530,6 +533,9 @@ final class ChatPresentationInterfaceState: Equatable { if lhs.fontSize != rhs.fontSize { return false } + if lhs.bubbleCorners != rhs.bubbleCorners { + return false + } if lhs.accountPeerId != rhs.accountPeerId { return false } @@ -546,31 +552,31 @@ final class ChatPresentationInterfaceState: Equatable { } func updatedInterfaceState(_ f: (ChatInterfaceState) -> ChatInterfaceState) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: f(self.interfaceState), chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, isScheduledMessages: self.isScheduledMessages) + return ChatPresentationInterfaceState(interfaceState: f(self.interfaceState), chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, isScheduledMessages: self.isScheduledMessages) } func updatedPeer(_ f: (RenderedPeer?) -> RenderedPeer?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: f(self.renderedPeer), isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, isScheduledMessages: self.isScheduledMessages) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: f(self.renderedPeer), isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, isScheduledMessages: self.isScheduledMessages) } func updatedIsNotAccessible(_ isNotAccessible: Bool) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, isScheduledMessages: self.isScheduledMessages) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, isScheduledMessages: self.isScheduledMessages) } func updatedExplicitelyCanPinMessages(_ explicitelyCanPinMessages: Bool) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, isScheduledMessages: self.isScheduledMessages) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, isScheduledMessages: self.isScheduledMessages) } func updatedContactStatus(_ contactStatus: ChatContactStatus?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, isScheduledMessages: self.isScheduledMessages) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, isScheduledMessages: self.isScheduledMessages) } func updatedHasBots(_ hasBots: Bool) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, isScheduledMessages: self.isScheduledMessages) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, isScheduledMessages: self.isScheduledMessages) } func updatedIsArchived(_ isArchived: Bool) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, isScheduledMessages: self.isScheduledMessages) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, isScheduledMessages: self.isScheduledMessages) } func updatedInputQueryResult(queryKind: ChatPresentationInputQueryKind, _ f: (ChatPresentationInputQueryResult?) -> ChatPresentationInputQueryResult?) -> ChatPresentationInterfaceState { @@ -581,115 +587,119 @@ final class ChatPresentationInterfaceState: Equatable { } else { inputQueryResults.removeValue(forKey: queryKind) } - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, isScheduledMessages: self.isScheduledMessages) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, isScheduledMessages: self.isScheduledMessages) } func updatedInputTextPanelState(_ f: (ChatTextInputPanelState) -> ChatTextInputPanelState) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: f(self.inputTextPanelState), editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, isScheduledMessages: self.isScheduledMessages) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: f(self.inputTextPanelState), editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, isScheduledMessages: self.isScheduledMessages) } func updatedEditMessageState(_ editMessageState: ChatEditInterfaceMessageState?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, isScheduledMessages: self.isScheduledMessages) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, isScheduledMessages: self.isScheduledMessages) } func updatedRecordedMediaPreview(_ recordedMediaPreview: ChatRecordedMediaPreview?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, isScheduledMessages: self.isScheduledMessages) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, isScheduledMessages: self.isScheduledMessages) } func updatedInputMode(_ f: (ChatInputMode) -> ChatInputMode) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: f(self.inputMode), titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, isScheduledMessages: self.isScheduledMessages) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: f(self.inputMode), titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, isScheduledMessages: self.isScheduledMessages) } func updatedTitlePanelContext(_ f: ([ChatTitlePanelContext]) -> [ChatTitlePanelContext]) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: f(self.titlePanelContexts), keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, isScheduledMessages: self.isScheduledMessages) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: f(self.titlePanelContexts), keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, isScheduledMessages: self.isScheduledMessages) } func updatedKeyboardButtonsMessage(_ message: Message?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: message, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, isScheduledMessages: self.isScheduledMessages) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: message, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, isScheduledMessages: self.isScheduledMessages) } func updatedPinnedMessageId(_ pinnedMessageId: MessageId?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, isScheduledMessages: self.isScheduledMessages) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, isScheduledMessages: self.isScheduledMessages) } func updatedPinnedMessage(_ pinnedMessage: Message?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, isScheduledMessages: self.isScheduledMessages) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, isScheduledMessages: self.isScheduledMessages) } func updatedPeerIsBlocked(_ peerIsBlocked: Bool) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, isScheduledMessages: self.isScheduledMessages) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, isScheduledMessages: self.isScheduledMessages) } func updatedPeerIsMuted(_ peerIsMuted: Bool) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, isScheduledMessages: self.isScheduledMessages) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, isScheduledMessages: self.isScheduledMessages) } func updatedPeerDiscussionId(_ peerDiscussionId: PeerId?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, isScheduledMessages: self.isScheduledMessages) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, isScheduledMessages: self.isScheduledMessages) } func updatedPeerGeoLocation(_ peerGeoLocation: PeerGeoLocation?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, isScheduledMessages: self.isScheduledMessages) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, isScheduledMessages: self.isScheduledMessages) } func updatedCallsAvailable(_ callsAvailable: Bool) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, isScheduledMessages: self.isScheduledMessages) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, isScheduledMessages: self.isScheduledMessages) } func updatedCallsPrivate(_ callsPrivate: Bool) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, isScheduledMessages: self.isScheduledMessages) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, isScheduledMessages: self.isScheduledMessages) } func updatedSlowmodeState(_ slowmodeState: ChatSlowmodeState?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, isScheduledMessages: self.isScheduledMessages) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, isScheduledMessages: self.isScheduledMessages) } func updatedBotStartPayload(_ botStartPayload: String?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, isScheduledMessages: self.isScheduledMessages) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, isScheduledMessages: self.isScheduledMessages) } func updatedChatHistoryState(_ chatHistoryState: ChatHistoryNodeHistoryState?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, isScheduledMessages: self.isScheduledMessages) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, isScheduledMessages: self.isScheduledMessages) } func updatedUrlPreview(_ urlPreview: (String, TelegramMediaWebpage)?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, isScheduledMessages: self.isScheduledMessages) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, isScheduledMessages: self.isScheduledMessages) } func updatedEditingUrlPreview(_ editingUrlPreview: (String, TelegramMediaWebpage)?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, isScheduledMessages: self.isScheduledMessages) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, isScheduledMessages: self.isScheduledMessages) } func updatedSearch(_ search: ChatSearchData?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, isScheduledMessages: self.isScheduledMessages) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, isScheduledMessages: self.isScheduledMessages) } func updatedSearchQuerySuggestionResult(_ f: (ChatPresentationInputQueryResult?) -> ChatPresentationInputQueryResult?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: f(self.searchQuerySuggestionResult), chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, isScheduledMessages: self.isScheduledMessages) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: f(self.searchQuerySuggestionResult), chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, isScheduledMessages: self.isScheduledMessages) } func updatedMode(_ mode: ChatControllerPresentationMode) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: mode, hasScheduledMessages: self.hasScheduledMessages, isScheduledMessages: self.isScheduledMessages) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: mode, hasScheduledMessages: self.hasScheduledMessages, isScheduledMessages: self.isScheduledMessages) } func updatedTheme(_ theme: PresentationTheme) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, isScheduledMessages: self.isScheduledMessages) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, isScheduledMessages: self.isScheduledMessages) } func updatedStrings(_ strings: PresentationStrings) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, isScheduledMessages: self.isScheduledMessages) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, isScheduledMessages: self.isScheduledMessages) } func updatedDateTimeFormat(_ dateTimeFormat: PresentationDateTimeFormat) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, isScheduledMessages: self.isScheduledMessages) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, isScheduledMessages: self.isScheduledMessages) } func updatedChatWallpaper(_ chatWallpaper: TelegramWallpaper) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, isScheduledMessages: self.isScheduledMessages) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, isScheduledMessages: self.isScheduledMessages) + } + + func updatedBubbleCorners(_ bubbleCorners: PresentationChatBubbleCorners) -> ChatPresentationInterfaceState { + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, isScheduledMessages: self.isScheduledMessages) } func updatedHasScheduledMessages(_ hasScheduledMessages: Bool) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: hasScheduledMessages, isScheduledMessages: self.isScheduledMessages) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: hasScheduledMessages, isScheduledMessages: self.isScheduledMessages) } } diff --git a/submodules/TelegramUI/TelegramUI/ChatRecentActionsControllerNode.swift b/submodules/TelegramUI/TelegramUI/ChatRecentActionsControllerNode.swift index 0935b94b95..271975abbe 100644 --- a/submodules/TelegramUI/TelegramUI/ChatRecentActionsControllerNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatRecentActionsControllerNode.swift @@ -107,13 +107,13 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode { self.listNode = ListView() self.listNode.dynamicBounceEnabled = !self.presentationData.disableAnimations self.listNode.transform = CATransform3DMakeRotation(CGFloat(Double.pi), 0.0, 0.0, 1.0) - self.loadingNode = ChatLoadingNode(theme: self.presentationData.theme, chatWallpaper: self.presentationData.chatWallpaper) - self.emptyNode = ChatRecentActionsEmptyNode(theme: self.presentationData.theme, chatWallpaper: self.presentationData.chatWallpaper) + self.loadingNode = ChatLoadingNode(theme: self.presentationData.theme, chatWallpaper: self.presentationData.chatWallpaper, bubbleCorners: self.presentationData.chatBubbleCorners) + self.emptyNode = ChatRecentActionsEmptyNode(theme: self.presentationData.theme, chatWallpaper: self.presentationData.chatWallpaper, chatBubbleCorners: self.presentationData.chatBubbleCorners) self.emptyNode.alpha = 0.0 self.state = ChatRecentActionsControllerState(chatWallpaper: self.presentationData.chatWallpaper, theme: self.presentationData.theme, strings: self.presentationData.strings, fontSize: self.presentationData.chatFontSize) - self.chatPresentationDataPromise = Promise(ChatPresentationData(theme: ChatPresentationThemeData(theme: self.presentationData.theme, wallpaper: self.presentationData.chatWallpaper), fontSize: self.presentationData.chatFontSize, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameDisplayOrder: self.presentationData.nameDisplayOrder, disableAnimations: self.presentationData.disableAnimations, largeEmoji: self.presentationData.largeEmoji)) + self.chatPresentationDataPromise = Promise(ChatPresentationData(theme: ChatPresentationThemeData(theme: self.presentationData.theme, wallpaper: self.presentationData.chatWallpaper), fontSize: self.presentationData.chatFontSize, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameDisplayOrder: self.presentationData.nameDisplayOrder, disableAnimations: self.presentationData.disableAnimations, largeEmoji: self.presentationData.largeEmoji, chatBubbleCorners: self.presentationData.chatBubbleCorners)) self.eventLogContext = ChannelAdminEventLogContext(postbox: self.context.account.postbox, network: self.context.account.network, peerId: self.peer.id) @@ -403,7 +403,8 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode { }, requestRedeliveryOfFailedMessages: { _ in }, addContact: { _ in }, rateCall: { _, _ in - }, requestSelectMessagePollOption: { _, _ in + }, requestSelectMessagePollOptions: { _, _ in + }, requestOpenMessagePollResults: { _, _ in }, openAppStorePage: { [weak self] in if let strongSelf = self { strongSelf.context.sharedContext.applicationBindings.openAppStorePage() @@ -418,6 +419,8 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode { }, openMessageReactions: { _ in }, displaySwipeToReplyHint: { }, dismissReplyMarkupMessage: { _ in + }, openMessagePollResults: { _, _ in + }, openPollCreation: { _ in }, requestMessageUpdate: { _ in }, cancelInteractiveKeyboardGestures: { }, automaticMediaDownloadSettings: self.automaticMediaDownloadSettings, @@ -493,7 +496,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode { |> deliverOnMainQueue).start(next: { [weak self] presentationData in if let strongSelf = self { strongSelf.presentationData = presentationData - strongSelf.chatPresentationDataPromise.set(.single(ChatPresentationData(theme: ChatPresentationThemeData(theme: presentationData.theme, wallpaper: presentationData.chatWallpaper), fontSize: presentationData.chatFontSize, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, disableAnimations: presentationData.disableAnimations, largeEmoji: presentationData.largeEmoji))) + strongSelf.chatPresentationDataPromise.set(.single(ChatPresentationData(theme: ChatPresentationThemeData(theme: presentationData.theme, wallpaper: presentationData.chatWallpaper), fontSize: presentationData.chatFontSize, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, disableAnimations: presentationData.disableAnimations, largeEmoji: presentationData.largeEmoji, chatBubbleCorners: presentationData.chatBubbleCorners))) strongSelf.updateThemeAndStrings(theme: presentationData.theme, strings: presentationData.strings) } diff --git a/submodules/TelegramUI/TelegramUI/ChatRecentActionsEmptyNode.swift b/submodules/TelegramUI/TelegramUI/ChatRecentActionsEmptyNode.swift index 3d45cd250d..8b4dfd82d5 100644 --- a/submodules/TelegramUI/TelegramUI/ChatRecentActionsEmptyNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatRecentActionsEmptyNode.swift @@ -22,7 +22,7 @@ final class ChatRecentActionsEmptyNode: ASDisplayNode { private var title: String = "" private var text: String = "" - init(theme: PresentationTheme, chatWallpaper: TelegramWallpaper) { + init(theme: PresentationTheme, chatWallpaper: TelegramWallpaper, chatBubbleCorners: PresentationChatBubbleCorners) { self.theme = theme self.chatWallpaper = chatWallpaper @@ -37,7 +37,7 @@ final class ChatRecentActionsEmptyNode: ASDisplayNode { super.init() - let graphics = PresentationResourcesChat.additionalGraphics(theme, wallpaper: chatWallpaper) + let graphics = PresentationResourcesChat.additionalGraphics(theme, wallpaper: chatWallpaper, bubbleCorners: chatBubbleCorners) self.backgroundNode.image = graphics.chatEmptyItemBackgroundImage self.addSubnode(self.backgroundNode) diff --git a/submodules/TelegramUI/TelegramUI/ChatSearchResultsContollerNode.swift b/submodules/TelegramUI/TelegramUI/ChatSearchResultsContollerNode.swift index 8bb2cf5207..eaf72dfcfe 100644 --- a/submodules/TelegramUI/TelegramUI/ChatSearchResultsContollerNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatSearchResultsContollerNode.swift @@ -168,6 +168,7 @@ class ChatSearchResultsControllerNode: ViewControllerTracingNode, UIScrollViewDe let interaction = ChatListNodeInteraction(activateSearch: { }, peerSelected: { _ in + }, disabledPeerSelected: { _ in }, togglePeerSelected: { _ in }, messageSelected: { [weak self] peer, message, _ in if let strongSelf = self { diff --git a/submodules/TelegramUI/TelegramUI/ChatSendMessageActionSheetControllerNode.swift b/submodules/TelegramUI/TelegramUI/ChatSendMessageActionSheetControllerNode.swift index b30f9e2e02..9c845b4699 100644 --- a/submodules/TelegramUI/TelegramUI/ChatSendMessageActionSheetControllerNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatSendMessageActionSheetControllerNode.swift @@ -272,7 +272,10 @@ final class ChatSendMessageActionSheetControllerNode: ViewControllerTracingNode, self.messageBackgroundNode.contentMode = .scaleToFill let outgoing: PresentationThemeBubbleColorComponents = self.presentationData.chatWallpaper.isEmpty ? self.presentationData.theme.chat.message.outgoing.bubble.withoutWallpaper : self.presentationData.theme.chat.message.outgoing.bubble.withWallpaper - self.messageBackgroundNode.image = messageBubbleImage(incoming: false, fillColor: outgoing.gradientFill, strokeColor: outgoing.fill == outgoing.gradientFill ? outgoing.stroke : .clear, neighbors: .none, theme: self.presentationData.theme.chat, wallpaper: self.presentationData.chatWallpaper, knockout: false) + + let maxCornerRadius = self.presentationData.chatBubbleCorners.mainRadius + let minCornerRadius = self.presentationData.chatBubbleCorners.auxiliaryRadius + self.messageBackgroundNode.image = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: maxCornerRadius, incoming: false, fillColor: outgoing.gradientFill, strokeColor: outgoing.fill == outgoing.gradientFill ? outgoing.stroke : .clear, neighbors: .none, theme: self.presentationData.theme.chat, wallpaper: self.presentationData.chatWallpaper, knockout: false) self.view.addSubview(self.effectView) self.addSubnode(self.dimNode) @@ -376,7 +379,9 @@ final class ChatSendMessageActionSheetControllerNode: ViewControllerTracingNode, } let outgoing: PresentationThemeBubbleColorComponents = self.presentationData.chatWallpaper.isEmpty ? self.presentationData.theme.chat.message.outgoing.bubble.withoutWallpaper : self.presentationData.theme.chat.message.outgoing.bubble.withWallpaper - self.messageBackgroundNode.image = messageBubbleImage(incoming: false, fillColor: outgoing.gradientFill, strokeColor: outgoing.fill == outgoing.gradientFill ? outgoing.stroke : .clear, neighbors: .none, theme: self.presentationData.theme.chat, wallpaper: self.presentationData.chatWallpaper, knockout: false) + let maxCornerRadius = self.presentationData.chatBubbleCorners.mainRadius + let minCornerRadius = self.presentationData.chatBubbleCorners.auxiliaryRadius + self.messageBackgroundNode.image = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: maxCornerRadius, incoming: false, fillColor: outgoing.gradientFill, strokeColor: outgoing.fill == outgoing.gradientFill ? outgoing.stroke : .clear, neighbors: .none, theme: self.presentationData.theme.chat, wallpaper: self.presentationData.chatWallpaper, knockout: false) for node in self.contentNodes { node.updateTheme(presentationData.theme) diff --git a/submodules/TelegramUI/TelegramUI/ComposeController.swift b/submodules/TelegramUI/TelegramUI/ComposeController.swift index 3b90b76cc6..bf8741570a 100644 --- a/submodules/TelegramUI/TelegramUI/ComposeController.swift +++ b/submodules/TelegramUI/TelegramUI/ComposeController.swift @@ -241,9 +241,6 @@ public class ComposeController: ViewController { private func activateSearch() { if self.displayNavigationBar { - if let scrollToTop = self.scrollToTop { - scrollToTop() - } if let searchContentNode = self.searchContentNode { self.contactsNode.activateSearch(placeholderNode: searchContentNode.placeholderNode) } diff --git a/submodules/TelegramUI/TelegramUI/ConfettiView.swift b/submodules/TelegramUI/TelegramUI/ConfettiView.swift new file mode 100644 index 0000000000..c17a9f32a4 --- /dev/null +++ b/submodules/TelegramUI/TelegramUI/ConfettiView.swift @@ -0,0 +1,271 @@ +import Foundation +import UIKit +import Display + +private struct Vector2 { + var x: Float + var y: Float +} + +private final class NullActionClass: NSObject, CAAction { + @objc func run(forKey event: String, object anObject: Any, arguments dict: [AnyHashable : Any]?) { + } +} + +private let nullAction = NullActionClass() + +private final class ParticleLayer: CALayer { + let mass: Float + var velocity: Vector2 + var angularVelocity: Float + var rotationAngle: Float = 0.0 + var localTime: Float = 0.0 + var type: Int + + init(image: CGImage, size: CGSize, position: CGPoint, mass: Float, velocity: Vector2, angularVelocity: Float, type: Int) { + self.mass = mass + self.velocity = velocity + self.angularVelocity = angularVelocity + self.type = type + + super.init() + + self.contents = image + self.bounds = CGRect(origin: CGPoint(), size: size) + self.position = position + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func action(forKey event: String) -> CAAction? { + return nullAction + } +} + +final class ConfettiView: UIView { + private var particles: [ParticleLayer] = [] + private var displayLink: ConstantDisplayLinkAnimator? + + private var localTime: Float = 0.0 + + override init(frame: CGRect) { + super.init(frame: frame) + + self.isUserInteractionEnabled = false + + let colors: [UIColor] = ([ + 0x56CE6B, + 0xCD89D0, + 0x1E9AFF, + 0xFF8724 + ] as [UInt32]).map(UIColor.init(rgb:)) + let imageSize = CGSize(width: 8.0, height: 8.0) + var images: [(CGImage, CGSize)] = [] + for imageType in 0 ..< 2 { + for color in colors { + if imageType == 0 { + images.append((generateFilledCircleImage(diameter: imageSize.width, color: color)!.cgImage!, imageSize)) + } else { + let spriteSize = CGSize(width: 2.0, height: 6.0) + images.append((generateImage(spriteSize, opaque: false, rotatedContext: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + context.setFillColor(color.cgColor) + context.fillEllipse(in: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: size.width))) + context.fillEllipse(in: CGRect(origin: CGPoint(x: 0.0, y: size.height - size.width), size: CGSize(width: size.width, height: size.width))) + context.fill(CGRect(origin: CGPoint(x: 0.0, y: size.width / 2.0), size: CGSize(width: size.width, height: size.height - size.width))) + })!.cgImage!, spriteSize)) + } + } + } + let imageCount = images.count + + let originXRange = 0 ..< Int(frame.width) + let originYRange = Int(-frame.height) ..< Int(0) + let topMassRange: Range = 40.0 ..< 50.0 + let velocityYRange = Float(3.0) ..< Float(5.0) + let angularVelocityRange = Float(1.0) ..< Float(6.0) + let sizeVariation = Float(0.8) ..< Float(1.6) + let topDelayRange = Float(0.0) ..< Float(0.0) + + for i in 0 ..< 70 { + let (image, size) = images[i % imageCount] + let sizeScale = CGFloat(Float.random(in: sizeVariation)) + let particle = ParticleLayer(image: image, size: CGSize(width: size.width * sizeScale, height: size.height * sizeScale), position: CGPoint(x: CGFloat(Int.random(in: originXRange)), y: CGFloat(Int.random(in: originYRange))), mass: Float.random(in: topMassRange), velocity: Vector2(x: 0.0, y: Float.random(in: velocityYRange)), angularVelocity: Float.random(in: angularVelocityRange), type: 0) + self.particles.append(particle) + self.layer.addSublayer(particle) + } + + let sideMassRange: Range = 110.0 ..< 120.0 + let sideOriginYBase: Float = Float(frame.size.height * 9.0 / 10.0) + let sideOriginYVariation: Float = Float(frame.size.height / 12.0) + let sideOriginYRange = Float(sideOriginYBase - sideOriginYVariation) ..< Float(sideOriginYBase + sideOriginYVariation) + let sideOriginXRange = Float(0.0) ..< Float(100.0) + let sideOriginVelocityValueRange = Float(1.1) ..< Float(1.3) + let sideOriginVelocityValueScaling: Float = 2400.0 * Float(frame.height) / 896.0 + let sideOriginVelocityBase: Float = Float.pi / 2.0 + atanf(Float(CGFloat(sideOriginYBase) / (frame.size.width * 0.8))) + let sideOriginVelocityVariation: Float = 0.09 + let sideOriginVelocityAngleRange = Float(sideOriginVelocityBase - sideOriginVelocityVariation) ..< Float(sideOriginVelocityBase + sideOriginVelocityVariation) + let originAngleRange = Float(0.0) ..< (Float.pi * 2.0) + let originAmplitudeDiameter: CGFloat = 230.0 + let originAmplitudeRange = Float(0.0) ..< Float(originAmplitudeDiameter / 2.0) + + let sideTypes: [Int] = [0, 1, 2] + + for sideIndex in 0 ..< 2 { + let sideSign: Float = sideIndex == 0 ? 1.0 : -1.0 + let baseOriginX: CGFloat = sideIndex == 0 ? -originAmplitudeDiameter / 2.0 : (frame.width + originAmplitudeDiameter / 2.0) + + for i in 0 ..< 40 { + let originAngle = Float.random(in: originAngleRange) + let originAmplitude = Float.random(in: originAmplitudeRange) + let originX = baseOriginX + CGFloat(cosf(originAngle) * originAmplitude) + let originY = CGFloat(sideOriginYBase + sinf(originAngle) * originAmplitude) + + let velocityValue = Float.random(in: sideOriginVelocityValueRange) * sideOriginVelocityValueScaling + let velocityAngle = Float.random(in: sideOriginVelocityAngleRange) + let velocityX = sideSign * velocityValue * sinf(velocityAngle) + let velocityY = velocityValue * cosf(velocityAngle) + let (image, size) = images[i % imageCount] + let sizeScale = CGFloat(Float.random(in: sizeVariation)) + let particle = ParticleLayer(image: image, size: CGSize(width: size.width * sizeScale, height: size.height * sizeScale), position: CGPoint(x: originX, y: originY), mass: Float.random(in: sideMassRange), velocity: Vector2(x: velocityX, y: velocityY), angularVelocity: Float.random(in: angularVelocityRange), type: sideTypes[i % 3]) + self.particles.append(particle) + self.layer.addSublayer(particle) + } + } + + self.displayLink = ConstantDisplayLinkAnimator(update: { [weak self] in + self?.step() + }) + + self.displayLink?.isPaused = false + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + private var slowdownStartTimestamps: [Float?] = [nil, nil, nil] + + private func step() { + self.slowdownStartTimestamps[0] = 0.33 + + var haveParticlesAboveGround = false + let minPositionY: CGFloat = 0.0 + let maxPositionY = self.bounds.height + 30.0 + let minDampingX: CGFloat = 40.0 + let maxDampingX: CGFloat = self.bounds.width - 40.0 + let centerX: CGFloat = self.bounds.width / 2.0 + let dt: Float = 1.0 * 1.0 / 60.0 + + let typeDelays: [Float] = [0.0, 0.01, 0.08] + var dtAndDamping: [(Float, Float)] = [] + + for i in 0 ..< 3 { + let typeDelay = typeDelays[i] + let currentTime = self.localTime - typeDelay + if currentTime < 0.0 { + dtAndDamping.append((0.0, 1.0)) + } else if let slowdownStart = self.slowdownStartTimestamps[i] { + let slowdownDt: Float + let slowdownDuration: Float = 0.5 + let damping: Float + if currentTime >= slowdownStart && currentTime <= slowdownStart + slowdownDuration { + let slowdownTimestamp: Float = currentTime - slowdownStart + + let slowdownRampInDuration: Float = 0.05 + let slowdownRampOutDuration: Float = 0.2 + let rawSlowdownT: Float + if slowdownTimestamp < slowdownRampInDuration { + rawSlowdownT = slowdownTimestamp / slowdownRampInDuration + } else if slowdownTimestamp >= slowdownDuration - slowdownRampOutDuration { + let reverseTransition = (slowdownTimestamp - (slowdownDuration - slowdownRampOutDuration)) / slowdownRampOutDuration + rawSlowdownT = 1.0 - reverseTransition + } else { + rawSlowdownT = 1.0 + } + + let slowdownTransition = rawSlowdownT * rawSlowdownT + + let slowdownFactor: Float = 0.8 * slowdownTransition + 1.0 * (1.0 - slowdownTransition) + slowdownDt = dt * slowdownFactor + let dampingFactor: Float = 0.937 * slowdownTransition + 1.0 * (1.0 - slowdownTransition) + + damping = dampingFactor + } else { + slowdownDt = dt + damping = 1.0 + } + if i == 1 { + //print("type 1 dt = \(slowdownDt), slowdownStart = \(slowdownStart), currentTime = \(currentTime)") + } + dtAndDamping.append((slowdownDt, damping)) + } else { + dtAndDamping.append((dt, 1.0)) + } + } + self.localTime += dt + + let g: Vector2 = Vector2(x: 0.0, y: 9.8) + CATransaction.begin() + CATransaction.setDisableActions(true) + var turbulenceVariation: [Float] = [] + for _ in 0 ..< 20 { + turbulenceVariation.append(Float.random(in: -16.0 ..< 16.0) * 60.0) + } + let turbulenceVariationCount = turbulenceVariation.count + var index = 0 + + var typesWithPositiveVelocity: [Bool] = [false, false, false] + + for particle in self.particles { + let (localDt, damping_) = dtAndDamping[particle.type] + if localDt.isZero { + continue + } + let damping: Float = 0.93 + + particle.localTime += localDt + + var position = particle.position + + position.x += CGFloat(particle.velocity.x * localDt) + position.y += CGFloat(particle.velocity.y * localDt) + particle.position = position + + particle.rotationAngle += particle.angularVelocity * localDt + particle.transform = CATransform3DMakeRotation(CGFloat(particle.rotationAngle), 0.0, 0.0, 1.0) + + let acceleration = g + + var velocity = particle.velocity + velocity.x += acceleration.x * particle.mass * localDt + velocity.y += acceleration.y * particle.mass * localDt + if velocity.y < 0.0 { + velocity.x *= damping + velocity.y *= damping + } else { + velocity.x += turbulenceVariation[index % turbulenceVariationCount] * localDt + typesWithPositiveVelocity[particle.type] = true + } + particle.velocity = velocity + + index += 1 + + if position.y < maxPositionY { + haveParticlesAboveGround = true + } + } + for i in 0 ..< 3 { + if typesWithPositiveVelocity[i] && self.slowdownStartTimestamps[i] == nil { + self.slowdownStartTimestamps[i] = max(0.0, self.localTime - typeDelays[i]) + } + } + CATransaction.commit() + if !haveParticlesAboveGround { + self.displayLink?.isPaused = true + self.removeFromSuperview() + } + } +} diff --git a/submodules/TelegramUI/TelegramUI/HashtagChatInputContextPanelNode.swift b/submodules/TelegramUI/TelegramUI/HashtagChatInputContextPanelNode.swift index 4105db2245..13a84953cb 100644 --- a/submodules/TelegramUI/TelegramUI/HashtagChatInputContextPanelNode.swift +++ b/submodules/TelegramUI/TelegramUI/HashtagChatInputContextPanelNode.swift @@ -10,6 +10,7 @@ import TelegramUIPreferences import MergeLists import AccountContext import AccountContext +import ItemListUI private struct HashtagChatInputContextPanelEntryStableId: Hashable { let text: String @@ -19,25 +20,26 @@ private struct HashtagChatInputContextPanelEntry: Comparable, Identifiable { let index: Int let theme: PresentationTheme let text: String + let revealed: Bool var stableId: HashtagChatInputContextPanelEntryStableId { return HashtagChatInputContextPanelEntryStableId(text: self.text) } func withUpdatedTheme(_ theme: PresentationTheme) -> HashtagChatInputContextPanelEntry { - return HashtagChatInputContextPanelEntry(index: self.index, theme: theme, text: self.text) + return HashtagChatInputContextPanelEntry(index: self.index, theme: theme, text: self.text, revealed: self.revealed) } static func ==(lhs: HashtagChatInputContextPanelEntry, rhs: HashtagChatInputContextPanelEntry) -> Bool { - return lhs.index == rhs.index && lhs.text == rhs.text && lhs.theme === rhs.theme + return lhs.index == rhs.index && lhs.text == rhs.text && lhs.theme === rhs.theme && lhs.revealed == rhs.revealed } static func <(lhs: HashtagChatInputContextPanelEntry, rhs: HashtagChatInputContextPanelEntry) -> Bool { return lhs.index < rhs.index } - func item(account: Account, fontSize: PresentationFontSize, hashtagSelected: @escaping (String) -> Void) -> ListViewItem { - return HashtagChatInputPanelItem(theme: self.theme, fontSize: fontSize, text: self.text, hashtagSelected: hashtagSelected) + func item(account: Account, presentationData: PresentationData, setHashtagRevealed: @escaping (String?) -> Void, hashtagSelected: @escaping (String) -> Void, removeRequested: @escaping (String) -> Void) -> ListViewItem { + return HashtagChatInputPanelItem(presentationData: ItemListPresentationData(presentationData), text: self.text, revealed: self.revealed, setHashtagRevealed: setHashtagRevealed, hashtagSelected: hashtagSelected, removeRequested: removeRequested) } } @@ -47,12 +49,12 @@ private struct HashtagChatInputContextPanelTransition { let updates: [ListViewUpdateItem] } -private func preparedTransition(from fromEntries: [HashtagChatInputContextPanelEntry], to toEntries: [HashtagChatInputContextPanelEntry], account: Account, fontSize: PresentationFontSize, hashtagSelected: @escaping (String) -> Void) -> HashtagChatInputContextPanelTransition { +private func preparedTransition(from fromEntries: [HashtagChatInputContextPanelEntry], to toEntries: [HashtagChatInputContextPanelEntry], account: Account, presentationData: PresentationData, setHashtagRevealed: @escaping (String?) -> Void, hashtagSelected: @escaping (String) -> Void, removeRequested: @escaping (String) -> Void) -> HashtagChatInputContextPanelTransition { let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries) let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) } - let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, fontSize: fontSize, hashtagSelected: hashtagSelected), directionHint: nil) } - let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, fontSize: fontSize, hashtagSelected: hashtagSelected), directionHint: nil) } + let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, presentationData: presentationData, setHashtagRevealed: setHashtagRevealed, hashtagSelected: hashtagSelected, removeRequested: removeRequested), directionHint: nil) } + let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, presentationData: presentationData, setHashtagRevealed: setHashtagRevealed, hashtagSelected: hashtagSelected, removeRequested: removeRequested), directionHint: nil) } return HashtagChatInputContextPanelTransition(deletions: deletions, insertions: insertions, updates: updates) } @@ -61,6 +63,9 @@ final class HashtagChatInputContextPanelNode: ChatInputContextPanelNode { private let listView: ListView private var currentEntries: [HashtagChatInputContextPanelEntry]? + private var currentResults: [String] = [] + private var revealedHashtag: String? + private var enqueuedTransitions: [(HashtagChatInputContextPanelTransition, Bool)] = [] private var validLayout: (CGSize, CGFloat, CGFloat, CGFloat)? @@ -81,11 +86,13 @@ final class HashtagChatInputContextPanelNode: ChatInputContextPanelNode { } func updateResults(_ results: [String]) { + self.currentResults = results + var entries: [HashtagChatInputContextPanelEntry] = [] var index = 0 var stableIds = Set() for text in results { - let entry = HashtagChatInputContextPanelEntry(index: index, theme: self.theme, text: text) + let entry = HashtagChatInputContextPanelEntry(index: index, theme: self.theme, text: text, revealed: text == self.revealedHashtag) if stableIds.contains(entry.stableId) { continue } @@ -98,7 +105,13 @@ final class HashtagChatInputContextPanelNode: ChatInputContextPanelNode { private func prepareTransition(from: [HashtagChatInputContextPanelEntry]? , to: [HashtagChatInputContextPanelEntry]) { let firstTime = from == nil - let transition = preparedTransition(from: from ?? [], to: to, account: self.context.account, fontSize: self.fontSize, hashtagSelected: { [weak self] text in + let presentationData = self.context.sharedContext.currentPresentationData.with { $0 } + let transition = preparedTransition(from: from ?? [], to: to, account: self.context.account, presentationData: presentationData, setHashtagRevealed: { [weak self] text in + if let strongSelf = self { + strongSelf.revealedHashtag = text + strongSelf.updateResults(strongSelf.currentResults) + } + }, hashtagSelected: { [weak self] text in if let strongSelf = self, let interfaceInteraction = strongSelf.interfaceInteraction { interfaceInteraction.updateTextInputStateAndMode { textInputState, inputMode in var hashtagQueryRange: NSRange? @@ -123,6 +136,11 @@ final class HashtagChatInputContextPanelNode: ChatInputContextPanelNode { return (textInputState, inputMode) } } + }, removeRequested: { [weak self] text in + if let strongSelf = self { + let _ = removeRecentlyUsedHashtag(postbox: strongSelf.context.account.postbox, string: text).start() + strongSelf.revealedHashtag = nil + } }) self.currentEntries = to self.enqueueTransition(transition, firstTime: firstTime) diff --git a/submodules/TelegramUI/TelegramUI/HashtagChatInputPanelItem.swift b/submodules/TelegramUI/TelegramUI/HashtagChatInputPanelItem.swift index 57247e8a23..1f8a32d4a7 100644 --- a/submodules/TelegramUI/TelegramUI/HashtagChatInputPanelItem.swift +++ b/submodules/TelegramUI/TelegramUI/HashtagChatInputPanelItem.swift @@ -8,20 +8,25 @@ import SwiftSignalKit import Postbox import TelegramPresentationData import TelegramUIPreferences +import ItemListUI final class HashtagChatInputPanelItem: ListViewItem { - fileprivate let theme: PresentationTheme - fileprivate let fontSize: PresentationFontSize + fileprivate let presentationData: ItemListPresentationData fileprivate let text: String + fileprivate let revealed: Bool + fileprivate let setHashtagRevealed: (String?) -> Void private let hashtagSelected: (String) -> Void + fileprivate let removeRequested: (String) -> Void let selectable: Bool = true - public init(theme: PresentationTheme, fontSize: PresentationFontSize, text: String, hashtagSelected: @escaping (String) -> Void) { - self.theme = theme - self.fontSize = fontSize + public init(presentationData: ItemListPresentationData, text: String, revealed: Bool, setHashtagRevealed: @escaping (String?) -> Void, hashtagSelected: @escaping (String) -> Void, removeRequested: @escaping (String) -> Void) { + self.presentationData = presentationData self.text = text + self.revealed = revealed + self.setHashtagRevealed = setHashtagRevealed self.hashtagSelected = hashtagSelected + self.removeRequested = removeRequested } public func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal?, (ListViewItemApply) -> Void)) -> Void) { @@ -72,7 +77,11 @@ final class HashtagChatInputPanelItem: ListViewItem { } func selected(listView: ListView) { - self.hashtagSelected(self.text) + if self.revealed { + self.setHashtagRevealed(nil) + } else { + self.hashtagSelected(self.text) + } } } @@ -82,6 +91,17 @@ final class HashtagChatInputPanelItemNode: ListViewItemNode { private let topSeparatorNode: ASDisplayNode private let separatorNode: ASDisplayNode private let highlightedBackgroundNode: ASDisplayNode + + private var revealNode: ItemListRevealOptionsNode? + private var revealOptions: [ItemListRevealOption] = [] + private var initialRevealOffset: CGFloat = 0.0 + public private(set) var revealOffset: CGFloat = 0.0 + private var recognizer: ItemListRevealOptionsGestureRecognizer? + private var hapticFeedback: HapticFeedback? + + private var item: HashtagChatInputPanelItem? + + private var validLayout: (CGSize, CGFloat, CGFloat)? init() { self.textNode = TextNode() @@ -102,6 +122,15 @@ final class HashtagChatInputPanelItemNode: ListViewItemNode { self.addSubnode(self.textNode) } + override func didLoad() { + super.didLoad() + + let recognizer = ItemListRevealOptionsGestureRecognizer(target: self, action: #selector(self.revealGesture(_:))) + self.recognizer = recognizer + recognizer.allowAnyDirection = false + self.view.addGestureRecognizer(recognizer) + } + override public func layoutForParams(_ params: ListViewItemLayoutParams, item: ListViewItem, previousItem: ListViewItem?, nextItem: ListViewItem?) { if let item = item as? HashtagChatInputPanelItem { let doLayout = self.asyncLayout() @@ -116,26 +145,31 @@ final class HashtagChatInputPanelItemNode: ListViewItemNode { func asyncLayout() -> (_ item: HashtagChatInputPanelItem, _ params: ListViewItemLayoutParams, _ mergedTop: Bool, _ mergedBottom: Bool) -> (ListViewItemNodeLayout, (ListViewItemUpdateAnimation) -> Void) { let makeTextLayout = TextNode.asyncLayout(self.textNode) return { [weak self] item, params, mergedTop, mergedBottom in - let textFont = Font.medium(floor(item.fontSize.baseDisplaySize * 14.0 / 17.0)) + let textFont = Font.medium(floor(item.presentationData.fontSize.baseDisplaySize * 14.0 / 17.0)) let baseWidth = params.width - params.leftInset - params.rightInset let leftInset: CGFloat = 15.0 + params.leftInset let rightInset: CGFloat = 10.0 + params.rightInset - let (textLayout, textApply) = makeTextLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: "#\(item.text)", font: textFont, textColor: item.theme.list.itemPrimaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: baseWidth, height: 100.0), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) + let (textLayout, textApply) = makeTextLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: "#\(item.text)", font: textFont, textColor: item.presentationData.theme.list.itemPrimaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: baseWidth, height: 100.0), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) let nodeLayout = ListViewItemNodeLayout(contentSize: CGSize(width: params.width, height: HashtagChatInputPanelItemNode.itemHeight), insets: UIEdgeInsets()) - return (nodeLayout, { _ in + return (nodeLayout, { animation in if let strongSelf = self { - strongSelf.separatorNode.backgroundColor = item.theme.list.itemPlainSeparatorColor - strongSelf.topSeparatorNode.backgroundColor = item.theme.list.itemPlainSeparatorColor - strongSelf.backgroundColor = item.theme.list.plainBackgroundColor - strongSelf.highlightedBackgroundNode.backgroundColor = item.theme.list.itemHighlightedBackgroundColor + strongSelf.item = item + strongSelf.validLayout = (nodeLayout.contentSize, params.leftInset, params.rightInset) + + let revealOffset = strongSelf.revealOffset + + strongSelf.separatorNode.backgroundColor = item.presentationData.theme.list.itemPlainSeparatorColor + strongSelf.topSeparatorNode.backgroundColor = item.presentationData.theme.list.itemPlainSeparatorColor + strongSelf.backgroundColor = item.presentationData.theme.list.plainBackgroundColor + strongSelf.highlightedBackgroundNode.backgroundColor = item.presentationData.theme.list.itemHighlightedBackgroundColor let _ = textApply() - strongSelf.textNode.frame = CGRect(origin: CGPoint(x: leftInset, y: floor((nodeLayout.contentSize.height - textLayout.size.height) / 2.0)), size: textLayout.size) + strongSelf.textNode.frame = CGRect(origin: CGPoint(x: revealOffset + leftInset, y: floor((nodeLayout.contentSize.height - textLayout.size.height) / 2.0)), size: textLayout.size) strongSelf.topSeparatorNode.isHidden = mergedTop strongSelf.separatorNode.isHidden = !mergedBottom @@ -144,14 +178,27 @@ final class HashtagChatInputPanelItemNode: ListViewItemNode { strongSelf.separatorNode.frame = CGRect(origin: CGPoint(x: leftInset, y: nodeLayout.contentSize.height - UIScreenPixel), size: CGSize(width: params.width - leftInset, height: UIScreenPixel)) strongSelf.highlightedBackgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: params.width, height: nodeLayout.size.height + UIScreenPixel)) + + strongSelf.setRevealOptions([ItemListRevealOption(key: 0, title: item.presentationData.strings.Common_Delete, icon: .none, color: item.presentationData.theme.list.itemDisclosureActions.destructive.fillColor, textColor: item.presentationData.theme.list.itemDisclosureActions.destructive.foregroundColor)]) + strongSelf.setRevealOptionsOpened(item.revealed, animated: animation.isAnimated) } }) } } + func updateRevealOffset(offset: CGFloat, transition: ContainedViewLayoutTransition) { + if let (size, leftInset, rightInset) = self.validLayout { + transition.updateFrameAdditive(node: self.textNode, frame: CGRect(origin: CGPoint(x: min(offset, 0.0) + 15.0 + leftInset, y: self.textNode.frame.minY), size: self.textNode.frame.size)) + } + } + override func setHighlighted(_ highlighted: Bool, at point: CGPoint, animated: Bool) { super.setHighlighted(highlighted, at: point, animated: animated) + if let revealNode = self.revealNode, self.revealOffset != 0 { + return + } + if highlighted { self.highlightedBackgroundNode.alpha = 1.0 if self.highlightedBackgroundNode.supernode == nil { @@ -174,4 +221,199 @@ final class HashtagChatInputPanelItemNode: ListViewItemNode { } } } + + func setRevealOptions(_ options: [ItemListRevealOption]) { + if self.revealOptions == options { + return + } + let previousOptions = self.revealOptions + let wasEmpty = self.revealOptions.isEmpty + self.revealOptions = options + let isEmpty = options.isEmpty + if options.isEmpty { + if let _ = self.revealNode { + self.recognizer?.becomeCancelled() + self.updateRevealOffsetInternal(offset: 0.0, transition: .animated(duration: 0.3, curve: .spring)) + } + } + if wasEmpty != isEmpty { + self.recognizer?.isEnabled = !isEmpty + } + } + + private func setRevealOptionsOpened(_ value: Bool, animated: Bool) { + if value != !self.revealOffset.isZero { + if !self.revealOffset.isZero { + self.recognizer?.becomeCancelled() + } + let transition: ContainedViewLayoutTransition + if animated { + transition = .animated(duration: 0.3, curve: .spring) + } else { + transition = .immediate + } + if value { + if self.revealNode == nil { + self.setupAndAddRevealNode() + if let revealNode = self.revealNode, revealNode.isNodeLoaded, let _ = self.validLayout { + revealNode.layout() + let revealSize = revealNode.bounds.size + self.updateRevealOffsetInternal(offset: -revealSize.width, transition: transition) + } + } + } else if !self.revealOffset.isZero { + self.updateRevealOffsetInternal(offset: 0.0, transition: transition) + } + } + } + + override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { + return true + } + + func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { + if let recognizer = self.recognizer, otherGestureRecognizer == recognizer { + return true + } else { + return false + } + } + + @objc func revealGesture(_ recognizer: ItemListRevealOptionsGestureRecognizer) { + guard let (size, _, _) = self.validLayout else { + return + } + switch recognizer.state { + case .began: + if let revealNode = self.revealNode { + let revealSize = revealNode.bounds.size + let location = recognizer.location(in: self.view) + if location.x > size.width - revealSize.width { + recognizer.becomeCancelled() + } else { + self.initialRevealOffset = self.revealOffset + } + } else { + if self.revealOptions.isEmpty { + recognizer.becomeCancelled() + } + self.initialRevealOffset = self.revealOffset + } + case .changed: + var translation = recognizer.translation(in: self.view) + translation.x += self.initialRevealOffset + if self.revealNode == nil && translation.x.isLess(than: 0.0) { + self.setupAndAddRevealNode() + self.revealOptionsInteractivelyOpened() + } + self.updateRevealOffsetInternal(offset: translation.x, transition: .immediate) + if self.revealNode == nil { + self.revealOptionsInteractivelyClosed() + } + case .ended, .cancelled: + guard let recognizer = self.recognizer else { + break + } + + if let revealNode = self.revealNode { + let velocity = recognizer.velocity(in: self.view) + let revealSize = revealNode.bounds.size + var reveal = false + if abs(velocity.x) < 100.0 { + if self.initialRevealOffset.isZero && self.revealOffset < 0.0 { + reveal = true + } else if self.revealOffset < -revealSize.width { + reveal = true + } else { + reveal = false + } + } else { + if velocity.x < 0.0 { + reveal = true + } else { + reveal = false + } + } + self.updateRevealOffsetInternal(offset: reveal ? -revealSize.width : 0.0, transition: .animated(duration: 0.3, curve: .spring)) + if !reveal { + self.revealOptionsInteractivelyClosed() + } + } + default: + break + } + } + + private func revealOptionSelected(_ option: ItemListRevealOption, animated: Bool) { + guard let item = self.item else { + return + } + item.removeRequested(item.text) + } + + private func setupAndAddRevealNode() { + if !self.revealOptions.isEmpty { + let revealNode = ItemListRevealOptionsNode(optionSelected: { [weak self] option in + self?.revealOptionSelected(option, animated: false) + }, tapticAction: { [weak self] in + self?.hapticImpact() + }) + revealNode.setOptions(self.revealOptions, isLeft: false) + self.revealNode = revealNode + + if let (size, _, rightInset) = self.validLayout { + var revealSize = revealNode.measure(CGSize(width: CGFloat.greatestFiniteMagnitude, height: size.height)) + revealSize.width += rightInset + + revealNode.frame = CGRect(origin: CGPoint(x: size.width + max(self.revealOffset, -revealSize.width), y: 0.0), size: revealSize) + revealNode.updateRevealOffset(offset: 0.0, sideInset: -rightInset, transition: .immediate) + } + + self.addSubnode(revealNode) + } + } + + private func updateRevealOffsetInternal(offset: CGFloat, transition: ContainedViewLayoutTransition) { + self.revealOffset = offset + guard let (size, leftInset, rightInset) = self.validLayout else { + return + } + + if let revealNode = self.revealNode { + let revealSize = revealNode.bounds.size + + let revealFrame = CGRect(origin: CGPoint(x: size.width + max(self.revealOffset, -revealSize.width), y: 0.0), size: revealSize) + let revealNodeOffset = -max(self.revealOffset, -revealSize.width) + revealNode.updateRevealOffset(offset: revealNodeOffset, sideInset: -rightInset, transition: transition) + + if CGFloat(0.0).isLessThanOrEqualTo(offset) { + self.revealNode = nil + transition.updateFrame(node: revealNode, frame: revealFrame, completion: { [weak revealNode] _ in + revealNode?.removeFromSupernode() + }) + } else { + transition.updateFrame(node: revealNode, frame: revealFrame) + } + } + self.updateRevealOffset(offset: offset, transition: transition) + } + + func revealOptionsInteractivelyOpened() { + if let item = self.item { + item.setHashtagRevealed(item.text) + } + } + + func revealOptionsInteractivelyClosed() { + if let item = self.item { + item.setHashtagRevealed(nil) + } + } + + private func hapticImpact() { + if self.hapticFeedback == nil { + self.hapticFeedback = HapticFeedback() + } + self.hapticFeedback?.impact(.medium) + } } diff --git a/submodules/TelegramUI/TelegramUI/LegacyLiveUploadInterface.swift b/submodules/TelegramUI/TelegramUI/LegacyLiveUploadInterface.swift index 82c7b859f7..eaa93bf258 100644 --- a/submodules/TelegramUI/TelegramUI/LegacyLiveUploadInterface.swift +++ b/submodules/TelegramUI/TelegramUI/LegacyLiveUploadInterface.swift @@ -62,6 +62,7 @@ final class LegacyLiveUploadInterface: VideoConversionWatcher, TGLiveUploadInter override func fileUpdated(_ completed: Bool) -> Any! { let _ = super.fileUpdated(completed) + print("**fileUpdated \(completed)") if completed { let result = self.dataValue.modify { dataValue in if let dataValue = dataValue { diff --git a/submodules/TelegramUI/TelegramUI/MentionChatInputContextPanelNode.swift b/submodules/TelegramUI/TelegramUI/MentionChatInputContextPanelNode.swift index 6dc770a61c..284e8bd01c 100644 --- a/submodules/TelegramUI/TelegramUI/MentionChatInputContextPanelNode.swift +++ b/submodules/TelegramUI/TelegramUI/MentionChatInputContextPanelNode.swift @@ -11,25 +11,27 @@ import MergeLists import TextFormat import AccountContext import LocalizedPeerData +import ItemListUI private struct MentionChatInputContextPanelEntry: Comparable, Identifiable { let index: Int let peer: Peer + let revealed: Bool var stableId: Int64 { return self.peer.id.toInt64() } static func ==(lhs: MentionChatInputContextPanelEntry, rhs: MentionChatInputContextPanelEntry) -> Bool { - return lhs.index == rhs.index && lhs.peer.isEqual(rhs.peer) + return lhs.index == rhs.index && lhs.peer.isEqual(rhs.peer) && lhs.revealed == rhs.revealed } static func <(lhs: MentionChatInputContextPanelEntry, rhs: MentionChatInputContextPanelEntry) -> Bool { return lhs.index < rhs.index } - func item(context: AccountContext, theme: PresentationTheme, fontSize: PresentationFontSize, inverted: Bool, peerSelected: @escaping (Peer) -> Void) -> ListViewItem { - return MentionChatInputPanelItem(context: context, theme: theme, fontSize: fontSize, inverted: inverted, peer: self.peer, peerSelected: peerSelected) + func item(context: AccountContext, presentationData: PresentationData, inverted: Bool, setPeerIdRevealed: @escaping (PeerId?) -> Void, peerSelected: @escaping (Peer) -> Void, removeRequested: @escaping (PeerId) -> Void) -> ListViewItem { + return MentionChatInputPanelItem(context: context, presentationData: ItemListPresentationData(presentationData), inverted: inverted, peer: self.peer, revealed: self.revealed, setPeerIdRevealed: setPeerIdRevealed, peerSelected: peerSelected, removeRequested: removeRequested) } } @@ -39,12 +41,12 @@ private struct CommandChatInputContextPanelTransition { let updates: [ListViewUpdateItem] } -private func preparedTransition(from fromEntries: [MentionChatInputContextPanelEntry], to toEntries: [MentionChatInputContextPanelEntry], context: AccountContext, theme: PresentationTheme, fontSize: PresentationFontSize, inverted: Bool, forceUpdate: Bool, peerSelected: @escaping (Peer) -> Void) -> CommandChatInputContextPanelTransition { +private func preparedTransition(from fromEntries: [MentionChatInputContextPanelEntry], to toEntries: [MentionChatInputContextPanelEntry], context: AccountContext, presentationData: PresentationData, inverted: Bool, forceUpdate: Bool, setPeerIdRevealed: @escaping (PeerId?) -> Void, peerSelected: @escaping (Peer) -> Void, removeRequested: @escaping (PeerId) -> Void) -> CommandChatInputContextPanelTransition { let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries, allUpdated: forceUpdate) let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) } - let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, theme: theme, fontSize: fontSize, inverted: inverted, peerSelected: peerSelected), directionHint: nil) } - let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, theme: theme, fontSize: fontSize, inverted: inverted, peerSelected: peerSelected), directionHint: nil) } + let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, presentationData: presentationData, inverted: inverted, setPeerIdRevealed: setPeerIdRevealed, peerSelected: peerSelected, removeRequested: removeRequested), directionHint: nil) } + let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, presentationData: presentationData, inverted: inverted, setPeerIdRevealed: setPeerIdRevealed, peerSelected: peerSelected, removeRequested: removeRequested), directionHint: nil) } return CommandChatInputContextPanelTransition(deletions: deletions, insertions: insertions, updates: updates) } @@ -60,6 +62,9 @@ final class MentionChatInputContextPanelNode: ChatInputContextPanelNode { private let listView: ListView private var currentEntries: [MentionChatInputContextPanelEntry]? + private var currentResults: [Peer] = [] + private var revealedPeerId: PeerId? + private var enqueuedTransitions: [(CommandChatInputContextPanelTransition, Bool)] = [] private var validLayout: (CGSize, CGFloat, CGFloat, CGFloat)? @@ -86,6 +91,8 @@ final class MentionChatInputContextPanelNode: ChatInputContextPanelNode { } func updateResults(_ results: [Peer]) { + self.currentResults = results + var entries: [MentionChatInputContextPanelEntry] = [] var index = 0 var peerIdSet = Set() @@ -95,7 +102,7 @@ final class MentionChatInputContextPanelNode: ChatInputContextPanelNode { continue } peerIdSet.insert(peerId) - entries.append(MentionChatInputContextPanelEntry(index: index, peer: peer)) + entries.append(MentionChatInputContextPanelEntry(index: index, peer: peer, revealed: self.revealedPeerId == peer.id)) index += 1 } self.updateToEntries(entries: entries, forceUpdate: false) @@ -103,7 +110,13 @@ final class MentionChatInputContextPanelNode: ChatInputContextPanelNode { private func updateToEntries(entries: [MentionChatInputContextPanelEntry], forceUpdate: Bool) { let firstTime = self.currentEntries == nil - let transition = preparedTransition(from: self.currentEntries ?? [], to: entries, context: self.context, theme: self.theme, fontSize: self.fontSize, inverted: self.mode == .search, forceUpdate: forceUpdate, peerSelected: { [weak self] peer in + let presentationData = self.context.sharedContext.currentPresentationData.with { $0 } + let transition = preparedTransition(from: self.currentEntries ?? [], to: entries, context: self.context, presentationData: presentationData, inverted: self.mode == .search, forceUpdate: forceUpdate, setPeerIdRevealed: { [weak self] peerId in + if let strongSelf = self { + strongSelf.revealedPeerId = peerId + strongSelf.updateResults(strongSelf.currentResults) + } + }, peerSelected: { [weak self] peer in if let strongSelf = self, let interfaceInteraction = strongSelf.interfaceInteraction { switch strongSelf.mode { case .input: @@ -147,6 +160,14 @@ final class MentionChatInputContextPanelNode: ChatInputContextPanelNode { interfaceInteraction.beginMessageSearch(.member(peer), "") } } + }, removeRequested: { [weak self] peerId in + if let strongSelf = self { + let _ = removeRecentlyUsedInlineBot(account: strongSelf.context.account, peerId: peerId).start() + + strongSelf.revealedPeerId = nil + strongSelf.currentResults = strongSelf.currentResults.filter { $0.id != peerId } + strongSelf.updateResults(strongSelf.currentResults) + } }) self.currentEntries = entries self.enqueueTransition(transition, firstTime: firstTime) diff --git a/submodules/TelegramUI/TelegramUI/MentionChatInputPanelItem.swift b/submodules/TelegramUI/TelegramUI/MentionChatInputPanelItem.swift index a97d6708fe..4a6b0b748b 100644 --- a/submodules/TelegramUI/TelegramUI/MentionChatInputPanelItem.swift +++ b/submodules/TelegramUI/TelegramUI/MentionChatInputPanelItem.swift @@ -10,24 +10,29 @@ import TelegramPresentationData import TelegramUIPreferences import AvatarNode import AccountContext +import ItemListUI final class MentionChatInputPanelItem: ListViewItem { fileprivate let context: AccountContext - fileprivate let theme: PresentationTheme - fileprivate let fontSize: PresentationFontSize + fileprivate let presentationData: ItemListPresentationData + fileprivate let revealed: Bool fileprivate let inverted: Bool fileprivate let peer: Peer private let peerSelected: (Peer) -> Void + fileprivate let setPeerIdRevealed: (PeerId?) -> Void + fileprivate let removeRequested: (PeerId) -> Void let selectable: Bool = true - public init(context: AccountContext, theme: PresentationTheme, fontSize: PresentationFontSize, inverted: Bool, peer: Peer, peerSelected: @escaping (Peer) -> Void) { + public init(context: AccountContext, presentationData: ItemListPresentationData, inverted: Bool, peer: Peer, revealed: Bool, setPeerIdRevealed: @escaping (PeerId?) -> Void, peerSelected: @escaping (Peer) -> Void, removeRequested: @escaping (PeerId) -> Void) { self.context = context - self.theme = theme - self.fontSize = fontSize + self.presentationData = presentationData self.inverted = inverted self.peer = peer + self.revealed = revealed + self.setPeerIdRevealed = setPeerIdRevealed self.peerSelected = peerSelected + self.removeRequested = removeRequested } public func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal?, (ListViewItemApply) -> Void)) -> Void) { @@ -78,7 +83,11 @@ final class MentionChatInputPanelItem: ListViewItem { } func selected(listView: ListView) { - self.peerSelected(self.peer) + if self.revealed { + self.setPeerIdRevealed(nil) + } else { + self.peerSelected(self.peer) + } } } @@ -86,15 +95,24 @@ private let avatarFont = avatarPlaceholderFont(size: 16.0) final class MentionChatInputPanelItemNode: ListViewItemNode { static let itemHeight: CGFloat = 42.0 - - private var item: MentionChatInputPanelItem? - + private let avatarNode: AvatarNode private let textNode: TextNode private let topSeparatorNode: ASDisplayNode private let separatorNode: ASDisplayNode private let highlightedBackgroundNode: ASDisplayNode + private var revealNode: ItemListRevealOptionsNode? + private var revealOptions: [ItemListRevealOption] = [] + private var initialRevealOffset: CGFloat = 0.0 + public private(set) var revealOffset: CGFloat = 0.0 + private var recognizer: ItemListRevealOptionsGestureRecognizer? + private var hapticFeedback: HapticFeedback? + + private var item: MentionChatInputPanelItem? + + private var validLayout: (CGSize, CGFloat, CGFloat)? + init() { self.avatarNode = AvatarNode(font: avatarFont) self.textNode = TextNode() @@ -117,6 +135,15 @@ final class MentionChatInputPanelItemNode: ListViewItemNode { self.addSubnode(self.textNode) } + override func didLoad() { + super.didLoad() + + let recognizer = ItemListRevealOptionsGestureRecognizer(target: self, action: #selector(self.revealGesture(_:))) + self.recognizer = recognizer + recognizer.allowAnyDirection = false + self.view.addGestureRecognizer(recognizer) + } + override public func layoutForParams(_ params: ListViewItemLayoutParams, item: ListViewItem, previousItem: ListViewItem?, nextItem: ListViewItem?) { if let item = item as? MentionChatInputPanelItem { let doLayout = self.asyncLayout() @@ -134,8 +161,8 @@ final class MentionChatInputPanelItemNode: ListViewItemNode { let previousItem = self.item return { [weak self] item, params, mergedTop, mergedBottom in - let primaryFont = Font.medium(floor(item.fontSize.baseDisplaySize * 14.0 / 17.0)) - let secondaryFont = Font.regular(floor(item.fontSize.baseDisplaySize * 14.0 / 17.0)) + let primaryFont = Font.medium(floor(item.presentationData.fontSize.baseDisplaySize * 14.0 / 17.0)) + let secondaryFont = Font.regular(floor(item.presentationData.fontSize.baseDisplaySize * 14.0 / 17.0)) let leftInset: CGFloat = 55.0 + params.leftInset let rightInset: CGFloat = 10.0 + params.rightInset @@ -146,19 +173,23 @@ final class MentionChatInputPanelItemNode: ListViewItemNode { } let string = NSMutableAttributedString() - string.append(NSAttributedString(string: item.peer.debugDisplayTitle, font: primaryFont, textColor: item.theme.list.itemPrimaryTextColor)) + string.append(NSAttributedString(string: item.peer.debugDisplayTitle, font: primaryFont, textColor: item.presentationData.theme.list.itemPrimaryTextColor)) if let addressName = item.peer.addressName, !addressName.isEmpty { - string.append(NSAttributedString(string: " @\(addressName)", font: secondaryFont, textColor: item.theme.list.itemSecondaryTextColor)) + string.append(NSAttributedString(string: " @\(addressName)", font: secondaryFont, textColor: item.presentationData.theme.list.itemSecondaryTextColor)) } let (textLayout, textApply) = makeTextLayout(TextNodeLayoutArguments(attributedString: string, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - rightInset, height: 100.0), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) let nodeLayout = ListViewItemNodeLayout(contentSize: CGSize(width: params.width, height: HashtagChatInputPanelItemNode.itemHeight), insets: UIEdgeInsets()) - return (nodeLayout, { _ in + return (nodeLayout, { animation in if let strongSelf = self { strongSelf.item = item + strongSelf.validLayout = (nodeLayout.contentSize, params.leftInset, params.rightInset) + + let revealOffset = strongSelf.revealOffset + if let updatedInverted = updatedInverted { if updatedInverted { strongSelf.transform = CATransform3DMakeRotation(CGFloat(Double.pi), 0.0, 0.0, 1.0) @@ -167,12 +198,12 @@ final class MentionChatInputPanelItemNode: ListViewItemNode { } } - strongSelf.separatorNode.backgroundColor = item.theme.list.itemPlainSeparatorColor - strongSelf.topSeparatorNode.backgroundColor = item.theme.list.itemPlainSeparatorColor - strongSelf.backgroundColor = item.theme.list.plainBackgroundColor - strongSelf.highlightedBackgroundNode.backgroundColor = item.theme.list.itemHighlightedBackgroundColor + strongSelf.separatorNode.backgroundColor = item.presentationData.theme.list.itemPlainSeparatorColor + strongSelf.topSeparatorNode.backgroundColor = item.presentationData.theme.list.itemPlainSeparatorColor + strongSelf.backgroundColor = item.presentationData.theme.list.plainBackgroundColor + strongSelf.highlightedBackgroundNode.backgroundColor = item.presentationData.theme.list.itemHighlightedBackgroundColor - strongSelf.avatarNode.setPeer(context: item.context, theme: item.theme, peer: item.peer, emptyColor: item.theme.list.mediaPlaceholderColor) + strongSelf.avatarNode.setPeer(context: item.context, theme: item.presentationData.theme, peer: item.peer, emptyColor: item.presentationData.theme.list.mediaPlaceholderColor) let _ = textApply() @@ -186,14 +217,33 @@ final class MentionChatInputPanelItemNode: ListViewItemNode { strongSelf.separatorNode.frame = CGRect(origin: CGPoint(x: leftInset, y: !item.inverted ? (nodeLayout.contentSize.height - UIScreenPixel) : 0.0), size: CGSize(width: params.width - leftInset, height: UIScreenPixel)) strongSelf.highlightedBackgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: params.width, height: nodeLayout.size.height + UIScreenPixel)) + + if let peer = item.peer as? TelegramUser, let _ = peer.botInfo { + strongSelf.setRevealOptions([ItemListRevealOption(key: 0, title: item.presentationData.strings.Common_Delete, icon: .none, color: item.presentationData.theme.list.itemDisclosureActions.destructive.fillColor, textColor: item.presentationData.theme.list.itemDisclosureActions.destructive.foregroundColor)]) + strongSelf.setRevealOptionsOpened(item.revealed, animated: animation.isAnimated) + } else { + strongSelf.setRevealOptions([]) + strongSelf.setRevealOptionsOpened(false, animated: animation.isAnimated) + } } }) } } + func updateRevealOffset(offset: CGFloat, transition: ContainedViewLayoutTransition) { + if let (size, leftInset, rightInset) = self.validLayout { + transition.updateFrameAdditive(node: self.avatarNode, frame: CGRect(origin: CGPoint(x: min(offset, 0.0) + 12.0 + leftInset, y: self.avatarNode.frame.minY), size: self.avatarNode.frame.size)) + transition.updateFrameAdditive(node: self.textNode, frame: CGRect(origin: CGPoint(x: min(offset, 0.0) + 55.0 + leftInset, y: self.textNode.frame.minY), size: self.textNode.frame.size)) + } + } + override func setHighlighted(_ highlighted: Bool, at point: CGPoint, animated: Bool) { super.setHighlighted(highlighted, at: point, animated: animated) + if let revealNode = self.revealNode, self.revealOffset != 0 { + return + } + if highlighted { self.highlightedBackgroundNode.alpha = 1.0 if self.highlightedBackgroundNode.supernode == nil { @@ -216,4 +266,199 @@ final class MentionChatInputPanelItemNode: ListViewItemNode { } } } + + func setRevealOptions(_ options: [ItemListRevealOption]) { + if self.revealOptions == options { + return + } + let previousOptions = self.revealOptions + let wasEmpty = self.revealOptions.isEmpty + self.revealOptions = options + let isEmpty = options.isEmpty + if options.isEmpty { + if let _ = self.revealNode { + self.recognizer?.becomeCancelled() + self.updateRevealOffsetInternal(offset: 0.0, transition: .animated(duration: 0.3, curve: .spring)) + } + } + if wasEmpty != isEmpty { + self.recognizer?.isEnabled = !isEmpty + } + } + + private func setRevealOptionsOpened(_ value: Bool, animated: Bool) { + if value != !self.revealOffset.isZero { + if !self.revealOffset.isZero { + self.recognizer?.becomeCancelled() + } + let transition: ContainedViewLayoutTransition + if animated { + transition = .animated(duration: 0.3, curve: .spring) + } else { + transition = .immediate + } + if value { + if self.revealNode == nil { + self.setupAndAddRevealNode() + if let revealNode = self.revealNode, revealNode.isNodeLoaded, let _ = self.validLayout { + revealNode.layout() + let revealSize = revealNode.bounds.size + self.updateRevealOffsetInternal(offset: -revealSize.width, transition: transition) + } + } + } else if !self.revealOffset.isZero { + self.updateRevealOffsetInternal(offset: 0.0, transition: transition) + } + } + } + + override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { + return true + } + + func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { + if let recognizer = self.recognizer, otherGestureRecognizer == recognizer { + return true + } else { + return false + } + } + + @objc func revealGesture(_ recognizer: ItemListRevealOptionsGestureRecognizer) { + guard let (size, _, _) = self.validLayout else { + return + } + switch recognizer.state { + case .began: + if let revealNode = self.revealNode { + let revealSize = revealNode.bounds.size + let location = recognizer.location(in: self.view) + if location.x > size.width - revealSize.width { + recognizer.becomeCancelled() + } else { + self.initialRevealOffset = self.revealOffset + } + } else { + if self.revealOptions.isEmpty { + recognizer.becomeCancelled() + } + self.initialRevealOffset = self.revealOffset + } + case .changed: + var translation = recognizer.translation(in: self.view) + translation.x += self.initialRevealOffset + if self.revealNode == nil && translation.x.isLess(than: 0.0) { + self.setupAndAddRevealNode() + self.revealOptionsInteractivelyOpened() + } + self.updateRevealOffsetInternal(offset: translation.x, transition: .immediate) + if self.revealNode == nil { + self.revealOptionsInteractivelyClosed() + } + case .ended, .cancelled: + guard let recognizer = self.recognizer else { + break + } + + if let revealNode = self.revealNode { + let velocity = recognizer.velocity(in: self.view) + let revealSize = revealNode.bounds.size + var reveal = false + if abs(velocity.x) < 100.0 { + if self.initialRevealOffset.isZero && self.revealOffset < 0.0 { + reveal = true + } else if self.revealOffset < -revealSize.width { + reveal = true + } else { + reveal = false + } + } else { + if velocity.x < 0.0 { + reveal = true + } else { + reveal = false + } + } + self.updateRevealOffsetInternal(offset: reveal ? -revealSize.width : 0.0, transition: .animated(duration: 0.3, curve: .spring)) + if !reveal { + self.revealOptionsInteractivelyClosed() + } + } + default: + break + } + } + + private func revealOptionSelected(_ option: ItemListRevealOption, animated: Bool) { + guard let item = self.item else { + return + } + item.removeRequested(item.peer.id) + } + + private func setupAndAddRevealNode() { + if !self.revealOptions.isEmpty { + let revealNode = ItemListRevealOptionsNode(optionSelected: { [weak self] option in + self?.revealOptionSelected(option, animated: false) + }, tapticAction: { [weak self] in + self?.hapticImpact() + }) + revealNode.setOptions(self.revealOptions, isLeft: false) + self.revealNode = revealNode + + if let (size, _, rightInset) = self.validLayout { + var revealSize = revealNode.measure(CGSize(width: CGFloat.greatestFiniteMagnitude, height: size.height)) + revealSize.width += rightInset + + revealNode.frame = CGRect(origin: CGPoint(x: size.width + max(self.revealOffset, -revealSize.width), y: 0.0), size: revealSize) + revealNode.updateRevealOffset(offset: 0.0, sideInset: -rightInset, transition: .immediate) + } + + self.addSubnode(revealNode) + } + } + + private func updateRevealOffsetInternal(offset: CGFloat, transition: ContainedViewLayoutTransition) { + self.revealOffset = offset + guard let (size, leftInset, rightInset) = self.validLayout else { + return + } + + if let revealNode = self.revealNode { + let revealSize = revealNode.bounds.size + + let revealFrame = CGRect(origin: CGPoint(x: size.width + max(self.revealOffset, -revealSize.width), y: 0.0), size: revealSize) + let revealNodeOffset = -max(self.revealOffset, -revealSize.width) + revealNode.updateRevealOffset(offset: revealNodeOffset, sideInset: -rightInset, transition: transition) + + if CGFloat(0.0).isLessThanOrEqualTo(offset) { + self.revealNode = nil + transition.updateFrame(node: revealNode, frame: revealFrame, completion: { [weak revealNode] _ in + revealNode?.removeFromSupernode() + }) + } else { + transition.updateFrame(node: revealNode, frame: revealFrame) + } + } + self.updateRevealOffset(offset: offset, transition: transition) + } + + func revealOptionsInteractivelyOpened() { + if let item = self.item { + item.setPeerIdRevealed(item.peer.id) + } + } + + func revealOptionsInteractivelyClosed() { + if let item = self.item { + item.setPeerIdRevealed(nil) + } + } + + private func hapticImpact() { + if self.hapticFeedback == nil { + self.hapticFeedback = HapticFeedback() + } + self.hapticFeedback?.impact(.medium) + } } diff --git a/submodules/TelegramUI/TelegramUI/OpenChatMessage.swift b/submodules/TelegramUI/TelegramUI/OpenChatMessage.swift index cd0a53e09f..6ff8ed7f57 100644 --- a/submodules/TelegramUI/TelegramUI/OpenChatMessage.swift +++ b/submodules/TelegramUI/TelegramUI/OpenChatMessage.swift @@ -305,7 +305,7 @@ func openChatMessageImpl(_ params: OpenChatMessageParams) -> Bool { params.navigationController?.pushViewController(controller) return true case let .stickerPack(reference): - let controller = StickerPackScreen(context: params.context, mainStickerPack: reference, stickerPacks: [reference], sendSticker: params.sendSticker, actionPerformed: { info, items, action in + let controller = StickerPackScreen(context: params.context, mainStickerPack: reference, stickerPacks: [reference], parentNavigationController: params.navigationController, sendSticker: params.sendSticker, actionPerformed: { info, items, action in let presentationData = params.context.sharedContext.currentPresentationData.with { $0 } var animateInAsReplacement = false if let navigationController = params.navigationController { @@ -334,6 +334,7 @@ func openChatMessageImpl(_ params: OpenChatMessageParams) -> Bool { params.present(controller, nil) return true case let .document(file, immediateShare): + params.dismissInput() let presentationData = params.context.sharedContext.currentPresentationData.with { $0 } if immediateShare { let controller = ShareController(context: params.context, subject: .media(.standalone(media: file)), immediateExternalShare: true) diff --git a/submodules/TelegramUI/TelegramUI/OverlayPlayerControllerNode.swift b/submodules/TelegramUI/TelegramUI/OverlayPlayerControllerNode.swift index e208056b5e..b7645e5646 100644 --- a/submodules/TelegramUI/TelegramUI/OverlayPlayerControllerNode.swift +++ b/submodules/TelegramUI/TelegramUI/OverlayPlayerControllerNode.swift @@ -107,7 +107,8 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, UIGestu }, requestRedeliveryOfFailedMessages: { _ in }, addContact: { _ in }, rateCall: { _, _ in - }, requestSelectMessagePollOption: { _, _ in + }, requestSelectMessagePollOptions: { _, _ in + }, requestOpenMessagePollResults: { _, _ in }, openAppStorePage: { }, displayMessageTooltip: { _, _, _, _ in }, seekToTimecode: { _, _, _ in @@ -119,6 +120,8 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, UIGestu }, openMessageReactions: { _ in }, displaySwipeToReplyHint: { }, dismissReplyMarkupMessage: { _ in + }, openMessagePollResults: { _, _ in + }, openPollCreation: { _ in }, requestMessageUpdate: { _ in }, cancelInteractiveKeyboardGestures: { }, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings, pollActionState: ChatInterfacePollActionState(), stickerSettings: ChatInterfaceStickerSettings(loopAnimatedStickers: false)) @@ -257,7 +260,18 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, UIGestu panRecognizer.delegate = self panRecognizer.delaysTouchesBegan = false panRecognizer.cancelsTouchesInView = true - //self.view.addGestureRecognizer(panRecognizer) + panRecognizer.shouldBegin = { [weak self] point in + guard let strongSelf = self else { + return false + } + if strongSelf.controlsNode.bounds.contains(strongSelf.view.convert(point, to: strongSelf.controlsNode.view)) { + if strongSelf.controlsNode.frame.maxY <= strongSelf.historyNode.frame.minY { + return true + } + } + return false + } + self.view.addGestureRecognizer(panRecognizer) } func updatePresentationData(_ presentationData: PresentationData) { @@ -323,7 +337,9 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, UIGestu if self.controlsNode.bounds.contains(self.view.convert(point, to: self.controlsNode.view)) { let controlsHitTest = self.controlsNode.view.hitTest(self.view.convert(point, to: self.controlsNode.view), with: event) if controlsHitTest == nil { - return self.historyNode.view + if self.controlsNode.frame.maxY > self.historyNode.frame.minY { + return self.historyNode.view + } } } diff --git a/submodules/TelegramUI/TelegramUI/OverlayPlayerControlsNode.swift b/submodules/TelegramUI/TelegramUI/OverlayPlayerControlsNode.swift index 443ca664c0..e866e57ec7 100644 --- a/submodules/TelegramUI/TelegramUI/OverlayPlayerControlsNode.swift +++ b/submodules/TelegramUI/TelegramUI/OverlayPlayerControlsNode.swift @@ -349,14 +349,14 @@ final class OverlayPlayerControlsNode: ASDisplayNode { } let duration = value.status.duration - if duration != strongSelf.currentDuration { + if duration != strongSelf.currentDuration && !duration.isZero { strongSelf.currentDuration = duration if let layout = strongSelf.validLayout { strongSelf.updateLayout(width: layout.0, leftInset: layout.1, rightInset: layout.2, maxHeight: layout.3, transition: .immediate) } } - strongSelf.rateButton.isHidden = rateButtonIsHidden || strongSelf.currentDuration.isZero + strongSelf.rateButton.isHidden = rateButtonIsHidden } else { strongSelf.playPauseButton.isEnabled = false strongSelf.backwardButton.isEnabled = false diff --git a/submodules/TelegramUI/TelegramUI/PeerMediaCollectionController.swift b/submodules/TelegramUI/TelegramUI/PeerMediaCollectionController.swift index 790f9394e1..6863ff66a1 100644 --- a/submodules/TelegramUI/TelegramUI/PeerMediaCollectionController.swift +++ b/submodules/TelegramUI/TelegramUI/PeerMediaCollectionController.swift @@ -412,7 +412,8 @@ public class PeerMediaCollectionController: TelegramBaseController { }, requestRedeliveryOfFailedMessages: { _ in }, addContact: { _ in }, rateCall: { _, _ in - }, requestSelectMessagePollOption: { _, _ in + }, requestSelectMessagePollOptions: { _, _ in + }, requestOpenMessagePollResults: { _, _ in }, openAppStorePage: { }, displayMessageTooltip: { _, _, _, _ in }, seekToTimecode: { _, _, _ in @@ -424,6 +425,8 @@ public class PeerMediaCollectionController: TelegramBaseController { }, openMessageReactions: { _ in }, displaySwipeToReplyHint: { }, dismissReplyMarkupMessage: { _ in + }, openMessagePollResults: { _, _ in + }, openPollCreation: { _ in }, requestMessageUpdate: { _ in }, cancelInteractiveKeyboardGestures: { }, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings, diff --git a/submodules/TelegramUI/TelegramUI/PeerMediaCollectionControllerNode.swift b/submodules/TelegramUI/TelegramUI/PeerMediaCollectionControllerNode.swift index 43b2d14d1d..cae95a602a 100644 --- a/submodules/TelegramUI/TelegramUI/PeerMediaCollectionControllerNode.swift +++ b/submodules/TelegramUI/TelegramUI/PeerMediaCollectionControllerNode.swift @@ -155,7 +155,7 @@ class PeerMediaCollectionControllerNode: ASDisplayNode { self.historyEmptyNode = PeerMediaCollectionEmptyNode(mode: self.mediaCollectionInterfaceState.mode, theme: self.presentationData.theme, strings: self.presentationData.strings) self.historyEmptyNode.isHidden = true - self.chatPresentationInterfaceState = ChatPresentationInterfaceState(chatWallpaper: self.presentationData.chatWallpaper, theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameDisplayOrder: self.presentationData.nameDisplayOrder, limitsConfiguration: .defaultValue, fontSize: self.presentationData.listsFontSize, accountPeerId: context.account.peerId, mode: .standard(previewing: false), chatLocation: .peer(self.peerId), isScheduledMessages: false) + self.chatPresentationInterfaceState = ChatPresentationInterfaceState(chatWallpaper: self.presentationData.chatWallpaper, theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameDisplayOrder: self.presentationData.nameDisplayOrder, limitsConfiguration: .defaultValue, fontSize: self.presentationData.listsFontSize, bubbleCorners: self.presentationData.chatBubbleCorners, accountPeerId: context.account.peerId, mode: .standard(previewing: false), chatLocation: .peer(self.peerId), isScheduledMessages: false) super.init() diff --git a/submodules/TelegramUI/TelegramUI/PeerSelectionController.swift b/submodules/TelegramUI/TelegramUI/PeerSelectionController.swift index feb988676a..994cab2b6b 100644 --- a/submodules/TelegramUI/TelegramUI/PeerSelectionController.swift +++ b/submodules/TelegramUI/TelegramUI/PeerSelectionController.swift @@ -22,6 +22,8 @@ public final class PeerSelectionControllerImpl: ViewController, PeerSelectionCon public var peerSelected: ((PeerId) -> Void)? private let filter: ChatListNodePeersFilter + private let attemptSelection: ((Peer) -> Void)? + public var inProgress: Bool = false { didSet { if self.inProgress != oldValue { @@ -58,6 +60,7 @@ public final class PeerSelectionControllerImpl: ViewController, PeerSelectionCon self.filter = params.filter self.hasContactSelector = params.hasContactSelector self.presentationData = self.context.sharedContext.currentPresentationData.with { $0 } + self.attemptSelection = params.attemptSelection super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData)) @@ -139,6 +142,12 @@ public final class PeerSelectionControllerImpl: ViewController, PeerSelectionCon } } + self.peerSelectionNode.requestOpenDisabledPeer = { [weak self] peer in + if let strongSelf = self { + strongSelf.attemptSelection?(peer) + } + } + self.peerSelectionNode.requestOpenPeerFromSearch = { [weak self] peer in if let strongSelf = self { let storedPeer = strongSelf.context.account.postbox.transaction { transaction -> Void in diff --git a/submodules/TelegramUI/TelegramUI/PeerSelectionControllerNode.swift b/submodules/TelegramUI/TelegramUI/PeerSelectionControllerNode.swift index 2803639ab2..45938193a8 100644 --- a/submodules/TelegramUI/TelegramUI/PeerSelectionControllerNode.swift +++ b/submodules/TelegramUI/TelegramUI/PeerSelectionControllerNode.swift @@ -47,6 +47,7 @@ final class PeerSelectionControllerNode: ASDisplayNode { var requestActivateSearch: (() -> Void)? var requestDeactivateSearch: (() -> Void)? var requestOpenPeer: ((PeerId) -> Void)? + var requestOpenDisabledPeer: ((Peer) -> Void)? var requestOpenPeerFromSearch: ((Peer) -> Void)? var requestOpenMessageFromSearch: ((Peer, MessageId) -> Void)? @@ -102,6 +103,10 @@ final class PeerSelectionControllerNode: ASDisplayNode { self?.requestOpenPeer?(peerId) } + self.chatListNode.disabledPeerSelected = { [weak self] peer in + self?.requestOpenDisabledPeer?(peer) + } + self.chatListNode.contentOffsetChanged = { [weak self] offset in self?.contentOffsetChanged?(offset) } @@ -211,6 +216,8 @@ final class PeerSelectionControllerNode: ASDisplayNode { if let requestOpenPeerFromSearch = self?.requestOpenPeerFromSearch { requestOpenPeerFromSearch(peer) } + }, openDisabledPeer: { [weak self] peer in + self?.requestOpenDisabledPeer?(peer) }, openRecentPeerOptions: { _ in }, openMessage: { [weak self] peer, messageId in if let requestOpenMessageFromSearch = self?.requestOpenMessageFromSearch { diff --git a/submodules/TelegramUI/TelegramUI/PollResultsController.swift b/submodules/TelegramUI/TelegramUI/PollResultsController.swift new file mode 100644 index 0000000000..177ca29828 --- /dev/null +++ b/submodules/TelegramUI/TelegramUI/PollResultsController.swift @@ -0,0 +1,382 @@ +import Foundation +import Postbox +import SyncCore +import TelegramCore +import SwiftSignalKit +import TelegramPresentationData +import AccountContext +import ItemListUI +import Display +import ItemListPeerItem +import ItemListPeerActionItem + +private let collapsedResultCount: Int = 10 +private let collapsedInitialLimit: Int = 14 + +private final class PollResultsControllerArguments { + let context: AccountContext + let collapseOption: (Data) -> Void + let expandOption: (Data) -> Void + let openPeer: (RenderedPeer) -> Void + + init(context: AccountContext, collapseOption: @escaping (Data) -> Void, expandOption: @escaping (Data) -> Void, openPeer: @escaping (RenderedPeer) -> Void) { + self.context = context + self.collapseOption = collapseOption + self.expandOption = expandOption + self.openPeer = openPeer + } +} + +private enum PollResultsSection { + case text + case option(Int) + + var rawValue: Int32 { + switch self { + case .text: + return 0 + case let .option(index): + return 1 + Int32(index) + } + } +} + +private enum PollResultsEntryId: Hashable { + case text + case optionPeer(Int, Int) + case optionExpand(Int) +} + +private enum PollResultsItemTag: ItemListItemTag, Equatable { + case firstOptionPeer(opaqueIdentifier: Data) + + func isEqual(to other: ItemListItemTag) -> Bool { + if let other = other as? PollResultsItemTag, self == other { + return true + } else { + return false + } + } +} + +private enum PollResultsEntry: ItemListNodeEntry { + case text(String) + case optionPeer(optionId: Int, index: Int, peer: RenderedPeer, optionText: String, optionAdditionalText: String, optionCount: Int32, optionExpanded: Bool, opaqueIdentifier: Data, shimmeringAlternation: Int?, isFirstInOption: Bool) + case optionExpand(optionId: Int, opaqueIdentifier: Data, text: String, enabled: Bool) + + var section: ItemListSectionId { + switch self { + case .text: + return PollResultsSection.text.rawValue + case let .optionPeer(optionPeer): + return PollResultsSection.option(optionPeer.optionId).rawValue + case let .optionExpand(optionExpand): + return PollResultsSection.option(optionExpand.optionId).rawValue + } + } + + var stableId: PollResultsEntryId { + switch self { + case .text: + return .text + case let .optionPeer(optionPeer): + return .optionPeer(optionPeer.optionId, optionPeer.index) + case let .optionExpand(optionExpand): + return .optionExpand(optionExpand.optionId) + } + } + + static func <(lhs: PollResultsEntry, rhs: PollResultsEntry) -> Bool { + switch lhs { + case .text: + switch rhs { + case .text: + return false + default: + return true + } + case let .optionPeer(lhsOptionPeer): + switch rhs { + case .text: + return false + case let .optionPeer(rhsOptionPeer): + if lhsOptionPeer.optionId == rhsOptionPeer.optionId { + return lhsOptionPeer.index < rhsOptionPeer.index + } else { + return lhsOptionPeer.optionId < rhsOptionPeer.optionId + } + case let .optionExpand(rhsOptionExpand): + if lhsOptionPeer.optionId == rhsOptionExpand.optionId { + return true + } else { + return lhsOptionPeer.optionId < rhsOptionExpand.optionId + } + } + case let .optionExpand(lhsOptionExpand): + switch rhs { + case .text: + return false + case let .optionPeer(rhsOptionPeer): + if lhsOptionExpand.optionId == rhsOptionPeer.optionId { + return false + } else { + return lhsOptionExpand.optionId < rhsOptionPeer.optionId + } + case let .optionExpand(rhsOptionExpand): + if lhsOptionExpand.optionId == rhsOptionExpand.optionId { + return false + } else { + return lhsOptionExpand.optionId < rhsOptionExpand.optionId + } + } + } + } + + func item(presentationData: ItemListPresentationData, arguments: Any) -> ListViewItem { + let arguments = arguments as! PollResultsControllerArguments + switch self { + case let .text(text): + return ItemListTextItem(presentationData: presentationData, text: .large(text), sectionId: self.section) + case let .optionPeer(optionId, _, peer, optionText, optionAdditionalText, optionCount, optionExpanded, opaqueIdentifier, shimmeringAlternation, isFirstInOption): + let header = ItemListPeerItemHeader(theme: presentationData.theme, strings: presentationData.strings, text: optionText, additionalText: optionAdditionalText, actionTitle: optionExpanded ? presentationData.strings.PollResults_Collapse : presentationData.strings.MessagePoll_VotedCount(optionCount), id: Int64(optionId), action: optionExpanded ? { + arguments.collapseOption(opaqueIdentifier) + } : nil) + return ItemListPeerItem(presentationData: presentationData, dateTimeFormat: PresentationDateTimeFormat(timeFormat: .regular, dateFormat: .dayFirst, dateSeparator: ".", decimalSeparator: ".", groupingSeparator: ""), nameDisplayOrder: .firstLast, context: arguments.context, peer: peer.peers[peer.peerId]!, presence: nil, text: .none, label: .none, editing: ItemListPeerItemEditing(editable: false, editing: false, revealed: false), switchValue: nil, enabled: true, selectable: shimmeringAlternation == nil, sectionId: self.section, action: { + arguments.openPeer(peer) + }, setPeerIdWithRevealedOptions: { _, _ in + }, removePeer: { _ in + }, noInsets: true, tag: isFirstInOption ? PollResultsItemTag.firstOptionPeer(opaqueIdentifier: opaqueIdentifier) : nil, header: header, shimmering: shimmeringAlternation.flatMap { ItemListPeerItemShimmering(alternationIndex: $0) }) + case let .optionExpand(_, opaqueIdentifier, text, enabled): + return ItemListPeerActionItem(presentationData: presentationData, icon: PresentationResourcesItemList.downArrowImage(presentationData.theme), title: text, sectionId: self.section, editing: false, action: enabled ? { + arguments.expandOption(opaqueIdentifier) + } : nil) + } + } +} + +private struct PollResultsControllerState: Equatable { + var expandedOptions: [Data: Int] = [:] +} + +private func pollResultsControllerEntries(presentationData: PresentationData, poll: TelegramMediaPoll, state: PollResultsControllerState, resultsState: PollResultsState) -> [PollResultsEntry] { + var entries: [PollResultsEntry] = [] + + var isEmpty = false + for (_, optionState) in resultsState.options { + if !optionState.hasLoadedOnce { + isEmpty = true + break + } + } + + entries.append(.text(poll.text)) + + var optionVoterCount: [Int: Int32] = [:] + let totalVoterCount = poll.results.totalVoters ?? 0 + var optionPercentage: [Int] = [] + + if totalVoterCount != 0 { + if let voters = poll.results.voters, let totalVoters = poll.results.totalVoters { + for i in 0 ..< poll.options.count { + inner: for optionVoters in voters { + if optionVoters.opaqueIdentifier == poll.options[i].opaqueIdentifier { + optionVoterCount[i] = optionVoters.count + break inner + } + } + } + } + + optionPercentage = countNicePercent(votes: (0 ..< poll.options.count).map({ Int(optionVoterCount[$0] ?? 0) }), total: Int(totalVoterCount)) + } + + for i in 0 ..< poll.options.count { + let percentage = optionPercentage.count > i ? optionPercentage[i] : 0 + let option = poll.options[i] + let optionTextHeader = option.text.uppercased() + let optionAdditionalTextHeader = " — \(percentage)%" + if isEmpty { + if let voterCount = optionVoterCount[i], voterCount != 0 { + let displayCount: Int + if Int(voterCount) > collapsedInitialLimit { + displayCount = collapsedResultCount + } else { + displayCount = Int(voterCount) + } + for peerIndex in 0 ..< displayCount { + let fakeUser = TelegramUser(id: PeerId(namespace: -1, id: 0), accessHash: nil, firstName: "", lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: []) + let peer = RenderedPeer(peer: fakeUser) + entries.append(.optionPeer(optionId: i, index: peerIndex, peer: peer, optionText: optionTextHeader, optionAdditionalText: optionAdditionalTextHeader, optionCount: voterCount, optionExpanded: false, opaqueIdentifier: option.opaqueIdentifier, shimmeringAlternation: peerIndex % 2, isFirstInOption: peerIndex == 0)) + } + if displayCount < Int(voterCount) { + let remainingCount = Int(voterCount) - displayCount + entries.append(.optionExpand(optionId: i, opaqueIdentifier: option.opaqueIdentifier, text: presentationData.strings.PollResults_ShowMore(Int32(remainingCount)), enabled: false)) + } + } + } else { + if let optionState = resultsState.options[option.opaqueIdentifier], !optionState.peers.isEmpty { + var hasMore = false + let optionExpandedAtCount = state.expandedOptions[option.opaqueIdentifier] + + let peers = optionState.peers + let count = optionState.count + + let displayCount: Int + if peers.count > collapsedInitialLimit + 1 { + if optionExpandedAtCount != nil { + displayCount = peers.count + } else { + displayCount = collapsedResultCount + } + } else { + if let optionExpandedAtCount = optionExpandedAtCount { + if optionExpandedAtCount == collapsedInitialLimit + 1 && optionState.canLoadMore { + displayCount = collapsedResultCount + } else { + displayCount = peers.count + } + } else { + if !optionState.canLoadMore { + displayCount = peers.count + } else { + displayCount = collapsedResultCount + } + } + } + + var peerIndex = 0 + inner: for peer in peers { + if peerIndex >= displayCount { + break inner + } + entries.append(.optionPeer(optionId: i, index: peerIndex, peer: peer, optionText: optionTextHeader, optionAdditionalText: optionAdditionalTextHeader, optionCount: Int32(count), optionExpanded: optionExpandedAtCount != nil, opaqueIdentifier: option.opaqueIdentifier, shimmeringAlternation: nil, isFirstInOption: peerIndex == 0)) + peerIndex += 1 + } + + let remainingCount = count - peerIndex + if remainingCount > 0 { + entries.append(.optionExpand(optionId: i, opaqueIdentifier: option.opaqueIdentifier, text: presentationData.strings.PollResults_ShowMore(Int32(remainingCount)), enabled: true)) + } + } + } + } + + return entries +} + +public func pollResultsController(context: AccountContext, messageId: MessageId, poll: TelegramMediaPoll, focusOnOptionWithOpaqueIdentifier: Data? = nil) -> ViewController { + let statePromise = ValuePromise(PollResultsControllerState(), ignoreRepeated: true) + let stateValue = Atomic(value: PollResultsControllerState()) + let updateState: ((PollResultsControllerState) -> PollResultsControllerState) -> Void = { f in + statePromise.set(stateValue.modify { f($0) }) + } + + var pushControllerImpl: ((ViewController) -> Void)? + var dismissImpl: (() -> Void)? + + let actionsDisposable = DisposableSet() + + let resultsContext = PollResultsContext(account: context.account, messageId: messageId, poll: poll) + + let arguments = PollResultsControllerArguments(context: context, + collapseOption: { optionId in + updateState { state in + var state = state + state.expandedOptions.removeValue(forKey: optionId) + return state + } + }, expandOption: { optionId in + let _ = (resultsContext.state + |> take(1) + |> deliverOnMainQueue).start(next: { [weak resultsContext] state in + if let optionState = state.options[optionId] { + updateState { state in + var state = state + state.expandedOptions[optionId] = optionState.peers.count + return state + } + + if optionState.canLoadMore { + resultsContext?.loadMore(optionOpaqueIdentifier: optionId) + } + } + }) + }, openPeer: { peer in + if let peer = peer.peers[peer.peerId] { + if let controller = context.sharedContext.makePeerInfoController(context: context, peer: peer, mode: .generic) { + pushControllerImpl?(controller) + } + } + }) + + let previousWasEmpty = Atomic(value: nil) + + let signal = combineLatest(queue: .mainQueue(), + context.sharedContext.presentationData, + statePromise.get(), + resultsContext.state + ) + |> map { presentationData, state, resultsState -> (ItemListControllerState, (ItemListNodeState, Any)) in + let leftNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Close), style: .regular, enabled: true, action: { + dismissImpl?() + }) + + var isEmpty = false + for (_, optionState) in resultsState.options { + if !optionState.hasLoadedOnce { + isEmpty = true + break + } + } + + let previousWasEmptyValue = previousWasEmpty.swap(isEmpty) + + var totalVoters: Int32 = 0 + if let totalVotersValue = poll.results.totalVoters { + totalVoters = totalVotersValue + } + + let entries = pollResultsControllerEntries(presentationData: presentationData, poll: poll, state: state, resultsState: resultsState) + + var initialScrollToItem: ListViewScrollToItem? + if let focusOnOptionWithOpaqueIdentifier = focusOnOptionWithOpaqueIdentifier, previousWasEmptyValue == nil { + var isFirstOption = true + loop: for i in 0 ..< entries.count { + switch entries[i] { + case let .optionPeer(optionPeer): + if optionPeer.opaqueIdentifier == focusOnOptionWithOpaqueIdentifier { + if !isFirstOption { + initialScrollToItem = ListViewScrollToItem(index: i, position: .top(0.0), animated: false, curve: .Default(duration: nil), directionHint: .Down) + } + break loop + } + isFirstOption = false + default: + break + } + } + } + + let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .textWithSubtitle(presentationData.strings.PollResults_Title, presentationData.strings.MessagePoll_VotedCount(totalVoters)), leftNavigationButton: leftNavigationButton, rightNavigationButton: nil, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: false) + let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: entries, style: .blocks, focusItemTag: nil, emptyStateItem: nil, initialScrollToItem: initialScrollToItem, crossfadeState: previousWasEmptyValue != nil && previousWasEmptyValue == true && isEmpty == false, animateChanges: false) + + return (controllerState, (listState, arguments)) + } + |> afterDisposed { + actionsDisposable.dispose() + } + + let controller = ItemListController(context: context, state: signal) + controller.navigationPresentation = .modal + pushControllerImpl = { [weak controller] c in + controller?.push(c) + } + dismissImpl = { [weak controller] in + controller?.dismiss() + } + controller.isOpaqueWhenInOverlay = true + controller.blocksBackgroundWhenInOverlay = true + + return controller +} + diff --git a/submodules/TelegramUI/TelegramUI/Resources/ChatWallpaperBuiltin0.jpg b/submodules/TelegramUI/TelegramUI/Resources/ChatWallpaperBuiltin0.jpg index 688289d25e..44f7cf5262 100644 Binary files a/submodules/TelegramUI/TelegramUI/Resources/ChatWallpaperBuiltin0.jpg and b/submodules/TelegramUI/TelegramUI/Resources/ChatWallpaperBuiltin0.jpg differ diff --git a/submodules/TelegramUI/TelegramUI/Resources/PresentationStrings.mapping b/submodules/TelegramUI/TelegramUI/Resources/PresentationStrings.mapping index c997aa4de0..7a9fbcb92d 100644 Binary files a/submodules/TelegramUI/TelegramUI/Resources/PresentationStrings.mapping and b/submodules/TelegramUI/TelegramUI/Resources/PresentationStrings.mapping differ diff --git a/submodules/TelegramUI/TelegramUI/SharedAccountContext.swift b/submodules/TelegramUI/TelegramUI/SharedAccountContext.swift index 926fab00f0..f6fa41cde3 100644 --- a/submodules/TelegramUI/TelegramUI/SharedAccountContext.swift +++ b/submodules/TelegramUI/TelegramUI/SharedAccountContext.swift @@ -1094,53 +1094,56 @@ public final class SharedAccountContextImpl: SharedAccountContext { return PeerSelectionControllerImpl(params) } - public func makeChatMessagePreviewItem(context: AccountContext, message: Message, theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, dateTimeFormat: PresentationDateTimeFormat, nameOrder: PresentationPersonNameOrder, forcedResourceStatus: FileMediaResourceStatus?, tapMessage: ((Message) -> Void)? = nil, clickThroughMessage: (() -> Void)? = nil) -> ListViewItem { + public func makeChatMessagePreviewItem(context: AccountContext, message: Message, theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, chatBubbleCorners: PresentationChatBubbleCorners, dateTimeFormat: PresentationDateTimeFormat, nameOrder: PresentationPersonNameOrder, forcedResourceStatus: FileMediaResourceStatus?, tapMessage: ((Message) -> Void)? = nil, clickThroughMessage: (() -> Void)? = nil) -> ListViewItem { let controllerInteraction: ChatControllerInteraction if tapMessage != nil || clickThroughMessage != nil { controllerInteraction = ChatControllerInteraction(openMessage: { _, _ in return false }, openPeer: { _, _, _ in }, openPeerMention: { _ in }, openMessageContextMenu: { _, _, _, _, _ in }, openMessageContextActions: { _, _, _, _ in }, navigateToMessage: { _, _ in }, tapMessage: { message in - tapMessage?(message) - }, clickThroughMessage: { - clickThroughMessage?() - }, toggleMessagesSelection: { _, _ in }, sendCurrentMessage: { _ in }, sendMessage: { _ in }, sendSticker: { _, _, _, _ in return false }, sendGif: { _, _, _ in return false }, requestMessageActionCallback: { _, _, _ in }, requestMessageActionUrlAuth: { _, _, _ in }, activateSwitchInline: { _, _ in }, openUrl: { _, _, _, _ in }, shareCurrentLocation: {}, shareAccountContact: {}, sendBotCommand: { _, _ in }, openInstantPage: { _, _ in }, openWallpaper: { _ in }, openTheme: { _ in }, openHashtag: { _, _ in }, updateInputState: { _ in }, updateInputMode: { _ in }, openMessageShareMenu: { _ in - }, presentController: { _, _ in }, navigationController: { - return nil - }, chatControllerNode: { - return nil - }, reactionContainerNode: { - return nil - }, presentGlobalOverlayController: { _, _ in }, callPeer: { _ in }, longTap: { _, _ in }, openCheckoutOrReceipt: { _ in }, openSearch: { }, setupReply: { _ in - }, canSetupReply: { _ in - return false - }, navigateToFirstDateMessage: { _ in - }, requestRedeliveryOfFailedMessages: { _ in - }, addContact: { _ in - }, rateCall: { _, _ in - }, requestSelectMessagePollOption: { _, _ in - }, openAppStorePage: { - }, displayMessageTooltip: { _, _, _, _ in - }, seekToTimecode: { _, _, _ in - }, scheduleCurrentMessage: { - }, sendScheduledMessagesNow: { _ in - }, editScheduledMessagesTime: { _ in - }, performTextSelectionAction: { _, _, _ in - }, updateMessageReaction: { _, _ in - }, openMessageReactions: { _ in - }, displaySwipeToReplyHint: { - }, dismissReplyMarkupMessage: { _ in - }, requestMessageUpdate: { _ in - }, cancelInteractiveKeyboardGestures: { - }, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings, - pollActionState: ChatInterfacePollActionState(), stickerSettings: ChatInterfaceStickerSettings(loopAnimatedStickers: false)) + tapMessage?(message) + }, clickThroughMessage: { + clickThroughMessage?() + }, toggleMessagesSelection: { _, _ in }, sendCurrentMessage: { _ in }, sendMessage: { _ in }, sendSticker: { _, _, _, _ in return false }, sendGif: { _, _, _ in return false }, requestMessageActionCallback: { _, _, _ in }, requestMessageActionUrlAuth: { _, _, _ in }, activateSwitchInline: { _, _ in }, openUrl: { _, _, _, _ in }, shareCurrentLocation: {}, shareAccountContact: {}, sendBotCommand: { _, _ in }, openInstantPage: { _, _ in }, openWallpaper: { _ in }, openTheme: { _ in }, openHashtag: { _, _ in }, updateInputState: { _ in }, updateInputMode: { _ in }, openMessageShareMenu: { _ in + }, presentController: { _, _ in }, navigationController: { + return nil + }, chatControllerNode: { + return nil + }, reactionContainerNode: { + return nil + }, presentGlobalOverlayController: { _, _ in }, callPeer: { _ in }, longTap: { _, _ in }, openCheckoutOrReceipt: { _ in }, openSearch: { }, setupReply: { _ in + }, canSetupReply: { _ in + return false + }, navigateToFirstDateMessage: { _ in + }, requestRedeliveryOfFailedMessages: { _ in + }, addContact: { _ in + }, rateCall: { _, _ in + }, requestSelectMessagePollOptions: { _, _ in + }, requestOpenMessagePollResults: { _, _ in + }, openAppStorePage: { + }, displayMessageTooltip: { _, _, _, _ in + }, seekToTimecode: { _, _, _ in + }, scheduleCurrentMessage: { + }, sendScheduledMessagesNow: { _ in + }, editScheduledMessagesTime: { _ in + }, performTextSelectionAction: { _, _, _ in + }, updateMessageReaction: { _, _ in + }, openMessageReactions: { _ in + }, displaySwipeToReplyHint: { + }, dismissReplyMarkupMessage: { _ in + }, openMessagePollResults: { _, _ in + }, openPollCreation: { _ in + }, requestMessageUpdate: { _ in + }, cancelInteractiveKeyboardGestures: { + }, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings, + pollActionState: ChatInterfacePollActionState(), stickerSettings: ChatInterfaceStickerSettings(loopAnimatedStickers: false)) } else { controllerInteraction = defaultChatControllerInteraction } - return ChatMessageItem(presentationData: ChatPresentationData(theme: ChatPresentationThemeData(theme: theme, wallpaper: wallpaper), fontSize: fontSize, strings: strings, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameOrder, disableAnimations: false, largeEmoji: false, animatedEmojiScale: 1.0, isPreview: true), context: context, chatLocation: .peer(message.id.peerId), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .contact, automaticDownloadNetworkType: .cellular, isRecentActions: false, isScheduledMessages: false, contactsPeerIds: Set(), animatedEmojiStickers: [:], forcedResourceStatus: forcedResourceStatus), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes()), disableDate: true, additionalContent: nil) + return ChatMessageItem(presentationData: ChatPresentationData(theme: ChatPresentationThemeData(theme: theme, wallpaper: wallpaper), fontSize: fontSize, strings: strings, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameOrder, disableAnimations: false, largeEmoji: false, chatBubbleCorners: chatBubbleCorners, animatedEmojiScale: 1.0, isPreview: true), context: context, chatLocation: .peer(message.id.peerId), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .contact, automaticDownloadNetworkType: .cellular, isRecentActions: false, isScheduledMessages: false, contactsPeerIds: Set(), animatedEmojiStickers: [:], forcedResourceStatus: forcedResourceStatus), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes()), disableDate: true, additionalContent: nil) } - public func makeChatMessageDateHeaderItem(context: AccountContext, timestamp: Int32, theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, dateTimeFormat: PresentationDateTimeFormat, nameOrder: PresentationPersonNameOrder) -> ListViewItemHeader { - return ChatMessageDateHeader(timestamp: timestamp, scheduled: false, presentationData: ChatPresentationData(theme: ChatPresentationThemeData(theme: theme, wallpaper: wallpaper), fontSize: fontSize, strings: strings, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameOrder, disableAnimations: false, largeEmoji: false, animatedEmojiScale: 1.0, isPreview: true), context: context) + public func makeChatMessageDateHeaderItem(context: AccountContext, timestamp: Int32, theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, chatBubbleCorners: PresentationChatBubbleCorners, dateTimeFormat: PresentationDateTimeFormat, nameOrder: PresentationPersonNameOrder) -> ListViewItemHeader { + return ChatMessageDateHeader(timestamp: timestamp, scheduled: false, presentationData: ChatPresentationData(theme: ChatPresentationThemeData(theme: theme, wallpaper: wallpaper), fontSize: fontSize, strings: strings, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameOrder, disableAnimations: false, largeEmoji: false, chatBubbleCorners: chatBubbleCorners, animatedEmojiScale: 1.0, isPreview: true), context: context) } public func openWallet(context: AccountContext, walletContext: OpenWalletContext, present: @escaping (ViewController) -> Void) { diff --git a/submodules/TelegramUI/TelegramUI/TelegramRootController.swift b/submodules/TelegramUI/TelegramUI/TelegramRootController.swift index 1e93467e36..9e44967397 100644 --- a/submodules/TelegramUI/TelegramUI/TelegramRootController.swift +++ b/submodules/TelegramUI/TelegramUI/TelegramRootController.swift @@ -120,6 +120,19 @@ public final class TelegramRootController: NavigationController { self.accountSettingsController = accountSettingsController self.rootTabController = tabBarController self.pushViewController(tabBarController, animated: false) + +// let _ = (archivedStickerPacks(account: self.context.account, namespace: .stickers) +// |> deliverOnMainQueue).start(next: { [weak self] stickerPacks in +// var packs: [(StickerPackCollectionInfo, StickerPackItem?)] = [] +// for pack in stickerPacks { +// packs.append((pack.info, pack.topItems.first)) +// } +// +// if let strongSelf = self { +// let controller = archivedStickersNoticeController(context: strongSelf.context, archivedStickerPacks: packs) +// strongSelf.chatListController?.present(controller, in: .window(.root)) +// } +// }) } public func updateRootControllers(showCallsTab: Bool) { diff --git a/submodules/TelegramUI/TelegramUI/ThemeUpdateManager.swift b/submodules/TelegramUI/TelegramUI/ThemeUpdateManager.swift index b1a4e73adb..2371c501a3 100644 --- a/submodules/TelegramUI/TelegramUI/ThemeUpdateManager.swift +++ b/submodules/TelegramUI/TelegramUI/ThemeUpdateManager.swift @@ -111,10 +111,10 @@ final class ThemeUpdateManagerImpl: ThemeUpdateManager { return .complete() } accountManager.mediaBox.storeResourceData(file.file.resource.id, data: fullSizeData, synchronous: true) - return .single((.cloud(PresentationCloudTheme(theme: theme, resolvedWallpaper: wallpaper)), presentationTheme)) + return .single((.cloud(PresentationCloudTheme(theme: theme, resolvedWallpaper: wallpaper, creatorAccountId: theme.isCreator ? account.id : nil)), presentationTheme)) } } else { - return .single((.cloud(PresentationCloudTheme(theme: theme, resolvedWallpaper: nil)), presentationTheme)) + return .single((.cloud(PresentationCloudTheme(theme: theme, resolvedWallpaper: nil, creatorAccountId: theme.isCreator ? account.id : nil)), presentationTheme)) } } } @@ -139,7 +139,7 @@ final class ThemeUpdateManagerImpl: ThemeUpdateManager { theme = updatedTheme } - return PresentationThemeSettings(theme: theme, themeSpecificAccentColors: current.themeSpecificAccentColors, themeSpecificChatWallpapers: current.themeSpecificChatWallpapers, useSystemFont: current.useSystemFont, fontSize: current.fontSize, listsFontSize: current.listsFontSize, automaticThemeSwitchSetting: automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations) + return PresentationThemeSettings(theme: theme, themeSpecificAccentColors: current.themeSpecificAccentColors, themeSpecificChatWallpapers: current.themeSpecificChatWallpapers, useSystemFont: current.useSystemFont, fontSize: current.fontSize, listsFontSize: current.listsFontSize, chatBubbleSettings: current.chatBubbleSettings, automaticThemeSwitchSetting: automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations) }) }).start() } diff --git a/submodules/TelegramUI/TelegramUI/WallpaperUploadManager.swift b/submodules/TelegramUI/TelegramUI/WallpaperUploadManager.swift index 9f20c48e3e..0527746dd8 100644 --- a/submodules/TelegramUI/TelegramUI/WallpaperUploadManager.swift +++ b/submodules/TelegramUI/TelegramUI/WallpaperUploadManager.swift @@ -133,7 +133,7 @@ final class WallpaperUploadManagerImpl: WallpaperUploadManager { var themeSpecificChatWallpapers = current.themeSpecificChatWallpapers themeSpecificChatWallpapers[themeReference.index] = updatedWallpaper themeSpecificChatWallpapers[coloredThemeIndex(reference: themeReference, accentColor: current.themeSpecificAccentColors[themeReference.index])] = updatedWallpaper - return PresentationThemeSettings(theme: current.theme, themeSpecificAccentColors: current.themeSpecificAccentColors, themeSpecificChatWallpapers: themeSpecificChatWallpapers, useSystemFont: current.useSystemFont, fontSize: current.fontSize, listsFontSize: current.listsFontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations) + return PresentationThemeSettings(theme: current.theme, themeSpecificAccentColors: current.themeSpecificAccentColors, themeSpecificChatWallpapers: themeSpecificChatWallpapers, useSystemFont: current.useSystemFont, fontSize: current.fontSize, listsFontSize: current.listsFontSize, chatBubbleSettings: current.chatBubbleSettings, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations) })).start() } diff --git a/submodules/TelegramUIPreferences/Sources/CallListSettings.swift b/submodules/TelegramUIPreferences/Sources/CallListSettings.swift index ff81d69b1a..1a7640f660 100644 --- a/submodules/TelegramUIPreferences/Sources/CallListSettings.swift +++ b/submodules/TelegramUIPreferences/Sources/CallListSettings.swift @@ -3,22 +3,58 @@ import Postbox import SwiftSignalKit public struct CallListSettings: PreferencesEntry, Equatable { - public var showTab: Bool + public var _showTab: Bool? + public var defaultShowTab: Bool? public static var defaultSettings: CallListSettings { - return CallListSettings(showTab: false) + return CallListSettings(showTab: true) + } + + public var showTab: Bool { + get { + if let value = self._showTab { + return value + } else if let defaultValue = self.defaultShowTab { + return defaultValue + } else { + return CallListSettings.defaultSettings.showTab + } + } set { + self._showTab = newValue + } } public init(showTab: Bool) { - self.showTab = showTab + self._showTab = showTab + } + + public init(showTab: Bool?, defaultShowTab: Bool?) { + self._showTab = showTab + self.defaultShowTab = defaultShowTab } public init(decoder: PostboxDecoder) { - self.showTab = decoder.decodeInt32ForKey("showTab", orElse: 0) != 0 + var defaultValue = CallListSettings.defaultSettings.showTab + if let alternativeDefaultValue = decoder.decodeOptionalInt32ForKey("defaultShowTab") { + defaultValue = alternativeDefaultValue != 0 + self.defaultShowTab = alternativeDefaultValue != 0 + } + if let value = decoder.decodeOptionalInt32ForKey("showTab") { + self._showTab = value != 0 + } } public func encode(_ encoder: PostboxEncoder) { - encoder.encodeInt32(self.showTab ? 1 : 0, forKey: "showTab") + if let defaultShowTab = self.defaultShowTab { + encoder.encodeInt32(defaultShowTab ? 1 : 0, forKey: "defaultShowTab") + } else { + encoder.encodeNil(forKey: "defaultShowTab") + } + if let showTab = self._showTab { + encoder.encodeInt32(showTab ? 1 : 0, forKey: "showTab") + } else { + encoder.encodeNil(forKey: "showTab") + } } public func isEqual(to: PreferencesEntry) -> Bool { @@ -30,11 +66,11 @@ public struct CallListSettings: PreferencesEntry, Equatable { } public static func ==(lhs: CallListSettings, rhs: CallListSettings) -> Bool { - return lhs.showTab == rhs.showTab + return lhs._showTab == rhs._showTab && lhs.defaultShowTab == rhs.defaultShowTab } public func withUpdatedShowTab(_ showTab: Bool) -> CallListSettings { - return CallListSettings(showTab: showTab) + return CallListSettings(showTab: showTab, defaultShowTab: self.defaultShowTab) } } @@ -51,3 +87,17 @@ public func updateCallListSettingsInteractively(accountManager: AccountManager, }) } } + +public func storeCurrentCallListTabDefaultValue(accountManager: AccountManager) -> Signal { + return accountManager.transaction { transaction -> Void in + transaction.updateSharedData(ApplicationSpecificSharedDataKeys.callListSettings, { entry in + let currentSettings: CallListSettings + if let entry = entry as? CallListSettings { + currentSettings = entry + } else { + currentSettings = CallListSettings(showTab: nil, defaultShowTab: CallListSettings.defaultSettings.showTab) + } + return currentSettings + }) + } +} diff --git a/submodules/TelegramUIPreferences/Sources/IntentsSettings.swift b/submodules/TelegramUIPreferences/Sources/IntentsSettings.swift index b7ba32db2b..77ba26eea7 100644 --- a/submodules/TelegramUIPreferences/Sources/IntentsSettings.swift +++ b/submodules/TelegramUIPreferences/Sources/IntentsSettings.swift @@ -27,7 +27,7 @@ public struct IntentsSettings: PreferencesEntry, Equatable { } public init(decoder: PostboxDecoder) { - self.initiallyReset = decoder.decodeBoolForKey("initiallyReset_v1", orElse: false) + self.initiallyReset = decoder.decodeBoolForKey("initiallyReset_v2", orElse: false) self.account = decoder.decodeOptionalInt64ForKey("account").flatMap { PeerId($0) } self.contacts = decoder.decodeBoolForKey("contacts", orElse: true) self.privateChats = decoder.decodeBoolForKey("privateChats", orElse: false) @@ -37,7 +37,7 @@ public struct IntentsSettings: PreferencesEntry, Equatable { } public func encode(_ encoder: PostboxEncoder) { - encoder.encodeBool(self.initiallyReset, forKey: "initiallyReset_v1") + encoder.encodeBool(self.initiallyReset, forKey: "initiallyReset_v2") if let account = self.account { encoder.encodeInt64(account.toInt64(), forKey: "account") } else { diff --git a/submodules/TelegramUIPreferences/Sources/PresentationThemeSettings.swift b/submodules/TelegramUIPreferences/Sources/PresentationThemeSettings.swift index 4a5b29ef8f..ab9d7e7291 100644 --- a/submodules/TelegramUIPreferences/Sources/PresentationThemeSettings.swift +++ b/submodules/TelegramUIPreferences/Sources/PresentationThemeSettings.swift @@ -97,15 +97,18 @@ public struct PresentationLocalTheme: PostboxCoding, Equatable { public struct PresentationCloudTheme: PostboxCoding, Equatable { public let theme: TelegramTheme public let resolvedWallpaper: TelegramWallpaper? + public let creatorAccountId: AccountRecordId? - public init(theme: TelegramTheme, resolvedWallpaper: TelegramWallpaper?) { + public init(theme: TelegramTheme, resolvedWallpaper: TelegramWallpaper?, creatorAccountId: AccountRecordId?) { self.theme = theme self.resolvedWallpaper = resolvedWallpaper + self.creatorAccountId = creatorAccountId } public init(decoder: PostboxDecoder) { self.theme = decoder.decodeObjectForKey("theme", decoder: { TelegramTheme(decoder: $0) }) as! TelegramTheme self.resolvedWallpaper = decoder.decodeObjectForKey("wallpaper", decoder: { TelegramWallpaper(decoder: $0) }) as? TelegramWallpaper + self.creatorAccountId = decoder.decodeOptionalInt64ForKey("account").flatMap { AccountRecordId(rawValue: $0) } } public func encode(_ encoder: PostboxEncoder) { @@ -115,6 +118,11 @@ public struct PresentationCloudTheme: PostboxCoding, Equatable { } else { encoder.encodeNil(forKey: "wallpaper") } + if let accountId = self.creatorAccountId { + encoder.encodeInt64(accountId.int64, forKey: "account") + } else { + encoder.encodeNil(forKey: "account") + } } public static func ==(lhs: PresentationCloudTheme, rhs: PresentationCloudTheme) -> Bool { @@ -523,6 +531,32 @@ public struct PresentationThemeAccentColor: PostboxCoding, Equatable { } } +public struct PresentationChatBubbleSettings: PostboxCoding, Equatable { + public var mainRadius: Int32 + public var auxiliaryRadius: Int32 + public var mergeBubbleCorners: Bool + + public static var `default`: PresentationChatBubbleSettings = PresentationChatBubbleSettings(mainRadius: 16, auxiliaryRadius: 8, mergeBubbleCorners: true) + + public init(mainRadius: Int32, auxiliaryRadius: Int32, mergeBubbleCorners: Bool) { + self.mainRadius = mainRadius + self.auxiliaryRadius = auxiliaryRadius + self.mergeBubbleCorners = mergeBubbleCorners + } + + public init(decoder: PostboxDecoder) { + self.mainRadius = decoder.decodeInt32ForKey("mainRadius", orElse: 16) + self.auxiliaryRadius = decoder.decodeInt32ForKey("auxiliaryRadius", orElse: 8) + self.mergeBubbleCorners = decoder.decodeInt32ForKey("mergeBubbleCorners", orElse: 1) != 0 + } + + public func encode(_ encoder: PostboxEncoder) { + encoder.encodeInt32(self.mainRadius, forKey: "mainRadius") + encoder.encodeInt32(self.auxiliaryRadius, forKey: "auxiliaryRadius") + encoder.encodeInt32(self.mergeBubbleCorners ? 1 : 0, forKey: "mergeBubbleCorners") + } +} + public struct PresentationThemeSettings: PreferencesEntry { public var theme: PresentationThemeReference public var themeSpecificAccentColors: [Int64: PresentationThemeAccentColor] @@ -530,6 +564,7 @@ public struct PresentationThemeSettings: PreferencesEntry { public var useSystemFont: Bool public var fontSize: PresentationFontSize public var listsFontSize: PresentationFontSize + public var chatBubbleSettings: PresentationChatBubbleSettings public var automaticThemeSwitchSetting: AutomaticThemeSwitchSetting public var largeEmoji: Bool public var disableAnimations: Bool @@ -570,16 +605,17 @@ public struct PresentationThemeSettings: PreferencesEntry { } public static var defaultSettings: PresentationThemeSettings { - return PresentationThemeSettings(theme: .builtin(.dayClassic), themeSpecificAccentColors: [:], themeSpecificChatWallpapers: [:], useSystemFont: true, fontSize: .regular, listsFontSize: .regular, automaticThemeSwitchSetting: AutomaticThemeSwitchSetting(trigger: .system, theme: .builtin(.night)), largeEmoji: true, disableAnimations: true) + return PresentationThemeSettings(theme: .builtin(.dayClassic), themeSpecificAccentColors: [:], themeSpecificChatWallpapers: [:], useSystemFont: true, fontSize: .regular, listsFontSize: .regular, chatBubbleSettings: .default, automaticThemeSwitchSetting: AutomaticThemeSwitchSetting(trigger: .system, theme: .builtin(.night)), largeEmoji: true, disableAnimations: true) } - public init(theme: PresentationThemeReference, themeSpecificAccentColors: [Int64: PresentationThemeAccentColor], themeSpecificChatWallpapers: [Int64: TelegramWallpaper], useSystemFont: Bool, fontSize: PresentationFontSize, listsFontSize: PresentationFontSize, automaticThemeSwitchSetting: AutomaticThemeSwitchSetting, largeEmoji: Bool, disableAnimations: Bool) { + public init(theme: PresentationThemeReference, themeSpecificAccentColors: [Int64: PresentationThemeAccentColor], themeSpecificChatWallpapers: [Int64: TelegramWallpaper], useSystemFont: Bool, fontSize: PresentationFontSize, listsFontSize: PresentationFontSize, chatBubbleSettings: PresentationChatBubbleSettings, automaticThemeSwitchSetting: AutomaticThemeSwitchSetting, largeEmoji: Bool, disableAnimations: Bool) { self.theme = theme self.themeSpecificAccentColors = themeSpecificAccentColors self.themeSpecificChatWallpapers = themeSpecificChatWallpapers self.useSystemFont = useSystemFont self.fontSize = fontSize self.listsFontSize = listsFontSize + self.chatBubbleSettings = chatBubbleSettings self.automaticThemeSwitchSetting = automaticThemeSwitchSetting self.largeEmoji = largeEmoji self.disableAnimations = disableAnimations @@ -604,6 +640,7 @@ public struct PresentationThemeSettings: PreferencesEntry { let fontSize = PresentationFontSize(rawValue: decoder.decodeInt32ForKey("f", orElse: PresentationFontSize.regular.rawValue)) ?? .regular self.fontSize = fontSize self.listsFontSize = PresentationFontSize(rawValue: decoder.decodeInt32ForKey("lf", orElse: PresentationFontSize.regular.rawValue)) ?? fontSize + self.chatBubbleSettings = decoder.decodeObjectForKey("chatBubbleSettings", decoder: { PresentationChatBubbleSettings(decoder: $0) }) as? PresentationChatBubbleSettings ?? PresentationChatBubbleSettings.default self.automaticThemeSwitchSetting = (decoder.decodeObjectForKey("automaticThemeSwitchSetting", decoder: { AutomaticThemeSwitchSetting(decoder: $0) }) as? AutomaticThemeSwitchSetting) ?? AutomaticThemeSwitchSetting(trigger: .system, theme: .builtin(.night)) self.largeEmoji = decoder.decodeBoolForKey("largeEmoji", orElse: true) self.disableAnimations = decoder.decodeBoolForKey("disableAnimations", orElse: true) @@ -620,6 +657,7 @@ public struct PresentationThemeSettings: PreferencesEntry { encoder.encodeInt32(self.useSystemFont ? 1 : 0, forKey: "useSystemFont") encoder.encodeInt32(self.fontSize.rawValue, forKey: "f") encoder.encodeInt32(self.listsFontSize.rawValue, forKey: "lf") + encoder.encodeObject(self.chatBubbleSettings, forKey: "chatBubbleSettings") encoder.encodeObject(self.automaticThemeSwitchSetting, forKey: "automaticThemeSwitchSetting") encoder.encodeBool(self.largeEmoji, forKey: "largeEmoji") encoder.encodeBool(self.disableAnimations, forKey: "disableAnimations") @@ -634,39 +672,43 @@ public struct PresentationThemeSettings: PreferencesEntry { } public static func ==(lhs: PresentationThemeSettings, rhs: PresentationThemeSettings) -> Bool { - return lhs.theme == rhs.theme && lhs.themeSpecificAccentColors == rhs.themeSpecificAccentColors && lhs.themeSpecificChatWallpapers == rhs.themeSpecificChatWallpapers && lhs.useSystemFont == rhs.useSystemFont && lhs.fontSize == rhs.fontSize && lhs.listsFontSize == rhs.listsFontSize && lhs.automaticThemeSwitchSetting == rhs.automaticThemeSwitchSetting && lhs.largeEmoji == rhs.largeEmoji && lhs.disableAnimations == rhs.disableAnimations + return lhs.theme == rhs.theme && lhs.themeSpecificAccentColors == rhs.themeSpecificAccentColors && lhs.themeSpecificChatWallpapers == rhs.themeSpecificChatWallpapers && lhs.useSystemFont == rhs.useSystemFont && lhs.fontSize == rhs.fontSize && lhs.listsFontSize == rhs.listsFontSize && lhs.chatBubbleSettings == rhs.chatBubbleSettings && lhs.automaticThemeSwitchSetting == rhs.automaticThemeSwitchSetting && lhs.largeEmoji == rhs.largeEmoji && lhs.disableAnimations == rhs.disableAnimations } public func withUpdatedTheme(_ theme: PresentationThemeReference) -> PresentationThemeSettings { - return PresentationThemeSettings(theme: theme, themeSpecificAccentColors: self.themeSpecificAccentColors, themeSpecificChatWallpapers: self.themeSpecificChatWallpapers, useSystemFont: self.useSystemFont, fontSize: self.fontSize, listsFontSize: self.listsFontSize, automaticThemeSwitchSetting: self.automaticThemeSwitchSetting, largeEmoji: self.largeEmoji, disableAnimations: self.disableAnimations) + return PresentationThemeSettings(theme: theme, themeSpecificAccentColors: self.themeSpecificAccentColors, themeSpecificChatWallpapers: self.themeSpecificChatWallpapers, useSystemFont: self.useSystemFont, fontSize: self.fontSize, listsFontSize: self.listsFontSize, chatBubbleSettings: self.chatBubbleSettings, automaticThemeSwitchSetting: self.automaticThemeSwitchSetting, largeEmoji: self.largeEmoji, disableAnimations: self.disableAnimations) } public func withUpdatedThemeSpecificAccentColors(_ themeSpecificAccentColors: [Int64: PresentationThemeAccentColor]) -> PresentationThemeSettings { - return PresentationThemeSettings(theme: self.theme, themeSpecificAccentColors: themeSpecificAccentColors, themeSpecificChatWallpapers: self.themeSpecificChatWallpapers, useSystemFont: self.useSystemFont, fontSize: self.fontSize, listsFontSize: self.listsFontSize, automaticThemeSwitchSetting: self.automaticThemeSwitchSetting, largeEmoji: self.largeEmoji, disableAnimations: self.disableAnimations) + return PresentationThemeSettings(theme: self.theme, themeSpecificAccentColors: themeSpecificAccentColors, themeSpecificChatWallpapers: self.themeSpecificChatWallpapers, useSystemFont: self.useSystemFont, fontSize: self.fontSize, listsFontSize: self.listsFontSize, chatBubbleSettings: self.chatBubbleSettings, automaticThemeSwitchSetting: self.automaticThemeSwitchSetting, largeEmoji: self.largeEmoji, disableAnimations: self.disableAnimations) } public func withUpdatedThemeSpecificChatWallpapers(_ themeSpecificChatWallpapers: [Int64: TelegramWallpaper]) -> PresentationThemeSettings { - return PresentationThemeSettings(theme: self.theme, themeSpecificAccentColors: self.themeSpecificAccentColors, themeSpecificChatWallpapers: themeSpecificChatWallpapers, useSystemFont: self.useSystemFont, fontSize: self.fontSize, listsFontSize: self.listsFontSize, automaticThemeSwitchSetting: self.automaticThemeSwitchSetting, largeEmoji: self.largeEmoji, disableAnimations: self.disableAnimations) + return PresentationThemeSettings(theme: self.theme, themeSpecificAccentColors: self.themeSpecificAccentColors, themeSpecificChatWallpapers: themeSpecificChatWallpapers, useSystemFont: self.useSystemFont, fontSize: self.fontSize, listsFontSize: self.listsFontSize, chatBubbleSettings: self.chatBubbleSettings, automaticThemeSwitchSetting: self.automaticThemeSwitchSetting, largeEmoji: self.largeEmoji, disableAnimations: self.disableAnimations) } public func withUpdatedUseSystemFont(_ useSystemFont: Bool) -> PresentationThemeSettings { - return PresentationThemeSettings(theme: self.theme, themeSpecificAccentColors: self.themeSpecificAccentColors, themeSpecificChatWallpapers: self.themeSpecificChatWallpapers, useSystemFont: useSystemFont, fontSize: self.fontSize, listsFontSize: self.listsFontSize, automaticThemeSwitchSetting: self.automaticThemeSwitchSetting, largeEmoji: self.largeEmoji, disableAnimations: self.disableAnimations) + return PresentationThemeSettings(theme: self.theme, themeSpecificAccentColors: self.themeSpecificAccentColors, themeSpecificChatWallpapers: self.themeSpecificChatWallpapers, useSystemFont: useSystemFont, fontSize: self.fontSize, listsFontSize: self.listsFontSize, chatBubbleSettings: self.chatBubbleSettings, automaticThemeSwitchSetting: self.automaticThemeSwitchSetting, largeEmoji: self.largeEmoji, disableAnimations: self.disableAnimations) } public func withUpdatedFontSizes(fontSize: PresentationFontSize, listsFontSize: PresentationFontSize) -> PresentationThemeSettings { - return PresentationThemeSettings(theme: self.theme, themeSpecificAccentColors: self.themeSpecificAccentColors, themeSpecificChatWallpapers: self.themeSpecificChatWallpapers, useSystemFont: self.useSystemFont, fontSize: fontSize, listsFontSize: listsFontSize, automaticThemeSwitchSetting: self.automaticThemeSwitchSetting, largeEmoji: self.largeEmoji, disableAnimations: self.disableAnimations) + return PresentationThemeSettings(theme: self.theme, themeSpecificAccentColors: self.themeSpecificAccentColors, themeSpecificChatWallpapers: self.themeSpecificChatWallpapers, useSystemFont: self.useSystemFont, fontSize: fontSize, listsFontSize: listsFontSize, chatBubbleSettings: self.chatBubbleSettings, automaticThemeSwitchSetting: self.automaticThemeSwitchSetting, largeEmoji: self.largeEmoji, disableAnimations: self.disableAnimations) + } + + public func withUpdatedChatBubbleSettings(_ chatBubbleSettings: PresentationChatBubbleSettings) -> PresentationThemeSettings { + return PresentationThemeSettings(theme: self.theme, themeSpecificAccentColors: self.themeSpecificAccentColors, themeSpecificChatWallpapers: self.themeSpecificChatWallpapers, useSystemFont: self.useSystemFont, fontSize: self.fontSize, listsFontSize: self.listsFontSize, chatBubbleSettings: chatBubbleSettings, automaticThemeSwitchSetting: self.automaticThemeSwitchSetting, largeEmoji: self.largeEmoji, disableAnimations: self.disableAnimations) } public func withUpdatedAutomaticThemeSwitchSetting(_ automaticThemeSwitchSetting: AutomaticThemeSwitchSetting) -> PresentationThemeSettings { - return PresentationThemeSettings(theme: self.theme, themeSpecificAccentColors: self.themeSpecificAccentColors, themeSpecificChatWallpapers: self.themeSpecificChatWallpapers, useSystemFont: self.useSystemFont, fontSize: self.fontSize, listsFontSize: self.listsFontSize, automaticThemeSwitchSetting: automaticThemeSwitchSetting, largeEmoji: self.largeEmoji, disableAnimations: self.disableAnimations) + return PresentationThemeSettings(theme: self.theme, themeSpecificAccentColors: self.themeSpecificAccentColors, themeSpecificChatWallpapers: self.themeSpecificChatWallpapers, useSystemFont: self.useSystemFont, fontSize: self.fontSize, listsFontSize: self.listsFontSize, chatBubbleSettings: self.chatBubbleSettings, automaticThemeSwitchSetting: automaticThemeSwitchSetting, largeEmoji: self.largeEmoji, disableAnimations: self.disableAnimations) } public func withUpdatedLargeEmoji(_ largeEmoji: Bool) -> PresentationThemeSettings { - return PresentationThemeSettings(theme: self.theme, themeSpecificAccentColors: self.themeSpecificAccentColors, themeSpecificChatWallpapers: self.themeSpecificChatWallpapers, useSystemFont: self.useSystemFont, fontSize: self.fontSize, listsFontSize: self.listsFontSize, automaticThemeSwitchSetting: self.automaticThemeSwitchSetting, largeEmoji: largeEmoji, disableAnimations: self.disableAnimations) + return PresentationThemeSettings(theme: self.theme, themeSpecificAccentColors: self.themeSpecificAccentColors, themeSpecificChatWallpapers: self.themeSpecificChatWallpapers, useSystemFont: self.useSystemFont, fontSize: self.fontSize, listsFontSize: self.listsFontSize, chatBubbleSettings: self.chatBubbleSettings, automaticThemeSwitchSetting: self.automaticThemeSwitchSetting, largeEmoji: largeEmoji, disableAnimations: self.disableAnimations) } public func withUpdatedDisableAnimations(_ disableAnimations: Bool) -> PresentationThemeSettings { - return PresentationThemeSettings(theme: self.theme, themeSpecificAccentColors: self.themeSpecificAccentColors, themeSpecificChatWallpapers: self.themeSpecificChatWallpapers, useSystemFont: self.useSystemFont, fontSize: self.fontSize, listsFontSize: self.listsFontSize, automaticThemeSwitchSetting: self.automaticThemeSwitchSetting, largeEmoji: self.largeEmoji, disableAnimations: disableAnimations) + return PresentationThemeSettings(theme: self.theme, themeSpecificAccentColors: self.themeSpecificAccentColors, themeSpecificChatWallpapers: self.themeSpecificChatWallpapers, useSystemFont: self.useSystemFont, fontSize: self.fontSize, listsFontSize: self.listsFontSize, chatBubbleSettings: self.chatBubbleSettings, automaticThemeSwitchSetting: self.automaticThemeSwitchSetting, largeEmoji: self.largeEmoji, disableAnimations: disableAnimations) } } diff --git a/submodules/TelegramUniversalVideoContent/Sources/WebEmbedVideoContent.swift b/submodules/TelegramUniversalVideoContent/Sources/WebEmbedVideoContent.swift index bd59af1e90..25d71fd5f1 100644 --- a/submodules/TelegramUniversalVideoContent/Sources/WebEmbedVideoContent.swift +++ b/submodules/TelegramUniversalVideoContent/Sources/WebEmbedVideoContent.swift @@ -151,6 +151,8 @@ private final class WebEmbedVideoContentNode: ASDisplayNode, UniversalVideoConte func playOnceWithSound(playAndRecord: Bool, seek: MediaPlayerSeek, actionAtEnd: MediaPlayerPlayOnceWithSoundActionAtEnd) { if case let .timecode(time) = seek { self.playerNode.seek(timestamp: time) + } else { + self.playerNode.play() } } diff --git a/submodules/WallpaperResources/Sources/WallpaperResources.swift b/submodules/WallpaperResources/Sources/WallpaperResources.swift index ad2d8bb588..0c39b08129 100644 --- a/submodules/WallpaperResources/Sources/WallpaperResources.swift +++ b/submodules/WallpaperResources/Sources/WallpaperResources.swift @@ -429,17 +429,7 @@ public func patternWallpaperImageInternal(thumbnailData: Data?, fullSizeData: Da var scale = scale let drawingRect = arguments.drawingRect - var fittedSize = arguments.imageSize - if abs(fittedSize.width - arguments.boundingSize.width).isLessThanOrEqualTo(CGFloat(1.0)) { - fittedSize.width = arguments.boundingSize.width - } - if abs(fittedSize.height - arguments.boundingSize.height).isLessThanOrEqualTo(CGFloat(1.0)) { - fittedSize.height = arguments.boundingSize.height - } - fittedSize = fittedSize.aspectFilled(arguments.drawingRect.size) - - let fittedRect = CGRect(origin: CGPoint(x: drawingRect.origin.x + (drawingRect.size.width - fittedSize.width) / 2.0, y: drawingRect.origin.y + (drawingRect.size.height - fittedSize.height) / 2.0), size: fittedSize) - + if let customArguments = arguments.custom as? PatternWallpaperArguments, let combinedColor = customArguments.colors.first { if customArguments.preview { scale = max(1.0, UIScreenScale - 1.0) @@ -479,6 +469,17 @@ public func patternWallpaperImageInternal(thumbnailData: Data?, fullSizeData: Da let image = customArguments.preview ? (scaledSizeImage ?? fullSizeImage) : fullSizeImage if let image = image { + var fittedSize = CGSize(width: image.width, height: image.height) + if abs(fittedSize.width - arguments.boundingSize.width).isLessThanOrEqualTo(CGFloat(1.0)) { + fittedSize.width = arguments.boundingSize.width + } + if abs(fittedSize.height - arguments.boundingSize.height).isLessThanOrEqualTo(CGFloat(1.0)) { + fittedSize.height = arguments.boundingSize.height + } + fittedSize = fittedSize.aspectFilled(arguments.drawingRect.size) + + let fittedRect = CGRect(origin: CGPoint(x: drawingRect.origin.x + (drawingRect.size.width - fittedSize.width) / 2.0, y: drawingRect.origin.y + (drawingRect.size.height - fittedSize.height) / 2.0), size: fittedSize) + c.setBlendMode(.normal) c.interpolationQuality = customArguments.preview ? .low : .medium c.clip(to: fittedRect, mask: image) @@ -538,7 +539,7 @@ public func solidColorImage(_ color: UIColor) -> Signal<(TransformImageArguments let context = DrawingContext(size: arguments.drawingSize, clear: true) context.withFlippedContext { c in - c.setFillColor(color.cgColor) + c.setFillColor(color.withAlphaComponent(1.0).cgColor) c.fill(arguments.drawingRect) } @@ -563,7 +564,7 @@ public func gradientImage(_ colors: [UIColor], rotation: Int32? = nil) -> Signal let context = DrawingContext(size: arguments.drawingSize, clear: !arguments.corners.isEmpty) context.withContext { c in - let gradientColors = colors.map { $0.cgColor } as CFArray + let gradientColors = colors.map { $0.withAlphaComponent(1.0).cgColor } as CFArray let delta: CGFloat = 1.0 / (CGFloat(colors.count) - 1.0) var locations: [CGFloat] = [] @@ -779,7 +780,7 @@ public func drawThemeImage(context c: CGContext, theme: PresentationTheme, wallp c.fill(CGRect(origin: CGPoint(x: 0.0, y: drawingRect.height - 42.0), size: CGSize(width: drawingRect.width, height: 42.0))) c.setFillColor(theme.rootController.navigationBar.separatorColor.cgColor) - c.fill(CGRect(origin: CGPoint(x: 1.0, y: drawingRect.height - 43.0), size: CGSize(width: drawingRect.width - 2.0, height: 1.0))) + c.fill(CGRect(origin: CGPoint(x: 1.0, y: drawingRect.height - 42.0 - UIScreenPixel), size: CGSize(width: drawingRect.width - 2.0, height: UIScreenPixel))) c.setFillColor(theme.rootController.navigationBar.secondaryTextColor.cgColor) c.fillEllipse(in: CGRect(origin: CGPoint(x: drawingRect.width - 28.0 - 7.0, y: drawingRect.height - 7.0 - 28.0 - UIScreenPixel), size: CGSize(width: 28.0, height: 28.0))) @@ -796,7 +797,7 @@ public func drawThemeImage(context c: CGContext, theme: PresentationTheme, wallp c.fill(CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: drawingRect.width, height: 42.0))) c.setFillColor(theme.chat.inputPanel.panelSeparatorColor.cgColor) - c.fill(CGRect(origin: CGPoint(x: 1.0, y: 42.0), size: CGSize(width: drawingRect.width - 2.0, height: 1.0))) + c.fill(CGRect(origin: CGPoint(x: 1.0, y: 42.0), size: CGSize(width: drawingRect.width - 2.0, height: UIScreenPixel))) } c.setFillColor(theme.chat.inputPanel.inputBackgroundColor.cgColor) @@ -1323,7 +1324,6 @@ public func themeIconImage(account: Account, accountManager: AccountManager, the c.translateBy(x: drawingRect.width / 2.0, y: drawingRect.height / 2.0) c.scaleBy(x: 1.0, y: -1.0) c.translateBy(x: -drawingRect.width / 2.0, y: -drawingRect.height / 2.0) - c.draw(incoming!.cgImage!, in: CGRect(x: 9.0, y: 34.0, width: 57.0, height: 16.0)) c.translateBy(x: drawingRect.width / 2.0, y: drawingRect.height / 2.0) diff --git a/submodules/WatchBridge/Sources/WatchRequestHandlers.swift b/submodules/WatchBridge/Sources/WatchRequestHandlers.swift index 95f32f18e2..0609691bde 100644 --- a/submodules/WatchBridge/Sources/WatchRequestHandlers.swift +++ b/submodules/WatchBridge/Sources/WatchRequestHandlers.swift @@ -400,7 +400,7 @@ final class WatchMediaHandler: WatchRequestHandler { } } |> mapToSignal({ peer -> Signal in if let peer = peer, let representation = peer.smallProfileImage { - let imageData = peerAvatarImageData(account: context.account, peer: peer, authorOfMessage: nil, representation: representation, synchronousLoad: false) + let imageData = peerAvatarImageData(account: context.account, peerReference: PeerReference(peer), authorOfMessage: nil, representation: representation, synchronousLoad: false) if let imageData = imageData { return imageData |> map { data -> UIImage? in diff --git a/submodules/libtgvoip/BUCK b/submodules/libtgvoip/BUCK index 07c8a2a579..bb0302b98b 100644 --- a/submodules/libtgvoip/BUCK +++ b/submodules/libtgvoip/BUCK @@ -44,6 +44,7 @@ static_library( '-DWEBRTC_NS_FLOAT', '-DWEBRTC_IOS', '-DWEBRTC_HAS_NEON', + '-DTGVOIP_USE_INSTALLED_OPUS', ]), ('.*', [ '-DTGVOIP_USE_CUSTOM_CRYPTO', @@ -52,6 +53,7 @@ static_library( '-DTGVOIP_HAVE_TGLOG', '-DWEBRTC_NS_FLOAT', '-DWEBRTC_IOS', + '-DTGVOIP_USE_INSTALLED_OPUS', ]), ], deps = [ @@ -67,4 +69,4 @@ static_library( "$SDKROOT/System/Library/Frameworks/CoreMedia.framework", "$SDKROOT/System/Library/Frameworks/AVFoundation.framework", ], -) \ No newline at end of file +) diff --git a/submodules/libtgvoip/OngoingCallThreadLocalContext.mm b/submodules/libtgvoip/OngoingCallThreadLocalContext.mm index be12b7e620..1556821dfb 100644 --- a/submodules/libtgvoip/OngoingCallThreadLocalContext.mm +++ b/submodules/libtgvoip/OngoingCallThreadLocalContext.mm @@ -248,7 +248,10 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL; TgVoipEndpoint endpoint; endpoint.endpointId = connection.connectionId; - endpoint.host = std::string(connection.ip.UTF8String); + endpoint.host = { + .ipv4 = std::string(connection.ip.UTF8String), + .ipv6 = std::string(connection.ipv6.UTF8String) + }; endpoint.port = (uint16_t)connection.port; endpoint.type = TgVoipEndpointType::UdpRelay; memcpy(endpoint.peerTag, peerTag, 16); @@ -277,14 +280,26 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL; .isOutgoing = isOutgoing, }; + /* + TgVoipConfig const &config, + TgVoipPersistentState const &persistentState, + std::vector const &endpoints, + std::unique_ptr const &proxy, + TgVoipNetworkType initialNetworkType, + TgVoipEncryptionKey const &encryptionKey + #ifdef TGVOIP_USE_CUSTOM_CRYPTO + , + TgVoipCrypto const &crypto + */ + _tgVoip = TgVoip::makeInstance( - crypto, config, { derivedStateValue }, endpoints, proxyValue, callControllerNetworkTypeForType(networkType), - encryptionKey + encryptionKey, + crypto ); _state = OngoingCallStateInitializing; diff --git a/submodules/libtgvoip/libtgvoip b/submodules/libtgvoip/libtgvoip index 77e1f5d27a..24f778d589 160000 --- a/submodules/libtgvoip/libtgvoip +++ b/submodules/libtgvoip/libtgvoip @@ -1 +1 @@ -Subproject commit 77e1f5d27a2253a3263e4e98de7adfea84f0f562 +Subproject commit 24f778d58931e6924bb12bbfaa2422ed058ab193 diff --git a/submodules/rlottie/rlottie b/submodules/rlottie/rlottie index a09896b3e7..0ee2e9c584 160000 --- a/submodules/rlottie/rlottie +++ b/submodules/rlottie/rlottie @@ -1 +1 @@ -Subproject commit a09896b3e72e76681c12e80572a7d570108cf885 +Subproject commit 0ee2e9c5843257ccd11672611829b9bb5d02aa98