From 2c9563185636fce87e538f45f69a4864c02bc9ae Mon Sep 17 00:00:00 2001 From: Peter <> Date: Mon, 22 Oct 2018 00:37:34 +0300 Subject: [PATCH 1/3] no message --- TelegramUI.xcodeproj/project.pbxproj | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/TelegramUI.xcodeproj/project.pbxproj b/TelegramUI.xcodeproj/project.pbxproj index c321e24a4c..9d7b7dbd1f 100644 --- a/TelegramUI.xcodeproj/project.pbxproj +++ b/TelegramUI.xcodeproj/project.pbxproj @@ -107,6 +107,7 @@ D01C06C01FBF118A001561AB /* MessageUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01C06BF1FBF118A001561AB /* MessageUtils.swift */; }; D01C99781F4F382C00DCFAF6 /* InstantPageSettingsItemTheme.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01C99771F4F382C00DCFAF6 /* InstantPageSettingsItemTheme.swift */; }; D01DBA9B209CC6AD00C64E64 /* ChatLinkPreview.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01DBA9A209CC6AD00C64E64 /* ChatLinkPreview.swift */; }; + D01FB437217CEC62009C6134 /* FetchVideoThumbnail.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01FB436217CEC62009C6134 /* FetchVideoThumbnail.swift */; }; D0208AD51FA33D14001F0D5F /* RaiseToListenActivator.h in Headers */ = {isa = PBXBuildFile; fileRef = D0208AD31FA33D14001F0D5F /* RaiseToListenActivator.h */; }; D0208AD61FA33D14001F0D5F /* RaiseToListenActivator.m in Sources */ = {isa = PBXBuildFile; fileRef = D0208AD41FA33D14001F0D5F /* RaiseToListenActivator.m */; }; D0208AD91FA34017001F0D5F /* DeviceProximityManager.h in Headers */ = {isa = PBXBuildFile; fileRef = D0208AD71FA34017001F0D5F /* DeviceProximityManager.h */; }; @@ -1200,6 +1201,7 @@ D01D6BFB1E42AB3C006151C6 /* EmojiUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EmojiUtils.swift; sourceTree = ""; }; D01DBA9A209CC6AD00C64E64 /* ChatLinkPreview.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatLinkPreview.swift; sourceTree = ""; }; D01F66121DE8903300345CBE /* ChatTextInputMediaRecordingButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChatTextInputMediaRecordingButton.swift; sourceTree = ""; }; + D01FB436217CEC62009C6134 /* FetchVideoThumbnail.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FetchVideoThumbnail.swift; sourceTree = ""; }; D0208AD31FA33D14001F0D5F /* RaiseToListenActivator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RaiseToListenActivator.h; sourceTree = ""; }; D0208AD41FA33D14001F0D5F /* RaiseToListenActivator.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RaiseToListenActivator.m; sourceTree = ""; }; D0208AD71FA34017001F0D5F /* DeviceProximityManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DeviceProximityManager.h; sourceTree = ""; }; @@ -4426,6 +4428,7 @@ D007019D2029EFDD006B9E34 /* ICloudResources.swift */, D09F9DCE20768DAF00DB4DE1 /* SecureIdLocalResource.swift */, 0941A9A3210B0E2E00EBE194 /* OpenInAppIconResources.swift */, + D01FB436217CEC62009C6134 /* FetchVideoThumbnail.swift */, ); name = Resources; sourceTree = ""; @@ -5572,6 +5575,7 @@ D0DFD5E21FCE2BA50039B3B1 /* CalculatingCacheSizeItem.swift in Sources */, D0EC6E631EB9F58900EBF1C3 /* TwoStepVerificationUnlockController.swift in Sources */, D0EC6E641EB9F58900EBF1C3 /* TwoStepVerificationPasswordEntryController.swift in Sources */, + D01FB437217CEC62009C6134 /* FetchVideoThumbnail.swift in Sources */, D0EC6E651EB9F58900EBF1C3 /* TwoStepVerificationResetController.swift in Sources */, D0EC6E661EB9F58900EBF1C3 /* PasscodeOptionsController.swift in Sources */, D0383EDE207D1A1600C45548 /* emoji_suggestions.cpp in Sources */, From 73f230a9af91cecc4921bc7560fab966582d644b Mon Sep 17 00:00:00 2001 From: Peter <> Date: Fri, 26 Oct 2018 19:33:45 +0300 Subject: [PATCH 2/3] Added video streaming Updated message sending animation --- TelegramUI.xcodeproj/project.pbxproj | 24 + TelegramUI/AvatarNode.swift | 2 +- TelegramUI/CallListControllerNode.swift | 4 +- TelegramUI/CallListViewTransition.swift | 4 +- .../ChannelMembersSearchContainerNode.swift | 2 +- .../ChannelMembersSearchControllerNode.swift | 4 +- TelegramUI/ChatController.swift | 31 +- TelegramUI/ChatControllerInteraction.swift | 9 +- TelegramUI/ChatControllerNode.swift | 2 +- .../ChatHistorySearchContainerNode.swift | 2 +- .../ChatInterfaceStateContextMenus.swift | 8 +- TelegramUI/ChatListControllerNode.swift | 2 +- TelegramUI/ChatListNode.swift | 4 +- TelegramUI/ChatListSearchContainerNode.swift | 2 +- .../ChatListSearchRecentPeersNode.swift | 2 +- TelegramUI/ChatListViewTransition.swift | 4 +- TelegramUI/ChatMediaInputNode.swift | 4 +- TelegramUI/ChatMediaInputTrendingPane.swift | 4 +- .../ChatMessageAttachedContentNode.swift | 10 +- TelegramUI/ChatMessageBubbleItemNode.swift | 2 +- .../ChatMessageContactBubbleContentNode.swift | 4 +- .../ChatMessageFileBubbleContentNode.swift | 2 +- .../ChatMessageGameBubbleContentNode.swift | 2 +- ...atMessageInteractiveInstantVideoNode.swift | 2 +- .../ChatMessageInteractiveMediaBadge.swift | 129 ++++- .../ChatMessageInteractiveMediaNode.swift | 69 ++- TelegramUI/ChatMessageItemView.swift | 2 +- .../ChatMessageMapBubbleContentNode.swift | 2 +- .../ChatMessageMediaBubbleContentNode.swift | 6 +- TelegramUI/ChatMessageStickerItemNode.swift | 2 +- .../ChatMessageWebpageBubbleContentNode.swift | 4 +- .../ChatRecentActionsControllerNode.swift | 6 +- .../CommandChatInputContextPanelNode.swift | 4 +- TelegramUI/ContactListNode.swift | 4 +- TelegramUI/ContactsSearchContainerNode.swift | 2 +- TelegramUI/CreatePasswordController.swift | 8 +- TelegramUI/DocumentPreviewController.swift | 48 +- .../EmojisChatInputContextPanelNode.swift | 4 +- .../FFMpegMediaFrameSourceContext.swift | 22 +- TelegramUI/FeedGroupingControllerNode.swift | 2 +- TelegramUI/FetchCachedRepresentations.swift | 54 +- TelegramUI/FetchResource.swift | 2 +- TelegramUI/FetchVideoThumbnail.swift | 403 +++++++++++++++ TelegramUI/GalleryController.swift | 11 +- TelegramUI/GridMessageItem.swift | 4 +- .../HashtagChatInputContextPanelNode.swift | 4 +- TelegramUI/HashtagSearchControllerNode.swift | 2 +- ...textResultsChatInputContextPanelNode.swift | 2 +- TelegramUI/InviteContactsControllerNode.swift | 4 +- TelegramUI/IsMediaStreamable.swift | 13 + TelegramUI/ItemListControllerNode.swift | 4 +- .../LanguageLinkPreviewContentNode.swift | 102 ++++ .../LanguageLinkPreviewController.swift | 118 +++++ .../LanguageLinkPreviewControllerNode.swift | 473 ++++++++++++++++++ TelegramUI/ListMessageFileItemNode.swift | 2 +- TelegramUI/ListMessageSnippetItemNode.swift | 6 +- TelegramUI/MediaPlayerAudioRenderer.swift | 15 +- .../MentionChatInputContextPanelNode.swift | 4 +- TelegramUI/NotificationExceptions.swift | 2 +- TelegramUI/NotificationsAndSounds.swift | 2 +- TelegramUI/OpenResolvedUrl.swift | 9 +- TelegramUI/OpenUrl.swift | 16 + TelegramUI/OverlayPlayerControllerNode.swift | 10 +- .../PeerMediaCollectionController.swift | 2 +- .../PeerMediaCollectionControllerNode.swift | 4 +- TelegramUI/PeerSelectionControllerNode.swift | 2 +- TelegramUI/PhotoResources.swift | 63 ++- .../PreparedChatHistoryViewTransition.swift | 14 +- TelegramUI/PresentationResourceKey.swift | 1 + TelegramUI/PresentationResourcesChat.swift | 12 + TelegramUI/RadialProgressContentNode.swift | 2 +- TelegramUI/SecureIdLocalResource.swift | 2 +- TelegramUI/StickerPreviewPeekContent.swift | 2 +- TelegramUI/StickerResources.swift | 64 +-- TelegramUI/ThemeSettingsChatPreviewItem.swift | 2 +- ...pVerificationPasswordEntryController.swift | 8 +- TelegramUI/UrlHandling.swift | 36 +- ...textResultsChatInputContextPanelNode.swift | 4 +- 78 files changed, 1659 insertions(+), 271 deletions(-) create mode 100644 TelegramUI/FetchVideoThumbnail.swift create mode 100644 TelegramUI/IsMediaStreamable.swift create mode 100644 TelegramUI/LanguageLinkPreviewContentNode.swift create mode 100644 TelegramUI/LanguageLinkPreviewController.swift create mode 100644 TelegramUI/LanguageLinkPreviewControllerNode.swift diff --git a/TelegramUI.xcodeproj/project.pbxproj b/TelegramUI.xcodeproj/project.pbxproj index 9d7b7dbd1f..259b9cfa7e 100644 --- a/TelegramUI.xcodeproj/project.pbxproj +++ b/TelegramUI.xcodeproj/project.pbxproj @@ -68,6 +68,7 @@ D0104F281F47171F004E4881 /* InstantPageGalleryController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0104F271F47171F004E4881 /* InstantPageGalleryController.swift */; }; D0104F2A1F471DA6004E4881 /* InstantImageGalleryItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0104F291F471DA6004E4881 /* InstantImageGalleryItem.swift */; }; D0104F2C1F471EEB004E4881 /* InstantPageGalleryFooterContentNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0104F2B1F471EEB004E4881 /* InstantPageGalleryFooterContentNode.swift */; }; + D0105D682182680E007C04A7 /* IsMediaStreamable.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0105D672182680E007C04A7 /* IsMediaStreamable.swift */; }; D0119CD020CAE75F00895300 /* LegacySecureIdAttachmentMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0119CCF20CAE75F00895300 /* LegacySecureIdAttachmentMenu.swift */; }; D013630C208FA62400EB3653 /* SecureIdDocumentGalleryFooterContentNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D013630B208FA62400EB3653 /* SecureIdDocumentGalleryFooterContentNode.swift */; }; D0147BA7206E8B4F00E40378 /* SecureIdAuthAcceptNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0147BA6206E8B4F00E40378 /* SecureIdAuthAcceptNode.swift */; }; @@ -251,6 +252,9 @@ D07E413B208A432100FCA8F0 /* ChatListTitleProxyNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D07E413A208A432100FCA8F0 /* ChatListTitleProxyNode.swift */; }; D07E413D208A494D00FCA8F0 /* ProxyServerActionSheetController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D07E413C208A494D00FCA8F0 /* ProxyServerActionSheetController.swift */; }; D080B27F1F4C7C6000AA3847 /* InstantPageManagedMediaId.swift in Sources */ = {isa = PBXBuildFile; fileRef = D080B27E1F4C7C6000AA3847 /* InstantPageManagedMediaId.swift */; }; + D081E104217F57D2003CD921 /* LanguageLinkPreviewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D081E103217F57D2003CD921 /* LanguageLinkPreviewController.swift */; }; + D081E106217F5834003CD921 /* LanguageLinkPreviewControllerNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D081E105217F5834003CD921 /* LanguageLinkPreviewControllerNode.swift */; }; + D081E108217F583F003CD921 /* LanguageLinkPreviewContentNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D081E107217F583F003CD921 /* LanguageLinkPreviewContentNode.swift */; }; D083491C209361DC008CFD52 /* AvatarGalleryItemFooterContentNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D083491B209361DC008CFD52 /* AvatarGalleryItemFooterContentNode.swift */; }; D084023420E295F000065674 /* GroupStickerPackSetupController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D084023320E295F000065674 /* GroupStickerPackSetupController.swift */; }; D087BFAD1F741B9D003FD209 /* ShareContentContainerNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D087BFAC1F741B9D003FD209 /* ShareContentContainerNode.swift */; }; @@ -1135,6 +1139,7 @@ D0104F291F471DA6004E4881 /* InstantImageGalleryItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InstantImageGalleryItem.swift; sourceTree = ""; }; D0104F2B1F471EEB004E4881 /* InstantPageGalleryFooterContentNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InstantPageGalleryFooterContentNode.swift; sourceTree = ""; }; D0105D591D80B957008755D8 /* ChatChannelSubscriberInputPanelNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChatChannelSubscriberInputPanelNode.swift; sourceTree = ""; }; + D0105D672182680E007C04A7 /* IsMediaStreamable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IsMediaStreamable.swift; sourceTree = ""; }; D010C2C91EA7A59F00F41B96 /* PresentationThemeSettings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PresentationThemeSettings.swift; sourceTree = ""; }; D010C2CB1EA7D74800F41B96 /* DefaultPresentationTheme.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DefaultPresentationTheme.swift; sourceTree = ""; }; D010C2CD1EA7DDD600F41B96 /* DefaultPresentationStrings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DefaultPresentationStrings.swift; sourceTree = ""; }; @@ -1555,6 +1560,9 @@ D07E413A208A432100FCA8F0 /* ChatListTitleProxyNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatListTitleProxyNode.swift; sourceTree = ""; }; D07E413C208A494D00FCA8F0 /* ProxyServerActionSheetController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProxyServerActionSheetController.swift; sourceTree = ""; }; D080B27E1F4C7C6000AA3847 /* InstantPageManagedMediaId.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstantPageManagedMediaId.swift; sourceTree = ""; }; + D081E103217F57D2003CD921 /* LanguageLinkPreviewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LanguageLinkPreviewController.swift; sourceTree = ""; }; + D081E105217F5834003CD921 /* LanguageLinkPreviewControllerNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LanguageLinkPreviewControllerNode.swift; sourceTree = ""; }; + D081E107217F583F003CD921 /* LanguageLinkPreviewContentNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LanguageLinkPreviewContentNode.swift; sourceTree = ""; }; D083491B209361DC008CFD52 /* AvatarGalleryItemFooterContentNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AvatarGalleryItemFooterContentNode.swift; sourceTree = ""; }; D084023320E295F000065674 /* GroupStickerPackSetupController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupStickerPackSetupController.swift; sourceTree = ""; }; D08774F71E3DE7BF00A97350 /* ItemListEditableDeleteControlNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ItemListEditableDeleteControlNode.swift; sourceTree = ""; }; @@ -3047,6 +3055,16 @@ name = "Chat List Node"; sourceTree = ""; }; + D081E102217F57B2003CD921 /* Language Link Preview */ = { + isa = PBXGroup; + children = ( + D081E103217F57D2003CD921 /* LanguageLinkPreviewController.swift */, + D081E105217F5834003CD921 /* LanguageLinkPreviewControllerNode.swift */, + D081E107217F583F003CD921 /* LanguageLinkPreviewContentNode.swift */, + ); + name = "Language Link Preview"; + sourceTree = ""; + }; D087750A1E3E7A6D00A97350 /* Settings */ = { isa = PBXGroup; children = ( @@ -3910,6 +3928,7 @@ D0177B831DFB095000A5083A /* FileMediaResourceStatus.swift */, D0FA08BF20483F9600DD23FC /* ExtractVideoData.swift */, D0ADF965212E05A300310BBC /* TonePlayer.swift */, + D0105D672182680E007C04A7 /* IsMediaStreamable.swift */, ); name = Media; sourceTree = ""; @@ -4226,6 +4245,7 @@ D0D748041E7AF62000F4B1F6 /* Stickers */, D020A9D81FEAE611008C66F7 /* Player */, D01C06AD1FBB45ED001561AB /* Join Link Preview */, + D081E102217F57B2003CD921 /* Language Link Preview */, ); name = Media; sourceTree = ""; @@ -4823,6 +4843,7 @@ D01C06C01FBF118A001561AB /* MessageUtils.swift in Sources */, D0104F281F47171F004E4881 /* InstantPageGalleryController.swift in Sources */, D0EC6CC81EB9F58800EBF1C3 /* ProgressiveImage.swift in Sources */, + D081E108217F583F003CD921 /* LanguageLinkPreviewContentNode.swift in Sources */, D0EC6CC91EB9F58800EBF1C3 /* WebP.swift in Sources */, D0EC6CCA1EB9F58800EBF1C3 /* PeerPresenceStatusManager.swift in Sources */, D0EC6CCB1EB9F58800EBF1C3 /* ApplicationSpecificData.swift in Sources */, @@ -5005,6 +5026,7 @@ D0AFCC791F4C8D2C000720C6 /* InstantPageSlideshowItem.swift in Sources */, D04281EF200E3D88009DDE36 /* GroupInfoSearchItem.swift in Sources */, D02660941F34CE5C000E2DC5 /* LegacyLocationVenueIconDataSource.swift in Sources */, + D081E104217F57D2003CD921 /* LanguageLinkPreviewController.swift in Sources */, D0EC6FFD1EBA1F2400EBF1C3 /* OngoingCallThreadLocalContext.mm in Sources */, D0E9BAE21F0574D800F079A4 /* STPBankAccount.m in Sources */, D0104F2A1F471DA6004E4881 /* InstantImageGalleryItem.swift in Sources */, @@ -5041,6 +5063,7 @@ D06F1EA41F6C0A5D00FE8B74 /* ChatHistorySearchContainerNode.swift in Sources */, D0EC6D391EB9F58800EBF1C3 /* ImageContainingNode.swift in Sources */, D0EC6D3A1EB9F58800EBF1C3 /* AudioWaveformNode.swift in Sources */, + D0105D682182680E007C04A7 /* IsMediaStreamable.swift in Sources */, D0EB41F71F30D4A800838FE6 /* LegacyMediaLocations.swift in Sources */, D0EC6D3B1EB9F58800EBF1C3 /* EditableTokenListNode.swift in Sources */, D0EC6D3C1EB9F58800EBF1C3 /* PhoneInputNode.swift in Sources */, @@ -5548,6 +5571,7 @@ D0EC6E531EB9F58900EBF1C3 /* ChannelMembersController.swift in Sources */, D02B676320800A00001A864A /* StickerPaneSearchBarPlaceholderItem.swift in Sources */, D093D8242069A06600BC3599 /* FormControllerScrollerNode.swift in Sources */, + D081E106217F5834003CD921 /* LanguageLinkPreviewControllerNode.swift in Sources */, D093D7E72063E57F00BC3599 /* BotPaymentActionItemNode.swift in Sources */, D01C06BA1FBBB076001561AB /* ItemListSelectableControlNode.swift in Sources */, D0EC6E541EB9F58900EBF1C3 /* ConvertToSupergroupController.swift in Sources */, diff --git a/TelegramUI/AvatarNode.swift b/TelegramUI/AvatarNode.swift index d69987481c..95c1f132d3 100644 --- a/TelegramUI/AvatarNode.swift +++ b/TelegramUI/AvatarNode.swift @@ -311,7 +311,7 @@ public final class AvatarNode: ASDisplayNode { if peerId.namespace == -1 { colorIndex = -1 } else { - colorIndex = abs(Int(accountPeerId.id + peerId.id)) + colorIndex = abs(Int(clamping: accountPeerId.id &+ peerId.id)) } } else { colorIndex = -1 diff --git a/TelegramUI/CallListControllerNode.swift b/TelegramUI/CallListControllerNode.swift index 7b3069af48..597fb9db8a 100644 --- a/TelegramUI/CallListControllerNode.swift +++ b/TelegramUI/CallListControllerNode.swift @@ -505,7 +505,7 @@ final class CallListControllerNode: ASDisplayNode { func scrollToLatest() { if let view = self.callListView?.originalView, view.later == nil { - self.listNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous], scrollToItem: ListViewScrollToItem(index: 0, position: .top(0.0), animated: true, curve: .Default, directionHint: .Up), updateSizeAndInsets: nil, stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in }) + self.listNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous], scrollToItem: ListViewScrollToItem(index: 0, position: .top(0.0), animated: true, curve: .Default(duration: nil), directionHint: .Up), updateSizeAndInsets: nil, stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in }) } else { let location: CallListNodeLocation = .scroll(index: MessageIndex.absoluteUpperBound(), sourceIndex: MessageIndex.absoluteLowerBound(), scrollPosition: .top(0.0), animated: true) self.currentLocationAndType = CallListNodeLocationAndType(location: location, type: self.currentLocationAndType.type) @@ -562,7 +562,7 @@ final class CallListControllerNode: ASDisplayNode { if curve == 7 { listViewCurve = .Spring(duration: duration) } else { - listViewCurve = .Default + listViewCurve = .Default(duration: duration) } let updateSizeAndInsets = ListViewUpdateSizeAndInsets(size: layout.size, insets: insets, duration: duration, curve: listViewCurve) diff --git a/TelegramUI/CallListViewTransition.swift b/TelegramUI/CallListViewTransition.swift index fdf0d6cdce..b982dc2d76 100644 --- a/TelegramUI/CallListViewTransition.swift +++ b/TelegramUI/CallListViewTransition.swift @@ -150,7 +150,7 @@ func preparedCallListNodeViewTransition(from fromView: CallListNodeView?, to toV var index = toView.filteredEntries.count - 1 for entry in toView.filteredEntries { if entry.index >= scrollIndex { - scrollToItem = ListViewScrollToItem(index: index, position: position, animated: animated, curve: .Default, directionHint: directionHint) + scrollToItem = ListViewScrollToItem(index: index, position: position, animated: animated, curve: .Default(duration: nil), directionHint: directionHint) break } index -= 1 @@ -160,7 +160,7 @@ func preparedCallListNodeViewTransition(from fromView: CallListNodeView?, to toV var index = 0 for entry in toView.filteredEntries.reversed() { if entry.index < scrollIndex { - scrollToItem = ListViewScrollToItem(index: index, position: position, animated: animated, curve: .Default, directionHint: directionHint) + scrollToItem = ListViewScrollToItem(index: index, position: position, animated: animated, curve: .Default(duration: nil), directionHint: directionHint) break } index += 1 diff --git a/TelegramUI/ChannelMembersSearchContainerNode.swift b/TelegramUI/ChannelMembersSearchContainerNode.swift index 6af3a6ac1a..742228c46e 100644 --- a/TelegramUI/ChannelMembersSearchContainerNode.swift +++ b/TelegramUI/ChannelMembersSearchContainerNode.swift @@ -464,7 +464,7 @@ final class ChannelMembersSearchContainerNode: SearchDisplayControllerContentNod if curve == 7 { listViewCurve = .Spring(duration: duration) } else { - listViewCurve = .Default + listViewCurve = .Default(duration: nil) } self.listNode.frame = CGRect(origin: CGPoint(), size: layout.size) diff --git a/TelegramUI/ChannelMembersSearchControllerNode.swift b/TelegramUI/ChannelMembersSearchControllerNode.swift index 0a443a797c..692a1d5607 100644 --- a/TelegramUI/ChannelMembersSearchControllerNode.swift +++ b/TelegramUI/ChannelMembersSearchControllerNode.swift @@ -253,7 +253,7 @@ class ChannelMembersSearchControllerNode: ASDisplayNode { if curve == 7 { listViewCurve = .Spring(duration: duration) } else { - listViewCurve = .Default + listViewCurve = .Default(duration: duration) } let updateSizeAndInsets = ListViewUpdateSizeAndInsets(size: layout.size, insets: insets, duration: duration, curve: listViewCurve) @@ -358,6 +358,6 @@ class ChannelMembersSearchControllerNode: ASDisplayNode { } func scrollToTop() { - self.listNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous, .LowLatency], scrollToItem: ListViewScrollToItem(index: 0, position: .top(0.0), animated: true, curve: .Default, directionHint: .Up), updateSizeAndInsets: nil, stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in }) + self.listNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous, .LowLatency], scrollToItem: ListViewScrollToItem(index: 0, position: .top(0.0), animated: true, curve: .Default(duration: nil), directionHint: .Up), updateSizeAndInsets: nil, stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in }) } } diff --git a/TelegramUI/ChatController.swift b/TelegramUI/ChatController.swift index d7f60e491a..d38de12151 100644 --- a/TelegramUI/ChatController.swift +++ b/TelegramUI/ChatController.swift @@ -278,7 +278,7 @@ public final class ChatController: TelegramController, KeyShortcutResponder, UID return true } - let controllerInteraction = ChatControllerInteraction(openMessage: { [weak self] message in + let controllerInteraction = ChatControllerInteraction(openMessage: { [weak self] message, mode in guard let strongSelf = self, strongSelf.isNodeLoaded, let message = strongSelf.chatDisplayNode.historyNode.messageInCurrentHistoryView(message.id) else { return false } @@ -298,6 +298,13 @@ public final class ChatController: TelegramController, KeyShortcutResponder, UID } case let .photoUpdated(image): openMessageByAction = image != nil + case .gameScore: + for attribute in message.attributes { + if let attribute = attribute as? ReplyMessageAttribute { + strongSelf.navigateToMessage(from: message.id, to: .id(attribute.messageId)) + break + } + } default: break } @@ -306,6 +313,12 @@ public final class ChatController: TelegramController, KeyShortcutResponder, UID } } } + + if case .stream = mode { + strongSelf.debugStreamSingleVideo(message.id) + return true + } + return openChatMessage(account: account, message: message, standalone: false, reverseMessageGalleryOrder: false, navigationController: strongSelf.navigationController as? NavigationController, dismissInput: { self?.chatDisplayNode.dismissInput() }, present: { c, a in @@ -395,9 +408,7 @@ public final class ChatController: TelegramController, KeyShortcutResponder, UID break } } - let _ = contextMenuForChatPresentationIntefaceState(chatPresentationInterfaceState: strongSelf.presentationInterfaceState, account: strongSelf.account, messages: updatedMessages, controllerInteraction: strongSelf.controllerInteraction, interfaceInteraction: strongSelf.interfaceInteraction, debugStreamSingleVideo: { id in - self?.debugStreamSingleVideo(id) - }).start(next: { actions in + let _ = contextMenuForChatPresentationIntefaceState(chatPresentationInterfaceState: strongSelf.presentationInterfaceState, account: strongSelf.account, messages: updatedMessages, controllerInteraction: strongSelf.controllerInteraction, interfaceInteraction: strongSelf.interfaceInteraction).start(next: { actions in guard let strongSelf = self, !actions.isEmpty else { return } @@ -1710,7 +1721,7 @@ public final class ChatController: TelegramController, KeyShortcutResponder, UID if let strongSelf = self, let validLayout = strongSelf.validLayout { var mappedTransition: (ChatHistoryListViewTransition, ListViewUpdateSizeAndInsets?)? - strongSelf.chatDisplayNode.containerLayoutUpdated(validLayout, navigationBarHeight: strongSelf.navigationHeight, transition: .animated(duration: 0.4, curve: .spring), listViewTransaction: { updateSizeAndInsets, _, _ in + strongSelf.chatDisplayNode.containerLayoutUpdated(validLayout, navigationBarHeight: strongSelf.navigationHeight, transition: .animated(duration: 0.2, curve: .easeInOut), listViewTransaction: { updateSizeAndInsets, _, _ in var options = transition.options let _ = options.insert(.Synchronous) let _ = options.insert(.LowLatency) @@ -1731,7 +1742,7 @@ public final class ChatController: TelegramController, KeyShortcutResponder, UID insertItems.append(ListViewInsertItem(index: item.index, previousIndex: item.previousIndex, item: item.item, directionHint: item.directionHint == .Down ? .Up : nil)) } - let scrollToItem = ListViewScrollToItem(index: 0, position: .top(0.0), animated: true, curve: .Spring(duration: 0.4), directionHint: .Up) + let scrollToItem = ListViewScrollToItem(index: 0, position: .top(0.0), animated: true, curve: .Default(duration: 0.2), directionHint: .Up) var stationaryItemRange: (Int, Int)? if let maxInsertedItem = maxInsertedItem { @@ -2824,7 +2835,9 @@ public final class ChatController: TelegramController, KeyShortcutResponder, UID return } if let currentItem = currentItem?.id as? PeerMessagesMediaPlaylistItemId, let previousItem = previousItem?.id as? PeerMessagesMediaPlaylistItemId, previousItem.messageId.peerId == peerId, currentItem.messageId.peerId == peerId, currentItem.messageId != previousItem.messageId { - strongSelf.navigateToMessage(from: nil, to: .id(currentItem.messageId), scrollPosition: .center(.bottom), rememberInStack: false, animated: true, completion: nil) + if strongSelf.chatDisplayNode.historyNode.messageInCurrentHistoryView(currentItem.messageId) != nil { + strongSelf.navigateToMessage(from: nil, to: .id(currentItem.messageId), scrollPosition: .center(.bottom), rememberInStack: false, animated: true, completion: nil) + } } } } @@ -3797,7 +3810,7 @@ public final class ChatController: TelegramController, KeyShortcutResponder, UID private func activateRaiseGesture() { if let messageToListen = self.firstLoadedMessageToListen() { - let _ = self.controllerInteraction?.openMessage(messageToListen) + let _ = self.controllerInteraction?.openMessage(messageToListen, .default) } else { self.requestAudioRecorder(beginWithTone: true) } @@ -4587,6 +4600,8 @@ public final class ChatController: TelegramController, KeyShortcutResponder, UID default: break } + }, sendFile: { [weak self] f in + self?.interfaceInteraction?.sendSticker(f) }, present: { [weak self] c, a in self?.present(c, in: .window(.root), with: a) }, dismissInput: { [weak self] in diff --git a/TelegramUI/ChatControllerInteraction.swift b/TelegramUI/ChatControllerInteraction.swift index 5299618d5e..24d65859d4 100644 --- a/TelegramUI/ChatControllerInteraction.swift +++ b/TelegramUI/ChatControllerInteraction.swift @@ -37,8 +37,13 @@ public enum ChatControllerInteractionLongTapAction { case hashtag(String) } +public enum ChatControllerInteractionOpenMessageMode { + case `default` + case stream +} + public final class ChatControllerInteraction { - let openMessage: (Message) -> Bool + let openMessage: (Message, ChatControllerInteractionOpenMessageMode) -> Bool let openPeer: (PeerId?, ChatControllerInteractionNavigateToPeer, Message?) -> Void let openPeerMention: (String) -> Void let openMessageContextMenu: (Message, ASDisplayNode, CGRect) -> Void @@ -79,7 +84,7 @@ public final class ChatControllerInteraction { var contextHighlightedState: ChatInterfaceHighlightedState? var automaticMediaDownloadSettings: AutomaticMediaDownloadSettings - init(openMessage: @escaping (Message) -> Bool, openPeer: @escaping (PeerId?, ChatControllerInteractionNavigateToPeer, Message?) -> Void, openPeerMention: @escaping (String) -> Void, openMessageContextMenu: @escaping (Message, ASDisplayNode, CGRect) -> Void, navigateToMessage: @escaping (MessageId, MessageId) -> Void, clickThroughMessage: @escaping () -> Void, toggleMessagesSelection: @escaping ([MessageId], Bool) -> Void, sendMessage: @escaping (String) -> Void, sendSticker: @escaping (FileMediaReference, Bool) -> Void, sendGif: @escaping (FileMediaReference) -> Void, requestMessageActionCallback: @escaping (MessageId, MemoryBuffer?, Bool) -> Void, activateSwitchInline: @escaping (PeerId?, String) -> Void, openUrl: @escaping (String, Bool, Bool?) -> Void, shareCurrentLocation: @escaping () -> Void, shareAccountContact: @escaping () -> Void, sendBotCommand: @escaping (MessageId?, String) -> Void, openInstantPage: @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?, presentGlobalOverlayController: @escaping (ViewController, Any?) -> Void, callPeer: @escaping (PeerId) -> Void, longTap: @escaping (ChatControllerInteractionLongTapAction) -> Void, openCheckoutOrReceipt: @escaping (MessageId) -> Void, openSearch: @escaping () -> Void, setupReply: @escaping (MessageId) -> Void, canSetupReply: @escaping (Message) -> Bool, navigateToFirstDateMessage: @escaping(Int32)->Void, requestMessageUpdate: @escaping (MessageId) -> Void, cancelInteractiveKeyboardGestures: @escaping () -> Void, automaticMediaDownloadSettings: AutomaticMediaDownloadSettings) { + init(openMessage: @escaping (Message, ChatControllerInteractionOpenMessageMode) -> Bool, openPeer: @escaping (PeerId?, ChatControllerInteractionNavigateToPeer, Message?) -> Void, openPeerMention: @escaping (String) -> Void, openMessageContextMenu: @escaping (Message, ASDisplayNode, CGRect) -> Void, navigateToMessage: @escaping (MessageId, MessageId) -> Void, clickThroughMessage: @escaping () -> Void, toggleMessagesSelection: @escaping ([MessageId], Bool) -> Void, sendMessage: @escaping (String) -> Void, sendSticker: @escaping (FileMediaReference, Bool) -> Void, sendGif: @escaping (FileMediaReference) -> Void, requestMessageActionCallback: @escaping (MessageId, MemoryBuffer?, Bool) -> Void, activateSwitchInline: @escaping (PeerId?, String) -> Void, openUrl: @escaping (String, Bool, Bool?) -> Void, shareCurrentLocation: @escaping () -> Void, shareAccountContact: @escaping () -> Void, sendBotCommand: @escaping (MessageId?, String) -> Void, openInstantPage: @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?, presentGlobalOverlayController: @escaping (ViewController, Any?) -> Void, callPeer: @escaping (PeerId) -> Void, longTap: @escaping (ChatControllerInteractionLongTapAction) -> Void, openCheckoutOrReceipt: @escaping (MessageId) -> Void, openSearch: @escaping () -> Void, setupReply: @escaping (MessageId) -> Void, canSetupReply: @escaping (Message) -> Bool, navigateToFirstDateMessage: @escaping(Int32)->Void, requestMessageUpdate: @escaping (MessageId) -> Void, cancelInteractiveKeyboardGestures: @escaping () -> Void, automaticMediaDownloadSettings: AutomaticMediaDownloadSettings) { self.openMessage = openMessage self.openPeer = openPeer self.openPeerMention = openPeerMention diff --git a/TelegramUI/ChatControllerNode.swift b/TelegramUI/ChatControllerNode.swift index 593e00c804..840b29ccfb 100644 --- a/TelegramUI/ChatControllerNode.swift +++ b/TelegramUI/ChatControllerNode.swift @@ -707,7 +707,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { if curve == 7 { listViewCurve = .Spring(duration: duration) } else { - listViewCurve = .Default + listViewCurve = .Default(duration: duration) } var accessoryPanelSize: CGSize? diff --git a/TelegramUI/ChatHistorySearchContainerNode.swift b/TelegramUI/ChatHistorySearchContainerNode.swift index e5f83bfacf..d3fc25ff25 100644 --- a/TelegramUI/ChatHistorySearchContainerNode.swift +++ b/TelegramUI/ChatHistorySearchContainerNode.swift @@ -223,7 +223,7 @@ final class ChatHistorySearchContainerNode: SearchDisplayControllerContentNode { transition.updateFrame(node: self.dimNode, frame: CGRect(origin: CGPoint(x: 0.0, y: topInset), size: CGSize(width: layout.size.width, height: layout.size.height - topInset))) self.listNode.frame = CGRect(origin: CGPoint(), size: layout.size) - self.listNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous], scrollToItem: nil, updateSizeAndInsets: ListViewUpdateSizeAndInsets(size: layout.size, insets: UIEdgeInsets(top: topInset, left: 0.0, bottom: 0.0, right: 0.0), duration: 0.0, curve: .Default), stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in }) + self.listNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous], scrollToItem: nil, updateSizeAndInsets: ListViewUpdateSizeAndInsets(size: layout.size, insets: UIEdgeInsets(top: topInset, left: 0.0, bottom: 0.0, right: 0.0), duration: 0.0, curve: .Default(duration: nil)), stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in }) if firstValidLayout { diff --git a/TelegramUI/ChatInterfaceStateContextMenus.swift b/TelegramUI/ChatInterfaceStateContextMenus.swift index 2c2b444dbf..cdc3e5f6e9 100644 --- a/TelegramUI/ChatInterfaceStateContextMenus.swift +++ b/TelegramUI/ChatInterfaceStateContextMenus.swift @@ -149,7 +149,7 @@ func updatedChatEditInterfaceMessagetState(state: ChatPresentationInterfaceState return updated } -func contextMenuForChatPresentationIntefaceState(chatPresentationInterfaceState: ChatPresentationInterfaceState, account: Account, messages: [Message], controllerInteraction: ChatControllerInteraction?, interfaceInteraction: ChatPanelInterfaceInteraction?, debugStreamSingleVideo: @escaping (MessageId) -> Void) -> Signal<[ChatMessageContextMenuAction], NoError> { +func contextMenuForChatPresentationIntefaceState(chatPresentationInterfaceState: ChatPresentationInterfaceState, account: Account, messages: [Message], controllerInteraction: ChatControllerInteraction?, interfaceInteraction: ChatPanelInterfaceInteraction?) -> Signal<[ChatMessageContextMenuAction], NoError> { guard let interfaceInteraction = interfaceInteraction, let controllerInteraction = controllerInteraction else { return .single([]) } @@ -418,10 +418,6 @@ func contextMenuForChatPresentationIntefaceState(chatPresentationInterfaceState: actions.append(.sheet(ChatMessageContextMenuSheetAction(color: .accent, title: chatPresentationInterfaceState.strings.Conversation_LinkDialogSave, action: { let _ = addSavedGif(postbox: account.postbox, fileReference: .message(message: MessageReference(message), media: file)).start() }))) - } else if !GlobalExperimentalSettings.isAppStoreBuild { - actions.append(.sheet(ChatMessageContextMenuSheetAction(color: .accent, title: "Stream", action: { - debugStreamSingleVideo(message.id) - }))) } break } @@ -442,7 +438,7 @@ func contextMenuForChatPresentationIntefaceState(chatPresentationInterfaceState: if data.messageActions.options.contains(.viewStickerPack) { actions.append(.sheet(ChatMessageContextMenuSheetAction(color: .accent, title: chatPresentationInterfaceState.strings.StickerPack_ViewPack, action: { - let _ = controllerInteraction.openMessage(message) + let _ = controllerInteraction.openMessage(message, .default) }))) } diff --git a/TelegramUI/ChatListControllerNode.swift b/TelegramUI/ChatListControllerNode.swift index 20bec02b39..b394b3522e 100644 --- a/TelegramUI/ChatListControllerNode.swift +++ b/TelegramUI/ChatListControllerNode.swift @@ -102,7 +102,7 @@ class ChatListControllerNode: ASDisplayNode { if curve == 7 { listViewCurve = .Spring(duration: duration) } else { - listViewCurve = .Default + listViewCurve = .Default(duration: duration) } let updateSizeAndInsets = ListViewUpdateSizeAndInsets(size: layout.size, insets: insets, duration: duration, curve: listViewCurve) diff --git a/TelegramUI/ChatListNode.swift b/TelegramUI/ChatListNode.swift index 198a3779dc..20732cf167 100644 --- a/TelegramUI/ChatListNode.swift +++ b/TelegramUI/ChatListNode.swift @@ -353,6 +353,8 @@ final class ChatListNode: ListView { super.init() + //self.verticalScrollIndicatorColor = UIColor(white: 0.3, alpha: 0.8) + let nodeInteraction = ChatListNodeInteraction(activateSearch: { [weak self] in if let strongSelf = self, let activateSearch = strongSelf.activateSearch { activateSearch() @@ -941,7 +943,7 @@ final class ChatListNode: ListView { } if view.laterIndex == nil { - self.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous], scrollToItem: ListViewScrollToItem(index: 0, position: .top(0.0), animated: true, curve: .Default, directionHint: .Up), updateSizeAndInsets: nil, stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in }) + self.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous], scrollToItem: ListViewScrollToItem(index: 0, position: .top(0.0), animated: true, curve: .Default(duration: nil), directionHint: .Up), updateSizeAndInsets: nil, stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in }) } else { let location: ChatListNodeLocation = .scroll(index: ChatListIndex.absoluteUpperBound, sourceIndex: ChatListIndex.absoluteLowerBound , scrollPosition: .top(0.0), animated: true) diff --git a/TelegramUI/ChatListSearchContainerNode.swift b/TelegramUI/ChatListSearchContainerNode.swift index d1cd500e65..a274f40fa2 100644 --- a/TelegramUI/ChatListSearchContainerNode.swift +++ b/TelegramUI/ChatListSearchContainerNode.swift @@ -877,7 +877,7 @@ final class ChatListSearchContainerNode: SearchDisplayControllerContentNode { if curve == 7 { listViewCurve = .Spring(duration: duration) } else { - listViewCurve = .Default + listViewCurve = .Default(duration: duration) } self.recentListNode.frame = CGRect(origin: CGPoint(), size: layout.size) diff --git a/TelegramUI/ChatListSearchRecentPeersNode.swift b/TelegramUI/ChatListSearchRecentPeersNode.swift index 5823bbf795..bcd6e25bcf 100644 --- a/TelegramUI/ChatListSearchRecentPeersNode.swift +++ b/TelegramUI/ChatListSearchRecentPeersNode.swift @@ -264,7 +264,7 @@ final class ChatListSearchRecentPeersNode: ASDisplayNode { self.listView.bounds = CGRect(x: 0.0, y: 0.0, width: 92.0, height: size.width) self.listView.position = CGPoint(x: size.width / 2.0, y: 92.0 / 2.0 + 29.0) - self.listView.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous], scrollToItem: nil, updateSizeAndInsets: ListViewUpdateSizeAndInsets(size: CGSize(width: 92.0, height: size.width), insets: insets, duration: 0.0, curve: .Default), stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in }) + self.listView.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous], scrollToItem: nil, updateSizeAndInsets: ListViewUpdateSizeAndInsets(size: CGSize(width: 92.0, height: size.width), insets: insets, duration: 0.0, curve: .Default(duration: nil)), stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in }) itemCustomWidthValuePromise.set(itemCustomWidth) } diff --git a/TelegramUI/ChatListViewTransition.swift b/TelegramUI/ChatListViewTransition.swift index 67b13e5343..5ca0474eba 100644 --- a/TelegramUI/ChatListViewTransition.swift +++ b/TelegramUI/ChatListViewTransition.swift @@ -164,7 +164,7 @@ func preparedChatListNodeViewTransition(from fromView: ChatListNodeView?, to toV var index = toView.filteredEntries.count - 1 for entry in toView.filteredEntries { if entry.index >= scrollIndex { - scrollToItem = ListViewScrollToItem(index: index, position: position, animated: animated, curve: .Default, directionHint: directionHint) + scrollToItem = ListViewScrollToItem(index: index, position: position, animated: animated, curve: .Default(duration: nil), directionHint: directionHint) break } index -= 1 @@ -174,7 +174,7 @@ func preparedChatListNodeViewTransition(from fromView: ChatListNodeView?, to toV var index = 0 for entry in toView.filteredEntries.reversed() { if entry.index < scrollIndex { - scrollToItem = ListViewScrollToItem(index: index, position: position, animated: animated, curve: .Default, directionHint: directionHint) + scrollToItem = ListViewScrollToItem(index: index, position: position, animated: animated, curve: .Default(duration: nil), directionHint: directionHint) break } index += 1 diff --git a/TelegramUI/ChatMediaInputNode.swift b/TelegramUI/ChatMediaInputNode.swift index bd591bdcc3..72737b3846 100644 --- a/TelegramUI/ChatMediaInputNode.swift +++ b/TelegramUI/ChatMediaInputNode.swift @@ -984,7 +984,7 @@ final class ChatMediaInputNode: ChatInputNode { let firstVisibleIndex = currentView.collectionInfos.index(where: { id, _, _ in return id == firstVisibleCollectionId }) if let targetIndex = targetIndex, let firstVisibleIndex = firstVisibleIndex { let toRight = targetIndex > firstVisibleIndex - self.listView.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [], scrollToItem: ListViewScrollToItem(index: targetIndex, position: toRight ? .bottom(0.0) : .top(0.0), animated: true, curve: .Default, directionHint: toRight ? .Down : .Up), updateSizeAndInsets: nil, stationaryItemRange: nil, updateOpaqueState: nil) + self.listView.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [], scrollToItem: ListViewScrollToItem(index: targetIndex, position: toRight ? .bottom(0.0) : .top(0.0), animated: true, curve: .Default(duration: nil), directionHint: toRight ? .Down : .Up), updateSizeAndInsets: nil, stationaryItemRange: nil, updateOpaqueState: nil) } } } @@ -1126,7 +1126,7 @@ final class ChatMediaInputNode: ChatInputNode { if curve == 7 { listViewCurve = .Spring(duration: duration) } else { - listViewCurve = .Default + listViewCurve = .Default(duration: duration) } let updateSizeAndInsets = ListViewUpdateSizeAndInsets(size: CGSize(width: 41.0, height: width), insets: UIEdgeInsets(top: 4.0 + leftInset, left: 0.0, bottom: 4.0 + rightInset, right: 0.0), duration: duration, curve: listViewCurve) diff --git a/TelegramUI/ChatMediaInputTrendingPane.swift b/TelegramUI/ChatMediaInputTrendingPane.swift index 377348edb5..18aadbf3c5 100644 --- a/TelegramUI/ChatMediaInputTrendingPane.swift +++ b/TelegramUI/ChatMediaInputTrendingPane.swift @@ -199,7 +199,7 @@ final class ChatMediaInputTrendingPane: ChatMediaInputPane { transition.updateFrame(node: self.listNode, frame: CGRect(origin: CGPoint(), size: size)) var duration: Double = 0.0 - var listViewCurve: ListViewAnimationCurve = .Default + var listViewCurve: ListViewAnimationCurve = .Default(duration: nil) switch transition { case .immediate: break @@ -207,7 +207,7 @@ final class ChatMediaInputTrendingPane: ChatMediaInputPane { duration = animationDuration switch animationCurve { case .easeInOut: - listViewCurve = .Default + listViewCurve = .Default(duration: duration) case .spring: listViewCurve = .Spring(duration: duration) } diff --git a/TelegramUI/ChatMessageAttachedContentNode.swift b/TelegramUI/ChatMessageAttachedContentNode.swift index 37c53449fb..6b01901fd1 100644 --- a/TelegramUI/ChatMessageAttachedContentNode.swift +++ b/TelegramUI/ChatMessageAttachedContentNode.swift @@ -225,7 +225,7 @@ final class ChatMessageAttachedContentNode: ASDisplayNode { private var media: Media? private var theme: ChatPresentationThemeData? - var openMedia: (() -> Void)? + var openMedia: ((Bool) -> Void)? var activateAction: (() -> Void)? var visibility: ListViewItemNodeVisibility = .none { @@ -739,9 +739,9 @@ final class ChatMessageAttachedContentNode: ASDisplayNode { if strongSelf.contentImageNode !== contentImageNode { strongSelf.contentImageNode = contentImageNode strongSelf.addSubnode(contentImageNode) - contentImageNode.activateLocalContent = { [weak strongSelf] in + contentImageNode.activateLocalContent = { [weak strongSelf] mode in if let strongSelf = strongSelf { - strongSelf.openMedia?() + strongSelf.openMedia?(mode == .stream) } } contentImageNode.visibility = strongSelf.visibility @@ -772,7 +772,7 @@ final class ChatMessageAttachedContentNode: ASDisplayNode { strongSelf.additionalImageBadgeNode = updatedAdditionalImageBadge contentImageNode.addSubnode(updatedAdditionalImageBadge) updatedAdditionalImageBadge.contentMode = .topRight - updatedAdditionalImageBadge.content = additionalImageBadgeContent + updatedAdditionalImageBadge.update(theme: presentationData.theme.theme, content: additionalImageBadgeContent, mediaDownloadState: nil, animated: false) updatedAdditionalImageBadge.frame = CGRect(origin: CGPoint(x: contentImageSize.width - 2.0, y: contentImageSize.height - 18.0 - 2.0), size: CGSize(width: 0.0, height: 0.0)) } else if let additionalImageBadgeNode = strongSelf.additionalImageBadgeNode { strongSelf.additionalImageBadgeNode = nil @@ -788,7 +788,7 @@ final class ChatMessageAttachedContentNode: ASDisplayNode { strongSelf.addSubnode(contentFileNode) contentFileNode.activateLocalContent = { [weak strongSelf] in if let strongSelf = strongSelf { - strongSelf.openMedia?() + strongSelf.openMedia?(false) } } } diff --git a/TelegramUI/ChatMessageBubbleItemNode.swift b/TelegramUI/ChatMessageBubbleItemNode.swift index 27569668a6..fe21928b75 100644 --- a/TelegramUI/ChatMessageBubbleItemNode.swift +++ b/TelegramUI/ChatMessageBubbleItemNode.swift @@ -1574,7 +1574,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView { case .openMessage: foundTapAction = true if let item = self.item { - let _ = item.controllerInteraction.openMessage(item.message) + let _ = item.controllerInteraction.openMessage(item.message, .default) } break loop } diff --git a/TelegramUI/ChatMessageContactBubbleContentNode.swift b/TelegramUI/ChatMessageContactBubbleContentNode.swift index ad7585b2f5..5e1a40cbb8 100644 --- a/TelegramUI/ChatMessageContactBubbleContentNode.swift +++ b/TelegramUI/ChatMessageContactBubbleContentNode.swift @@ -319,14 +319,14 @@ class ChatMessageContactBubbleContentNode: ChatMessageBubbleContentNode { @objc func contactTap(_ recognizer: UITapGestureRecognizer) { if case .ended = recognizer.state { if let item = self.item { - let _ = item.controllerInteraction.openMessage(item.message) + let _ = item.controllerInteraction.openMessage(item.message, .default) } } } @objc private func buttonPressed() { if let item = self.item { - let _ = item.controllerInteraction.openMessage(item.message) + let _ = item.controllerInteraction.openMessage(item.message, .default) } } } diff --git a/TelegramUI/ChatMessageFileBubbleContentNode.swift b/TelegramUI/ChatMessageFileBubbleContentNode.swift index 2959603b5c..6793126d7e 100644 --- a/TelegramUI/ChatMessageFileBubbleContentNode.swift +++ b/TelegramUI/ChatMessageFileBubbleContentNode.swift @@ -18,7 +18,7 @@ class ChatMessageFileBubbleContentNode: ChatMessageBubbleContentNode { self.interactiveFileNode.activateLocalContent = { [weak self] in if let strongSelf = self { if let item = strongSelf.item { - let _ = item.controllerInteraction.openMessage(item.message) + let _ = item.controllerInteraction.openMessage(item.message, .default) } } } diff --git a/TelegramUI/ChatMessageGameBubbleContentNode.swift b/TelegramUI/ChatMessageGameBubbleContentNode.swift index da39bb1a50..649ad762a2 100644 --- a/TelegramUI/ChatMessageGameBubbleContentNode.swift +++ b/TelegramUI/ChatMessageGameBubbleContentNode.swift @@ -22,7 +22,7 @@ final class ChatMessageGameBubbleContentNode: ChatMessageBubbleContentNode { super.init() self.addSubnode(self.contentNode) - self.contentNode.openMedia = { [weak self] in + self.contentNode.openMedia = { [weak self] _ in if let strongSelf = self, let item = strongSelf.item { item.controllerInteraction.requestMessageActionCallback(item.message.id, nil, true) } diff --git a/TelegramUI/ChatMessageInteractiveInstantVideoNode.swift b/TelegramUI/ChatMessageInteractiveInstantVideoNode.swift index d8f3ca9f4b..9b909a724c 100644 --- a/TelegramUI/ChatMessageInteractiveInstantVideoNode.swift +++ b/TelegramUI/ChatMessageInteractiveInstantVideoNode.swift @@ -581,7 +581,7 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode { if self.infoBackgroundNode.alpha.isZero { item.account.telegramApplicationContext.mediaManager?.playlistControl(.playback(.togglePlayPause), type: .voice) } else { - let _ = item.controllerInteraction.openMessage(item.message) + let _ = item.controllerInteraction.openMessage(item.message, .default) } } diff --git a/TelegramUI/ChatMessageInteractiveMediaBadge.swift b/TelegramUI/ChatMessageInteractiveMediaBadge.swift index 0d1171b48c..6d9d1d805d 100644 --- a/TelegramUI/ChatMessageInteractiveMediaBadge.swift +++ b/TelegramUI/ChatMessageInteractiveMediaBadge.swift @@ -5,27 +5,16 @@ import AsyncDisplayKit enum ChatMessageInteractiveMediaBadgeShape: Equatable { case round case corners(CGFloat) - - static func ==(lhs: ChatMessageInteractiveMediaBadgeShape, rhs: ChatMessageInteractiveMediaBadgeShape) -> Bool { - switch lhs { - case .round: - if case .round = rhs { - return true - } else { - return false - } - case let .corners(radius): - if case .corners(radius) = rhs { - return true - } else { - return false - } - } - } +} + +enum ChatMessageInteractiveMediaDownloadState: Equatable { + case remote + case fetching(progress: Float) } enum ChatMessageInteractiveMediaBadgeContent: Equatable { case text(backgroundColor: UIColor, foregroundColor: UIColor, shape: ChatMessageInteractiveMediaBadgeShape, text: NSAttributedString) + case mediaDownload(backgroundColor: UIColor, foregroundColor: UIColor, duration: String, size: String) static func ==(lhs: ChatMessageInteractiveMediaBadgeContent, rhs: ChatMessageInteractiveMediaBadgeContent) -> Bool { switch lhs { @@ -35,6 +24,12 @@ enum ChatMessageInteractiveMediaBadgeContent: Equatable { } else { return false } + case let .mediaDownload(lhsBackgroundColor, lhsForegroundColor, lhsDuration, lhsSize): + if case let .mediaDownload(rhsBackgroundColor, rhsForegroundColor, rhsDuration, rhsSize) = rhs, lhsBackgroundColor.isEqual(rhsBackgroundColor), lhsForegroundColor.isEqual(rhsForegroundColor), lhsDuration == rhsDuration, lhsSize == rhsSize { + return true + } else { + return false + } } } } @@ -51,22 +46,78 @@ private final class ChatMessageInteractiveMediaBadgeParams: NSObject { } final class ChatMessageInteractiveMediaBadge: ASDisplayNode { - var content: ChatMessageInteractiveMediaBadgeContent? { - didSet { - if oldValue != self.content { - self.setNeedsDisplay() - } - } - } + var pressed: (() -> Void)? + + private var content: ChatMessageInteractiveMediaBadgeContent? + + private var mediaDownloadStatusNode: RadialStatusNode? + private var mediaDownloadState: ChatMessageInteractiveMediaDownloadState? override init() { super.init() - self.isLayerBacked = true self.contentMode = .topLeft self.contentsScale = UIScreenScale } + override func didLoad() { + super.didLoad() + + self.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:)))) + } + + @objc private func tapGesture(_ recognizer: UITapGestureRecognizer) { + if case .ended = recognizer.state { + self.pressed?() + } + } + + override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { + if let contents = self.contents, CFGetTypeID(contents as CFTypeRef) == CGImage.typeID { + let image = contents as! CGImage + if CGRect(origin: CGPoint(), size: CGSize(width: CGFloat(image.width) / UIScreenScale, height: CGFloat(image.height) / UIScreenScale)).contains(point) { + return self.view + } + } + return nil + } + + func update(theme: PresentationTheme, content: ChatMessageInteractiveMediaBadgeContent?, mediaDownloadState: ChatMessageInteractiveMediaDownloadState?, animated: Bool) { + if self.content != content { + self.content = content + self.setNeedsDisplay() + } + if self.mediaDownloadState != mediaDownloadState { + self.mediaDownloadState = mediaDownloadState + if let mediaDownloadState = self.mediaDownloadState { + let mediaDownloadStatusNode: RadialStatusNode + if let current = self.mediaDownloadStatusNode { + mediaDownloadStatusNode = current + } else { + mediaDownloadStatusNode = RadialStatusNode(backgroundNodeColor: .clear) + self.mediaDownloadStatusNode = mediaDownloadStatusNode + mediaDownloadStatusNode.frame = CGRect(origin: CGPoint(x: 7.0, y: 6.0), size: CGSize(width: 28.0, height: 28.0)) + self.addSubnode(mediaDownloadStatusNode) + } + let state: RadialStatusNodeState + switch mediaDownloadState { + case .remote: + if let image = PresentationResourcesChat.chatBubbleFileCloudFetchMediaIcon(theme) { + state = .customIcon(image) + } else { + state = .none + } + case let .fetching(progress): + state = .cloudProgress(color: .white, strokeBackgroundColor: UIColor(white: 1.0, alpha: 0.3), lineWidth: 2.0, value: CGFloat(progress)) + } + mediaDownloadStatusNode.transitionToState(state, animated: true, completion: {}) + } else if let mediaDownloadStatusNode = self.mediaDownloadStatusNode { + self.mediaDownloadStatusNode = nil + mediaDownloadStatusNode.removeFromSupernode() + } + } + } + override func drawParameters(forAsyncLayer layer: _ASDisplayLayer) -> NSObjectProtocol? { return ChatMessageInteractiveMediaBadgeParams(content: self.content) } @@ -107,6 +158,34 @@ final class ChatMessageInteractiveMediaBadge: ASDisplayNode { convertedText.draw(at: CGPoint(x: floor((size.width - textRect.size.width) / 2.0) + textRect.origin.x, y: 2.0 + textRect.origin.y)) UIGraphicsPopContext() }) + case let .mediaDownload(backgroundColor, foregroundColor, duration, size): + let durationString = NSMutableAttributedString(string: duration, attributes: [.font: font, .foregroundColor: foregroundColor]) + let sizeString = NSMutableAttributedString(string: size, attributes: [.font: font, .foregroundColor: foregroundColor]) + let durationRect = durationString.boundingRect(with: CGSize(width: 200.0, height: 100.0), options: .usesLineFragmentOrigin, context: nil) + let sizeRect = sizeString.boundingRect(with: CGSize(width: 200.0, height: 100.0), options: .usesLineFragmentOrigin, context: nil) + let leftInset: CGFloat = 42.0 + let imageSize = CGSize(width: leftInset + max(ceil(durationRect.width), ceil(sizeRect.width)) + 10.0, height: 40.0) + return generateImage(imageSize, rotatedContext: { size, context in + context.clear(CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: size)) + context.setBlendMode(.copy) + context.setFillColor(backgroundColor.cgColor) + + let radius: CGFloat = 12.0 + let diameter = radius * 2.0 + context.fillEllipse(in: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: diameter, height: diameter))) + context.fillEllipse(in: CGRect(origin: CGPoint(x: 0.0, y: size.height - diameter), size: CGSize(width: diameter, height: diameter))) + context.fillEllipse(in: CGRect(origin: CGPoint(x: size.width - diameter, y: 0.0), size: CGSize(width: diameter, height: diameter))) + context.fillEllipse(in: CGRect(origin: CGPoint(x: size.width - diameter, y: size.height - diameter), size: CGSize(width: diameter, height: diameter))) + context.fill(CGRect(origin: CGPoint(x: 0.0, y: radius), size: CGSize(width: diameter, height: size.height - diameter))) + context.fill(CGRect(origin: CGPoint(x: radius, y: 0.0), size: CGSize(width: size.width - diameter, height: size.height))) + context.fill(CGRect(origin: CGPoint(x: size.width - diameter, y: radius), size: CGSize(width: diameter, height: size.height - diameter))) + + context.setBlendMode(.normal) + UIGraphicsPushContext(context) + durationString.draw(at: CGPoint(x: leftInset + durationRect.origin.x, y: 7.0 + durationRect.origin.y)) + sizeString.draw(at: CGPoint(x: leftInset + sizeRect.origin.x, y: 21.0 + sizeRect.origin.y)) + UIGraphicsPopContext() + }) } } return nil diff --git a/TelegramUI/ChatMessageInteractiveMediaNode.swift b/TelegramUI/ChatMessageInteractiveMediaNode.swift index 43f27b17ed..b11dfe926a 100644 --- a/TelegramUI/ChatMessageInteractiveMediaNode.swift +++ b/TelegramUI/ChatMessageInteractiveMediaNode.swift @@ -15,6 +15,11 @@ enum InteractiveMediaNodeSizeCalculation { case unconstrained } +enum InteractiveMediaNodeActivateContent { + case `default` + case stream +} + final class ChatMessageInteractiveMediaNode: ASDisplayNode { private let imageNode: TransformImageNode private var videoNode: UniversalVideoNode? @@ -54,7 +59,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode { } } - var activateLocalContent: () -> Void = { } + var activateLocalContent: (InteractiveMediaNodeActivateContent) -> Void = { _ in } override init() { self.imageNode = TransformImageNode() @@ -80,8 +85,18 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode { self.tapRecognizer = tapRecognizer } - @objc func progressPressed() { + private func progressPressed(canActivate: Bool) { if let fetchStatus = self.fetchStatus { + if canActivate, let state = self.statusNode?.state, case .play = state { + switch fetchStatus { + case .Remote, .Fetching: + self.activateLocalContent(.stream) + default: + break + } + return + } + switch fetchStatus { case .Fetching: if let account = self.account, let message = self.message, message.flags.isSending { @@ -112,14 +127,14 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode { if case .ended = recognizer.state { let point = recognizer.location(in: self.imageNode.view) if let fetchStatus = self.fetchStatus, case .Local = fetchStatus { - self.activateLocalContent() + self.activateLocalContent(.default) } else { if let message = self.message, message.flags.isSending { if let statusNode = self.statusNode, statusNode.frame.contains(point) { - self.progressPressed() + self.progressPressed(canActivate: true) } } else { - self.progressPressed() + self.progressPressed(canActivate: true) } } } @@ -537,6 +552,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode { var state: RadialStatusNodeState = .none var badgeContent: ChatMessageInteractiveMediaBadgeContent? + var mediaDownloadState: ChatMessageInteractiveMediaDownloadState? let bubbleTheme = theme.chat.bubble if let invoice = invoice { let string = NSMutableAttributedString() @@ -573,10 +589,23 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode { } else { state = .progress(color: bubbleTheme.mediaOverlayControlForegroundColor, lineWidth: nil, value: CGFloat(adjustedProgress), cancelEnabled: true) } + if case .constrained = sizeCalculation { if let file = media as? TelegramMediaFile, (!file.isAnimated || message.flags.contains(.Unsent)) { if let size = file.size { - badgeContent = .text(backgroundColor: bubbleTheme.mediaDateAndStatusFillColor, foregroundColor: bubbleTheme.mediaDateAndStatusTextColor, shape: .round, text: NSAttributedString(string: "\(dataSizeString(Int(Float(size) * progress))) / \(dataSizeString(size))")) + if let duration = file.duration { + if isMediaStreamable(message: message, media: file) { + let durationString = String(format: "%d:%02d", duration / 60, duration % 60) + let sizeString = "\(dataSizeString(Int(Float(size) * progress), forceDecimal: true)) / \(dataSizeString(size, forceDecimal: true))" + badgeContent = .mediaDownload(backgroundColor: bubbleTheme.mediaDateAndStatusFillColor, foregroundColor: bubbleTheme.mediaDateAndStatusTextColor, duration: durationString, size: sizeString) + mediaDownloadState = .fetching(progress: progress) + state = .play(bubbleTheme.mediaOverlayControlForegroundColor) + } else { + badgeContent = .text(backgroundColor: bubbleTheme.mediaDateAndStatusFillColor, foregroundColor: bubbleTheme.mediaDateAndStatusTextColor, shape: .round, text: NSAttributedString(string: "\(dataSizeString(Int(Float(size) * progress), forceDecimal: true)) / \(dataSizeString(size, forceDecimal: true))")) + } + } else { + badgeContent = .text(backgroundColor: bubbleTheme.mediaDateAndStatusFillColor, foregroundColor: bubbleTheme.mediaDateAndStatusTextColor, shape: .round, text: NSAttributedString(string: "\(dataSizeString(Int(Float(size) * progress), forceDecimal: true)) / \(dataSizeString(size, forceDecimal: true))")) + } } else if let _ = file.duration { badgeContent = .text(backgroundColor: bubbleTheme.mediaDateAndStatusFillColor, foregroundColor: bubbleTheme.mediaDateAndStatusTextColor, shape: .round, text: NSAttributedString(string: strings.Conversation_Processing)) } @@ -614,9 +643,18 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode { case .Remote: state = .download(bubbleTheme.mediaOverlayControlForegroundColor) if case .constrained = sizeCalculation { - if let file = media as? TelegramMediaFile, let duration = file.duration, !file.isAnimated { - let durationString = String(format: "%d:%02d", duration / 60, duration % 60) - badgeContent = .text(backgroundColor: bubbleTheme.mediaDateAndStatusFillColor, foregroundColor: bubbleTheme.mediaDateAndStatusTextColor, shape: .round, text: NSAttributedString(string: durationString)) + if let file = self.media as? TelegramMediaFile, let duration = file.duration, !file.isAnimated { + if isMediaStreamable(message: message, media: file) { + state = .play(bubbleTheme.mediaOverlayControlForegroundColor) + + let durationString = String(format: "%d:%02d", duration / 60, duration % 60) + + badgeContent = .mediaDownload(backgroundColor: bubbleTheme.mediaDateAndStatusFillColor, foregroundColor: bubbleTheme.mediaDateAndStatusTextColor, duration: durationString, size: dataSizeString(file.size ?? 0)) + mediaDownloadState = .remote + } else { + let durationString = String(format: "%d:%02d", duration / 60, duration % 60) + badgeContent = .text(backgroundColor: bubbleTheme.mediaDateAndStatusFillColor, foregroundColor: bubbleTheme.mediaDateAndStatusTextColor, shape: .round, text: NSAttributedString(string: durationString)) + } } } } @@ -648,10 +686,21 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode { if self.badgeNode == nil { let badgeNode = ChatMessageInteractiveMediaBadge() badgeNode.frame = CGRect(origin: CGPoint(x: 6.0, y: 6.0), size: CGSize(width: radialStatusSize, height: radialStatusSize)) + badgeNode.pressed = { [weak self] in + guard let strongSelf = self, let fetchStatus = strongSelf.fetchStatus else { + return + } + switch fetchStatus { + case .Remote, .Fetching: + strongSelf.progressPressed(canActivate: false) + default: + break + } + } self.badgeNode = badgeNode self.addSubnode(badgeNode) } - self.badgeNode?.content = badgeContent + self.badgeNode?.update(theme: theme, content: badgeContent, mediaDownloadState: mediaDownloadState, animated: false) } else if let badgeNode = self.badgeNode { self.badgeNode = nil badgeNode.removeFromSupernode() diff --git a/TelegramUI/ChatMessageItemView.swift b/TelegramUI/ChatMessageItemView.swift index a31aed59de..dbf751217b 100644 --- a/TelegramUI/ChatMessageItemView.swift +++ b/TelegramUI/ChatMessageItemView.swift @@ -132,7 +132,7 @@ public class ChatMessageItemView: ListViewItemNode { override public func animateInsertion(_ currentTimestamp: Double, duration: Double, short: Bool) { if short { - self.layer.animateBoundsOriginYAdditive(from: -self.bounds.size.height, to: 0.0, duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring) + //self.layer.animateBoundsOriginYAdditive(from: -self.bounds.size.height, to: 0.0, duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring) } else { self.transitionOffset = -self.bounds.size.height * 1.6 self.addTransitionOffsetAnimation(0.0, duration: duration, beginAt: currentTimestamp) diff --git a/TelegramUI/ChatMessageMapBubbleContentNode.swift b/TelegramUI/ChatMessageMapBubbleContentNode.swift index 235a42bf80..d61b51fa33 100644 --- a/TelegramUI/ChatMessageMapBubbleContentNode.swift +++ b/TelegramUI/ChatMessageMapBubbleContentNode.swift @@ -452,7 +452,7 @@ class ChatMessageMapBubbleContentNode: ChatMessageBubbleContentNode { @objc func imageTap(_ recognizer: UITapGestureRecognizer) { if case .ended = recognizer.state { if let item = self.item { - let _ = item.controllerInteraction.openMessage(item.message) + let _ = item.controllerInteraction.openMessage(item.message, .default) } } } diff --git a/TelegramUI/ChatMessageMediaBubbleContentNode.swift b/TelegramUI/ChatMessageMediaBubbleContentNode.swift index 6a1e1e00da..23fc6f62b7 100644 --- a/TelegramUI/ChatMessageMediaBubbleContentNode.swift +++ b/TelegramUI/ChatMessageMediaBubbleContentNode.swift @@ -31,10 +31,10 @@ class ChatMessageMediaBubbleContentNode: ChatMessageBubbleContentNode { self.addSubnode(self.interactiveImageNode) - self.interactiveImageNode.activateLocalContent = { [weak self] in + self.interactiveImageNode.activateLocalContent = { [weak self] mode in if let strongSelf = self { if let item = strongSelf.item { - let _ = item.controllerInteraction.openMessage(item.message) + let _ = item.controllerInteraction.openMessage(item.message, mode == .stream ? .stream : .default) } } } @@ -226,7 +226,7 @@ class ChatMessageMediaBubbleContentNode: ChatMessageBubbleContentNode { } override func transitionNode(messageId: MessageId, media: Media) -> (ASDisplayNode, () -> UIView?)? { - if self.item?.message.id == messageId, let currentMedia = self.media, currentMedia.isEqual(to: media) { + if self.item?.message.id == messageId, let currentMedia = self.media, currentMedia.isSemanticallyEqual(to: media) { let interactiveImageNode = self.interactiveImageNode return (self.interactiveImageNode, { [weak interactiveImageNode] in return interactiveImageNode?.view.snapshotContentTree(unhide: true) diff --git a/TelegramUI/ChatMessageStickerItemNode.swift b/TelegramUI/ChatMessageStickerItemNode.swift index 0e6319e47e..991ae0786a 100644 --- a/TelegramUI/ChatMessageStickerItemNode.swift +++ b/TelegramUI/ChatMessageStickerItemNode.swift @@ -354,7 +354,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView { } if let item = self.item, self.imageNode.frame.contains(location) { - let _ = item.controllerInteraction.openMessage(item.message) + let _ = item.controllerInteraction.openMessage(item.message, .default) return } diff --git a/TelegramUI/ChatMessageWebpageBubbleContentNode.swift b/TelegramUI/ChatMessageWebpageBubbleContentNode.swift index 7cab20bd84..228551ef91 100644 --- a/TelegramUI/ChatMessageWebpageBubbleContentNode.swift +++ b/TelegramUI/ChatMessageWebpageBubbleContentNode.swift @@ -120,7 +120,7 @@ final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContentNode { super.init() self.addSubnode(self.contentNode) - self.contentNode.openMedia = { [weak self] in + self.contentNode.openMedia = { [weak self] stream in if let strongSelf = self, let item = strongSelf.item { if let webPage = strongSelf.webPage, case let .Loaded(content) = webPage.content, let image = content.image, let instantPage = content.instantPage { var isGallery = false @@ -138,7 +138,7 @@ final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContentNode { return } } - let _ = item.controllerInteraction.openMessage(item.message) + let _ = item.controllerInteraction.openMessage(item.message, stream ? .stream : .default) } } self.contentNode.activateAction = { [weak self] in diff --git a/TelegramUI/ChatRecentActionsControllerNode.swift b/TelegramUI/ChatRecentActionsControllerNode.swift index 138265f7bf..957e9e4468 100644 --- a/TelegramUI/ChatRecentActionsControllerNode.swift +++ b/TelegramUI/ChatRecentActionsControllerNode.swift @@ -120,7 +120,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode { }) self.adminsDisposable = adminsDisposable - let controllerInteraction = ChatControllerInteraction(openMessage: { [weak self] message in + let controllerInteraction = ChatControllerInteraction(openMessage: { [weak self] message, _ in if let strongSelf = self, let navigationController = strongSelf.getNavigationController() { guard let state = strongSelf.listNode.opaqueTransactionState as? ChatRecentActionsListOpaqueState else { return false @@ -479,7 +479,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode { if curve == 7 { listViewCurve = .Spring(duration: duration) } else { - listViewCurve = .Default + listViewCurve = .Default(duration: duration) } let contentBottomInset: CGFloat = panelHeight + 4.0 @@ -730,6 +730,8 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode { strongSelf.openPeer(peerId: peerId, peer: nil) } }), nil) + case let .localization(identifier): + strongSelf.presentController(LanguageLinkPreviewController(account: strongSelf.account, identifier: identifier), nil) case .proxy: openResolvedUrl(result, account: strongSelf.account, navigationController: strongSelf.getNavigationController(), openPeer: { peerId, _ in if let strongSelf = self { diff --git a/TelegramUI/CommandChatInputContextPanelNode.swift b/TelegramUI/CommandChatInputContextPanelNode.swift index 226c0cd3f1..c2f797b3ad 100644 --- a/TelegramUI/CommandChatInputContextPanelNode.swift +++ b/TelegramUI/CommandChatInputContextPanelNode.swift @@ -164,7 +164,7 @@ final class CommandChatInputContextPanelNode: ChatInputContextPanelNode { insets.left = validLayout.1 insets.right = validLayout.2 - let updateSizeAndInsets = ListViewUpdateSizeAndInsets(size: self.listView.bounds.size, insets: insets, duration: 0.0, curve: .Default) + let updateSizeAndInsets = ListViewUpdateSizeAndInsets(size: self.listView.bounds.size, insets: insets, duration: 0.0, curve: .Default(duration: nil)) self.listView.transaction(deleteIndices: transition.deletions, insertIndicesAndItems: transition.insertions, updateIndicesAndItems: transition.updates, options: options, updateSizeAndInsets: updateSizeAndInsets, updateOpaqueState: nil, completion: { [weak self] _ in if let strongSelf = self, firstTime { @@ -220,7 +220,7 @@ final class CommandChatInputContextPanelNode: ChatInputContextPanelNode { if curve == 7 { listViewCurve = .Spring(duration: duration) } else { - listViewCurve = .Default + listViewCurve = .Default(duration: duration) } let updateSizeAndInsets = ListViewUpdateSizeAndInsets(size: size, insets: insets, duration: duration, curve: listViewCurve) diff --git a/TelegramUI/ContactListNode.swift b/TelegramUI/ContactListNode.swift index 6929f7339c..00c7c06c95 100644 --- a/TelegramUI/ContactListNode.swift +++ b/TelegramUI/ContactListNode.swift @@ -824,7 +824,7 @@ final class ContactListNode: ASDisplayNode { if curve == 7 { listViewCurve = .Spring(duration: duration) } else { - listViewCurve = .Default + listViewCurve = .Default(duration: duration) } let updateSizeAndInsets = ListViewUpdateSizeAndInsets(size: layout.size, insets: insets, duration: duration, curve: listViewCurve) @@ -872,6 +872,6 @@ final class ContactListNode: ASDisplayNode { } func scrollToTop() { - self.listNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous, .LowLatency], scrollToItem: ListViewScrollToItem(index: 0, position: .top(0.0), animated: true, curve: .Default, directionHint: .Up), updateSizeAndInsets: nil, stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in }) + self.listNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous, .LowLatency], scrollToItem: ListViewScrollToItem(index: 0, position: .top(0.0), animated: true, curve: .Default(duration: nil), directionHint: .Up), updateSizeAndInsets: nil, stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in }) } } diff --git a/TelegramUI/ContactsSearchContainerNode.swift b/TelegramUI/ContactsSearchContainerNode.swift index 8431e1b512..b26a756568 100644 --- a/TelegramUI/ContactsSearchContainerNode.swift +++ b/TelegramUI/ContactsSearchContainerNode.swift @@ -320,7 +320,7 @@ final class ContactsSearchContainerNode: SearchDisplayControllerContentNode { transition.updateFrame(node: self.dimNode, frame: CGRect(origin: CGPoint(x: 0.0, y: topInset), size: CGSize(width: layout.size.width, height: layout.size.height - topInset))) self.listNode.frame = CGRect(origin: CGPoint(), size: layout.size) - self.listNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous], scrollToItem: nil, updateSizeAndInsets: ListViewUpdateSizeAndInsets(size: layout.size, insets: UIEdgeInsets(top: topInset, left: 0.0, bottom: layout.intrinsicInsets.bottom, right: 0.0), duration: 0.0, curve: .Default), stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in }) + self.listNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous], scrollToItem: nil, updateSizeAndInsets: ListViewUpdateSizeAndInsets(size: layout.size, insets: UIEdgeInsets(top: topInset, left: 0.0, bottom: layout.intrinsicInsets.bottom, right: 0.0), duration: 0.0, curve: .Default(duration: nil)), stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in }) if !hadValidLayout { while !self.enqueuedTransitions.isEmpty { diff --git a/TelegramUI/CreatePasswordController.swift b/TelegramUI/CreatePasswordController.swift index 5166df796e..94bb7ed50f 100644 --- a/TelegramUI/CreatePasswordController.swift +++ b/TelegramUI/CreatePasswordController.swift @@ -270,18 +270,18 @@ func createPasswordController(account: Account, context: CreatePasswordContext, switch update { case .none: break - case let .password(password, pendingEmailPattern): - if let pendingEmailPattern = pendingEmailPattern { + case let .password(password, pendingEmail): + if let pendingEmail = pendingEmail { if processPasswordEmailConfirmation { updateState { state in var state = state state.saving = false - state.state = .pendingVerification(emailPattern: pendingEmailPattern) + state.state = .pendingVerification(emailPattern: pendingEmail.pattern) return state } } - updatePasswordEmailConfirmation(pendingEmailPattern) + updatePasswordEmailConfirmation(pendingEmail.pattern) } else { completion(password, state.hintText, !state.emailText.isEmpty) } diff --git a/TelegramUI/DocumentPreviewController.swift b/TelegramUI/DocumentPreviewController.swift index 32ed19e19f..d5f1f6f9b7 100644 --- a/TelegramUI/DocumentPreviewController.swift +++ b/TelegramUI/DocumentPreviewController.swift @@ -30,6 +30,8 @@ final class DocumentPreviewController: UINavigationController, QLPreviewControll private var item: DocumentPreviewItem? + private var tempFile: TempBoxFile? + init(theme: PresentationTheme, strings: PresentationStrings, postbox: Postbox, file: TelegramMediaFile) { self.postbox = postbox self.file = file @@ -55,16 +57,14 @@ final class DocumentPreviewController: UINavigationController, QLPreviewControll controller.navigationItem.setLeftBarButton(UIBarButtonItem(title: strings.Common_Cancel, style: .plain, target: self, action: #selector(self.cancelPressed)), animated: false) self.setViewControllers([controller], animated: false) - var pathExtension: String? - if let fileName = self.file.fileName { - let pathExtensionCandidate = (fileName as NSString).pathExtension - if !pathExtensionCandidate.isEmpty { - pathExtension = pathExtensionCandidate + if let path = self.postbox.mediaBox.completedResourcePath(self.file.resource) { + var updatedPath = path + if let fileName = self.file.fileName { + let tempFile = TempBox.shared.file(path: path, fileName: fileName) + updatedPath = tempFile.path + self.tempFile = tempFile } - } - - if let path = self.postbox.mediaBox.completedResourcePath(self.file.resource, pathExtension: pathExtension) { - self.item = DocumentPreviewItem(url: URL(fileURLWithPath: path), title: self.file.fileName ?? strings.Message_File) + self.item = DocumentPreviewItem(url: URL(fileURLWithPath: updatedPath), title: self.file.fileName ?? strings.Message_File) } } @@ -72,6 +72,12 @@ final class DocumentPreviewController: UINavigationController, QLPreviewControll fatalError("init(coder:) has not been implemented") } + deinit { + if let tempFile = self.tempFile { + TempBox.shared.dispose(tempFile) + } + } + @objc private func cancelPressed() { self.presentingViewController?.dismiss(animated: true, completion: nil) } @@ -108,6 +114,8 @@ final class CompactDocumentPreviewController: QLPreviewController, QLPreviewCont private var item: DocumentPreviewItem? + private var tempFile: TempBoxFile? + init(theme: PresentationTheme, strings: PresentationStrings, postbox: Postbox, file: TelegramMediaFile) { self.postbox = postbox self.file = file @@ -137,8 +145,14 @@ final class CompactDocumentPreviewController: QLPreviewController, QLPreviewCont } } - if let path = self.postbox.mediaBox.completedResourcePath(self.file.resource, pathExtension: pathExtension) { - self.item = DocumentPreviewItem(url: URL(fileURLWithPath: path), title: self.file.fileName ?? strings.Message_File) + if let path = self.postbox.mediaBox.completedResourcePath(self.file.resource) { + var updatedPath = path + if let fileName = self.file.fileName { + let tempFile = TempBox.shared.file(path: path, fileName: fileName) + updatedPath = tempFile.path + self.tempFile = tempFile + } + self.item = DocumentPreviewItem(url: URL(fileURLWithPath: updatedPath), title: self.file.fileName ?? strings.Message_File) } } @@ -146,6 +160,12 @@ final class CompactDocumentPreviewController: QLPreviewController, QLPreviewCont fatalError("init(coder:) has not been implemented") } + deinit { + if let tempFile = self.tempFile { + TempBox.shared.dispose(tempFile) + } + } + @objc private func cancelPressed() { self.presentingViewController?.dismiss(animated: true, completion: nil) } @@ -177,9 +197,9 @@ final class CompactDocumentPreviewController: QLPreviewController, QLPreviewCont } func presentDocumentPreviewController(rootController: UIViewController, theme: PresentationTheme, strings: PresentationStrings, postbox: Postbox, file: TelegramMediaFile) { - if #available(iOS 10.0, *) { + /*if #available(iOS 10.0, *) { rootController.present(DocumentPreviewController(theme: theme, strings: strings, postbox: postbox, file: file), animated: true, completion: nil) - } else { + } else {*/ if #available(iOSApplicationExtension 9.0, *) { let navigationBar = UINavigationBar.appearance(whenContainedInInstancesOf: [QLPreviewController.self]) navigationBar.barTintColor = theme.rootController.navigationBar.backgroundColor @@ -193,5 +213,5 @@ func presentDocumentPreviewController(rootController: UIViewController, theme: P } rootController.present(CompactDocumentPreviewController(theme: theme, strings: strings, postbox: postbox, file: file), animated: true, completion: nil) - } + //} } diff --git a/TelegramUI/EmojisChatInputContextPanelNode.swift b/TelegramUI/EmojisChatInputContextPanelNode.swift index 887efc1edd..cff19074b7 100644 --- a/TelegramUI/EmojisChatInputContextPanelNode.swift +++ b/TelegramUI/EmojisChatInputContextPanelNode.swift @@ -157,7 +157,7 @@ final class EmojisChatInputContextPanelNode: ChatInputContextPanelNode { insets.left = validLayout.1 insets.right = validLayout.2 - let updateSizeAndInsets = ListViewUpdateSizeAndInsets(size: validLayout.0, insets: insets, duration: 0.0, curve: .Default) + let updateSizeAndInsets = ListViewUpdateSizeAndInsets(size: validLayout.0, insets: insets, duration: 0.0, curve: .Default(duration: nil)) self.listView.transaction(deleteIndices: transition.deletions, insertIndicesAndItems: transition.insertions, updateIndicesAndItems: transition.updates, options: options, updateSizeAndInsets: updateSizeAndInsets, updateOpaqueState: nil, completion: { [weak self] _ in if let strongSelf = self, firstTime { @@ -213,7 +213,7 @@ final class EmojisChatInputContextPanelNode: ChatInputContextPanelNode { if curve == 7 { listViewCurve = .Spring(duration: duration) } else { - listViewCurve = .Default + listViewCurve = .Default(duration: duration) } let updateSizeAndInsets = ListViewUpdateSizeAndInsets(size: size, insets: insets, duration: duration, curve: listViewCurve) diff --git a/TelegramUI/FFMpegMediaFrameSourceContext.swift b/TelegramUI/FFMpegMediaFrameSourceContext.swift index 989b63e502..2441c5266b 100644 --- a/TelegramUI/FFMpegMediaFrameSourceContext.swift +++ b/TelegramUI/FFMpegMediaFrameSourceContext.swift @@ -78,7 +78,8 @@ private func readPacketCallback(userData: UnsafeMutableRawPointer?, buffer: Unsa let data: Signal let resourceSize: Int = resourceReference.resource.size ?? Int(Int32.max - 1) let readCount = min(resourceSize - context.readingOffset, Int(bufferSize)) - data = postbox.mediaBox.resourceData(resourceReference.resource, size: resourceSize, in: context.readingOffset ..< (context.readingOffset + readCount), mode: .complete) + let requestRange: Range = context.readingOffset ..< (context.readingOffset + readCount) + data = postbox.mediaBox.resourceData(resourceReference.resource, size: resourceSize, in: requestRange, mode: .complete) let semaphore = DispatchSemaphore(value: 0) if readCount == 0 { fetchedData = Data() @@ -169,10 +170,10 @@ private func seekCallback(userData: UnsafeMutableRawPointer?, offset: Int64, whe if context.readingOffset >= resourceSize { context.fetchedDataDisposable.set(nil) - context.requestedCompleteFetch = false } else { if streamable { - context.fetchedDataDisposable.set(fetchedMediaResource(postbox: postbox, reference: resourceReference, range: context.readingOffset ..< Int(Int32.max), statsCategory: statsCategory, preferBackgroundReferenceRevalidation: streamable).start()) + let fetchRange: Range = context.readingOffset ..< Int(Int32.max) + context.fetchedDataDisposable.set(fetchedMediaResource(postbox: postbox, reference: resourceReference, range: (fetchRange, .elevated), statsCategory: statsCategory, preferBackgroundReferenceRevalidation: streamable).start()) } else if !context.requestedCompleteFetch && context.fetchAutomatically { context.requestedCompleteFetch = true context.fetchedDataDisposable.set(fetchedMediaResource(postbox: postbox, reference: resourceReference, statsCategory: statsCategory, preferBackgroundReferenceRevalidation: streamable).start()) @@ -199,6 +200,7 @@ final class FFMpegMediaFrameSourceContext: NSObject { fileprivate var requestedDataOffset: Int? fileprivate let fetchedDataDisposable = MetaDisposable() + fileprivate let fetchedFullDataDisposable = MetaDisposable() fileprivate var requestedCompleteFetch = false fileprivate var readingError = false @@ -216,7 +218,8 @@ final class FFMpegMediaFrameSourceContext: NSObject { deinit { assert(Thread.current === self.thread) - fetchedDataDisposable.dispose() + self.fetchedDataDisposable.dispose() + self.fetchedFullDataDisposable.dispose() } func initializeState(postbox: Postbox, resourceReference: MediaResourceReference, streamable: Bool, video: Bool, preferSoftwareDecoding: Bool, fetchAutomatically: Bool) { @@ -233,13 +236,11 @@ final class FFMpegMediaFrameSourceContext: NSObject { self.preferSoftwareDecoding = preferSoftwareDecoding self.fetchAutomatically = fetchAutomatically - let resourceSize: Int = resourceReference.resource.size ?? Int(Int32.max - 1) - if streamable { - self.fetchedDataDisposable.set(fetchedMediaResource(postbox: postbox, reference: resourceReference, range: 0 ..< Int(Int32.max), statsCategory: self.statsCategory ?? .generic, preferBackgroundReferenceRevalidation: streamable).start()) + self.fetchedDataDisposable.set(fetchedMediaResource(postbox: postbox, reference: resourceReference, range: (0 ..< Int(Int32.max), .elevated), statsCategory: self.statsCategory ?? .generic, preferBackgroundReferenceRevalidation: streamable).start()) } else if !self.requestedCompleteFetch && self.fetchAutomatically { self.requestedCompleteFetch = true - self.fetchedDataDisposable.set(fetchedMediaResource(postbox: postbox, reference: resourceReference, statsCategory: self.statsCategory ?? .generic, preferBackgroundReferenceRevalidation: streamable).start()) + self.fetchedFullDataDisposable.set(fetchedMediaResource(postbox: postbox, reference: resourceReference, statsCategory: self.statsCategory ?? .generic, preferBackgroundReferenceRevalidation: streamable).start()) } var avFormatContextRef = avformat_alloc_context() @@ -395,6 +396,11 @@ final class FFMpegMediaFrameSourceContext: NSObject { } self.initializedState = InitializedState(avIoContext: avIoContext, avFormatContext: avFormatContext, audioStream: audioStream, videoStream: videoStream) + + if streamable { + self.fetchedFullDataDisposable.set(fetchedMediaResource(postbox: postbox, reference: resourceReference, range: (0 ..< Int(Int32.max), .default), statsCategory: self.statsCategory ?? .generic, preferBackgroundReferenceRevalidation: streamable).start()) + self.requestedCompleteFetch = true + } } private func readPacket() -> FFMpegPacket? { diff --git a/TelegramUI/FeedGroupingControllerNode.swift b/TelegramUI/FeedGroupingControllerNode.swift index 9ed6b46406..a2db9c03f6 100644 --- a/TelegramUI/FeedGroupingControllerNode.swift +++ b/TelegramUI/FeedGroupingControllerNode.swift @@ -411,7 +411,7 @@ final class FeedGroupingControllerNode: ViewControllerTracingNode { if curve == 7 { listViewCurve = .Spring(duration: duration) } else { - listViewCurve = .Default + listViewCurve = .Default(duration: duration) } let listInsets = UIEdgeInsets(top: insets.top, left: layout.safeInsets.right, bottom: insets.bottom, right: layout.safeInsets.left) diff --git a/TelegramUI/FetchCachedRepresentations.swift b/TelegramUI/FetchCachedRepresentations.swift index 911f182234..956066968c 100644 --- a/TelegramUI/FetchCachedRepresentations.swift +++ b/TelegramUI/FetchCachedRepresentations.swift @@ -8,15 +8,52 @@ import Display import UIKit import AVFoundation -public func fetchCachedResourceRepresentation(account: Account, resource: MediaResource, resourceData: MediaResourceData, representation: CachedMediaResourceRepresentation) -> Signal { +//let signal = self.resourceData(resource, option: .complete(waitUntilFetchStatus: false)) + +public func fetchCachedResourceRepresentation(account: Account, resource: MediaResource, representation: CachedMediaResourceRepresentation) -> Signal { if let representation = representation as? CachedStickerAJpegRepresentation { - return fetchCachedStickerAJpegRepresentation(account: account, resource: resource, resourceData: resourceData, representation: representation) + return account.postbox.mediaBox.resourceData(resource, option: .complete(waitUntilFetchStatus: false)) + |> mapToSignal { data -> Signal in + if !data.complete { + return .complete() + } + return fetchCachedStickerAJpegRepresentation(account: account, resource: resource, resourceData: data, representation: representation) + } } else if let representation = representation as? CachedScaledImageRepresentation { - return fetchCachedScaledImageRepresentation(account: account, resource: resource, resourceData: resourceData, representation: representation) - } else if let representation = representation as? CachedVideoFirstFrameRepresentation { - return fetchCachedVideoFirstFrameRepresentation(account: account, resource: resource, resourceData: resourceData, representation: representation) + return account.postbox.mediaBox.resourceData(resource, option: .complete(waitUntilFetchStatus: false)) + |> mapToSignal { data -> Signal in + if !data.complete { + return .complete() + } + return fetchCachedScaledImageRepresentation(account: account, resource: resource, resourceData: data, representation: representation) + } + } else if let _ = representation as? CachedVideoFirstFrameRepresentation { + /*return fetchPartialVideoThumbnail(postbox: account.postbox, resource: resource) + |> mapToSignal { data -> Signal in + var randomId: Int64 = 0 + arc4random_buf(&randomId, 8) + let path = NSTemporaryDirectory() + "\(randomId)" + if let data = data, let _ = try? data.write(to: URL(fileURLWithPath: path)) { + return .single(CachedMediaResourceRepresentationResult(temporaryPath: path)) + } else { + return .complete() + } + }*/ + return account.postbox.mediaBox.resourceData(resource, option: .complete(waitUntilFetchStatus: false)) + |> mapToSignal { data -> Signal in + if !data.complete { + return .complete() + } + return fetchCachedVideoFirstFrameRepresentation(account: account, resource: resource, resourceData: data) + } } else if let representation = representation as? CachedScaledVideoFirstFrameRepresentation { - return fetchCachedScaledVideoFirstFrameRepresentation(account: account, resource: resource, resourceData: resourceData, representation: representation) + return account.postbox.mediaBox.resourceData(resource, option: .complete(waitUntilFetchStatus: false)) + |> mapToSignal { data -> Signal in + if !data.complete { + return .complete() + } + return fetchCachedScaledVideoFirstFrameRepresentation(account: account, resource: resource, resourceData: data, representation: representation) + } } return .never() } @@ -157,7 +194,7 @@ func generateVideoFirstFrame(_ path: String, maxDimensions: CGSize) -> UIImage? } } -private func fetchCachedVideoFirstFrameRepresentation(account: Account, resource: MediaResource, resourceData: MediaResourceData, representation: CachedVideoFirstFrameRepresentation) -> Signal { +private func fetchCachedVideoFirstFrameRepresentation(account: Account, resource: MediaResource, resourceData: MediaResourceData) -> Signal { return Signal { subscriber in if resourceData.complete { let tempFilePath = NSTemporaryDirectory() + "\(arc4random()).mov" @@ -203,7 +240,8 @@ private func fetchCachedVideoFirstFrameRepresentation(account: Account, resource } private func fetchCachedScaledVideoFirstFrameRepresentation(account: Account, resource: MediaResource, resourceData: MediaResourceData, representation: CachedScaledVideoFirstFrameRepresentation) -> Signal { - return account.postbox.mediaBox.cachedResourceRepresentation(resource, representation: CachedVideoFirstFrameRepresentation(), complete: true) |> mapToSignal { firstFrame -> Signal in + return account.postbox.mediaBox.cachedResourceRepresentation(resource, representation: CachedVideoFirstFrameRepresentation(), complete: true) + |> mapToSignal { firstFrame -> Signal in return Signal({ subscriber in if let data = try? Data(contentsOf: URL(fileURLWithPath: firstFrame.path), options: [.mappedIfSafe]) { if let image = UIImage(data: data) { diff --git a/TelegramUI/FetchResource.swift b/TelegramUI/FetchResource.swift index a390ee2a60..0210791a26 100644 --- a/TelegramUI/FetchResource.swift +++ b/TelegramUI/FetchResource.swift @@ -3,7 +3,7 @@ import Postbox import TelegramCore import SwiftSignalKit -func fetchResource(account: Account, resource: MediaResource, ranges: Signal) -> Signal? { +func fetchResource(account: Account, resource: MediaResource, intervals: Signal<[(Range, MediaBoxFetchPriority)], NoError>) -> Signal? { return nil } diff --git a/TelegramUI/FetchVideoThumbnail.swift b/TelegramUI/FetchVideoThumbnail.swift new file mode 100644 index 0000000000..6e8ad54145 --- /dev/null +++ b/TelegramUI/FetchVideoThumbnail.swift @@ -0,0 +1,403 @@ +import Foundation +import TelegramCore +import Postbox +import SwiftSignalKit +import CoreMedia +import TelegramUIPrivateModule +import Display +import UIKit +import VideoToolbox + +private func readPacketCallback(userData: UnsafeMutableRawPointer?, buffer: UnsafeMutablePointer?, bufferSize: Int32) -> Int32 { + guard let buffer = buffer else { + return 0 + } + let context = Unmanaged.fromOpaque(userData!).takeUnretainedValue() + var bufferPointer = 0 + while bufferPointer < Int(bufferSize) && context.readOffset < context.size { + let bytesRemaining = Int(bufferSize) - bufferPointer + if context.readOffset < context.header.count { + let currentRead = min(bytesRemaining, context.header.count - context.readOffset) + if currentRead != 0 { + context.header.copyBytes(to: buffer.advanced(by: bufferPointer), from: context.readOffset ..< (context.readOffset + currentRead)) + context.readOffset += currentRead + bufferPointer += currentRead + } + } else if context.readOffset >= context.header.count && context.readOffset < context.header.count + context.spacing { + let currentRead = min(bytesRemaining, context.header.count + context.spacing - context.readOffset) + memset(buffer, 0, currentRead) + context.readOffset += currentRead + bufferPointer += currentRead + } else if context.readOffset >= context.header.count + context.spacing { + let normalizedReadOffset = context.readOffset - (context.header.count + context.spacing) + let currentRead = min(bytesRemaining, context.tail.count - normalizedReadOffset) + context.tail.copyBytes(to: buffer.advanced(by: bufferPointer), from: normalizedReadOffset ..< (normalizedReadOffset + currentRead)) + context.readOffset += currentRead + bufferPointer += currentRead + } + } + return Int32(bufferPointer) +} + +private func seekCallback(userData: UnsafeMutableRawPointer?, offset: Int64, whence: Int32) -> Int64 { + let context = Unmanaged.fromOpaque(userData!).takeUnretainedValue() + if (whence & AVSEEK_SIZE) != 0 { + return Int64(context.size) + } else { + context.readOffset = Int(offset) + return offset + } +} + +private final class SoftwareVideoStream { + let index: Int + let fps: CMTime + let timebase: CMTime + let duration: CMTime + let decoder: FFMpegMediaVideoFrameDecoder + let rotationAngle: Double + let aspect: Double + + init(index: Int, fps: CMTime, timebase: CMTime, duration: CMTime, decoder: FFMpegMediaVideoFrameDecoder, rotationAngle: Double, aspect: Double) { + self.index = index + self.fps = fps + self.timebase = timebase + self.duration = duration + self.decoder = decoder + self.rotationAngle = rotationAngle + self.aspect = aspect + } +} + +private final class FetchVideoThumbnailSource { + fileprivate let header: Data + fileprivate let spacing: Int + fileprivate let tail: Data + fileprivate var readOffset: Int = 0 + + private var readingError = false + private var videoStream: SoftwareVideoStream? + private var avIoContext: UnsafeMutablePointer? + private var avFormatContext: UnsafeMutablePointer? + fileprivate let size: Int32 + + init(header: Data, spacing: Int, tail: Data) { + let _ = FFMpegMediaFrameSourceContextHelpers.registerFFMpegGlobals + self.header = header + self.spacing = spacing + self.tail = tail + + self.size = Int32(header.count + spacing + tail.count) + + var avFormatContextRef = avformat_alloc_context() + guard let avFormatContext = avFormatContextRef else { + self.readingError = true + return + } + + let ioBufferSize = 8 * 1024 + let avIoBuffer = av_malloc(ioBufferSize)! + let avIoContextRef = avio_alloc_context(avIoBuffer.assumingMemoryBound(to: UInt8.self), Int32(ioBufferSize), 0, Unmanaged.passUnretained(self).toOpaque(), readPacketCallback, nil, seekCallback) + self.avIoContext = avIoContextRef + + avFormatContext.pointee.pb = self.avIoContext + + guard avformat_open_input(&avFormatContextRef, nil, nil, nil) >= 0 else { + self.readingError = true + return + } + + guard avformat_find_stream_info(avFormatContext, nil) >= 0 else { + self.readingError = true + return + } + + self.avFormatContext = avFormatContext + + var videoStream: SoftwareVideoStream? + + for streamIndex in FFMpegMediaFrameSourceContextHelpers.streamIndices(formatContext: avFormatContext, codecType: AVMEDIA_TYPE_VIDEO) { + if (avFormatContext.pointee.streams.advanced(by: streamIndex).pointee!.pointee.disposition & Int32(AV_DISPOSITION_ATTACHED_PIC)) == 0 { + + let codecPar = avFormatContext.pointee.streams.advanced(by: streamIndex).pointee!.pointee.codecpar! + + if let codec = avcodec_find_decoder(codecPar.pointee.codec_id) { + if let codecContext = avcodec_alloc_context3(codec) { + if avcodec_parameters_to_context(codecContext, avFormatContext.pointee.streams[streamIndex]!.pointee.codecpar) >= 0 { + if avcodec_open2(codecContext, codec, nil) >= 0 { + let (fps, timebase) = FFMpegMediaFrameSourceContextHelpers.streamFpsAndTimeBase(stream: avFormatContext.pointee.streams.advanced(by: streamIndex).pointee!, defaultTimeBase: CMTimeMake(1, 24)) + + let duration = CMTimeMake(avFormatContext.pointee.streams.advanced(by: streamIndex).pointee!.pointee.duration, timebase.timescale) + + var rotationAngle: Double = 0.0 + if let rotationInfo = av_dict_get(avFormatContext.pointee.streams.advanced(by: streamIndex).pointee!.pointee.metadata, "rotate", nil, 0), let value = rotationInfo.pointee.value { + if strcmp(value, "0") != 0 { + if let angle = Double(String(cString: value)) { + rotationAngle = angle * Double.pi / 180.0 + } + } + } + + let aspect = Double(codecPar.pointee.width) / Double(codecPar.pointee.height) + + videoStream = SoftwareVideoStream(index: streamIndex, fps: fps, timebase: timebase, duration: duration, decoder: FFMpegMediaVideoFrameDecoder(codecContext: codecContext), rotationAngle: rotationAngle, aspect: aspect) + break + } else { + var codecContextRef: UnsafeMutablePointer? = codecContext + avcodec_free_context(&codecContextRef) + } + } else { + var codecContextRef: UnsafeMutablePointer? = codecContext + avcodec_free_context(&codecContextRef) + } + } + } + } + } + + self.videoStream = videoStream + if self.videoStream == nil { + self.readingError = true + } + } + + deinit { + if let avIoContext = self.avIoContext { + if avIoContext.pointee.buffer != nil { + av_free(avIoContext.pointee.buffer) + } + av_free(avIoContext) + } + if let avFormatContext = self.avFormatContext { + avformat_free_context(avFormatContext) + } + } + + private func readPacketInternal() -> FFMpegPacket? { + guard let avFormatContext = self.avFormatContext else { + return nil + } + + let packet = FFMpegPacket() + if av_read_frame(avFormatContext, &packet.packet) < 0 { + return nil + } else { + return packet + } + } + + func readDecodableFrame() -> MediaTrackDecodableFrame? { + var frames: [MediaTrackDecodableFrame] = [] + + while !self.readingError && frames.isEmpty { + if let packet = self.readPacketInternal() { + if let videoStream = videoStream, Int(packet.packet.stream_index) == videoStream.index { + let avNoPtsRawValue: UInt64 = 0x8000000000000000 + let avNoPtsValue = Int64(bitPattern: avNoPtsRawValue) + let packetPts = packet.packet.pts == avNoPtsValue ? packet.packet.dts : packet.packet.pts + + let pts = CMTimeMake(packetPts, videoStream.timebase.timescale) + let dts = CMTimeMake(packet.packet.dts, videoStream.timebase.timescale) + + let duration: CMTime + + let frameDuration = packet.packet.duration + if frameDuration != 0 { + duration = CMTimeMake(frameDuration * videoStream.timebase.value, videoStream.timebase.timescale) + } else { + duration = videoStream.fps + } + + let frame = MediaTrackDecodableFrame(type: .video, packet: packet, pts: pts, dts: dts, duration: duration) + frames.append(frame) + } + } else { + self.readingError = true + } + } + + return frames.first + } + + func readFrame() -> (frame: MediaTrackFrame, rotationAngle: CGFloat, aspect: CGFloat)? { + guard let videoStream = self.videoStream else { + return nil + } + guard let decodableFrame = self.readDecodableFrame() else { + return nil + } + guard let decodedFrame = videoStream.decoder.decode(frame: decodableFrame, ptsOffset: nil) else { + return nil + } + + return (decodedFrame, CGFloat(videoStream.rotationAngle), CGFloat(videoStream.aspect)) + } +} + +private final class FetchVideoThumbnailSourceParameters: NSObject { + +} + +private final class FetchVideoThumbnailSourceTimerTarget: NSObject { + @objc func noop() { + } +} + +private final class FetchVideoThumbnailSourceThreadImpl: NSObject { + private var timer: Foundation.Timer + private var disposed = false + + override init() { + self.timer = Foundation.Timer.scheduledTimer(timeInterval: .greatestFiniteMagnitude, target: FetchVideoThumbnailSourceTimerTarget(), selector: #selector(FetchVideoThumbnailSourceTimerTarget.noop), userInfo: nil, repeats: true) + + super.init() + } + + @objc func dispose() { + self.disposed = true + self.timer.invalidate() + } + + @objc func entryPoint() { + RunLoop.current.add(self.timer, forMode: RunLoopMode.defaultRunLoopMode) + while !self.disposed { + if !RunLoop.current.run(mode: RunLoopMode.defaultRunLoopMode, before: .distantFuture) { + break + } + } + } + + @objc func fetch(_ parameters: FetchVideoThumbnailSourceParameters) { + print("fetch") + } +} + +private let headerSize = 250 * 1024 +private let tailSize = 16 * 1024 + +func fetchedPartialVideoThumbnailData(postbox: Postbox, fileReference: FileMediaReference) -> Signal { + return Signal { subscriber in + guard let size = fileReference.media.size else { + subscriber.putCompletion() + return EmptyDisposable + } + let fetchedHead = fetchedMediaResource(postbox: postbox, reference: fileReference.resourceReference(fileReference.media.resource), range: (0 ..< min(size, headerSize), .elevated), statsCategory: .video, reportResultStatus: false, preferBackgroundReferenceRevalidation: false).start() + let fetchedTail = fetchedMediaResource(postbox: postbox, reference: fileReference.resourceReference(fileReference.media.resource), range: (max(0, size - tailSize) ..< size, .elevated), statsCategory: .video, reportResultStatus: false, preferBackgroundReferenceRevalidation: false).start() + + return ActionDisposable { + fetchedHead.dispose() + fetchedTail.dispose() + } + } +} + +private func partialVideoThumbnailData(postbox: Postbox, resource: MediaResource) -> Signal<(Data, Int, Data), NoError> { + guard let size = resource.size else { + return .complete() + } + return combineLatest(postbox.mediaBox.resourceData(resource, size: size, in: 0 ..< min(size, headerSize)), postbox.mediaBox.resourceData(resource, size: size, in: max(0, size - tailSize) ..< size)) + |> mapToSignal { header, tail -> Signal<(Data, Int, Data), NoError> in + return .single((header, max(0, size - header.count - tail.count), tail)) + } +} + +private func imageFromSampleBuffer(sampleBuffer: CMSampleBuffer) -> UIImage? { + guard let imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else { + return nil + } + var maybeImage: CGImage? + if #available(iOSApplicationExtension 9.0, *) { + guard VTCreateCGImageFromCVPixelBuffer(imageBuffer, nil, &maybeImage) == noErr, let image = maybeImage else { + return nil + } + return UIImage(cgImage: image) + } else { + return nil + } + + /*CVPixelBufferLockBaseAddress(imageBuffer, []) + defer { + CVPixelBufferUnlockBaseAddress(imageBuffer, []) + } + + let width = CVPixelBufferGetWidth(imageBuffer) + let height = CVPixelBufferGetHeight(imageBuffer) + guard let yBuffer = CVPixelBufferGetBaseAddressOfPlane(imageBuffer, 0)?.assumingMemoryBound(to: UInt8.self) else { + return nil + } + let yPitch = CVPixelBufferGetBytesPerRowOfPlane(imageBuffer, 0) + guard let cbCrBuffer = CVPixelBufferGetBaseAddressOfPlane(imageBuffer, 1)?.assumingMemoryBound(to: UInt8.self) else { + return nil + } + let cbCrPitch = CVPixelBufferGetBytesPerRowOfPlane(imageBuffer, 1) + + let bytesPerPixel = 4 + let context = DrawingContext(size: CGSize(width: CGFloat(width), height: CGFloat(height)), scale: 1.0, clear: false) + let rgbBuffer = context.bytes.assumingMemoryBound(to: UInt8.self) + + for y in 0 ..< height { + let rgbBufferLine = rgbBuffer.advanced(by: y * width * bytesPerPixel) + let yBufferLine = yBuffer.advanced(by: y * yPitch) + let cbCrBufferLine = cbCrBuffer.advanced(by: y * 2 * cbCrPitch) + + for x in 0 ..< width { + let y = UInt16(yBufferLine[x]) + let cb = UInt16(cbCrBufferLine[x & ~1]) - 128 + let cr = UInt16(cbCrBufferLine[x | 1]) - 128 + + let rgbOutput = rgbBufferLine.advanced(by: x * bytesPerPixel) + + let r = UInt16(round(Float(y) + Float(cr) * 1.4)) + let g = UInt16(round(Float(y) + Float(cb) * -0.343 + Float(cr) * -0.711)) + let b = UInt16(round(Float(y) + Float(cb) * 1.765)) + + rgbOutput[0] = 0xff + rgbOutput[1] = UInt8(clamping: b > 255 ? 255 : (b < 0 ? 0 : b)) + rgbOutput[2] = UInt8(clamping: g > 255 ? 255 : (g < 0 ? 0 : g)) + rgbOutput[3] = UInt8(clamping: r > 255 ? 255 : (r < 0 ? 0 : r)) + } + } + + + return context.generateImage()*/ +} + + +func fetchPartialVideoThumbnail(postbox: Postbox, resource: MediaResource) -> Signal { + return partialVideoThumbnailData(postbox: postbox, resource: resource) + |> take(1) + |> mapToSignal { header, spacing, tail -> Signal in + return Signal { subscriber in + let source = FetchVideoThumbnailSource(header: header, spacing: spacing, tail: tail) + guard let (frame, rotationAngle, aspect) = source.readFrame() else { + subscriber.putNext(nil) + subscriber.putCompletion() + return EmptyDisposable + } + guard let image = imageFromSampleBuffer(sampleBuffer: frame.sampleBuffer) else { + subscriber.putNext(nil) + subscriber.putCompletion() + return EmptyDisposable + } + guard let data = UIImageJPEGRepresentation(image, 0.7) else { + subscriber.putNext(nil) + subscriber.putCompletion() + return EmptyDisposable + } + subscriber.putNext(data) + subscriber.putCompletion() + return EmptyDisposable + } + } + /*return Signal { subscriber in + let impl = FetchVideoThumbnailSourceThreadImpl() + let thread = Thread(target: impl, selector: #selector(impl.entryPoint), object: nil) + thread.name = "fetchPartialVideoThumbnail" + impl.perform(#selector(impl.fetch(_:)), on: thread, with: FetchVideoThumbnailSourceParameters(), waitUntilDone: false) + thread.start() + + return ActionDisposable { + impl.perform(#selector(impl.dispose), on: thread, with: nil, waitUntilDone: false) + } + }*/ +} diff --git a/TelegramUI/GalleryController.swift b/TelegramUI/GalleryController.swift index 3eff758772..0dd43c9a98 100644 --- a/TelegramUI/GalleryController.swift +++ b/TelegramUI/GalleryController.swift @@ -90,12 +90,6 @@ private let internalMimePrefixes: [String] = [ "image/png" ] -private let supportedVideoMimeTypes = Set([ - "video/mp4", - "video/mpeg4", - "video/mov" -]) - func internalDocumentItemSupportsMimeType(_ type: String, fileName: String?) -> Bool { if let fileName = fileName { let ext = (fileName as NSString).pathExtension @@ -110,9 +104,6 @@ func internalDocumentItemSupportsMimeType(_ type: String, fileName: String?) -> if internalMimeTypes.contains(type) { return true } - if supportedVideoMimeTypes.contains(type) { - return true - } for prefix in internalMimePrefixes { if type.hasPrefix(prefix) { return true @@ -128,7 +119,7 @@ func galleryItemForEntry(account: Account, presentationData: PresentationData, e if let _ = media as? TelegramMediaImage { return ChatImageGalleryItem(account: account, presentationData: presentationData, message: message, location: location) } else if let file = media as? TelegramMediaFile { - if file.isVideo || supportedVideoMimeTypes.contains(file.mimeType) { + if file.isVideo { let content: UniversalVideoContent if file.isAnimated { content = NativeVideoContent(id: .message(message.id, message.stableId + 1, file.fileId), fileReference: .message(message: MessageReference(message), media: file), streamVideo: streamVideos, loopVideo: true, enableSound: false) diff --git a/TelegramUI/GridMessageItem.swift b/TelegramUI/GridMessageItem.swift index d7f30fd379..f5b38ea6a3 100644 --- a/TelegramUI/GridMessageItem.swift +++ b/TelegramUI/GridMessageItem.swift @@ -393,13 +393,13 @@ final class GridMessageItemNode: GridItemNode { case .Fetching: messageMediaFileCancelInteractiveFetch(account: account, messageId: message.id, file: file) case .Local: - let _ = controllerInteraction.openMessage(message) + let _ = controllerInteraction.openMessage(message, .default) case .Remote: self.fetchDisposable.set(messageMediaFileInteractiveFetched(account: account, message: message, file: file, userInitiated: true).start()) } } } else { - let _ = controllerInteraction.openMessage(message) + let _ = controllerInteraction.openMessage(message, .default) } } case .longTap: diff --git a/TelegramUI/HashtagChatInputContextPanelNode.swift b/TelegramUI/HashtagChatInputContextPanelNode.swift index 80c9f84046..f2ed2a4747 100644 --- a/TelegramUI/HashtagChatInputContextPanelNode.swift +++ b/TelegramUI/HashtagChatInputContextPanelNode.swift @@ -159,7 +159,7 @@ final class HashtagChatInputContextPanelNode: ChatInputContextPanelNode { insets.left = validLayout.1 insets.right = validLayout.2 - let updateSizeAndInsets = ListViewUpdateSizeAndInsets(size: validLayout.0, insets: insets, duration: 0.0, curve: .Default) + let updateSizeAndInsets = ListViewUpdateSizeAndInsets(size: validLayout.0, insets: insets, duration: 0.0, curve: .Default(duration: nil)) self.listView.transaction(deleteIndices: transition.deletions, insertIndicesAndItems: transition.insertions, updateIndicesAndItems: transition.updates, options: options, updateSizeAndInsets: updateSizeAndInsets, updateOpaqueState: nil, completion: { [weak self] _ in if let strongSelf = self, firstTime { @@ -215,7 +215,7 @@ final class HashtagChatInputContextPanelNode: ChatInputContextPanelNode { if curve == 7 { listViewCurve = .Spring(duration: duration) } else { - listViewCurve = .Default + listViewCurve = .Default(duration: duration) } let updateSizeAndInsets = ListViewUpdateSizeAndInsets(size: size, insets: insets, duration: duration, curve: listViewCurve) diff --git a/TelegramUI/HashtagSearchControllerNode.swift b/TelegramUI/HashtagSearchControllerNode.swift index fa733e73e4..e1fe6c6810 100644 --- a/TelegramUI/HashtagSearchControllerNode.swift +++ b/TelegramUI/HashtagSearchControllerNode.swift @@ -136,7 +136,7 @@ final class HashtagSearchControllerNode: ASDisplayNode { if curve == 7 { listViewCurve = .Spring(duration: duration) } else { - listViewCurve = .Default + listViewCurve = .Default(duration: duration) } let updateSizeAndInsets = ListViewUpdateSizeAndInsets(size: layout.size, insets: insets, duration: duration, curve: listViewCurve) diff --git a/TelegramUI/HorizontalListContextResultsChatInputContextPanelNode.swift b/TelegramUI/HorizontalListContextResultsChatInputContextPanelNode.swift index bcb9a6b0e5..5b72ad787d 100644 --- a/TelegramUI/HorizontalListContextResultsChatInputContextPanelNode.swift +++ b/TelegramUI/HorizontalListContextResultsChatInputContextPanelNode.swift @@ -310,7 +310,7 @@ final class HorizontalListContextResultsChatInputContextPanelNode: ChatInputCont if curve == 7 { listViewCurve = .Spring(duration: duration) } else { - listViewCurve = .Default + listViewCurve = .Default(duration: duration) } let updateSizeAndInsets = ListViewUpdateSizeAndInsets(size: CGSize(width: listHeight, height: size.width), insets: insets, duration: duration, curve: listViewCurve) diff --git a/TelegramUI/InviteContactsControllerNode.swift b/TelegramUI/InviteContactsControllerNode.swift index c6686968ba..31105d73e9 100644 --- a/TelegramUI/InviteContactsControllerNode.swift +++ b/TelegramUI/InviteContactsControllerNode.swift @@ -492,7 +492,7 @@ final class InviteContactsControllerNode: ASDisplayNode { } func scrollToTop() { - self.listNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous, .LowLatency], scrollToItem: ListViewScrollToItem(index: 0, position: .top(0.0), animated: true, curve: .Default, directionHint: .Up), updateSizeAndInsets: nil, stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in }) + self.listNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous, .LowLatency], scrollToItem: ListViewScrollToItem(index: 0, position: .top(0.0), animated: true, curve: .Default(duration: nil), directionHint: .Up), updateSizeAndInsets: nil, stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in }) } func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) { @@ -542,7 +542,7 @@ final class InviteContactsControllerNode: ASDisplayNode { if curve == 7 { listViewCurve = .Spring(duration: duration) } else { - listViewCurve = .Default + listViewCurve = .Default(duration: nil) } let updateSizeAndInsets = ListViewUpdateSizeAndInsets(size: layout.size, insets: insets, duration: duration, curve: listViewCurve) diff --git a/TelegramUI/IsMediaStreamable.swift b/TelegramUI/IsMediaStreamable.swift new file mode 100644 index 0000000000..a3755b0238 --- /dev/null +++ b/TelegramUI/IsMediaStreamable.swift @@ -0,0 +1,13 @@ +import Foundation +import Postbox +import TelegramCore + +func isMediaStreamable(message: Message, media: TelegramMediaFile) -> Bool { + if message.id.peerId.namespace == Namespaces.Peer.SecretChat { + return false + } + if media.isVideo && !media.isAnimated { + return true + } + return false +} diff --git a/TelegramUI/ItemListControllerNode.swift b/TelegramUI/ItemListControllerNode.swift index d661ec7767..447e5aca5f 100644 --- a/TelegramUI/ItemListControllerNode.swift +++ b/TelegramUI/ItemListControllerNode.swift @@ -232,7 +232,7 @@ class ItemListControllerNode: ViewControllerTracingNod if curve == 7 { listViewCurve = .Spring(duration: duration) } else { - listViewCurve = .Default + listViewCurve = .Default(duration: duration) } var insets = layout.insets(options: [.input]) @@ -436,7 +436,7 @@ class ItemListControllerNode: ViewControllerTracingNod } func scrollToTop() { - self.listNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous, .LowLatency], scrollToItem: ListViewScrollToItem(index: 0, position: .top(0.0), animated: true, curve: .Default, directionHint: .Up), updateSizeAndInsets: nil, stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in }) + self.listNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous, .LowLatency], scrollToItem: ListViewScrollToItem(index: 0, position: .top(0.0), animated: true, curve: .Default(duration: nil), directionHint: .Up), updateSizeAndInsets: nil, stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in }) } func scrollViewDidScroll(_ scrollView: UIScrollView) { diff --git a/TelegramUI/LanguageLinkPreviewContentNode.swift b/TelegramUI/LanguageLinkPreviewContentNode.swift new file mode 100644 index 0000000000..e742ab283b --- /dev/null +++ b/TelegramUI/LanguageLinkPreviewContentNode.swift @@ -0,0 +1,102 @@ +import Foundation +import AsyncDisplayKit +import Display +import Postbox +import TelegramCore + +final class LanguageLinkPreviewContentNode: ASDisplayNode, ShareContentContainerNode { + private var contentOffsetUpdated: ((CGFloat, ContainedViewLayoutTransition) -> Void)? + + private let titleNode: ImmediateTextNode + private let textNode: ImmediateTextNode + + init(account: Account, localizationInfo: LocalizationInfo, theme: PresentationTheme, strings: PresentationStrings, openTranslationUrl: @escaping (String) -> Void) { + self.titleNode = ImmediateTextNode() + self.titleNode.textAlignment = .center + + self.textNode = ImmediateTextNode() + self.textNode.maximumNumberOfLines = 0 + self.textNode.textAlignment = .center + self.textNode.lineSpacing = 0.1 + + super.init() + + self.addSubnode(self.titleNode) + self.addSubnode(self.textNode) + + self.titleNode.attributedText = NSAttributedString(string: "Change Language?", font: Font.medium(20.0), textColor: theme.actionSheet.primaryTextColor, paragraphAlignment: .center) + + let completionScore = localizationInfo.translatedStringCount * 100 / max(1, localizationInfo.totalStringCount) + + let text: String + if localizationInfo.translatedStringCount == 0 { + self.titleNode.isHidden = true + text = "This language is not available yet." + } else { + text = "You are about to apply a custom language pack \(localizationInfo.title) that is \(completionScore)% complete.\nThis will translate the entire interface. You can suggest corrections in the [translation panel](https://translations.telegram.org/\(localizationInfo.languageCode)/).\nYou can change your language back at any time in Settings." + } + let body = MarkdownAttributeSet(font: Font.regular(15.0), textColor: theme.actionSheet.primaryTextColor) + let link = MarkdownAttributeSet(font: Font.regular(15.0), textColor: theme.actionSheet.controlAccentColor, additionalAttributes: [TelegramTextAttributes.URL: ""]) + + self.textNode.attributedText = parseMarkdownIntoAttributedString(text, attributes: MarkdownAttributes(body: body, bold: body, link: link, linkAttribute: { _ in nil }), textAlignment: .center) + self.textNode.linkHighlightColor = theme.actionSheet.controlAccentColor.withAlphaComponent(0.5) + self.textNode.highlightAttributeAction = { attributes in + if let _ = attributes[NSAttributedStringKey(rawValue: TelegramTextAttributes.URL)] { + return NSAttributedStringKey(rawValue: TelegramTextAttributes.URL) + } else { + return nil + } + } + self.textNode.tapAttributeAction = { attributes in + if let _ = attributes[NSAttributedStringKey(rawValue: TelegramTextAttributes.URL)] { + openTranslationUrl("https://translations.telegram.org/\(localizationInfo.languageCode)/") + } + } + + //self.textNode.attributedText = NSAttributedString(string: "", font: Font.regular(15.0), textColor: theme.actionSheet.primaryTextColor, paragraphAlignment: .center) + } + + func activate() { + } + + func deactivate() { + } + + func setEnsurePeerVisibleOnLayout(_ peerId: PeerId?) { + } + + func setContentOffsetUpdated(_ f: ((CGFloat, ContainedViewLayoutTransition) -> Void)?) { + self.contentOffsetUpdated = f + } + + func updateLayout(size: CGSize, bottomInset: CGFloat, transition: ContainedViewLayoutTransition) { + let insets = UIEdgeInsets(top: 12.0, left: 10.0, bottom: 12.0 + bottomInset, right: 10.0) + let titleSpacing: CGFloat = 7.0 + let titleSize = self.titleNode.updateLayout(CGSize(width: size.width - insets.left - insets.right, height: .greatestFiniteMagnitude)) + let textSize = self.textNode.updateLayout(CGSize(width: size.width - insets.left - insets.right, height: .greatestFiniteMagnitude)) + + let nodeHeight: CGFloat + if !self.titleNode.isHidden { + nodeHeight = titleSize.height + titleSpacing + textSize.height + insets.top + insets.bottom + } else { + nodeHeight = textSize.height + insets.top + insets.bottom + } + + let verticalOrigin = size.height - nodeHeight + + transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(x: floor((size.width - titleSize.width) / 2.0), y: verticalOrigin + insets.top), size: titleSize)) + let textOrigin: CGFloat + if !self.titleNode.isHidden { + textOrigin = verticalOrigin + insets.top + titleSize.height + titleSpacing + } else { + textOrigin = verticalOrigin + insets.top + } + + transition.updateFrame(node: self.textNode, frame: CGRect(origin: CGPoint(x: floor((size.width - textSize.width) / 2.0), y: textOrigin), size: textSize)) + + self.contentOffsetUpdated?(-size.height + nodeHeight - 64.0, transition) + } + + func updateSelectedPeers() { + } +} diff --git a/TelegramUI/LanguageLinkPreviewController.swift b/TelegramUI/LanguageLinkPreviewController.swift new file mode 100644 index 0000000000..73c8ca8a5d --- /dev/null +++ b/TelegramUI/LanguageLinkPreviewController.swift @@ -0,0 +1,118 @@ +import Foundation +import Display +import AsyncDisplayKit +import Postbox +import TelegramCore +import SwiftSignalKit + +public final class LanguageLinkPreviewController: ViewController { + private var controllerNode: LanguageLinkPreviewControllerNode { + return self.displayNode as! LanguageLinkPreviewControllerNode + } + + private var animatedIn = false + + private let account: Account + private let identifier: String + private var localizationInfo: LocalizationInfo? + private var presentationData: PresentationData + + private let disposable = MetaDisposable() + + public init(account: Account, identifier: String) { + self.account = account + self.identifier = identifier + + self.presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 } + + super.init(navigationBarPresentationData: nil) + } + + required public init(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + deinit { + self.disposable.dispose() + } + + override public func loadDisplayNode() { + self.displayNode = LanguageLinkPreviewControllerNode(account: self.account, requestLayout: { [weak self] transition in + self?.requestLayout(transition: transition) + }, openUrl: { [weak self] url in + guard let strongSelf = self else { + return + } + openExternalUrl(account: strongSelf.account, url: url, presentationData: strongSelf.presentationData, applicationContext: strongSelf.account.telegramApplicationContext, navigationController: nil, dismissInput: { + }) + }) + self.controllerNode.dismiss = { [weak self] in + self?.presentingViewController?.dismiss(animated: false, completion: nil) + } + self.controllerNode.cancel = { [weak self] in + self?.dismiss() + } + self.controllerNode.activate = { [weak self] in + self?.activate() + } + self.displayNodeDidLoad() + + self.disposable.set((requestLocalizationPreview(postbox: self.account.postbox, network: self.account.network, identifier: self.identifier) + |> deliverOnMainQueue).start(next: { [weak self] result in + guard let strongSelf = self else { + return + } + strongSelf.localizationInfo = result + strongSelf.controllerNode.setData(localizationInfo: result) + }, error: { [weak self] _ in + self?.dismiss() + })) + self.ready.set(self.controllerNode.ready.get()) + } + + override public func loadView() { + super.loadView() + + self.statusBar.removeFromSupernode() + } + + override public func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + + if !self.animatedIn { + self.animatedIn = true + self.controllerNode.animateIn() + } + } + + override public func dismiss(completion: (() -> Void)? = nil) { + self.controllerNode.animateOut(completion: completion) + } + + override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) { + super.containerLayoutUpdated(layout, transition: transition) + + self.controllerNode.containerLayoutUpdated(layout, navigationBarHeight: self.navigationHeight, transition: transition) + } + + private func activate() { + guard let localizationInfo = self.localizationInfo else { + return + } + self.controllerNode.setInProgress(true) + self.disposable.set((downoadAndApplyLocalization(postbox: self.account.postbox, network: self.account.network, languageCode: localizationInfo.languageCode) + |> deliverOnMainQueue).start(error: { [weak self] _ in + guard let strongSelf = self else { + return + } + strongSelf.controllerNode.setInProgress(false) + strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.presentationData.theme), title: nil, text: strongSelf.presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + }, completed: { [weak self] in + guard let strongSelf = self else { + return + } + strongSelf.controllerNode.setInProgress(false) + strongSelf.dismiss() + })) + } +} diff --git a/TelegramUI/LanguageLinkPreviewControllerNode.swift b/TelegramUI/LanguageLinkPreviewControllerNode.swift new file mode 100644 index 0000000000..5a03b69106 --- /dev/null +++ b/TelegramUI/LanguageLinkPreviewControllerNode.swift @@ -0,0 +1,473 @@ +import Foundation +import Display +import AsyncDisplayKit +import SwiftSignalKit +import Postbox +import TelegramCore + +final class LanguageLinkPreviewControllerNode: ViewControllerTracingNode, UIScrollViewDelegate { + private let account: Account + private var presentationData: PresentationData + + private let requestLayout: (ContainedViewLayoutTransition) -> Void + private let openUrl: (String) -> Void + + private var containerLayout: (ContainerViewLayout, CGFloat, CGFloat)? + + private let dimNode: ASDisplayNode + + private let wrappingScrollNode: ASScrollNode + private let cancelButtonNode: ASButtonNode + + private let contentContainerNode: ASDisplayNode + private let contentBackgroundNode: ASImageNode + + private var contentNode: (ASDisplayNode & ShareContentContainerNode)? + private var previousContentNode: (ASDisplayNode & ShareContentContainerNode)? + private var animateContentNodeOffsetFromBackgroundOffset: CGFloat? + + private let actionsBackgroundNode: ASImageNode + private let actionButtonNode: ShareActionButtonNode + private let actionIndicator: ActivityIndicator + private let actionSeparatorNode: ASDisplayNode + + var dismiss: (() -> Void)? + var cancel: (() -> Void)? + var activate: (() -> Void)? + + let ready = Promise() + private var didSetReady = false + + private var scheduledLayoutTransitionRequestId: Int = 0 + private var scheduledLayoutTransitionRequest: (Int, ContainedViewLayoutTransition)? + + private let disposable = MetaDisposable() + + init(account: Account, requestLayout: @escaping (ContainedViewLayoutTransition) -> Void, openUrl: @escaping (String) -> Void) { + self.account = account + self.presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 } + + self.requestLayout = requestLayout + self.openUrl = openUrl + + let roundedBackground = generateStretchableFilledCircleImage(radius: 16.0, color: self.presentationData.theme.actionSheet.opaqueItemBackgroundColor) + let highlightedRoundedBackground = generateStretchableFilledCircleImage(radius: 16.0, color: self.presentationData.theme.actionSheet.opaqueItemHighlightedBackgroundColor) + + let theme = self.presentationData.theme + let halfRoundedBackground = generateImage(CGSize(width: 32.0, height: 32.0), rotatedContext: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + context.setFillColor(theme.actionSheet.opaqueItemBackgroundColor.cgColor) + context.fillEllipse(in: CGRect(origin: CGPoint(), size: CGSize(width: size.width, height: size.height))) + context.fill(CGRect(origin: CGPoint(), size: CGSize(width: size.width, height: size.height / 2.0))) + })?.stretchableImage(withLeftCapWidth: 16, topCapHeight: 1) + + let highlightedHalfRoundedBackground = generateImage(CGSize(width: 32.0, height: 32.0), rotatedContext: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + context.setFillColor(theme.actionSheet.opaqueItemHighlightedBackgroundColor.cgColor) + context.fillEllipse(in: CGRect(origin: CGPoint(), size: CGSize(width: size.width, height: size.height))) + context.fill(CGRect(origin: CGPoint(), size: CGSize(width: size.width, height: size.height / 2.0))) + })?.stretchableImage(withLeftCapWidth: 16, topCapHeight: 1) + + self.wrappingScrollNode = ASScrollNode() + self.wrappingScrollNode.view.alwaysBounceVertical = true + self.wrappingScrollNode.view.delaysContentTouches = false + self.wrappingScrollNode.view.canCancelContentTouches = true + + self.dimNode = ASDisplayNode() + self.dimNode.backgroundColor = UIColor(white: 0.0, alpha: 0.5) + + self.cancelButtonNode = ASButtonNode() + self.cancelButtonNode.displaysAsynchronously = false + self.cancelButtonNode.setBackgroundImage(roundedBackground, for: .normal) + self.cancelButtonNode.setBackgroundImage(highlightedRoundedBackground, for: .highlighted) + + self.contentContainerNode = ASDisplayNode() + self.contentContainerNode.isOpaque = false + self.contentContainerNode.clipsToBounds = true + + self.contentBackgroundNode = ASImageNode() + self.contentBackgroundNode.displaysAsynchronously = false + self.contentBackgroundNode.displayWithoutProcessing = true + self.contentBackgroundNode.image = roundedBackground + + self.actionsBackgroundNode = ASImageNode() + self.actionsBackgroundNode.isLayerBacked = true + self.actionsBackgroundNode.displayWithoutProcessing = true + self.actionsBackgroundNode.displaysAsynchronously = false + self.actionsBackgroundNode.image = halfRoundedBackground + + self.actionButtonNode = ShareActionButtonNode(badgeBackgroundColor: self.presentationData.theme.actionSheet.controlAccentColor, badgeTextColor: self.presentationData.theme.actionSheet.opaqueItemBackgroundColor) + self.actionButtonNode.displaysAsynchronously = false + self.actionButtonNode.titleNode.displaysAsynchronously = false + self.actionButtonNode.setBackgroundImage(highlightedHalfRoundedBackground, for: .highlighted) + + self.actionIndicator = ActivityIndicator(type: .custom(theme.actionSheet.controlAccentColor, 22.0, 1.0, false)) + self.actionIndicator.isHidden = true + + self.actionSeparatorNode = ASDisplayNode() + self.actionSeparatorNode.isLayerBacked = true + self.actionSeparatorNode.displaysAsynchronously = false + self.actionSeparatorNode.backgroundColor = self.presentationData.theme.actionSheet.opaqueItemSeparatorColor + + super.init() + + self.backgroundColor = nil + self.isOpaque = false + + self.dimNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.dimTapGesture(_:)))) + self.addSubnode(self.dimNode) + + self.wrappingScrollNode.view.delegate = self + self.addSubnode(self.wrappingScrollNode) + + self.cancelButtonNode.setTitle(self.presentationData.strings.Common_Cancel, with: Font.medium(20.0), with: self.presentationData.theme.actionSheet.standardActionTextColor, for: .normal) + + self.wrappingScrollNode.addSubnode(self.cancelButtonNode) + self.cancelButtonNode.addTarget(self, action: #selector(self.cancelButtonPressed), forControlEvents: .touchUpInside) + + self.actionButtonNode.addTarget(self, action: #selector(self.installActionButtonPressed), forControlEvents: .touchUpInside) + + self.wrappingScrollNode.addSubnode(self.contentBackgroundNode) + + self.wrappingScrollNode.addSubnode(self.contentContainerNode) + self.contentContainerNode.addSubnode(self.actionSeparatorNode) + self.contentContainerNode.addSubnode(self.actionsBackgroundNode) + self.contentContainerNode.addSubnode(self.actionButtonNode) + self.contentContainerNode.addSubnode(self.actionIndicator) + + self.transitionToContentNode(ShareLoadingContainerNode(theme: theme)) + + self.actionButtonNode.alpha = 0.0 + self.actionSeparatorNode.alpha = 0.0 + self.actionsBackgroundNode.alpha = 0.0 + + self.ready.set(.single(true)) + self.didSetReady = true + } + + deinit { + self.disposable.dispose() + } + + override func didLoad() { + super.didLoad() + + if #available(iOSApplicationExtension 11.0, *) { + self.wrappingScrollNode.view.contentInsetAdjustmentBehavior = .never + } + } + + func transitionToContentNode(_ contentNode: (ASDisplayNode & ShareContentContainerNode)?, fastOut: Bool = false) { + if self.contentNode !== contentNode { + let transition: ContainedViewLayoutTransition + + let previous = self.contentNode + if let previous = previous { + previous.setContentOffsetUpdated(nil) + transition = .animated(duration: 0.4, curve: .spring) + + self.previousContentNode = previous + previous.alpha = 0.0 + previous.layer.animateAlpha(from: 1.0, to: 0.0, duration: fastOut ? 0.1 : 0.2, removeOnCompletion: true, completion: { [weak self, weak previous] _ in + if let strongSelf = self, let previous = previous { + if strongSelf.previousContentNode === previous { + strongSelf.previousContentNode = nil + } + previous.removeFromSupernode() + } + }) + } else { + transition = .immediate + } + self.contentNode = contentNode + + if let (layout, navigationBarHeight, bottomGridInset) = self.containerLayout { + if let contentNode = contentNode, let previous = previous { + contentNode.frame = previous.frame + contentNode.updateLayout(size: previous.bounds.size, bottomInset: bottomGridInset, transition: .immediate) + + contentNode.setContentOffsetUpdated({ [weak self] contentOffset, transition in + self?.contentNodeOffsetUpdated(contentOffset, transition: transition) + }) + self.contentContainerNode.insertSubnode(contentNode, at: 0) + + contentNode.alpha = 1.0 + let animation = contentNode.layer.makeAnimation(from: 0.0 as NSNumber, to: 1.0 as NSNumber, keyPath: "opacity", timingFunction: kCAMediaTimingFunctionEaseInEaseOut, duration: 0.35) + animation.fillMode = kCAFillModeBoth + if !fastOut { + animation.beginTime = CACurrentMediaTime() + 0.1 + } + contentNode.layer.add(animation, forKey: "opacity") + + self.animateContentNodeOffsetFromBackgroundOffset = self.contentBackgroundNode.frame.minY + self.scheduleInteractiveTransition(transition) + + contentNode.activate() + previous.deactivate() + } else { + if let contentNode = self.contentNode { + contentNode.setContentOffsetUpdated({ [weak self] contentOffset, transition in + self?.contentNodeOffsetUpdated(contentOffset, transition: transition) + }) + self.contentContainerNode.insertSubnode(contentNode, at: 0) + } + + self.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: transition) + } + } else if let contentNode = contentNode { + contentNode.setContentOffsetUpdated({ [weak self] contentOffset, transition in + self?.contentNodeOffsetUpdated(contentOffset, transition: transition) + }) + self.contentContainerNode.insertSubnode(contentNode, at: 0) + } + } + } + + func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) { + var insets = layout.insets(options: [.statusBar, .input]) + let cleanInsets = layout.insets(options: [.statusBar]) + insets.top = max(10.0, insets.top) + + var bottomInset: CGFloat = 10.0 + cleanInsets.bottom + if insets.bottom > 0 { + bottomInset -= 12.0 + } + let buttonHeight: CGFloat = 57.0 + let sectionSpacing: CGFloat = 8.0 + let titleAreaHeight: CGFloat = 64.0 + + let maximumContentHeight = layout.size.height - insets.top - max(bottomInset + buttonHeight, insets.bottom) - sectionSpacing + + let width = min(layout.size.width, layout.size.height) - 20.0 + let sideInset = floor((layout.size.width - width) / 2.0) + + let contentContainerFrame = CGRect(origin: CGPoint(x: sideInset, y: insets.top), size: CGSize(width: width, height: maximumContentHeight)) + let contentFrame = contentContainerFrame.insetBy(dx: 0.0, dy: 0.0) + + let bottomGridInset = buttonHeight + + self.containerLayout = (layout, navigationBarHeight, bottomGridInset) + self.scheduledLayoutTransitionRequest = nil + + transition.updateFrame(node: self.wrappingScrollNode, frame: CGRect(origin: CGPoint(), size: layout.size)) + + transition.updateFrame(node: self.dimNode, frame: CGRect(origin: CGPoint(), size: layout.size)) + + transition.updateFrame(node: self.cancelButtonNode, frame: CGRect(origin: CGPoint(x: sideInset, y: layout.size.height - bottomInset - buttonHeight), size: CGSize(width: width, height: buttonHeight))) + + transition.updateFrame(node: self.contentContainerNode, frame: contentContainerFrame) + + transition.updateFrame(node: self.actionsBackgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: contentContainerFrame.size.height - bottomGridInset), size: CGSize(width: contentContainerFrame.size.width, height: bottomGridInset))) + + let actionButtonFrame = CGRect(origin: CGPoint(x: 0.0, y: contentContainerFrame.size.height - buttonHeight), size: CGSize(width: contentContainerFrame.size.width, height: buttonHeight)) + transition.updateFrame(node: self.actionButtonNode, frame: actionButtonFrame) + let indicatorSize = self.actionIndicator.measure(CGSize(width: 100.0, height: 100.0)) + transition.updateFrame(node: self.actionIndicator, frame: CGRect(origin: CGPoint(x: actionButtonFrame.minX + 12.0, y: actionButtonFrame.minY + floor((actionButtonFrame.height - indicatorSize.height) / 2.0)), size: indicatorSize)) + + transition.updateFrame(node: self.actionSeparatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: contentContainerFrame.size.height - bottomGridInset - UIScreenPixel), size: CGSize(width: contentContainerFrame.size.width, height: UIScreenPixel))) + + let gridSize = CGSize(width: contentFrame.size.width, height: max(32.0, contentFrame.size.height - titleAreaHeight)) + + if let contentNode = self.contentNode { + transition.updateFrame(node: contentNode, frame: CGRect(origin: CGPoint(x: floor((contentContainerFrame.size.width - contentFrame.size.width) / 2.0), y: titleAreaHeight), size: gridSize)) + contentNode.updateLayout(size: gridSize, bottomInset: bottomGridInset, transition: transition) + } + } + + private func contentNodeOffsetUpdated(_ contentOffset: CGFloat, transition: ContainedViewLayoutTransition) { + if let (layout, _, _) = self.containerLayout { + var insets = layout.insets(options: [.statusBar, .input]) + insets.top = max(10.0, insets.top) + let cleanInsets = layout.insets(options: [.statusBar]) + + var bottomInset: CGFloat = 10.0 + cleanInsets.bottom + if insets.bottom > 0 { + bottomInset -= 12.0 + } + let buttonHeight: CGFloat = 57.0 + let sectionSpacing: CGFloat = 8.0 + + let width = min(layout.size.width, layout.size.height) - 20.0 + + let sideInset = floor((layout.size.width - width) / 2.0) + + let maximumContentHeight = layout.size.height - insets.top - max(bottomInset + buttonHeight, insets.bottom) - sectionSpacing + let contentFrame = CGRect(origin: CGPoint(x: sideInset, y: insets.top), size: CGSize(width: width, height: maximumContentHeight)) + + var backgroundFrame = CGRect(origin: CGPoint(x: contentFrame.minX, y: contentFrame.minY - contentOffset), size: contentFrame.size) + if backgroundFrame.minY < contentFrame.minY { + backgroundFrame.origin.y = contentFrame.minY + } + if backgroundFrame.maxY > contentFrame.maxY { + backgroundFrame.size.height += contentFrame.maxY - backgroundFrame.maxY + } + if backgroundFrame.size.height < buttonHeight + 32.0 { + backgroundFrame.origin.y -= buttonHeight + 32.0 - backgroundFrame.size.height + backgroundFrame.size.height = buttonHeight + 32.0 + } + transition.updateFrame(node: self.contentBackgroundNode, frame: backgroundFrame) + + if let animateContentNodeOffsetFromBackgroundOffset = self.animateContentNodeOffsetFromBackgroundOffset { + self.animateContentNodeOffsetFromBackgroundOffset = nil + let offset = backgroundFrame.minY - animateContentNodeOffsetFromBackgroundOffset + if let contentNode = self.contentNode { + transition.animatePositionAdditive(node: contentNode, offset: CGPoint(x: 0.0, y: -offset)) + } + if let previousContentNode = self.previousContentNode { + transition.updatePosition(node: previousContentNode, position: previousContentNode.position.offsetBy(dx: 0.0, dy: offset)) + } + } + } + } + + @objc func dimTapGesture(_ recognizer: UITapGestureRecognizer) { + if case .ended = recognizer.state { + self.cancelButtonPressed() + } + } + + @objc func cancelButtonPressed() { + self.cancel?() + } + + @objc func installActionButtonPressed() { + self.activate?() + } + + func animateIn() { + if self.contentNode != nil { + self.dimNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.4) + + let offset = self.bounds.size.height - self.contentBackgroundNode.frame.minY + + let dimPosition = self.dimNode.layer.position + self.dimNode.layer.animatePosition(from: CGPoint(x: dimPosition.x, y: dimPosition.y - offset), to: dimPosition, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring) + self.layer.animateBoundsOriginYAdditive(from: -offset, to: 0.0, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring) + } + } + + func animateOut(completion: (() -> Void)? = nil) { + if self.contentNode != nil { + var dimCompleted = false + var offsetCompleted = false + + let internalCompletion: () -> Void = { [weak self] in + if let strongSelf = self, dimCompleted && offsetCompleted { + strongSelf.dismiss?() + } + completion?() + } + + self.dimNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { _ in + dimCompleted = true + internalCompletion() + }) + + let offset = self.bounds.size.height - self.contentBackgroundNode.frame.minY + let dimPosition = self.dimNode.layer.position + self.dimNode.layer.animatePosition(from: dimPosition, to: CGPoint(x: dimPosition.x, y: dimPosition.y - offset), duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false) + self.layer.animateBoundsOriginYAdditive(from: 0.0, to: -offset, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, completion: { _ in + offsetCompleted = true + internalCompletion() + }) + } else { + self.dismiss?() + completion?() + } + } + + override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { + if let result = self.actionButtonNode.hitTest(self.actionButtonNode.convert(point, from: self), with: event) { + return result + } + if self.bounds.contains(point) { + if !self.contentBackgroundNode.bounds.contains(self.convert(point, to: self.contentBackgroundNode)) && !self.cancelButtonNode.bounds.contains(self.convert(point, to: self.cancelButtonNode)) { + return self.dimNode.view + } + } + return super.hitTest(point, with: event) + } + + func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) { + let contentOffset = scrollView.contentOffset + let additionalTopHeight = max(0.0, -contentOffset.y) + + if additionalTopHeight >= 30.0 { + self.cancelButtonPressed() + } + } + + private func scheduleInteractiveTransition(_ transition: ContainedViewLayoutTransition) { + if let scheduledLayoutTransitionRequest = self.scheduledLayoutTransitionRequest { + switch scheduledLayoutTransitionRequest.1 { + case .immediate: + self.scheduleLayoutTransitionRequest(transition) + default: + break + } + } else { + self.scheduleLayoutTransitionRequest(transition) + } + } + + private func scheduleLayoutTransitionRequest(_ transition: ContainedViewLayoutTransition) { + let requestId = self.scheduledLayoutTransitionRequestId + self.scheduledLayoutTransitionRequestId += 1 + self.scheduledLayoutTransitionRequest = (requestId, transition) + (self.view as? UITracingLayerView)?.schedule(layout: { [weak self] in + if let strongSelf = self { + if let (currentRequestId, currentRequestTransition) = strongSelf.scheduledLayoutTransitionRequest, currentRequestId == requestId { + strongSelf.scheduledLayoutTransitionRequest = nil + strongSelf.requestLayout(currentRequestTransition) + } + } + }) + self.setNeedsLayout() + } + + func transitionToProgress(signal: Signal) { + let transition = ContainedViewLayoutTransition.animated(duration: 0.12, curve: .easeInOut) + transition.updateAlpha(node: self.actionButtonNode, alpha: 0.0) + transition.updateAlpha(node: self.actionSeparatorNode, alpha: 0.0) + transition.updateAlpha(node: self.actionsBackgroundNode, alpha: 0.0) + + self.transitionToContentNode(ShareLoadingContainerNode(theme: self.presentationData.theme), fastOut: true) + let timestamp = CACurrentMediaTime() + self.disposable.set(signal.start(completed: { [weak self] in + let minDelay = 0.6 + let delay = max(0.0, (timestamp + minDelay) - CACurrentMediaTime()) + Queue.mainQueue().after(delay, { + if let strongSelf = self { + strongSelf.cancel?() + } + }) + })) + } + + func setData(localizationInfo: LocalizationInfo) { + let transition = ContainedViewLayoutTransition.animated(duration: 0.22, curve: .easeInOut) + transition.updateAlpha(node: self.actionButtonNode, alpha: 1.0) + transition.updateAlpha(node: self.actionSeparatorNode, alpha: 1.0) + transition.updateAlpha(node: self.actionsBackgroundNode, alpha: 1.0) + + if localizationInfo.translatedStringCount == 0 { + self.actionButtonNode.isEnabled = false + self.actionButtonNode.setTitle(self.presentationData.strings.Conversation_ApplyLocalization, with: Font.medium(20.0), with: self.presentationData.theme.actionSheet.standardActionTextColor, for: .normal) + self.actionButtonNode.setTitle(self.presentationData.strings.Conversation_ApplyLocalization, with: Font.medium(20.0), with: self.presentationData.theme.actionSheet.disabledActionTextColor, for: .disabled) + + } else { + self.actionButtonNode.isEnabled = true + self.actionButtonNode.setTitle(self.presentationData.strings.Conversation_ApplyLocalization, with: Font.medium(20.0), with: self.presentationData.theme.actionSheet.standardActionTextColor, for: .normal) + self.actionButtonNode.setTitle(self.presentationData.strings.Conversation_ApplyLocalization, with: Font.medium(20.0), with: self.presentationData.theme.actionSheet.disabledActionTextColor, for: .disabled) + } + + self.transitionToContentNode(LanguageLinkPreviewContentNode(account: self.account, localizationInfo: localizationInfo, theme: self.presentationData.theme, strings: self.presentationData.strings, openTranslationUrl: { [weak self] url in + self?.openUrl(url) + })) + } + + func setInProgress(_ value: Bool) { + self.actionIndicator.isHidden = !value + self.actionButtonNode.isEnabled = !value + } +} diff --git a/TelegramUI/ListMessageFileItemNode.swift b/TelegramUI/ListMessageFileItemNode.swift index 976a1ee9c4..f2f5f2c1f1 100644 --- a/TelegramUI/ListMessageFileItemNode.swift +++ b/TelegramUI/ListMessageFileItemNode.swift @@ -775,7 +775,7 @@ final class ListMessageFileItemNode: ListMessageNode { } case .Local: if let item = self.item, let controllerInteraction = self.controllerInteraction { - let _ = controllerInteraction.openMessage(item.message) + let _ = controllerInteraction.openMessage(item.message, .default) } } case .playbackStatus: diff --git a/TelegramUI/ListMessageSnippetItemNode.swift b/TelegramUI/ListMessageSnippetItemNode.swift index 8de7ad408d..1e196e9afd 100644 --- a/TelegramUI/ListMessageSnippetItemNode.swift +++ b/TelegramUI/ListMessageSnippetItemNode.swift @@ -478,14 +478,14 @@ final class ListMessageSnippetItemNode: ListMessageNode { if let item = self.item, let currentPrimaryUrl = self.currentPrimaryUrl { if let webpage = self.currentMedia as? TelegramMediaWebpage, case let .Loaded(content) = webpage.content, content.instantPage != nil { if websiteType(of: content) == .instagram { - if !item.controllerInteraction.openMessage(item.message) { + if !item.controllerInteraction.openMessage(item.message, .default) { item.controllerInteraction.openInstantPage(item.message) } } else { item.controllerInteraction.openInstantPage(item.message) } } else { - if !item.controllerInteraction.openMessage(item.message) { + if !item.controllerInteraction.openMessage(item.message, .default) { item.controllerInteraction.openUrl(currentPrimaryUrl, false, false) } } @@ -535,7 +535,7 @@ final class ListMessageSnippetItemNode: ListMessageNode { if case .longTap = gesture { item.controllerInteraction.longTap(ChatControllerInteractionLongTapAction.url(url)) } else if url == self.currentPrimaryUrl { - if !item.controllerInteraction.openMessage(item.message) { + if !item.controllerInteraction.openMessage(item.message, .default) { item.controllerInteraction.openUrl(url, false, false) } } else { diff --git a/TelegramUI/MediaPlayerAudioRenderer.swift b/TelegramUI/MediaPlayerAudioRenderer.swift index 163c3fbae8..390abb454c 100644 --- a/TelegramUI/MediaPlayerAudioRenderer.swift +++ b/TelegramUI/MediaPlayerAudioRenderer.swift @@ -579,9 +579,12 @@ private final class AudioPlayerRendererContext { } if let requestingFramesContext = self.requestingFramesContext { - requestingFramesContext.queue.async { + requestingFramesContext.queue.async { [weak self] in let takenFrame = requestingFramesContext.takeFrame() audioPlayerRendererQueue.async { + guard let strongSelf = self else { + return + } switch takenFrame { case let .frame(frame): if let dataBuffer = CMSampleBufferGetDataBuffer(frame.sampleBuffer) { @@ -593,10 +596,10 @@ private final class AudioPlayerRendererContext { let bytes = malloc(takeLength)! CMBlockBufferCopyDataBytes(dataBuffer, 0, takeLength, bytes) - self.enqueueSamples(Data(bytesNoCopy: bytes.assumingMemoryBound(to: UInt8.self), count: takeLength, deallocator: .free), sampleIndex: bufferSampleIndex) + strongSelf.enqueueSamples(Data(bytesNoCopy: bytes.assumingMemoryBound(to: UInt8.self), count: takeLength, deallocator: .free), sampleIndex: bufferSampleIndex) if takeLength < dataLength { - self.bufferContext.with { context in + strongSelf.bufferContext.with { context in let copyOffset = context.overflowData.count context.overflowData.count += dataLength - takeLength context.overflowData.withUnsafeMutableBytes { (bytes: UnsafeMutablePointer) -> Void in @@ -605,15 +608,15 @@ private final class AudioPlayerRendererContext { } } - self.checkBuffer() + strongSelf.checkBuffer() } else { assertionFailure() } case .skipFrame: - self.checkBuffer() + strongSelf.checkBuffer() break case .noFrames, .finished: - self.requestingFramesContext = nil + strongSelf.requestingFramesContext = nil } } } diff --git a/TelegramUI/MentionChatInputContextPanelNode.swift b/TelegramUI/MentionChatInputContextPanelNode.swift index db1cca04ed..159ebefda4 100644 --- a/TelegramUI/MentionChatInputContextPanelNode.swift +++ b/TelegramUI/MentionChatInputContextPanelNode.swift @@ -172,7 +172,7 @@ final class MentionChatInputContextPanelNode: ChatInputContextPanelNode { insets.left = validLayout.1 insets.right = validLayout.2 - let updateSizeAndInsets = ListViewUpdateSizeAndInsets(size: validLayout.0, insets: insets, duration: 0.0, curve: .Default) + let updateSizeAndInsets = ListViewUpdateSizeAndInsets(size: validLayout.0, insets: insets, duration: 0.0, curve: .Default(duration: nil)) self.listView.transaction(deleteIndices: transition.deletions, insertIndicesAndItems: transition.insertions, updateIndicesAndItems: transition.updates, options: options, updateSizeAndInsets: updateSizeAndInsets, updateOpaqueState: nil, completion: { [weak self] _ in if let strongSelf = self, firstTime { @@ -237,7 +237,7 @@ final class MentionChatInputContextPanelNode: ChatInputContextPanelNode { if curve == 7 { listViewCurve = .Spring(duration: duration) } else { - listViewCurve = .Default + listViewCurve = .Default(duration: duration) } let updateSizeAndInsets = ListViewUpdateSizeAndInsets(size: size, insets: insets, duration: duration, curve: listViewCurve) diff --git a/TelegramUI/NotificationExceptions.swift b/TelegramUI/NotificationExceptions.swift index ea469d975f..00573fb257 100644 --- a/TelegramUI/NotificationExceptions.swift +++ b/TelegramUI/NotificationExceptions.swift @@ -1002,7 +1002,7 @@ private final class NotificationExceptionsSearchControllerContentNode: SearchDis if curve == 7 { listViewCurve = .Spring(duration: duration) } else { - listViewCurve = .Default + listViewCurve = .Default(duration: duration) } self.listNode.containerLayoutUpdated(layout, navigationBarHeight: 0, transition: transition) diff --git a/TelegramUI/NotificationsAndSounds.swift b/TelegramUI/NotificationsAndSounds.swift index 6afb788ad3..9bf1194acd 100644 --- a/TelegramUI/NotificationsAndSounds.swift +++ b/TelegramUI/NotificationsAndSounds.swift @@ -423,7 +423,7 @@ private func notificationsAndSoundsEntries(globalSettings: GlobalNotificationSet entries.append(.messageAlerts(presentationData.theme, presentationData.strings.Notifications_MessageNotificationsAlert, globalSettings.privateChats.enabled)) entries.append(.messagePreviews(presentationData.theme, presentationData.strings.Notifications_MessageNotificationsPreview, globalSettings.privateChats.displayPreviews)) entries.append(.messageSound(presentationData.theme, presentationData.strings.Notifications_MessageNotificationsSound, localizedPeerNotificationSoundString(strings: presentationData.strings, sound: filteredGlobalSound(globalSettings.privateChats.sound)), filteredGlobalSound(globalSettings.privateChats.sound))) - entries.append(.userExceptions(presentationData.theme, presentationData.strings, presentationData.strings.Notifications_MessageNotificationsExceptions, exceptions.0)) + //entries.append(.userExceptions(presentationData.theme, presentationData.strings, presentationData.strings.Notifications_MessageNotificationsExceptions, exceptions.0)) entries.append(.messageNotice(presentationData.theme, presentationData.strings.Notifications_MessageNotificationsHelp)) entries.append(.groupHeader(presentationData.theme, presentationData.strings.Notifications_GroupNotifications)) diff --git a/TelegramUI/OpenResolvedUrl.swift b/TelegramUI/OpenResolvedUrl.swift index 87e1cc814b..ab2a35cc24 100644 --- a/TelegramUI/OpenResolvedUrl.swift +++ b/TelegramUI/OpenResolvedUrl.swift @@ -20,7 +20,7 @@ private func defaultNavigationForPeerId(_ peerId: PeerId?, navigation: ChatContr } } -func openResolvedUrl(_ resolvedUrl: ResolvedUrl, account: Account, context: OpenURLContext = .generic, navigationController: NavigationController?, openPeer: @escaping (PeerId, ChatControllerInteractionNavigateToPeer) -> Void, present: (ViewController, Any?) -> Void, dismissInput: @escaping () -> Void) { +func openResolvedUrl(_ resolvedUrl: ResolvedUrl, account: Account, context: OpenURLContext = .generic, navigationController: NavigationController?, openPeer: @escaping (PeerId, ChatControllerInteractionNavigateToPeer) -> Void, sendFile: ((FileMediaReference) -> Void)? = nil, present: (ViewController, Any?) -> Void, dismissInput: @escaping () -> Void) { switch resolvedUrl { case let .externalUrl(url): openExternalUrl(account: account, context: context, url: url, presentationData: account.telegramApplicationContext.currentPresentationData.with { $0 }, applicationContext: account.telegramApplicationContext, navigationController: navigationController, dismissInput: dismissInput) @@ -58,7 +58,9 @@ func openResolvedUrl(_ resolvedUrl: ResolvedUrl, account: Account, context: Open openPeer(peerId, .chat(textInputState: nil, messageId: messageId)) case let .stickerPack(name): dismissInput() - present(StickerPackPreviewController(account: account, stickerPack: .name(name), parentNavigationController: navigationController), nil) + let controller = StickerPackPreviewController(account: account, stickerPack: .name(name), parentNavigationController: navigationController) + controller.sendSticker = sendFile + present(controller, nil) case let .instantView(webpage, anchor): navigationController?.pushViewController(InstantPageController(account: account, webPage: webpage, anchor: anchor)) case let .join(link): @@ -66,6 +68,9 @@ func openResolvedUrl(_ resolvedUrl: ResolvedUrl, account: Account, context: Open present(JoinLinkPreviewController(account: account, link: link, navigateToPeer: { peerId in openPeer(peerId, .chat(textInputState: nil, messageId: nil)) }), nil) + case let .localization(identifier): + dismissInput() + present(LanguageLinkPreviewController(account: account, identifier: identifier), nil) case let .proxy(host, port, username, password, secret): let presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 } let server: ProxyServerSettings diff --git a/TelegramUI/OpenUrl.swift b/TelegramUI/OpenUrl.swift index 828838cb1c..e3e1196028 100644 --- a/TelegramUI/OpenUrl.swift +++ b/TelegramUI/OpenUrl.swift @@ -230,6 +230,22 @@ public func openExternalUrl(account: Account, context: OpenURLContext = .generic convertedUrl = "https://t.me/addstickers/\(set)" } } + } else if parsedUrl.host == "setlanguage" { + if let components = URLComponents(string: "/?" + query) { + var lang: String? + if let queryItems = components.queryItems { + for queryItem in queryItems { + if let value = queryItem.value { + if queryItem.name == "lang" { + lang = value + } + } + } + } + if let lang = lang { + convertedUrl = "https://t.me/setlanguage/\(lang)" + } + } } else if parsedUrl.host == "msg_url" { if let components = URLComponents(string: "/?" + query) { var shareUrl: String? diff --git a/TelegramUI/OverlayPlayerControllerNode.swift b/TelegramUI/OverlayPlayerControllerNode.swift index ddf67e39d9..694fbb3b66 100644 --- a/TelegramUI/OverlayPlayerControllerNode.swift +++ b/TelegramUI/OverlayPlayerControllerNode.swift @@ -48,7 +48,7 @@ final class OverlayPlayerControllerNode: ViewControllerTracingNode, UIGestureRec } var openMessageImpl: ((MessageId) -> Bool)? - self.controllerInteraction = ChatControllerInteraction(openMessage: { message in + self.controllerInteraction = ChatControllerInteraction(openMessage: { message, _ in if let openMessageImpl = openMessageImpl { return openMessageImpl(message.id) } else { @@ -232,13 +232,13 @@ final class OverlayPlayerControllerNode: ViewControllerTracingNode, UIGestureRec if curve == 7 { listViewCurve = .Spring(duration: duration) } else { - listViewCurve = .Default + listViewCurve = .Default(duration: duration) } let updateSizeAndInsets = ListViewUpdateSizeAndInsets(size: listNodeSize, insets: insets, duration: duration, curve: listViewCurve) self.historyNode.updateLayout(transition: transition, updateSizeAndInsets: updateSizeAndInsets) if let replacementHistoryNode = replacementHistoryNode { - let updateSizeAndInsets = ListViewUpdateSizeAndInsets(size: listNodeSize, insets: insets, duration: 0.0, curve: .Default) + let updateSizeAndInsets = ListViewUpdateSizeAndInsets(size: listNodeSize, insets: insets, duration: 0.0, curve: .Default(duration: nil)) replacementHistoryNode.updateLayout(transition: transition, updateSizeAndInsets: updateSizeAndInsets) } } @@ -424,7 +424,7 @@ final class OverlayPlayerControllerNode: ViewControllerTracingNode, UIGestureRec historyNode.frame = CGRect(origin: CGPoint(x: 0.0, y: listTopInset), size: listNodeSize) - let updateSizeAndInsets = ListViewUpdateSizeAndInsets(size: listNodeSize, insets: insets, duration: 0.0, curve: .Default) + let updateSizeAndInsets = ListViewUpdateSizeAndInsets(size: listNodeSize, insets: insets, duration: 0.0, curve: .Default(duration: nil)) historyNode.updateLayout(transition: .immediate, updateSizeAndInsets: updateSizeAndInsets) } self.replacementHistoryNodeReadyDisposable.set((historyNode.historyState.get() |> take(1) |> deliverOnMainQueue).start(next: { [weak self] _ in @@ -507,7 +507,7 @@ final class OverlayPlayerControllerNode: ViewControllerTracingNode, UIGestureRec self.historyNode.frame = CGRect(origin: CGPoint(x: 0.0, y: listTopInset), size: listNodeSize) - let updateSizeAndInsets = ListViewUpdateSizeAndInsets(size: listNodeSize, insets: insets, duration: 0.0, curve: .Default) + let updateSizeAndInsets = ListViewUpdateSizeAndInsets(size: listNodeSize, insets: insets, duration: 0.0, curve: .Default(duration: nil)) self.historyNode.updateLayout(transition: .immediate, updateSizeAndInsets: updateSizeAndInsets) self.historyNode.recursivelyEnsureDisplaySynchronously(true) diff --git a/TelegramUI/PeerMediaCollectionController.swift b/TelegramUI/PeerMediaCollectionController.swift index 9c67104545..3d2d7d541b 100644 --- a/TelegramUI/PeerMediaCollectionController.swift +++ b/TelegramUI/PeerMediaCollectionController.swift @@ -78,7 +78,7 @@ public class PeerMediaCollectionController: TelegramController { } }) - let controllerInteraction = ChatControllerInteraction(openMessage: { [weak self] message in + let controllerInteraction = ChatControllerInteraction(openMessage: { [weak self] message, _ in if let strongSelf = self, strongSelf.isNodeLoaded, let galleryMessage = strongSelf.mediaCollectionDisplayNode.messageForGallery(message.id) { guard let navigationController = strongSelf.navigationController as? NavigationController else { return false diff --git a/TelegramUI/PeerMediaCollectionControllerNode.swift b/TelegramUI/PeerMediaCollectionControllerNode.swift index fede97471e..9cc6f4f116 100644 --- a/TelegramUI/PeerMediaCollectionControllerNode.swift +++ b/TelegramUI/PeerMediaCollectionControllerNode.swift @@ -319,7 +319,7 @@ class PeerMediaCollectionControllerNode: ASDisplayNode { if curve == 7 { listViewCurve = .Spring(duration: duration) } else { - listViewCurve = .Default + listViewCurve = .Default(duration: duration) } var additionalBottomInset: CGFloat = 0.0 @@ -424,7 +424,7 @@ class PeerMediaCollectionControllerNode: ASDisplayNode { additionalBottomInset = selectionPanel.bounds.size.height } - node.updateLayout(transition: .immediate, updateSizeAndInsets: ListViewUpdateSizeAndInsets(size: containerLayout.0.size, insets: UIEdgeInsets(top: insets.top, left: insets.right + containerLayout.0.safeInsets.right, bottom: insets.bottom + additionalBottomInset, right: insets.left + containerLayout.0.safeInsets.left), duration: 0.0, curve: .Default)) + node.updateLayout(transition: .immediate, updateSizeAndInsets: ListViewUpdateSizeAndInsets(size: containerLayout.0.size, insets: UIEdgeInsets(top: insets.top, left: insets.right + containerLayout.0.safeInsets.right, bottom: insets.bottom + additionalBottomInset, right: insets.left + containerLayout.0.safeInsets.left), duration: 0.0, curve: .Default(duration: nil))) let historyEmptyNode = PeerMediaCollectionEmptyNode(mode: mediaCollectionInterfaceState.mode, theme: self.mediaCollectionInterfaceState.theme, strings: self.mediaCollectionInterfaceState.strings) historyEmptyNode.isHidden = true diff --git a/TelegramUI/PeerSelectionControllerNode.swift b/TelegramUI/PeerSelectionControllerNode.swift index 2054cb932a..2eb4c5fc3b 100644 --- a/TelegramUI/PeerSelectionControllerNode.swift +++ b/TelegramUI/PeerSelectionControllerNode.swift @@ -152,7 +152,7 @@ final class PeerSelectionControllerNode: ASDisplayNode { if curve == 7 { listViewCurve = .Spring(duration: duration) } else { - listViewCurve = .Default + listViewCurve = .Default(duration: duration) } let updateSizeAndInsets = ListViewUpdateSizeAndInsets(size: layout.size, insets: insets, duration: duration, curve: listViewCurve) diff --git a/TelegramUI/PhotoResources.swift b/TelegramUI/PhotoResources.swift index 57bbc728ec..bcc18993d6 100644 --- a/TelegramUI/PhotoResources.swift +++ b/TelegramUI/PhotoResources.swift @@ -165,9 +165,12 @@ private func chatMessageImageFileThumbnailDatas(account: Account, fileReference: let fullSizeResource: MediaResource = fileReference.media.resource - let maybeFullSize = account.postbox.mediaBox.cachedResourceRepresentation(fullSizeResource, representation: CachedScaledImageRepresentation(size: CGSize(width: 180.0, height: 180.0), mode: .aspectFit), complete: false) + let maybeFullSize = account.postbox.mediaBox.cachedResourceRepresentation(fullSizeResource, representation: CachedScaledImageRepresentation(size: CGSize(width: 180.0, height: 180.0), mode: .aspectFit), complete: false, fetch: false) + let fetchedFullSize = account.postbox.mediaBox.cachedResourceRepresentation(fullSizeResource, representation: CachedScaledImageRepresentation(size: CGSize(width: 180.0, height: 180.0), mode: .aspectFit), complete: false, fetch: true) - let signal = maybeFullSize |> take(1) |> mapToSignal { maybeData -> Signal<(Data?, String?, Bool), NoError> in + let signal = maybeFullSize + |> take(1) + |> mapToSignal { maybeData -> Signal<(Data?, String?, Bool), NoError> in if maybeData.complete { return .single((nil, maybeData.path, true)) } else { @@ -195,17 +198,20 @@ private func chatMessageImageFileThumbnailDatas(account: Account, fileReference: thumbnail = .single(nil) } - let fullSizeDataAndPath = maybeFullSize |> map { next -> (String?, Bool) in + let fullSizeDataAndPath = fetchedFullSize + |> map { next -> (String?, Bool) in return (next.size == 0 ? nil : next.path, next.complete) } - return thumbnail |> mapToSignal { thumbnailData in - return fullSizeDataAndPath |> map { (dataPath, complete) in + return thumbnail + |> mapToSignal { thumbnailData in + return fullSizeDataAndPath + |> map { (dataPath, complete) in return (thumbnailData, dataPath, complete) } } } - } |> filter({ $0.0 != nil || $0.1 != nil }) + } |> filter({ $0.0 != nil || $0.1 != nil }) return signal } @@ -215,7 +221,8 @@ private func chatMessageVideoDatas(postbox: Postbox, fileReference: FileMediaRef let thumbnailResource = smallestRepresentation.resource let fullSizeResource = fileReference.media.resource - let maybeFullSize = postbox.mediaBox.cachedResourceRepresentation(fullSizeResource, representation: thumbnailSize ? CachedScaledVideoFirstFrameRepresentation(size: CGSize(width: 160.0, height: 160.0)) : CachedVideoFirstFrameRepresentation(), complete: false) + let maybeFullSize = postbox.mediaBox.cachedResourceRepresentation(fullSizeResource, representation: thumbnailSize ? CachedScaledVideoFirstFrameRepresentation(size: CGSize(width: 160.0, height: 160.0)) : CachedVideoFirstFrameRepresentation(), complete: false, fetch: false) + let fetchedFullSize = postbox.mediaBox.cachedResourceRepresentation(fullSizeResource, representation: thumbnailSize ? CachedScaledVideoFirstFrameRepresentation(size: CGSize(width: 160.0, height: 160.0)) : CachedVideoFirstFrameRepresentation(), complete: false, fetch: true) let signal = maybeFullSize |> take(1) @@ -239,14 +246,27 @@ private func chatMessageVideoDatas(postbox: Postbox, fileReference: FileMediaRef } } - - let fullSizeDataAndPath = maybeFullSize |> map { next -> ((Data, String)?, Bool) in + let fullSizeDataAndPath = Signal { subscriber in + let dataDisposable = fetchedFullSize.start(next: { next in + subscriber.putNext(next) + }, completed: { + subscriber.putCompletion() + }) + //let fetchedDisposable = fetchedPartialVideoThumbnailData(postbox: postbox, fileReference: fileReference).start() + return ActionDisposable { + dataDisposable.dispose() + //fetchedDisposable.dispose() + } + } + |> map { next -> ((Data, String)?, Bool) in let data = next.size == 0 ? nil : try? Data(contentsOf: URL(fileURLWithPath: next.path), options: .mappedIfSafe) return (data == nil ? nil : (data!, next.path), next.complete) } - return thumbnail |> mapToSignal { thumbnailData in - return fullSizeDataAndPath |> map { (dataAndPath, complete) in + return thumbnail + |> mapToSignal { thumbnailData in + return fullSizeDataAndPath + |> map { (dataAndPath, complete) in return (thumbnailData, dataAndPath, complete) } } @@ -667,7 +687,8 @@ private func chatMessagePhotoThumbnailDatas(account: Account, photoReference: Im let fullRepresentationSize: CGSize = CGSize(width: 1280.0, height: 1280.0) if let smallestRepresentation = smallestImageRepresentation(photoReference.media.representations), let largestRepresentation = photoReference.media.representationForDisplayAtSize(fullRepresentationSize) { - let maybeFullSize = account.postbox.mediaBox.cachedResourceRepresentation(largestRepresentation.resource, representation: CachedScaledImageRepresentation(size: CGSize(width: 180.0, height: 180.0), mode: .aspectFit), complete: onlyFullSize) + let maybeFullSize = account.postbox.mediaBox.cachedResourceRepresentation(largestRepresentation.resource, representation: CachedScaledImageRepresentation(size: CGSize(width: 180.0, height: 180.0), mode: .aspectFit), complete: onlyFullSize, fetch: false) + let fetchedFullSize = account.postbox.mediaBox.cachedResourceRepresentation(largestRepresentation.resource, representation: CachedScaledImageRepresentation(size: CGSize(width: 180.0, height: 180.0), mode: .aspectFit), complete: onlyFullSize, fetch: true) let signal = maybeFullSize |> take(1) @@ -690,19 +711,21 @@ private func chatMessagePhotoThumbnailDatas(account: Account, photoReference: Im } } - let fullSizeData: Signal<(Data?, Bool), NoError> = maybeFullSize - |> map { next -> (Data?, Bool) in - return (next.size == 0 ? nil : try? Data(contentsOf: URL(fileURLWithPath: next.path), options: []), next.complete) - } + let fullSizeData: Signal<(Data?, Bool), NoError> = fetchedFullSize + |> map { next -> (Data?, Bool) in + return (next.size == 0 ? nil : try? Data(contentsOf: URL(fileURLWithPath: next.path), options: []), next.complete) + } - - return thumbnail |> mapToSignal { thumbnailData in - return fullSizeData |> map { (fullSizeData, complete) in + return thumbnail + |> mapToSignal { thumbnailData in + return fullSizeData + |> map { (fullSizeData, complete) in return (thumbnailData, fullSizeData, complete) } } } - } |> filter({ $0.0 != nil || $0.1 != nil }) + } + |> filter({ $0.0 != nil || $0.1 != nil }) return signal } else { diff --git a/TelegramUI/PreparedChatHistoryViewTransition.swift b/TelegramUI/PreparedChatHistoryViewTransition.swift index 7572131bf4..e834c5be40 100644 --- a/TelegramUI/PreparedChatHistoryViewTransition.swift +++ b/TelegramUI/PreparedChatHistoryViewTransition.swift @@ -117,7 +117,7 @@ func preparedChatHistoryViewTransition(from fromView: ChatHistoryView?, to toVie var index = toView.filteredEntries.count - 1 for entry in toView.filteredEntries { if case .UnreadEntry = entry { - scrollToItem = ListViewScrollToItem(index: index, position: .bottom(0.0), animated: false, curve: .Default, directionHint: .Down) + scrollToItem = ListViewScrollToItem(index: index, position: .bottom(0.0), animated: false, curve: .Default(duration: nil), directionHint: .Down) break } index -= 1 @@ -127,7 +127,7 @@ func preparedChatHistoryViewTransition(from fromView: ChatHistoryView?, to toVie var index = toView.filteredEntries.count - 1 for entry in toView.filteredEntries { if entry.index >= unreadIndex { - scrollToItem = ListViewScrollToItem(index: index, position: .bottom(0.0), animated: false, curve: .Default, directionHint: .Down) + scrollToItem = ListViewScrollToItem(index: index, position: .bottom(0.0), animated: false, curve: .Default(duration: nil), directionHint: .Down) break } index -= 1 @@ -138,7 +138,7 @@ func preparedChatHistoryViewTransition(from fromView: ChatHistoryView?, to toVie var index = 0 for entry in toView.filteredEntries.reversed() { if entry.index < unreadIndex { - scrollToItem = ListViewScrollToItem(index: index, position: .bottom(0.0), animated: false, curve: .Default, directionHint: .Down) + scrollToItem = ListViewScrollToItem(index: index, position: .bottom(0.0), animated: false, curve: .Default(duration: nil), directionHint: .Down) break } index += 1 @@ -148,7 +148,7 @@ func preparedChatHistoryViewTransition(from fromView: ChatHistoryView?, to toVie var index = toView.filteredEntries.count - 1 for entry in toView.filteredEntries { if entry.index >= scrollIndex { - scrollToItem = ListViewScrollToItem(index: index, position: .top(relativeOffset), animated: false, curve: .Default, directionHint: .Down) + scrollToItem = ListViewScrollToItem(index: index, position: .top(relativeOffset), animated: false, curve: .Default(duration: nil), directionHint: .Down) break } index -= 1 @@ -158,7 +158,7 @@ func preparedChatHistoryViewTransition(from fromView: ChatHistoryView?, to toVie var index = 0 for entry in toView.filteredEntries.reversed() { if entry.index < scrollIndex { - scrollToItem = ListViewScrollToItem(index: index, position: .top(0.0), animated: false, curve: .Default, directionHint: .Down) + scrollToItem = ListViewScrollToItem(index: index, position: .top(0.0), animated: false, curve: .Default(duration: nil), directionHint: .Down) break } index += 1 @@ -171,7 +171,7 @@ func preparedChatHistoryViewTransition(from fromView: ChatHistoryView?, to toVie var index = toView.filteredEntries.count - 1 for entry in toView.filteredEntries { if scrollIndex.isLessOrEqual(to: entry.index) { - scrollToItem = ListViewScrollToItem(index: index, position: position, animated: animated, curve: .Default, directionHint: directionHint) + scrollToItem = ListViewScrollToItem(index: index, position: position, animated: animated, curve: .Default(duration: nil), directionHint: directionHint) break } index -= 1 @@ -181,7 +181,7 @@ func preparedChatHistoryViewTransition(from fromView: ChatHistoryView?, to toVie var index = 0 for entry in toView.filteredEntries.reversed() { if !scrollIndex.isLess(than: entry.index) { - scrollToItem = ListViewScrollToItem(index: index, position: position, animated: animated, curve: .Default, directionHint: directionHint) + scrollToItem = ListViewScrollToItem(index: index, position: position, animated: animated, curve: .Default(duration: nil), directionHint: directionHint) break } index += 1 diff --git a/TelegramUI/PresentationResourceKey.swift b/TelegramUI/PresentationResourceKey.swift index 2b6616617e..4c002ec1ae 100644 --- a/TelegramUI/PresentationResourceKey.swift +++ b/TelegramUI/PresentationResourceKey.swift @@ -110,6 +110,7 @@ enum PresentationResourceKey: Int32 { case chatBubbleActionButtonOutgoingBottomRightImage case chatBubbleActionButtonOutgoingBottomSingleImage + case chatBubbleFileCloudFetchMediaIcon case chatBubbleFileCloudFetchIncomingIcon case chatBubbleFileCloudFetchOutgoingIcon case chatBubbleFileCloudFetchedIncomingIcon diff --git a/TelegramUI/PresentationResourcesChat.swift b/TelegramUI/PresentationResourcesChat.swift index 9c25036f3d..afa4a335f8 100644 --- a/TelegramUI/PresentationResourcesChat.swift +++ b/TelegramUI/PresentationResourcesChat.swift @@ -1012,6 +1012,18 @@ struct PresentationResourcesChat { }) } + static func chatBubbleFileCloudFetchMediaIcon(_ theme: PresentationTheme) -> UIImage? { + return theme.image(PresentationResourceKey.chatBubbleFileCloudFetchMediaIcon.rawValue, { theme in + guard let image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Message/FileCloudFetch"), color: theme.chat.bubble.mediaOverlayControlForegroundColor) else { + return nil + } + return generateImage(image.size, contextGenerator: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + context.draw(image.cgImage!, in: CGRect(origin: CGPoint(x: 0.0, y: -1.0), size: image.size)) + }) + }) + } + static func chatBubbleFileCloudFetchIncomingIcon(_ theme: PresentationTheme) -> UIImage? { return theme.image(PresentationResourceKey.chatBubbleFileCloudFetchIncomingIcon.rawValue, { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Message/FileCloudFetch"), color: theme.chat.bubble.incomingAccentControlColor) diff --git a/TelegramUI/RadialProgressContentNode.swift b/TelegramUI/RadialProgressContentNode.swift index bce06cbd73..a65349cbc1 100644 --- a/TelegramUI/RadialProgressContentNode.swift +++ b/TelegramUI/RadialProgressContentNode.swift @@ -271,7 +271,7 @@ final class RadialProgressContentNode: RadialStatusContentNode { } override func enqueueReadyForTransition(_ f: @escaping () -> Void) { - if self.spinnerNode.isAnimatingProgress { + if self.spinnerNode.isAnimatingProgress && self.progress == 1.0 { self.enqueuedReadyForTransition = f } else { f() diff --git a/TelegramUI/SecureIdLocalResource.swift b/TelegramUI/SecureIdLocalResource.swift index e5fb2d4c37..603ae015bf 100644 --- a/TelegramUI/SecureIdLocalResource.swift +++ b/TelegramUI/SecureIdLocalResource.swift @@ -68,7 +68,7 @@ func fetchSecureIdLocalImageResource(postbox: Postbox, resource: SecureIdLocalIm subscriber.putNext(.reset) - let fetch = fetchResource(resource.source, .single(IndexSet(integersIn: 0 ..< Int.max)), nil) + let fetch = fetchResource(resource.source, .single([(0 ..< Int.max, .default)]), nil) let buffer = Atomic(value: Buffer()) let disposable = fetch.start(next: { result in switch result { diff --git a/TelegramUI/StickerPreviewPeekContent.swift b/TelegramUI/StickerPreviewPeekContent.swift index d047cccc7f..73029c1f7d 100644 --- a/TelegramUI/StickerPreviewPeekContent.swift +++ b/TelegramUI/StickerPreviewPeekContent.swift @@ -75,7 +75,7 @@ private final class StickerPreviewPeekContentNode: ASDisplayNode, PeekController self.textNode.attributedText = NSAttributedString(string: text, font: Font.regular(32.0), textColor: .black) break } - self.imageNode.setSignal(chatMessageSticker(account: account, file: item.file, small: false)) + self.imageNode.setSignal(chatMessageSticker(account: account, file: item.file, small: false, fetched: true)) super.init() diff --git a/TelegramUI/StickerResources.swift b/TelegramUI/StickerResources.swift index 4e58f3117f..4ae81799c6 100644 --- a/TelegramUI/StickerResources.swift +++ b/TelegramUI/StickerResources.swift @@ -48,7 +48,7 @@ private func chatMessageStickerDatas(account: Account, file: TelegramMediaFile, let thumbnailResource = chatMessageStickerResource(file: file, small: true) let resource = chatMessageStickerResource(file: file, small: small) - let maybeFetched = account.postbox.mediaBox.cachedResourceRepresentation(resource, representation: CachedStickerAJpegRepresentation(size: small ? CGSize(width: 160.0, height: 160.0) : nil), complete: false) + let maybeFetched = account.postbox.mediaBox.cachedResourceRepresentation(resource, representation: CachedStickerAJpegRepresentation(size: small ? CGSize(width: 160.0, height: 160.0) : nil), complete: false, fetch: false) return maybeFetched |> take(1) @@ -64,45 +64,31 @@ private func chatMessageStickerDatas(account: Account, file: TelegramMediaFile, return (next.size == 0 ? nil : try? Data(contentsOf: URL(fileURLWithPath: next.path), options: .mappedIfSafe), next.complete) } - if fetched { - return Signal { subscriber in - let fetch = fetchedMediaResource(postbox: account.postbox, reference: stickerPackFileReference(file).resourceReference(resource)).start() - let disposable = (fullSizeData |> map { (data, complete) -> (Data?, Data?, Bool) in - return (nil, data, complete) - }).start(next: { next in - subscriber.putNext(next) - }, error: { error in - subscriber.putError(error) - }, completed: { - subscriber.putCompletion() - }) - - return ActionDisposable { - fetch.dispose() - disposable.dispose() - } + return Signal { subscriber in + var fetch: Disposable? + if fetched { + fetch = fetchedMediaResource(postbox: account.postbox, reference: stickerPackFileReference(file).resourceReference(resource)).start() } - } else { - return Signal { subscriber in - var fetchThumbnail: Disposable? - if !thumbnailResource.id.isEqual(to: resource.id) { - fetchThumbnail = fetchedMediaResource(postbox: account.postbox, reference: stickerPackFileReference(file).resourceReference(thumbnailResource)).start() - } - let disposable = (combineLatest(thumbnailData, fullSizeData) - |> map { thumbnailData, fullSizeData -> (Data?, Data?, Bool) in - return (thumbnailData.complete ? try? Data(contentsOf: URL(fileURLWithPath: thumbnailData.path)) : nil, fullSizeData.0, fullSizeData.1) - }).start(next: { next in - subscriber.putNext(next) - }, error: { error in - subscriber.putError(error) - }, completed: { - subscriber.putCompletion() - }) - - return ActionDisposable { - fetchThumbnail?.dispose() - disposable.dispose() - } + + var fetchThumbnail: Disposable? + if !thumbnailResource.id.isEqual(to: resource.id) { + fetchThumbnail = fetchedMediaResource(postbox: account.postbox, reference: stickerPackFileReference(file).resourceReference(thumbnailResource)).start() + } + let disposable = (combineLatest(thumbnailData, fullSizeData) + |> map { thumbnailData, fullSizeData -> (Data?, Data?, Bool) in + return (thumbnailData.complete ? try? Data(contentsOf: URL(fileURLWithPath: thumbnailData.path)) : nil, fullSizeData.0, fullSizeData.1) + }).start(next: { next in + subscriber.putNext(next) + }, error: { error in + subscriber.putError(error) + }, completed: { + subscriber.putCompletion() + }) + + return ActionDisposable { + fetch?.dispose() + fetchThumbnail?.dispose() + disposable.dispose() } } } diff --git a/TelegramUI/ThemeSettingsChatPreviewItem.swift b/TelegramUI/ThemeSettingsChatPreviewItem.swift index 823dd85c3d..4cb82d3cc6 100644 --- a/TelegramUI/ThemeSettingsChatPreviewItem.swift +++ b/TelegramUI/ThemeSettingsChatPreviewItem.swift @@ -90,7 +90,7 @@ class ThemeSettingsChatPreviewItemNode: ListViewItemNode { self.containerNode = ASDisplayNode() self.containerNode.subnodeTransform = CATransform3DMakeRotation(CGFloat.pi, 0.0, 0.0, 1.0) - self.controllerInteraction = ChatControllerInteraction(openMessage: { _ in + self.controllerInteraction = ChatControllerInteraction(openMessage: { _, _ in return false }, openPeer: { _, _, _ in }, openPeerMention: { _ in }, openMessageContextMenu: { _, _, _ in }, navigateToMessage: { _, _ in }, clickThroughMessage: { }, toggleMessagesSelection: { _, _ in }, sendMessage: { _ in }, sendSticker: { _, _ in }, sendGif: { _ in }, requestMessageActionCallback: { _, _, _ in }, activateSwitchInline: { _, _ in }, openUrl: { _, _, _ in }, shareCurrentLocation: {}, shareAccountContact: {}, sendBotCommand: { _, _ in }, openInstantPage: { _ in }, openHashtag: { _, _ in }, updateInputState: { _ in }, updateInputMode: { _ in }, openMessageShareMenu: { _ in }, presentController: { _, _ in }, navigationController: { return nil diff --git a/TelegramUI/TwoStepVerificationPasswordEntryController.swift b/TelegramUI/TwoStepVerificationPasswordEntryController.swift index 20358d0865..d39875fa7d 100644 --- a/TelegramUI/TwoStepVerificationPasswordEntryController.swift +++ b/TelegramUI/TwoStepVerificationPasswordEntryController.swift @@ -328,8 +328,8 @@ func twoStepVerificationPasswordEntryController(account: Account, mode: TwoStepV $0.withUpdatedUpdating(false) } switch update { - case let .password(password, pendingEmailPattern): - result.set(.single(TwoStepVerificationPasswordEntryResult(password: password, pendingEmailPattern: pendingEmailPattern))) + case let .password(password, pendingEmail): + result.set(.single(TwoStepVerificationPasswordEntryResult(password: password, pendingEmailPattern: pendingEmail?.pattern))) case .none: break } @@ -353,8 +353,8 @@ func twoStepVerificationPasswordEntryController(account: Account, mode: TwoStepV $0.withUpdatedUpdating(false) } switch update { - case let .password(password, pendingEmailPattern): - result.set(.single(TwoStepVerificationPasswordEntryResult(password: password, pendingEmailPattern: pendingEmailPattern))) + case let .password(password, pendingEmail): + result.set(.single(TwoStepVerificationPasswordEntryResult(password: password, pendingEmailPattern: pendingEmail?.pattern))) case .none: break } diff --git a/TelegramUI/UrlHandling.swift b/TelegramUI/UrlHandling.swift index 907f65e405..7aee778307 100644 --- a/TelegramUI/UrlHandling.swift +++ b/TelegramUI/UrlHandling.swift @@ -14,6 +14,7 @@ private enum ParsedInternalUrl { case peerName(String, ParsedInternalPeerUrlParameter?) case stickerPack(String) case join(String) + case localization(String) case proxy(host: String, port: Int32, username: String?, password: String?, secret: Data?) } @@ -32,6 +33,7 @@ enum ResolvedUrl { case instantView(TelegramMediaWebpage, String?) case proxy(host: String, port: Int32, username: String?, password: String?, secret: Data?) case join(String) + case localization(String) } private func parseInternalUrl(query: String) -> ParsedInternalUrl? { @@ -94,6 +96,8 @@ private func parseInternalUrl(query: String) -> ParsedInternalUrl? { return .stickerPack(pathComponents[1]) } else if pathComponents[0] == "joinchat" || pathComponents[0] == "joinchannel" { return .join(pathComponents[1]) + } else if pathComponents[0] == "setlanguage" { + return .localization(pathComponents[1]) } else if let value = Int(pathComponents[1]) { return .peerName(peerName, .channelMessage(Int32(value))) } else { @@ -111,29 +115,31 @@ private func resolveInternalUrl(account: Account, url: ParsedInternalUrl) -> Sig switch url { case let .peerName(name, parameter): return resolvePeerByName(account: account, name: name) - |> take(1) - |> map { peerId -> ResolvedUrl? in - if let peerId = peerId { - if let parameter = parameter { - switch parameter { - case let .botStart(payload): - return .botStart(peerId: peerId, payload: payload) - case let .groupBotStart(payload): - return .groupBotStart(peerId: peerId, payload: payload) - case let .channelMessage(id): - return .channelMessage(peerId: peerId, messageId: MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: id)) - } - } else { - return .peer(peerId, .chat(textInputState: nil, messageId: nil)) + |> take(1) + |> map { peerId -> ResolvedUrl? in + if let peerId = peerId { + if let parameter = parameter { + switch parameter { + case let .botStart(payload): + return .botStart(peerId: peerId, payload: payload) + case let .groupBotStart(payload): + return .groupBotStart(peerId: peerId, payload: payload) + case let .channelMessage(id): + return .channelMessage(peerId: peerId, messageId: MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: id)) } } else { - return nil + return .peer(peerId, .chat(textInputState: nil, messageId: nil)) } + } else { + return nil } + } case let .stickerPack(name): return .single(.stickerPack(name: name)) case let .join(link): return .single(.join(link)) + case let .localization(identifier): + return .single(.localization(identifier)) case let .proxy(host, port, username, password, secret): return .single(.proxy(host: host, port: port, username: username, password: password, secret: secret)) } diff --git a/TelegramUI/VerticalListContextResultsChatInputContextPanelNode.swift b/TelegramUI/VerticalListContextResultsChatInputContextPanelNode.swift index d1e392d93e..c5593558ac 100644 --- a/TelegramUI/VerticalListContextResultsChatInputContextPanelNode.swift +++ b/TelegramUI/VerticalListContextResultsChatInputContextPanelNode.swift @@ -210,7 +210,7 @@ final class VerticalListContextResultsChatInputContextPanelNode: ChatInputContex insets.left = validLayout.1 insets.right = validLayout.2 - let updateSizeAndInsets = ListViewUpdateSizeAndInsets(size: self.listView.bounds.size, insets: insets, duration: 0.0, curve: .Default) + let updateSizeAndInsets = ListViewUpdateSizeAndInsets(size: self.listView.bounds.size, insets: insets, duration: 0.0, curve: .Default(duration: nil)) self.listView.transaction(deleteIndices: transition.deletions, insertIndicesAndItems: transition.insertions, updateIndicesAndItems: transition.updates, options: options, updateSizeAndInsets: updateSizeAndInsets, updateOpaqueState: nil, completion: { [weak self] _ in if let strongSelf = self, firstTime { @@ -271,7 +271,7 @@ final class VerticalListContextResultsChatInputContextPanelNode: ChatInputContex if curve == 7 { listViewCurve = .Spring(duration: duration) } else { - listViewCurve = .Default + listViewCurve = .Default(duration: duration) } let updateSizeAndInsets = ListViewUpdateSizeAndInsets(size: size, insets: insets, duration: duration, curve: listViewCurve) From f110dd2b6638f34d68d9afaf96f4fef51ecf6d34 Mon Sep 17 00:00:00 2001 From: Peter <> Date: Thu, 1 Nov 2018 19:10:11 +0400 Subject: [PATCH 3/3] Support for updated global 3d touch API Fixed media sharing to secret chats --- TelegramUI/ChannelMembersController.swift | 3 +- TelegramUI/ChatAvatarNavigationNode.swift | 37 +- TelegramUI/ChatController.swift | 29 +- TelegramUI/ChatControllerNode.swift | 25 +- TelegramUI/ChatHistoryListNode.swift | 12 + TelegramUI/ChatHistoryViewForLocation.swift | 2 +- .../ChatInterfaceStateInputPanels.swift | 3 + TelegramUI/ChatMessageActionItemNode.swift | 2 + TelegramUI/ChatMessageBubbleItemNode.swift | 2 +- .../ChatMessageInteractiveMediaNode.swift | 14 +- TelegramUI/ChatMessageItem.swift | 2 +- .../ChatMessageMediaBubbleContentNode.swift | 1 + .../ChatPresentationInterfaceState.swift | 68 +- .../ChatTextInputMediaRecordingButton.swift | 6 +- TelegramUI/HorizontalPeerItem.swift | 4 +- TelegramUI/InAppNotificationSettings.swift | 30 +- TelegramUI/InstantPageMediaPlaylist.swift | 2 +- TelegramUI/IsMediaStreamable.swift | 9 + .../JoinLinkPreviewPeerContentNode.swift | 2 +- TelegramUI/LanguageSelectionController.swift | 2 +- TelegramUI/NotificationExceptions.swift | 8 +- TelegramUI/NotificationsAndSounds.swift | 52 +- TelegramUI/PeerMessagesMediaPlaylist.swift | 84 +- TelegramUI/PhotoResources.swift | 14 +- TelegramUI/PresentationStrings.swift | 3992 +++++++++-------- .../RadialCloudProgressContentNode.swift | 2 +- TelegramUI/RenderedTotalUnreadCount.swift | 65 +- TelegramUI/SelectablePeerNode.swift | 27 +- TelegramUI/ShareController.swift | 13 +- TelegramUI/ShareControllerNode.swift | 2 +- TelegramUI/ShareControllerPeerGridItem.swift | 32 +- TelegramUI/SharePeersContainerNode.swift | 51 +- TelegramUI/ShareSearchContainerNode.swift | 14 +- TelegramUI/SharedMediaPlayer.swift | 35 +- 34 files changed, 2435 insertions(+), 2211 deletions(-) diff --git a/TelegramUI/ChannelMembersController.swift b/TelegramUI/ChannelMembersController.swift index a27c764f3b..9e364a8b58 100644 --- a/TelegramUI/ChannelMembersController.swift +++ b/TelegramUI/ChannelMembersController.swift @@ -178,7 +178,8 @@ private enum ChannelMembersEntry: ItemListNodeEntry { case let .addMemberInfo(theme, text): return ItemListTextItem(theme: theme, text: .plain(text), sectionId: self.section) case let .peerItem(_, theme, strings, dateTimeFormat, participant, editing, enabled): - return ItemListPeerItem(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, account: arguments.account, peer: participant.peer, presence: nil, text: .none, label: .none, editing: editing, switchValue: nil, enabled: enabled, sectionId: self.section, action: { + + return ItemListPeerItem(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, account: arguments.account, peer: participant.peer, presence: participant.presences[participant.peer.id], text: .presence, label: .none, editing: editing, switchValue: nil, enabled: enabled, sectionId: self.section, action: { arguments.openPeer(participant.peer) }, setPeerIdWithRevealedOptions: { previousId, id in arguments.setPeerIdWithRevealedOptions(previousId, id) diff --git a/TelegramUI/ChatAvatarNavigationNode.swift b/TelegramUI/ChatAvatarNavigationNode.swift index 7fd2516bbf..a718be145b 100644 --- a/TelegramUI/ChatAvatarNavigationNode.swift +++ b/TelegramUI/ChatAvatarNavigationNode.swift @@ -5,17 +5,50 @@ import Display private let normalFont = UIFont(name: ".SFCompactRounded-Semibold", size: 16.0)! private let smallFont = UIFont(name: ".SFCompactRounded-Semibold", size: 12.0)! +final class ChatAvatarNavigationNodeView: UIView, PreviewingHostView { + @available(iOSApplicationExtension 9.0, *) + var previewingDelegate: UIViewControllerPreviewingDelegate? { + return self.chatController + } + + weak var chatController: ChatController? + weak var targetNode: ChatAvatarNavigationNode? + + override func layoutSubviews() { + super.layoutSubviews() + + self.targetNode?.onLayout() + } +} + final class ChatAvatarNavigationNode: ASDisplayNode { let avatarNode: AvatarNode + weak var chatController: ChatController? { + didSet { + if self.isNodeLoaded { + (self.view as? ChatAvatarNavigationNodeView)?.chatController = self.chatController + } + } + } override init() { self.avatarNode = AvatarNode(font: normalFont) super.init() + self.setViewBlock({ + return ChatAvatarNavigationNodeView() + }) + self.addSubnode(self.avatarNode) } + override func didLoad() { + super.didLoad() + (self.view as? ChatAvatarNavigationNodeView)?.targetNode = self + (self.view as? ChatAvatarNavigationNodeView)?.chatController = self.chatController + } + override func calculateSizeThatFits(_ constrainedSize: CGSize) -> CGSize { if constrainedSize.height.isLessThanOrEqualTo(32.0) { return CGSize(width: 26.0, height: 26.0) @@ -24,9 +57,7 @@ final class ChatAvatarNavigationNode: ASDisplayNode { } } - override func layout() { - super.layout() - + func onLayout() { let bounds = self.bounds if self.bounds.size.height.isLessThanOrEqualTo(26.0) { if !self.avatarNode.bounds.size.equalTo(bounds.size) { diff --git a/TelegramUI/ChatController.swift b/TelegramUI/ChatController.swift index d38de12151..700bc17488 100644 --- a/TelegramUI/ChatController.swift +++ b/TelegramUI/ChatController.swift @@ -1045,7 +1045,9 @@ public final class ChatController: TelegramController, KeyShortcutResponder, UID let chatInfoButtonItem: UIBarButtonItem switch chatLocation { case .peer: - chatInfoButtonItem = UIBarButtonItem(customDisplayNode: ChatAvatarNavigationNode())! + let avatarNode = ChatAvatarNavigationNode() + avatarNode.chatController = self + chatInfoButtonItem = UIBarButtonItem(customDisplayNode: avatarNode)! case .group: chatInfoButtonItem = UIBarButtonItem(customDisplayNode: ChatMultipleAvatarsNavigationNode())! } @@ -1130,13 +1132,18 @@ public final class ChatController: TelegramController, KeyShortcutResponder, UID renderedPeer = RenderedPeer(peerId: peer.id, peers: peers) } + var isNotAccessible: Bool = false + if let cachedChannelData = peerView.cachedData as? CachedChannelData { + isNotAccessible = cachedChannelData.isNotAccessible + } + var animated = false if let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer as? TelegramSecretChat, let updated = renderedPeer?.peer as? TelegramSecretChat, peer.embeddedState != updated.embeddedState { animated = true } strongSelf.updateChatPresentationInterfaceState(animated: animated, interactive: false, { return $0.updatedPeer { _ in return renderedPeer - }.updatedIsContact(isContact).updatedPeerIsMuted(peerIsMuted) + }.updatedisNotAccessible(isNotAccessible).updatedIsContact(isContact).updatedPeerIsMuted(peerIsMuted) }) if !strongSelf.didSetChatLocationInfoReady { strongSelf.didSetChatLocationInfoReady = true @@ -1461,7 +1468,7 @@ public final class ChatController: TelegramController, KeyShortcutResponder, UID } override public func loadDisplayNode() { - self.displayNode = ChatControllerNode(account: self.account, chatLocation: self.chatLocation, messageId: self.messageId, controllerInteraction: self.controllerInteraction!, chatPresentationInterfaceState: self.presentationInterfaceState, automaticMediaDownloadSettings: self.automaticMediaDownloadSettings, navigationBar: self.navigationBar) + self.displayNode = ChatControllerNode(account: self.account, chatLocation: self.chatLocation, messageId: self.messageId, controllerInteraction: self.controllerInteraction!, chatPresentationInterfaceState: self.presentationInterfaceState, automaticMediaDownloadSettings: self.automaticMediaDownloadSettings, navigationBar: self.navigationBar, controller: self) self.chatDisplayNode.peerView = self.peerView @@ -2622,7 +2629,7 @@ public final class ChatController: TelegramController, KeyShortcutResponder, UID switch self.chatLocation { case let .peer(peerId): - let unreadCountsKey: PostboxViewKey = .unreadCounts(items: [.peer(peerId), .total(.filtered, .messages)]) + let unreadCountsKey: PostboxViewKey = .unreadCounts(items: [.peer(peerId), .total(ApplicationSpecificPreferencesKeys.inAppNotificationSettings)]) let notificationSettingsKey: PostboxViewKey = .peerNotificationSettings(peerIds: Set([peerId])) self.chatUnreadCountDisposable = (self.account.postbox.combinedView(keys: [unreadCountsKey, notificationSettingsKey]) |> deliverOnMainQueue).start(next: { [weak self] views in @@ -2634,7 +2641,9 @@ public final class ChatController: TelegramController, KeyShortcutResponder, UID if let count = view.count(for: .peer(peerId)) { unreadCount = count } - if let count = view.count(for: .total(.filtered, .chats)) { + if let (preferencesEntry, state) = view.total() { + let inAppSettings = (preferencesEntry as? InAppNotificationSettings) ?? InAppNotificationSettings.defaultSettings + let (count, _) = renderedTotalUnreadCount(inAppSettings: inAppSettings, totalUnreadState: state) totalChatCount = count } } @@ -2711,7 +2720,7 @@ public final class ChatController: TelegramController, KeyShortcutResponder, UID } })) case let .group(groupId): - let unreadCountsKey: PostboxViewKey = .unreadCounts(items: [.group(groupId), .total(.filtered, .messages)]) + let unreadCountsKey: PostboxViewKey = .unreadCounts(items: [.group(groupId), .total(ApplicationSpecificPreferencesKeys.inAppNotificationSettings)]) self.chatUnreadCountDisposable = (self.account.postbox.combinedView(keys: [unreadCountsKey]) |> deliverOnMainQueue).start(next: { [weak self] views in if let strongSelf = self { var unreadCount: Int32 = 0 @@ -2721,7 +2730,9 @@ public final class ChatController: TelegramController, KeyShortcutResponder, UID if let count = view.count(for: .group(groupId)) { unreadCount = count } - if let count = view.count(for: .total(.filtered, .messages)) { + if let (preferencesEntry, state) = view.total() { + let inAppSettings = (preferencesEntry as? InAppNotificationSettings) ?? InAppNotificationSettings.defaultSettings + let (count, _) = renderedTotalUnreadCount(inAppSettings: inAppSettings, totalUnreadState: state) totalCount = count } } @@ -2835,7 +2846,7 @@ public final class ChatController: TelegramController, KeyShortcutResponder, UID return } if let currentItem = currentItem?.id as? PeerMessagesMediaPlaylistItemId, let previousItem = previousItem?.id as? PeerMessagesMediaPlaylistItemId, previousItem.messageId.peerId == peerId, currentItem.messageId.peerId == peerId, currentItem.messageId != previousItem.messageId { - if strongSelf.chatDisplayNode.historyNode.messageInCurrentHistoryView(currentItem.messageId) != nil { + if strongSelf.chatDisplayNode.historyNode.isMessageVisibleOnScreen(currentItem.messageId) { strongSelf.navigateToMessage(from: nil, to: .id(currentItem.messageId), scrollPosition: .center(.bottom), rememberInStack: false, animated: true, completion: nil) } } @@ -4665,6 +4676,8 @@ public final class ChatController: TelegramController, KeyShortcutResponder, UID } } + + @available(iOSApplicationExtension 9.0, *) public func previewingContext(_ previewingContext: UIViewControllerPreviewing, viewControllerForLocation location: CGPoint) -> UIViewController? { if previewingContext.sourceView === (self.chatInfoNavigationButton?.buttonItem.customDisplayNode as? ChatAvatarNavigationNode)?.avatarNode.view { diff --git a/TelegramUI/ChatControllerNode.swift b/TelegramUI/ChatControllerNode.swift index 840b29ccfb..c5ee81bf47 100644 --- a/TelegramUI/ChatControllerNode.swift +++ b/TelegramUI/ChatControllerNode.swift @@ -5,9 +5,15 @@ import SwiftSignalKit import Display import TelegramCore -private final class ChatControllerNodeView: UITracingLayerView, WindowInputAccessoryHeightProvider { +private final class ChatControllerNodeView: UITracingLayerView, WindowInputAccessoryHeightProvider, PreviewingHostView { var inputAccessoryHeight: (() -> CGFloat)? var hitTestImpl: ((CGPoint, UIEvent?) -> UIView?)? + @available(iOSApplicationExtension 9.0, *) + var previewingDelegate: UIViewControllerPreviewingDelegate? { + return self.controller + } + + weak var controller: ChatController? func getWindowInputAccessoryHeight() -> CGFloat { return self.inputAccessoryHeight?() ?? 0.0 @@ -42,6 +48,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { let account: Account let chatLocation: ChatLocation let controllerInteraction: ChatControllerInteraction + private weak var controller: ChatController? let navigationBar: NavigationBar? @@ -158,13 +165,14 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { } } - init(account: Account, chatLocation: ChatLocation, messageId: MessageId?, controllerInteraction: ChatControllerInteraction, chatPresentationInterfaceState: ChatPresentationInterfaceState, automaticMediaDownloadSettings: AutomaticMediaDownloadSettings, navigationBar: NavigationBar?) { + init(account: Account, chatLocation: ChatLocation, messageId: MessageId?, controllerInteraction: ChatControllerInteraction, chatPresentationInterfaceState: ChatPresentationInterfaceState, automaticMediaDownloadSettings: AutomaticMediaDownloadSettings, navigationBar: NavigationBar?, controller: ChatController?) { self.account = account self.chatLocation = chatLocation self.controllerInteraction = controllerInteraction self.chatPresentationInterfaceState = chatPresentationInterfaceState self.automaticMediaDownloadSettings = automaticMediaDownloadSettings self.navigationBar = navigationBar + self.controller = controller self.backgroundNode = ASDisplayNode() self.backgroundNode.isLayerBacked = true @@ -359,6 +367,8 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { } return false } + + (self.view as? ChatControllerNodeView)?.controller = self.controller } private func updateIsEmpty(_ isEmpty: Bool, animated: Bool) { @@ -1301,7 +1311,14 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { textInputPanelNode?.updateKeepSendButtonEnabled(keepSendButtonEnabled: keepSendButtonEnabled, extendedSearchLayout: extendedSearchLayout, animated: transition.isAnimated) } - if let peer = chatPresentationInterfaceState.renderedPeer?.peer, let restrictionText = peer.restrictionText { + var restrictionText: String? + if chatPresentationInterfaceState.isNotAccessible { + restrictionText = chatPresentationInterfaceState.strings.Channel_ErrorAccessDenied + } else if let peer = chatPresentationInterfaceState.renderedPeer?.peer { + restrictionText = peer.restrictionText + } + + if let restrictionText = restrictionText { if self.restrictedNode == nil { let restrictedNode = ChatRecentActionsEmptyNode(theme: chatPresentationInterfaceState.theme) self.historyNodeContainer.supernode?.insertSubnode(restrictedNode, aboveSubnode: self.historyNodeContainer) @@ -1310,11 +1327,13 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { self.restrictedNode?.setup(title: "", text: processedPeerRestrictionText(restrictionText)) self.historyNodeContainer.isHidden = true self.navigateButtons.isHidden = true + self.loadingNode.isHidden = true } else if let restrictedNode = self.restrictedNode { self.restrictedNode = nil restrictedNode.removeFromSupernode() self.historyNodeContainer.isHidden = false self.navigateButtons.isHidden = false + self.loadingNode.isHidden = false } let layoutTransition: ContainedViewLayoutTransition = transition diff --git a/TelegramUI/ChatHistoryListNode.swift b/TelegramUI/ChatHistoryListNode.swift index a821eaa3b5..4bb6228610 100644 --- a/TelegramUI/ChatHistoryListNode.swift +++ b/TelegramUI/ChatHistoryListNode.swift @@ -812,6 +812,18 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { return nil } + public func isMessageVisibleOnScreen(_ id: MessageId) -> Bool { + var result = false + self.forEachItemNode({ itemNode in + if let itemNode = itemNode as? ChatMessageItemView, let item = itemNode.item, item.content.contains(where: { $0.id == id }) { + if self.itemNodeVisibleInsideInsets(itemNode) { + result = true + } + } + }) + return result + } + public func messageInCurrentHistoryView(_ id: MessageId) -> Message? { if let historyView = self.historyView { for entry in historyView.filteredEntries { diff --git a/TelegramUI/ChatHistoryViewForLocation.swift b/TelegramUI/ChatHistoryViewForLocation.swift index ebf9c4e294..3c15d88322 100644 --- a/TelegramUI/ChatHistoryViewForLocation.swift +++ b/TelegramUI/ChatHistoryViewForLocation.swift @@ -200,7 +200,7 @@ private func extractAdditionalData(view: MessageHistoryView, chatLocation: ChatL case let .peer(peerId): if let combinedReadStates = view.combinedReadStates { if case let .peer(readStates) = combinedReadStates, let readState = readStates[peerId] { - readStateData[peerId] = ChatHistoryCombinedInitialReadStateData(unreadCount: readState.count, totalUnreadChatCount: totalUnreadState.filteredCounters.chatCount, notificationSettings: notificationSettings) + readStateData[peerId] = ChatHistoryCombinedInitialReadStateData(unreadCount: readState.count, totalUnreadChatCount: totalUnreadState.count(for: .filtered, in: .chats, with: [.regularChatsAndPrivateGroups]), notificationSettings: notificationSettings) } } case .group: diff --git a/TelegramUI/ChatInterfaceStateInputPanels.swift b/TelegramUI/ChatInterfaceStateInputPanels.swift index bf00c5aad8..71b8bb35b0 100644 --- a/TelegramUI/ChatInterfaceStateInputPanels.swift +++ b/TelegramUI/ChatInterfaceStateInputPanels.swift @@ -6,6 +6,9 @@ func inputPanelForChatPresentationIntefaceState(_ chatPresentationInterfaceState if let renderedPeer = chatPresentationInterfaceState.renderedPeer, renderedPeer.peer?.restrictionText != nil { return nil } + if chatPresentationInterfaceState.isNotAccessible { + return nil + } if let _ = chatPresentationInterfaceState.search { var hasSelection = false diff --git a/TelegramUI/ChatMessageActionItemNode.swift b/TelegramUI/ChatMessageActionItemNode.swift index 6c80b25f39..b067dbc8e4 100644 --- a/TelegramUI/ChatMessageActionItemNode.swift +++ b/TelegramUI/ChatMessageActionItemNode.swift @@ -407,6 +407,8 @@ private func universalServiceMessageString(theme: PresentationTheme?, strings: P } } attributedString = NSAttributedString(string: strings.Notification_PassportValuesSentMessage(message.peers[message.id.peerId]?.compactDisplayTitle ?? "", typesString).0, font: titleFont, textColor: primaryTextColor) + case .peerJoined: + attributedString = addAttributesToStringWithRanges(strings.Notification_Joined(authorName), body: bodyAttributes, argumentAttributes: peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: [(0, message.author?.id)])) case .unknown: attributedString = nil } diff --git a/TelegramUI/ChatMessageBubbleItemNode.swift b/TelegramUI/ChatMessageBubbleItemNode.swift index fe21928b75..6ea8edab4e 100644 --- a/TelegramUI/ChatMessageBubbleItemNode.swift +++ b/TelegramUI/ChatMessageBubbleItemNode.swift @@ -342,7 +342,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView { } if !isBroadcastChannel { - hasAvatar = true + hasAvatar = item.content.firstMessage.effectivelyIncoming(item.account.peerId) } } } else if incoming { diff --git a/TelegramUI/ChatMessageInteractiveMediaNode.swift b/TelegramUI/ChatMessageInteractiveMediaNode.swift index b11dfe926a..81d96256a1 100644 --- a/TelegramUI/ChatMessageInteractiveMediaNode.swift +++ b/TelegramUI/ChatMessageInteractiveMediaNode.swift @@ -593,7 +593,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode { if case .constrained = sizeCalculation { if let file = media as? TelegramMediaFile, (!file.isAnimated || message.flags.contains(.Unsent)) { if let size = file.size { - if let duration = file.duration { + if let duration = file.duration, !message.flags.contains(.Unsent) { if isMediaStreamable(message: message, media: file) { let durationString = String(format: "%d:%02d", duration / 60, duration % 60) let sizeString = "\(dataSizeString(Int(Float(size) * progress), forceDecimal: true)) / \(dataSizeString(size, forceDecimal: true))" @@ -764,4 +764,16 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode { return false } } + + func updateIsHidden(_ isHidden: Bool) { + guard let badgeNode = self.badgeNode, badgeNode.isHidden != isHidden else { + return + } + if isHidden { + badgeNode.isHidden = true + } else { + badgeNode.isHidden = false + badgeNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + } + } } diff --git a/TelegramUI/ChatMessageItem.swift b/TelegramUI/ChatMessageItem.swift index 9e92a2883c..5c719a441e 100644 --- a/TelegramUI/ChatMessageItem.swift +++ b/TelegramUI/ChatMessageItem.swift @@ -102,7 +102,7 @@ private func messagesShouldBeMerged(accountPeerId: PeerId, _ lhs: Message, _ rhs } } - if abs(lhs.timestamp - rhs.timestamp) < Int32(5 * 60) && lhsEffectiveAuthor?.id == rhsEffectiveAuthor?.id { + if abs(lhs.timestamp - rhs.timestamp) < Int32(10 * 60) && lhsEffectiveAuthor?.id == rhsEffectiveAuthor?.id { var upperStyle: Int32 = ChatMessageMerge.fullyMerged.rawValue var lowerStyle: Int32 = ChatMessageMerge.fullyMerged.rawValue for media in lhs.media { diff --git a/TelegramUI/ChatMessageMediaBubbleContentNode.swift b/TelegramUI/ChatMessageMediaBubbleContentNode.swift index 23fc6f62b7..0f5a6db2cc 100644 --- a/TelegramUI/ChatMessageMediaBubbleContentNode.swift +++ b/TelegramUI/ChatMessageMediaBubbleContentNode.swift @@ -256,6 +256,7 @@ class ChatMessageMediaBubbleContentNode: ChatMessageBubbleContentNode { } self.interactiveImageNode.isHidden = mediaHidden + self.interactiveImageNode.updateIsHidden(mediaHidden) return mediaHidden } diff --git a/TelegramUI/ChatPresentationInterfaceState.swift b/TelegramUI/ChatPresentationInterfaceState.swift index 30b379740e..cb368e69c4 100644 --- a/TelegramUI/ChatPresentationInterfaceState.swift +++ b/TelegramUI/ChatPresentationInterfaceState.swift @@ -363,6 +363,7 @@ final class ChatPresentationInterfaceState: Equatable { let interfaceState: ChatInterfaceState let chatLocation: ChatLocation let renderedPeer: RenderedPeer? + let isNotAccessible: Bool let isContact: Bool let inputTextPanelState: ChatTextInputPanelState let editMessageState: ChatEditInterfaceMessageState? @@ -399,6 +400,7 @@ final class ChatPresentationInterfaceState: Equatable { self.recordedMediaPreview = nil self.chatLocation = chatLocation self.renderedPeer = nil + self.isNotAccessible = false self.isContact = false self.inputQueryResults = [:] self.inputMode = .none @@ -426,10 +428,11 @@ final class ChatPresentationInterfaceState: Equatable { self.mode = mode } - init(interfaceState: ChatInterfaceState, chatLocation: ChatLocation, renderedPeer: RenderedPeer?, isContact: Bool, inputTextPanelState: ChatTextInputPanelState, editMessageState: ChatEditInterfaceMessageState?, recordedMediaPreview: ChatRecordedMediaPreview?, inputQueryResults: [ChatPresentationInputQueryKind: ChatPresentationInputQueryResult], inputMode: ChatInputMode, titlePanelContexts: [ChatTitlePanelContext], keyboardButtonsMessage: Message?, pinnedMessageId: MessageId?, pinnedMessage: Message?, peerIsBlocked: Bool, peerIsMuted: Bool, canReportPeer: Bool, callsAvailable: Bool, callsPrivate: Bool, chatHistoryState: ChatHistoryNodeHistoryState?, botStartPayload: String?, urlPreview: (String, TelegramMediaWebpage)?, editingUrlPreview: (String, TelegramMediaWebpage)?, search: ChatSearchData?, searchQuerySuggestionResult: ChatPresentationInputQueryResult?, chatWallpaper: TelegramWallpaper, theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, fontSize: PresentationFontSize, accountPeerId: PeerId, mode: ChatControllerPresentationMode) { + init(interfaceState: ChatInterfaceState, chatLocation: ChatLocation, renderedPeer: RenderedPeer?, isNotAccessible: Bool, isContact: Bool, inputTextPanelState: ChatTextInputPanelState, editMessageState: ChatEditInterfaceMessageState?, recordedMediaPreview: ChatRecordedMediaPreview?, inputQueryResults: [ChatPresentationInputQueryKind: ChatPresentationInputQueryResult], inputMode: ChatInputMode, titlePanelContexts: [ChatTitlePanelContext], keyboardButtonsMessage: Message?, pinnedMessageId: MessageId?, pinnedMessage: Message?, peerIsBlocked: Bool, peerIsMuted: Bool, canReportPeer: Bool, callsAvailable: Bool, callsPrivate: Bool, chatHistoryState: ChatHistoryNodeHistoryState?, botStartPayload: String?, urlPreview: (String, TelegramMediaWebpage)?, editingUrlPreview: (String, TelegramMediaWebpage)?, search: ChatSearchData?, searchQuerySuggestionResult: ChatPresentationInputQueryResult?, chatWallpaper: TelegramWallpaper, theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, fontSize: PresentationFontSize, accountPeerId: PeerId, mode: ChatControllerPresentationMode) { self.interfaceState = interfaceState self.chatLocation = chatLocation self.renderedPeer = renderedPeer + self.isNotAccessible = isNotAccessible self.isContact = isContact self.inputTextPanelState = inputTextPanelState self.editMessageState = editMessageState @@ -467,6 +470,9 @@ final class ChatPresentationInterfaceState: Equatable { if lhs.renderedPeer != rhs.renderedPeer { return false } + if lhs.isNotAccessible != rhs.isNotAccessible { + return false + } if lhs.isContact != rhs.isContact { return false } @@ -611,15 +617,19 @@ final class ChatPresentationInterfaceState: Equatable { } func updatedInterfaceState(_ f: (ChatInterfaceState) -> ChatInterfaceState) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: f(self.interfaceState), chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isContact: self.isContact, 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, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, 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, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) + return ChatPresentationInterfaceState(interfaceState: f(self.interfaceState), chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, isContact: self.isContact, 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, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, 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, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) } func updatedPeer(_ f: (RenderedPeer?) -> RenderedPeer?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: f(self.renderedPeer), isContact: self.isContact, 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, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, 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, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: f(self.renderedPeer), isNotAccessible: self.isNotAccessible, isContact: self.isContact, 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, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, 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, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) + } + + func updatedisNotAccessible(_ isNotAccessible: Bool) -> ChatPresentationInterfaceState { + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: isNotAccessible, isContact: self.isContact, 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, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, 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, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) } func updatedIsContact(_ isContact: Bool) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isContact: isContact, 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, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, 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, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, isContact: isContact, 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, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, 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, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) } func updatedInputQueryResult(queryKind: ChatPresentationInputQueryKind, _ f: (ChatPresentationInputQueryResult?) -> ChatPresentationInputQueryResult?) -> ChatPresentationInterfaceState { @@ -630,103 +640,103 @@ final class ChatPresentationInterfaceState: Equatable { } else { inputQueryResults.removeValue(forKey: queryKind) } - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isContact: self.isContact, 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, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, 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, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, isContact: self.isContact, 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, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, 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, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) } func updatedInputTextPanelState(_ f: (ChatTextInputPanelState) -> ChatTextInputPanelState) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isContact: self.isContact, 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, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, 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, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, isContact: self.isContact, 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, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, 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, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) } func updatedEditMessageState(_ editMessageState: ChatEditInterfaceMessageState?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isContact: self.isContact, 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, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, 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, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, isContact: self.isContact, 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, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, 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, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) } func updatedRecordedMediaPreview(_ recordedMediaPreview: ChatRecordedMediaPreview?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isContact: self.isContact, 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, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, 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, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, isContact: self.isContact, 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, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, 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, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) } func updatedInputMode(_ f: (ChatInputMode) -> ChatInputMode) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isContact: self.isContact, 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, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, 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, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, isContact: self.isContact, 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, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, 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, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) } func updatedTitlePanelContext(_ f: ([ChatTitlePanelContext]) -> [ChatTitlePanelContext]) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isContact: self.isContact, 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, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, 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, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, isContact: self.isContact, 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, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, 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, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) } func updatedKeyboardButtonsMessage(_ message: Message?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isContact: self.isContact, 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, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, 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, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, isContact: self.isContact, 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, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, 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, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) } func updatedPinnedMessageId(_ pinnedMessageId: MessageId?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isContact: self.isContact, 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, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, 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, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, isContact: self.isContact, 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, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, 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, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) } func updatedPinnedMessage(_ pinnedMessage: Message?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isContact: self.isContact, 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, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, 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, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, isContact: self.isContact, 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, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, 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, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) } func updatedPeerIsBlocked(_ peerIsBlocked: Bool) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isContact: self.isContact, 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, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, 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, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, isContact: self.isContact, 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, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, 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, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) } func updatedPeerIsMuted(_ peerIsMuted: Bool) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isContact: self.isContact, 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, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, 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, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, isContact: self.isContact, 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, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, 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, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) } func updatedCanReportPeer(_ canReportPeer: Bool) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isContact: self.isContact, 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, canReportPeer: canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, 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, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, isContact: self.isContact, 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, canReportPeer: canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, 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, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) } func updatedCallsAvailable(_ callsAvailable: Bool) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isContact: self.isContact, 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, canReportPeer: self.canReportPeer, callsAvailable: callsAvailable, callsPrivate: self.callsPrivate, 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, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, isContact: self.isContact, 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, canReportPeer: self.canReportPeer, callsAvailable: callsAvailable, callsPrivate: self.callsPrivate, 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, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) } func updatedCallsPrivate(_ callsPrivate: Bool) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isContact: self.isContact, 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, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: callsPrivate, 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, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, isContact: self.isContact, 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, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: callsPrivate, 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, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) } func updatedBotStartPayload(_ botStartPayload: String?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isContact: self.isContact, 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, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, 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, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, isContact: self.isContact, 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, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, 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, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) } func updatedChatHistoryState(_ chatHistoryState: ChatHistoryNodeHistoryState?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isContact: self.isContact, 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, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, 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, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, isContact: self.isContact, 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, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, 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, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) } func updatedUrlPreview(_ urlPreview: (String, TelegramMediaWebpage)?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isContact: self.isContact, 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, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, 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, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, isContact: self.isContact, 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, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, 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, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) } func updatedEditingUrlPreview(_ editingUrlPreview: (String, TelegramMediaWebpage)?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isContact: self.isContact, 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, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, 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, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, isContact: self.isContact, 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, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, 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, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) } func updatedSearch(_ search: ChatSearchData?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isContact: self.isContact, 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, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, 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, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, isContact: self.isContact, 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, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, 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, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) } func updatedSearchQuerySuggestionResult(_ f: (ChatPresentationInputQueryResult?) -> ChatPresentationInputQueryResult?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isContact: self.isContact, 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, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, 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, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, isContact: self.isContact, 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, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, 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, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) } func updatedMode(_ mode: ChatControllerPresentationMode) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isContact: self.isContact, 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, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, 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, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: mode) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, isContact: self.isContact, 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, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, 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, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: mode) } func updatedTheme(_ theme: PresentationTheme) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isContact: self.isContact, 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, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, 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, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, isContact: self.isContact, 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, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, 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, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) } func updatedStrings(_ strings: PresentationStrings) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isContact: self.isContact, 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, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, 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, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, isContact: self.isContact, 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, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, 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, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) } func updatedDateTimeFormat(_ dateTimeFormat: PresentationDateTimeFormat) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isContact: self.isContact, 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, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, 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, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, isContact: self.isContact, 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, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, 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, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) } func updatedChatWallpaper(_ chatWallpaper: TelegramWallpaper) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isContact: self.isContact, 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, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, 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, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, isContact: self.isContact, 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, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, 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, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) } } diff --git a/TelegramUI/ChatTextInputMediaRecordingButton.swift b/TelegramUI/ChatTextInputMediaRecordingButton.swift index 2935b10346..4efc590bd7 100644 --- a/TelegramUI/ChatTextInputMediaRecordingButton.swift +++ b/TelegramUI/ChatTextInputMediaRecordingButton.swift @@ -334,8 +334,9 @@ final class ChatTextInputMediaRecordingButton: TGModernConversationInputMicButto if self.fadeDisabled { self.recordingDisabled() } else { + //print("\(CFAbsoluteTimeGetCurrent()) began") self.modeTimeoutTimer?.invalidate() - let modeTimeoutTimer = SwiftSignalKit.Timer(timeout: 0.1, repeat: false, completion: { [weak self] in + let modeTimeoutTimer = SwiftSignalKit.Timer(timeout: 0.19, repeat: false, completion: { [weak self] in if let strongSelf = self { strongSelf.modeTimeoutTimer = nil strongSelf.beginRecording() @@ -347,12 +348,15 @@ final class ChatTextInputMediaRecordingButton: TGModernConversationInputMicButto } func micButtonInteractionCancelled(_ velocity: CGPoint) { + //print("\(CFAbsoluteTimeGetCurrent()) cancelled") self.modeTimeoutTimer?.invalidate() self.endRecording(false) } func micButtonInteractionCompleted(_ velocity: CGPoint) { + //print("\(CFAbsoluteTimeGetCurrent()) completed") if let modeTimeoutTimer = self.modeTimeoutTimer { + //print("\(CFAbsoluteTimeGetCurrent()) switch") modeTimeoutTimer.invalidate() self.modeTimeoutTimer = nil self.switchMode() diff --git a/TelegramUI/HorizontalPeerItem.swift b/TelegramUI/HorizontalPeerItem.swift index 4ba708e7ae..bfe8aa812c 100644 --- a/TelegramUI/HorizontalPeerItem.swift +++ b/TelegramUI/HorizontalPeerItem.swift @@ -164,13 +164,11 @@ final class HorizontalPeerItemNode: ListViewItemNode { badgeSize += max(currentBadgeBackgroundImage.size.width, badgeLayout.size.width + 10.0) + 5.0 } - - return (itemLayout, { animated in if let strongSelf = self { strongSelf.item = item strongSelf.peerNode.theme = itemTheme - strongSelf.peerNode.setup(account: item.account, strings: item.strings, peer: item.peer, chatPeer: nil, numberOfLines: 1) + strongSelf.peerNode.setup(account: item.account, strings: item.strings, peer: RenderedPeer(peer: item.peer), numberOfLines: 1) strongSelf.peerNode.frame = CGRect(origin: CGPoint(), size: itemLayout.size) strongSelf.peerNode.updateSelection(selected: item.isPeerSelected(item.peer.id), animated: false) diff --git a/TelegramUI/InAppNotificationSettings.swift b/TelegramUI/InAppNotificationSettings.swift index de223a08f9..f07657432e 100644 --- a/TelegramUI/InAppNotificationSettings.swift +++ b/TelegramUI/InAppNotificationSettings.swift @@ -5,11 +5,29 @@ import SwiftSignalKit public enum TotalUnreadCountDisplayStyle: Int32 { case filtered = 0 case raw = 1 + + var category: ChatListTotalUnreadStateCategory { + switch self { + case .filtered: + return .filtered + case .raw: + return .raw + } + } } public enum TotalUnreadCountDisplayCategory: Int32 { case chats = 0 case messages = 1 + + var statsType: ChatListTotalUnreadStateStats { + switch self { + case .chats: + return .chats + case .messages: + return .messages + } + } } public struct InAppNotificationSettings: PreferencesEntry, Equatable { @@ -18,18 +36,20 @@ public struct InAppNotificationSettings: PreferencesEntry, Equatable { public var displayPreviews: Bool public var totalUnreadCountDisplayStyle: TotalUnreadCountDisplayStyle public var totalUnreadCountDisplayCategory: TotalUnreadCountDisplayCategory + public var totalUnreadCountIncludeTags: PeerSummaryCounterTags public var displayNameOnLockscreen: Bool public static var defaultSettings: InAppNotificationSettings { - return InAppNotificationSettings(playSounds: true, vibrate: false, displayPreviews: true, totalUnreadCountDisplayStyle: .raw, totalUnreadCountDisplayCategory: .messages, displayNameOnLockscreen: true) + return InAppNotificationSettings(playSounds: true, vibrate: false, displayPreviews: true, totalUnreadCountDisplayStyle: .raw, totalUnreadCountDisplayCategory: .messages, totalUnreadCountIncludeTags: [.regularChatsAndPrivateGroups], displayNameOnLockscreen: true) } - init(playSounds: Bool, vibrate: Bool, displayPreviews: Bool, totalUnreadCountDisplayStyle: TotalUnreadCountDisplayStyle, totalUnreadCountDisplayCategory: TotalUnreadCountDisplayCategory, displayNameOnLockscreen: Bool) { + init(playSounds: Bool, vibrate: Bool, displayPreviews: Bool, totalUnreadCountDisplayStyle: TotalUnreadCountDisplayStyle, totalUnreadCountDisplayCategory: TotalUnreadCountDisplayCategory, totalUnreadCountIncludeTags: PeerSummaryCounterTags, displayNameOnLockscreen: Bool) { self.playSounds = playSounds self.vibrate = vibrate self.displayPreviews = displayPreviews self.totalUnreadCountDisplayStyle = totalUnreadCountDisplayStyle self.totalUnreadCountDisplayCategory = totalUnreadCountDisplayCategory + self.totalUnreadCountIncludeTags = totalUnreadCountIncludeTags self.displayNameOnLockscreen = displayNameOnLockscreen } @@ -39,6 +59,11 @@ public struct InAppNotificationSettings: PreferencesEntry, Equatable { self.displayPreviews = decoder.decodeInt32ForKey("p", orElse: 0) != 0 self.totalUnreadCountDisplayStyle = TotalUnreadCountDisplayStyle(rawValue: decoder.decodeInt32ForKey("tds", orElse: 1)) ?? .raw self.totalUnreadCountDisplayCategory = TotalUnreadCountDisplayCategory(rawValue: decoder.decodeInt32ForKey("totalUnreadCountDisplayCategory", orElse: 1)) ?? .messages + if let value = decoder.decodeOptionalInt32ForKey("totalUnreadCountIncludeTags") { + self.totalUnreadCountIncludeTags = PeerSummaryCounterTags(rawValue: value) + } else { + self.totalUnreadCountIncludeTags = [.regularChatsAndPrivateGroups] + } self.displayNameOnLockscreen = decoder.decodeInt32ForKey("displayNameOnLockscreen", orElse: 1) != 0 } @@ -48,6 +73,7 @@ public struct InAppNotificationSettings: PreferencesEntry, Equatable { encoder.encodeInt32(self.displayPreviews ? 1 : 0, forKey: "p") encoder.encodeInt32(self.totalUnreadCountDisplayStyle.rawValue, forKey: "tds") encoder.encodeInt32(self.totalUnreadCountDisplayCategory.rawValue, forKey: "totalUnreadCountDisplayCategory") + encoder.encodeInt32(self.totalUnreadCountIncludeTags.rawValue, forKey: "totalUnreadCountIncludeTags") encoder.encodeInt32(self.displayNameOnLockscreen ? 1 : 0, forKey: "displayNameOnLockscreen") } diff --git a/TelegramUI/InstantPageMediaPlaylist.swift b/TelegramUI/InstantPageMediaPlaylist.swift index 5675a25dcd..07cd25228d 100644 --- a/TelegramUI/InstantPageMediaPlaylist.swift +++ b/TelegramUI/InstantPageMediaPlaylist.swift @@ -222,7 +222,7 @@ final class InstantPageMediaPlaylist: SharedMediaPlaylist { } private func updateState() { - self.stateValue.set(.single(SharedMediaPlaylistState(loading: false, playedToEnd: self.playedToEnd, item: self.currentItem.flatMap({ InstantPageMediaPlaylistItem(webPage: self.webPage, item: $0) }), order: self.order, looping: self.looping))) + self.stateValue.set(.single(SharedMediaPlaylistState(loading: false, playedToEnd: self.playedToEnd, item: self.currentItem.flatMap({ InstantPageMediaPlaylistItem(webPage: self.webPage, item: $0) }), nextItem: nil, previousItem: nil, order: self.order, looping: self.looping))) } func onItemPlaybackStarted(_ item: SharedMediaPlaylistItem) { diff --git a/TelegramUI/IsMediaStreamable.swift b/TelegramUI/IsMediaStreamable.swift index a3755b0238..dcb79577ed 100644 --- a/TelegramUI/IsMediaStreamable.swift +++ b/TelegramUI/IsMediaStreamable.swift @@ -3,11 +3,20 @@ import Postbox import TelegramCore func isMediaStreamable(message: Message, media: TelegramMediaFile) -> Bool { + if message.containsSecretMedia { + return false + } if message.id.peerId.namespace == Namespaces.Peer.SecretChat { return false } if media.isVideo && !media.isAnimated { return true } + guard let size = media.size else { + return false + } + if size < 500 * 1024 { + return false + } return false } diff --git a/TelegramUI/JoinLinkPreviewPeerContentNode.swift b/TelegramUI/JoinLinkPreviewPeerContentNode.swift index 1ebed53a80..1a3813d142 100644 --- a/TelegramUI/JoinLinkPreviewPeerContentNode.swift +++ b/TelegramUI/JoinLinkPreviewPeerContentNode.swift @@ -43,7 +43,7 @@ final class JoinLinkPreviewPeerContentNode: ASDisplayNode, ShareContentContainer self.peerNodes = members.map { peer in let node = SelectablePeerNode() - node.setup(account: account, strings: strings, peer: peer, chatPeer: nil) + node.setup(account: account, strings: strings, peer: RenderedPeer(peer: peer)) node.theme = itemTheme return node } diff --git a/TelegramUI/LanguageSelectionController.swift b/TelegramUI/LanguageSelectionController.swift index 581bb41321..a82bd82327 100644 --- a/TelegramUI/LanguageSelectionController.swift +++ b/TelegramUI/LanguageSelectionController.swift @@ -29,7 +29,7 @@ private final class LanguageAccessoryView: UIView { self.check = UIImageView() self.check.image = PresentationResourcesItemList.checkIconImage(theme) - self.indicator = ActivityIndicator(type: .navigationAccent(theme)) + self.indicator = ActivityIndicator(type: .custom(theme.list.itemAccentColor, 22.0, 1.0, false)) super.init(frame: CGRect()) diff --git a/TelegramUI/NotificationExceptions.swift b/TelegramUI/NotificationExceptions.swift index 5a81c62d07..d27e76cf93 100644 --- a/TelegramUI/NotificationExceptions.swift +++ b/TelegramUI/NotificationExceptions.swift @@ -414,7 +414,7 @@ public func notificationExceptionsController(account: Account, mode: Notificatio activateSearch?() }, changeNotifications: { peerId, settings in - let globalSettings = globalValue.modify {$0} + /*let globalSettings = globalValue.modify {$0} let isPrivateChat = peerId.namespace == Namespaces.Peer.CloudUser @@ -458,9 +458,9 @@ public func notificationExceptionsController(account: Account, mode: Notificatio actionSheet?.dismissAnimated() }) ])]) - presentControllerImpl?(actionSheet, nil) + presentControllerImpl?(actionSheet, nil)*/ }, selectPeer: { - let filter: ChatListNodePeersFilter + /*let filter: ChatListNodePeersFilter switch mode { case .groups: filter = [.withoutSecretChats] @@ -541,7 +541,7 @@ public func notificationExceptionsController(account: Account, mode: Notificatio } - presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) + presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))*/ }) let peersSignal:Signal<[PeerId : Peer], NoError> = statePromise.get() |> mapToSignal { state in diff --git a/TelegramUI/NotificationsAndSounds.swift b/TelegramUI/NotificationsAndSounds.swift index 403385eba6..2f46c2b14d 100644 --- a/TelegramUI/NotificationsAndSounds.swift +++ b/TelegramUI/NotificationsAndSounds.swift @@ -24,13 +24,14 @@ private final class NotificationsAndSoundsArguments { let updateDisplayNameOnLockscreen: (Bool) -> Void let updateTotalUnreadCountStyle: (Bool) -> Void + let updateIncludeTag: (PeerSummaryCounterTags, Bool) -> Void let updateTotalUnreadCountCategory: (Bool) -> Void let resetNotifications: () -> Void let updatedExceptionMode: (NotificationExceptionMode) -> Void - init(account: Account, presentController: @escaping (ViewController, ViewControllerPresentationArguments?) -> Void, pushController: @escaping(ViewController)->Void, soundSelectionDisposable: MetaDisposable, updateMessageAlerts: @escaping (Bool) -> Void, updateMessagePreviews: @escaping (Bool) -> Void, updateMessageSound: @escaping (PeerMessageSound) -> Void, updateGroupAlerts: @escaping (Bool) -> Void, updateGroupPreviews: @escaping (Bool) -> Void, updateGroupSound: @escaping (PeerMessageSound) -> Void, updateInAppSounds: @escaping (Bool) -> Void, updateInAppVibration: @escaping (Bool) -> Void, updateInAppPreviews: @escaping (Bool) -> Void, updateDisplayNameOnLockscreen: @escaping (Bool) -> Void, updateTotalUnreadCountStyle: @escaping (Bool) -> Void, updateTotalUnreadCountCategory: @escaping (Bool) -> Void, resetNotifications: @escaping () -> Void, updatedExceptionMode: @escaping(NotificationExceptionMode) -> Void) { + init(account: Account, presentController: @escaping (ViewController, ViewControllerPresentationArguments?) -> Void, pushController: @escaping(ViewController)->Void, soundSelectionDisposable: MetaDisposable, updateMessageAlerts: @escaping (Bool) -> Void, updateMessagePreviews: @escaping (Bool) -> Void, updateMessageSound: @escaping (PeerMessageSound) -> Void, updateGroupAlerts: @escaping (Bool) -> Void, updateGroupPreviews: @escaping (Bool) -> Void, updateGroupSound: @escaping (PeerMessageSound) -> Void, updateInAppSounds: @escaping (Bool) -> Void, updateInAppVibration: @escaping (Bool) -> Void, updateInAppPreviews: @escaping (Bool) -> Void, updateDisplayNameOnLockscreen: @escaping (Bool) -> Void, updateTotalUnreadCountStyle: @escaping (Bool) -> Void, updateIncludeTag: @escaping (PeerSummaryCounterTags, Bool) -> Void, updateTotalUnreadCountCategory: @escaping (Bool) -> Void, resetNotifications: @escaping () -> Void, updatedExceptionMode: @escaping(NotificationExceptionMode) -> Void) { self.account = account self.presentController = presentController self.pushController = pushController @@ -46,6 +47,7 @@ private final class NotificationsAndSoundsArguments { self.updateInAppPreviews = updateInAppPreviews self.updateDisplayNameOnLockscreen = updateDisplayNameOnLockscreen self.updateTotalUnreadCountStyle = updateTotalUnreadCountStyle + self.updateIncludeTag = updateIncludeTag self.updateTotalUnreadCountCategory = updateTotalUnreadCountCategory self.resetNotifications = resetNotifications self.updatedExceptionMode = updatedExceptionMode @@ -87,6 +89,8 @@ private enum NotificationsAndSoundsEntry: ItemListNodeEntry { case badgeHeader(PresentationTheme, String) case unreadCountStyle(PresentationTheme, String, Bool) + case includePublicGroups(PresentationTheme, String, Bool) + case includeChannels(PresentationTheme, String, Bool) case unreadCountCategory(PresentationTheme, String, Bool) case unreadCountCategoryInfo(PresentationTheme, String) @@ -103,7 +107,7 @@ private enum NotificationsAndSoundsEntry: ItemListNodeEntry { return NotificationsAndSoundsSection.inApp.rawValue case .displayNamesOnLockscreen, .displayNamesOnLockscreenInfo: return NotificationsAndSoundsSection.displayNamesOnLockscreen.rawValue - case .badgeHeader, .unreadCountStyle, .unreadCountCategory, .unreadCountCategoryInfo: + case .badgeHeader, .unreadCountStyle, .includePublicGroups, .includeChannels, .unreadCountCategory, .unreadCountCategoryInfo: return NotificationsAndSoundsSection.badge.rawValue case .reset, .resetNotice: return NotificationsAndSoundsSection.reset.rawValue @@ -152,14 +156,18 @@ private enum NotificationsAndSoundsEntry: ItemListNodeEntry { return 18 case .unreadCountStyle: return 19 - case .unreadCountCategory: + case .includePublicGroups: return 20 - case .unreadCountCategoryInfo: + case .includeChannels: return 21 - case .reset: + case .unreadCountCategory: return 22 - case .resetNotice: + case .unreadCountCategoryInfo: return 23 + case .reset: + return 24 + case .resetNotice: + return 25 } } @@ -285,6 +293,18 @@ private enum NotificationsAndSoundsEntry: ItemListNodeEntry { } else { return false } + case let .includePublicGroups(lhsTheme, lhsText, lhsValue): + if case let .includePublicGroups(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue { + return true + } else { + return false + } + case let .includeChannels(lhsTheme, lhsText, lhsValue): + if case let .includeChannels(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue { + return true + } else { + return false + } case let .unreadCountCategory(lhsTheme, lhsText, lhsValue): if case let .unreadCountCategory(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue { return true @@ -392,6 +412,14 @@ private enum NotificationsAndSoundsEntry: ItemListNodeEntry { return ItemListSwitchItem(theme: theme, title: text, value: value, sectionId: self.section, style: .blocks, updated: { updatedValue in arguments.updateTotalUnreadCountStyle(updatedValue) }) + case let .includePublicGroups(theme, text, value): + return ItemListSwitchItem(theme: theme, title: text, value: value, sectionId: self.section, style: .blocks, updated: { updatedValue in + arguments.updateIncludeTag(.publicGroups, updatedValue) + }) + case let .includeChannels(theme, text, value): + return ItemListSwitchItem(theme: theme, title: text, value: value, sectionId: self.section, style: .blocks, updated: { updatedValue in + arguments.updateIncludeTag(.channels, updatedValue) + }) case let .unreadCountCategory(theme, text, value): return ItemListSwitchItem(theme: theme, title: text, value: value, sectionId: self.section, style: .blocks, updated: { updatedValue in arguments.updateTotalUnreadCountCategory(updatedValue) @@ -448,6 +476,8 @@ private func notificationsAndSoundsEntries(globalSettings: GlobalNotificationSet entries.append(.badgeHeader(presentationData.theme, presentationData.strings.Notifications_Badge)) entries.append(.unreadCountStyle(presentationData.theme, presentationData.strings.Notifications_Badge_IncludeMutedChats, inAppSettings.totalUnreadCountDisplayStyle == .raw)) + entries.append(.includePublicGroups(presentationData.theme, presentationData.strings.Notifications_Badge_IncludePublicGroups, inAppSettings.totalUnreadCountIncludeTags.contains(.publicGroups))) + entries.append(.includeChannels(presentationData.theme, presentationData.strings.Notifications_Badge_IncludeChannels, inAppSettings.totalUnreadCountIncludeTags.contains(.channels))) entries.append(.unreadCountCategory(presentationData.theme, presentationData.strings.Notifications_Badge_CountUnreadMessages, inAppSettings.totalUnreadCountDisplayCategory == .messages)) entries.append(.unreadCountCategoryInfo(presentationData.theme, inAppSettings.totalUnreadCountDisplayCategory == .chats ? presentationData.strings.Notifications_Badge_CountUnreadMessages_InfoOff : presentationData.strings.Notifications_Badge_CountUnreadMessages_InfoOn)) @@ -539,6 +569,16 @@ public func notificationsAndSoundsController(account: Account) -> ViewController settings.totalUnreadCountDisplayStyle = value ? .raw : .filtered return settings }).start() + }, updateIncludeTag: { tag, value in + let _ = updateInAppNotificationSettingsInteractively(postbox: account.postbox, { settings in + var settings = settings + if !value { + settings.totalUnreadCountIncludeTags.remove(tag) + } else { + settings.totalUnreadCountIncludeTags.insert(tag) + } + return settings + }).start() }, updateTotalUnreadCountCategory: { value in let _ = updateInAppNotificationSettingsInteractively(postbox: account.postbox, { settings in var settings = settings diff --git a/TelegramUI/PeerMessagesMediaPlaylist.swift b/TelegramUI/PeerMessagesMediaPlaylist.swift index 62bc1812ef..f4f2d39172 100644 --- a/TelegramUI/PeerMessagesMediaPlaylist.swift +++ b/TelegramUI/PeerMessagesMediaPlaylist.swift @@ -140,7 +140,7 @@ private enum NavigatedMessageFromViewPosition { case exact } -private func navigatedMessageFromView(_ view: MessageHistoryView, anchorIndex: MessageIndex, position: NavigatedMessageFromViewPosition) -> (message: Message, exact: Bool)? { +private func navigatedMessageFromView(_ view: MessageHistoryView, anchorIndex: MessageIndex, position: NavigatedMessageFromViewPosition) -> (message: Message, around: [Message], exact: Bool)? { var index = 0 for entry in view.entries { if entry.index.id == anchorIndex.id { @@ -148,7 +148,7 @@ private func navigatedMessageFromView(_ view: MessageHistoryView, anchorIndex: M case .exact: switch entry { case let .MessageEntry(message, _, _, _): - return (message, true) + return (message, [], true) default: return nil } @@ -156,7 +156,7 @@ private func navigatedMessageFromView(_ view: MessageHistoryView, anchorIndex: M if index + 1 < view.entries.count { switch view.entries[index + 1] { case let .MessageEntry(message, _, _, _): - return (message, true) + return (message, [], true) default: return nil } @@ -167,7 +167,7 @@ private func navigatedMessageFromView(_ view: MessageHistoryView, anchorIndex: M if index != 0 { switch view.entries[index - 1] { case let .MessageEntry(message, _, _, _): - return (message, true) + return (message, [], true) default: return nil } @@ -183,14 +183,14 @@ private func navigatedMessageFromView(_ view: MessageHistoryView, anchorIndex: M case .later, .exact: switch view.entries[view.entries.count - 1] { case let .MessageEntry(message, _, _, _): - return (message, false) + return (message, [], false) default: return nil } case .earlier: switch view.entries[0] { case let .MessageEntry(message, _, _, _): - return (message, false) + return (message, [], false) default: return nil } @@ -290,7 +290,7 @@ final class PeerMessagesMediaPlaylist: SharedMediaPlaylist { private let navigationDisposable = MetaDisposable() - private var currentItem: Message? + private var currentItem: (current: Message, around: [Message])? private var loadingItem: Bool = false private var playedToEnd: Bool = false private var order: MusicPlaybackSettingsOrder = .regular @@ -319,7 +319,7 @@ final class PeerMessagesMediaPlaylist: SharedMediaPlaylist { self.loadItem(anchor: .messageId(messageId), navigation: .later) case let .recentActions(message): self.loadingItem = false - self.currentItem = message + self.currentItem = (message, []) self.updateState() } } @@ -334,7 +334,7 @@ final class PeerMessagesMediaPlaylist: SharedMediaPlaylist { switch action { case .next, .previous: switch self.messagesLocation { - case let .recentActions(message): + case .recentActions: self.loadingItem = false self.currentItem = nil self.updateState() @@ -367,7 +367,7 @@ final class PeerMessagesMediaPlaylist: SharedMediaPlaylist { self.currentItem = nil self.updateState() } else { - self.loadItem(anchor: .index(MessageIndex(currentItem)), navigation: navigation) + self.loadItem(anchor: .index(MessageIndex(currentItem.current)), navigation: navigation) } } } @@ -389,7 +389,20 @@ final class PeerMessagesMediaPlaylist: SharedMediaPlaylist { } private func updateState() { - self.stateValue.set(.single(SharedMediaPlaylistState(loading: self.loadingItem, playedToEnd: self.playedToEnd, item: self.currentItem.flatMap(MessageMediaPlaylistItem.init), order: self.order, looping: self.looping))) + var item: MessageMediaPlaylistItem? + var nextItem: MessageMediaPlaylistItem? + var previousItem: MessageMediaPlaylistItem? + if let (message, aroundMessages) = self.currentItem { + item = MessageMediaPlaylistItem(message: message) + for around in aroundMessages { + if MessageIndex(around) < MessageIndex(message) { + previousItem = MessageMediaPlaylistItem(message: around) + } else { + nextItem = MessageMediaPlaylistItem(message: around) + } + } + } + self.stateValue.set(.single(SharedMediaPlaylistState(loading: self.loadingItem, playedToEnd: self.playedToEnd, item: item, nextItem: nextItem, previousItem: previousItem, order: self.order, looping: self.looping))) } private func loadItem(anchor: PeerMessagesMediaPlaylistLoadAnchor, navigation: PeerMessagesMediaPlaylistNavigation) { @@ -397,12 +410,18 @@ final class PeerMessagesMediaPlaylist: SharedMediaPlaylist { self.updateState() switch anchor { case let .messageId(messageId): - self.navigationDisposable.set((self.postbox.messageAtId(messageId) |> take(1) |> deliverOnMainQueue).start(next: { [weak self] message in + self.navigationDisposable.set((self.postbox.messageAtId(messageId) + |> take(1) + |> deliverOnMainQueue).start(next: { [weak self] message in if let strongSelf = self { assert(strongSelf.loadingItem) strongSelf.loadingItem = false - strongSelf.currentItem = message + if let message = message { + strongSelf.currentItem = (message, []) + } else { + strongSelf.currentItem = nil + } strongSelf.updateState() } })) @@ -419,9 +438,10 @@ final class PeerMessagesMediaPlaylist: SharedMediaPlaylist { return transaction.findRandomMessage(peerId: peerId, tagMask: tagMask, ignoreId: index.id) ?? index } } - let historySignal = inputIndex |> mapToSignal { inputIndex -> Signal in + let historySignal = inputIndex + |> mapToSignal { inputIndex -> Signal<(Message, [Message])?, NoError> in return self.postbox.aroundMessageHistoryViewForLocation(.peer(peerId), index: .message(inputIndex), anchorIndex: .message(inputIndex), count: 10, clipHoles: false, fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: [], tagMask: tagMask, orderStatistics: []) - |> mapToSignal { view -> Signal in + |> mapToSignal { view -> Signal<(Message, [Message])?, NoError> in let position: NavigatedMessageFromViewPosition switch navigation { case .later: @@ -432,13 +452,13 @@ final class PeerMessagesMediaPlaylist: SharedMediaPlaylist { position = .exact } - if let (message, exact) = navigatedMessageFromView(view.0, anchorIndex: inputIndex, position: position) { + if let (message, aroundMessages, exact) = navigatedMessageFromView(view.0, anchorIndex: inputIndex, position: position) { switch navigation { case .random: - return .single(message) + return .single((message, [])) default: if exact { - return .single(message) + return .single((message, aroundMessages)) } } } @@ -451,7 +471,7 @@ final class PeerMessagesMediaPlaylist: SharedMediaPlaylist { viewIndex = .lowerBound } return self.postbox.aroundMessageHistoryViewForLocation(.peer(peerId), index: viewIndex, anchorIndex: viewIndex, count: 10, clipHoles: false, fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: [], tagMask: tagMask, orderStatistics: []) - |> mapToSignal { view -> Signal in + |> mapToSignal { view -> Signal<(Message, [Message])?, NoError> in let position: NavigatedMessageFromViewPosition switch navigation { case .later, .random: @@ -459,8 +479,8 @@ final class PeerMessagesMediaPlaylist: SharedMediaPlaylist { case .earlier: position = .later } - if let (message, _) = navigatedMessageFromView(view.0, anchorIndex: MessageIndex.absoluteLowerBound(), position: position) { - return .single(message) + if let (message, aroundMessages, _) = navigatedMessageFromView(view.0, anchorIndex: MessageIndex.absoluteLowerBound(), position: position) { + return .single((message, aroundMessages)) } else { return .single(nil) } @@ -469,14 +489,16 @@ final class PeerMessagesMediaPlaylist: SharedMediaPlaylist { return .single(nil) } } - } |> take(1) |> deliverOnMainQueue - self.navigationDisposable.set(historySignal.start(next: { [weak self] message in + } + |> take(1) + |> deliverOnMainQueue + self.navigationDisposable.set(historySignal.start(next: { [weak self] messageAndAroundMessages in if let strongSelf = self { assert(strongSelf.loadingItem) strongSelf.loadingItem = false - if let message = message { - strongSelf.currentItem = message + if let (message, aroundMessages) = messageAndAroundMessages { + strongSelf.currentItem = (message, aroundMessages) strongSelf.playedToEnd = false } else { strongSelf.playedToEnd = true @@ -485,18 +507,24 @@ final class PeerMessagesMediaPlaylist: SharedMediaPlaylist { } })) case .singleMessage: - self.navigationDisposable.set((self.postbox.messageAtId(index.id) |> take(1) |> deliverOnMainQueue).start(next: { [weak self] message in + self.navigationDisposable.set((self.postbox.messageAtId(index.id) + |> take(1) + |> deliverOnMainQueue).start(next: { [weak self] message in if let strongSelf = self { assert(strongSelf.loadingItem) strongSelf.loadingItem = false - strongSelf.currentItem = message + if let message = message { + strongSelf.currentItem = (message, []) + } else { + strongSelf.currentItem = nil + } strongSelf.updateState() } })) case let .recentActions(message): self.loadingItem = false - self.currentItem = message + self.currentItem = (message, []) self.updateState() } } diff --git a/TelegramUI/PhotoResources.swift b/TelegramUI/PhotoResources.swift index b4b7aaeb86..74af44ebf2 100644 --- a/TelegramUI/PhotoResources.swift +++ b/TelegramUI/PhotoResources.swift @@ -1383,7 +1383,17 @@ func internalMediaGridMessageVideo(postbox: Postbox, videoReference: FileMediaRe var blurredThumbnailImage: UIImage? if let thumbnailImage = thumbnailImage { let thumbnailSize = CGSize(width: thumbnailImage.width, height: thumbnailImage.height) - let thumbnailContextSize = thumbnailSize.aspectFitted(CGSize(width: 150.0, height: 150.0)) + let fullScaleBlur = true + var thumbnailContextFittingSize: CGSize + if fullScaleBlur { + thumbnailContextFittingSize = fittedSize.fitted(CGSize(width: 320.0, height: 320.0)) + thumbnailContextFittingSize.width = floor(thumbnailContextFittingSize.width * 0.8) + thumbnailContextFittingSize.height = floor(thumbnailContextFittingSize.height * 0.8) + } else { + thumbnailContextFittingSize = CGSize(width: 150.0, height: 150.0) + } + + let thumbnailContextSize = thumbnailSize.aspectFitted(thumbnailContextFittingSize) let thumbnailContext = DrawingContext(size: thumbnailContextSize, scale: 1.0) thumbnailContext.withFlippedContext { c in c.interpolationQuality = .none @@ -1432,7 +1442,7 @@ func internalMediaGridMessageVideo(postbox: Postbox, videoReference: FileMediaRe c.setBlendMode(.copy) if let blurredThumbnailImage = blurredThumbnailImage, let cgImage = blurredThumbnailImage.cgImage { - c.interpolationQuality = .low + c.interpolationQuality = .default drawImage(context: c, image: cgImage, orientation: imageOrientation, in: fittedRect) c.setBlendMode(.normal) } diff --git a/TelegramUI/PresentationStrings.swift b/TelegramUI/PresentationStrings.swift index 7821ff82d6..8491870340 100644 --- a/TelegramUI/PresentationStrings.swift +++ b/TelegramUI/PresentationStrings.swift @@ -116,6 +116,7 @@ public final class PresentationStrings { } public let FastTwoStepSetup_PasswordSection: String public let FastTwoStepSetup_EmailSection: String + public let Notifications_Badge_IncludeChannels: String public let Cache_ClearCache: String public let Common_Close: String public let Passport_PasswordDescription: String @@ -1537,7 +1538,6 @@ public final class PresentationStrings { } public let Conversation_ClearPrivateHistory: String public let Conversation_ContextMenuShare: String - public let Notifications_ExceptionsResetToDefaults: String public let Notifications_ExceptionsNone: String private let _Time_MonthOfYear_m6: String private let _Time_MonthOfYear_m6_r: [(Int, NSRange)] @@ -3175,6 +3175,7 @@ public final class PresentationStrings { public let LastSeen_Offline: String public let Login_CodeFloodError: String public let Conversation_EncryptedDescription3: String + public let Notifications_Badge_IncludePublicGroups: String public let Conversation_EncryptedDescription4: String public let AppleWatch_Title: String public let Contacts_AccessDeniedError: String @@ -3613,270 +3614,6 @@ public final class PresentationStrings { public let PrivacySettings_PasscodeAndFaceId: String public let Settings_ChatBackground: String public let Login_TermsOfServiceDecline: String - private let _LastSeen_MinutesAgo_zero: String - private let _LastSeen_MinutesAgo_one: String - private let _LastSeen_MinutesAgo_two: String - private let _LastSeen_MinutesAgo_few: String - private let _LastSeen_MinutesAgo_many: String - private let _LastSeen_MinutesAgo_other: String - public func LastSeen_MinutesAgo(_ value: Int32) -> String { - switch presentationStringsPluralizationForm(self.lc, value) { - case .zero: - return String(format: self._LastSeen_MinutesAgo_zero, "\(value)") - case .one: - return String(format: self._LastSeen_MinutesAgo_one, "\(value)") - case .two: - return String(format: self._LastSeen_MinutesAgo_two, "\(value)") - case .few: - return String(format: self._LastSeen_MinutesAgo_few, "\(value)") - case .many: - return String(format: self._LastSeen_MinutesAgo_many, "\(value)") - case .other: - return String(format: self._LastSeen_MinutesAgo_other, "\(value)") - } - } - private let _Passport_Scans_zero: String - private let _Passport_Scans_one: String - private let _Passport_Scans_two: String - private let _Passport_Scans_few: String - private let _Passport_Scans_many: String - private let _Passport_Scans_other: String - public func Passport_Scans(_ value: Int32) -> String { - switch presentationStringsPluralizationForm(self.lc, value) { - case .zero: - return String(format: self._Passport_Scans_zero, "\(value)") - case .one: - return String(format: self._Passport_Scans_one, "\(value)") - case .two: - return String(format: self._Passport_Scans_two, "\(value)") - case .few: - return String(format: self._Passport_Scans_few, "\(value)") - case .many: - return String(format: self._Passport_Scans_many, "\(value)") - case .other: - return String(format: self._Passport_Scans_other, "\(value)") - } - } - private let _Notifications_Exceptions_zero: String - private let _Notifications_Exceptions_one: String - private let _Notifications_Exceptions_two: String - private let _Notifications_Exceptions_few: String - private let _Notifications_Exceptions_many: String - private let _Notifications_Exceptions_other: String - public func Notifications_Exceptions(_ value: Int32) -> String { - switch presentationStringsPluralizationForm(self.lc, value) { - case .zero: - return String(format: self._Notifications_Exceptions_zero, "\(value)") - case .one: - return String(format: self._Notifications_Exceptions_one, "\(value)") - case .two: - return String(format: self._Notifications_Exceptions_two, "\(value)") - case .few: - return String(format: self._Notifications_Exceptions_few, "\(value)") - case .many: - return String(format: self._Notifications_Exceptions_many, "\(value)") - case .other: - return String(format: self._Notifications_Exceptions_other, "\(value)") - } - } - private let _MuteExpires_Days_zero: String - private let _MuteExpires_Days_one: String - private let _MuteExpires_Days_two: String - private let _MuteExpires_Days_few: String - private let _MuteExpires_Days_many: String - private let _MuteExpires_Days_other: String - public func MuteExpires_Days(_ value: Int32) -> String { - switch presentationStringsPluralizationForm(self.lc, value) { - case .zero: - return String(format: self._MuteExpires_Days_zero, "\(value)") - case .one: - return String(format: self._MuteExpires_Days_one, "\(value)") - case .two: - return String(format: self._MuteExpires_Days_two, "\(value)") - case .few: - return String(format: self._MuteExpires_Days_few, "\(value)") - case .many: - return String(format: self._MuteExpires_Days_many, "\(value)") - case .other: - return String(format: self._MuteExpires_Days_other, "\(value)") - } - } - private let _Watch_UserInfo_Mute_zero: String - private let _Watch_UserInfo_Mute_one: String - private let _Watch_UserInfo_Mute_two: String - private let _Watch_UserInfo_Mute_few: String - private let _Watch_UserInfo_Mute_many: String - private let _Watch_UserInfo_Mute_other: String - public func Watch_UserInfo_Mute(_ value: Int32) -> String { - switch presentationStringsPluralizationForm(self.lc, value) { - case .zero: - return String(format: self._Watch_UserInfo_Mute_zero, "\(value)") - case .one: - return String(format: self._Watch_UserInfo_Mute_one, "\(value)") - case .two: - return String(format: self._Watch_UserInfo_Mute_two, "\(value)") - case .few: - return String(format: self._Watch_UserInfo_Mute_few, "\(value)") - case .many: - return String(format: self._Watch_UserInfo_Mute_many, "\(value)") - case .other: - return String(format: self._Watch_UserInfo_Mute_other, "\(value)") - } - } - private let _MessageTimer_Days_zero: String - private let _MessageTimer_Days_one: String - private let _MessageTimer_Days_two: String - private let _MessageTimer_Days_few: String - private let _MessageTimer_Days_many: String - private let _MessageTimer_Days_other: String - public func MessageTimer_Days(_ value: Int32) -> String { - switch presentationStringsPluralizationForm(self.lc, value) { - case .zero: - return String(format: self._MessageTimer_Days_zero, "\(value)") - case .one: - return String(format: self._MessageTimer_Days_one, "\(value)") - case .two: - return String(format: self._MessageTimer_Days_two, "\(value)") - case .few: - return String(format: self._MessageTimer_Days_few, "\(value)") - case .many: - return String(format: self._MessageTimer_Days_many, "\(value)") - case .other: - return String(format: self._MessageTimer_Days_other, "\(value)") - } - } - private let _Map_ETAMinutes_zero: String - private let _Map_ETAMinutes_one: String - private let _Map_ETAMinutes_two: String - private let _Map_ETAMinutes_few: String - private let _Map_ETAMinutes_many: String - private let _Map_ETAMinutes_other: String - public func Map_ETAMinutes(_ value: Int32) -> String { - switch presentationStringsPluralizationForm(self.lc, value) { - case .zero: - return String(format: self._Map_ETAMinutes_zero, "\(value)") - case .one: - return String(format: self._Map_ETAMinutes_one, "\(value)") - case .two: - return String(format: self._Map_ETAMinutes_two, "\(value)") - case .few: - return String(format: self._Map_ETAMinutes_few, "\(value)") - case .many: - return String(format: self._Map_ETAMinutes_many, "\(value)") - case .other: - return String(format: self._Map_ETAMinutes_other, "\(value)") - } - } - private let _Contacts_ImportersCount_zero: String - private let _Contacts_ImportersCount_one: String - private let _Contacts_ImportersCount_two: String - private let _Contacts_ImportersCount_few: String - private let _Contacts_ImportersCount_many: String - private let _Contacts_ImportersCount_other: String - public func Contacts_ImportersCount(_ value: Int32) -> String { - switch presentationStringsPluralizationForm(self.lc, value) { - case .zero: - return String(format: self._Contacts_ImportersCount_zero, "\(value)") - case .one: - return String(format: self._Contacts_ImportersCount_one, "\(value)") - case .two: - return String(format: self._Contacts_ImportersCount_two, "\(value)") - case .few: - return String(format: self._Contacts_ImportersCount_few, "\(value)") - case .many: - return String(format: self._Contacts_ImportersCount_many, "\(value)") - case .other: - return String(format: self._Contacts_ImportersCount_other, "\(value)") - } - } - private let _StickerPack_AddStickerCount_zero: String - private let _StickerPack_AddStickerCount_one: String - private let _StickerPack_AddStickerCount_two: String - private let _StickerPack_AddStickerCount_few: String - private let _StickerPack_AddStickerCount_many: String - private let _StickerPack_AddStickerCount_other: String - public func StickerPack_AddStickerCount(_ value: Int32) -> String { - switch presentationStringsPluralizationForm(self.lc, value) { - case .zero: - return String(format: self._StickerPack_AddStickerCount_zero, "\(value)") - case .one: - return String(format: self._StickerPack_AddStickerCount_one, "\(value)") - case .two: - return String(format: self._StickerPack_AddStickerCount_two, "\(value)") - case .few: - return String(format: self._StickerPack_AddStickerCount_few, "\(value)") - case .many: - return String(format: self._StickerPack_AddStickerCount_many, "\(value)") - case .other: - return String(format: self._StickerPack_AddStickerCount_other, "\(value)") - } - } - private let _UserCount_zero: String - private let _UserCount_one: String - private let _UserCount_two: String - private let _UserCount_few: String - private let _UserCount_many: String - private let _UserCount_other: String - public func UserCount(_ value: Int32) -> String { - switch presentationStringsPluralizationForm(self.lc, value) { - case .zero: - return String(format: self._UserCount_zero, "\(value)") - case .one: - return String(format: self._UserCount_one, "\(value)") - case .two: - return String(format: self._UserCount_two, "\(value)") - case .few: - return String(format: self._UserCount_few, "\(value)") - case .many: - return String(format: self._UserCount_many, "\(value)") - case .other: - return String(format: self._UserCount_other, "\(value)") - } - } - private let _Notifications_ExceptionMuteExpires_Days_zero: String - private let _Notifications_ExceptionMuteExpires_Days_one: String - private let _Notifications_ExceptionMuteExpires_Days_two: String - private let _Notifications_ExceptionMuteExpires_Days_few: String - private let _Notifications_ExceptionMuteExpires_Days_many: String - private let _Notifications_ExceptionMuteExpires_Days_other: String - public func Notifications_ExceptionMuteExpires_Days(_ value: Int32) -> String { - switch presentationStringsPluralizationForm(self.lc, value) { - case .zero: - return String(format: self._Notifications_ExceptionMuteExpires_Days_zero, "\(value)") - case .one: - return String(format: self._Notifications_ExceptionMuteExpires_Days_one, "\(value)") - case .two: - return String(format: self._Notifications_ExceptionMuteExpires_Days_two, "\(value)") - case .few: - return String(format: self._Notifications_ExceptionMuteExpires_Days_few, "\(value)") - case .many: - return String(format: self._Notifications_ExceptionMuteExpires_Days_many, "\(value)") - case .other: - return String(format: self._Notifications_ExceptionMuteExpires_Days_other, "\(value)") - } - } - private let _MuteFor_Hours_zero: String - private let _MuteFor_Hours_one: String - private let _MuteFor_Hours_two: String - private let _MuteFor_Hours_few: String - private let _MuteFor_Hours_many: String - private let _MuteFor_Hours_other: String - public func MuteFor_Hours(_ value: Int32) -> String { - switch presentationStringsPluralizationForm(self.lc, value) { - case .zero: - return String(format: self._MuteFor_Hours_zero, "\(value)") - case .one: - return String(format: self._MuteFor_Hours_one, "\(value)") - case .two: - return String(format: self._MuteFor_Hours_two, "\(value)") - case .few: - return String(format: self._MuteFor_Hours_few, "\(value)") - case .many: - return String(format: self._MuteFor_Hours_many, "\(value)") - case .other: - return String(format: self._MuteFor_Hours_other, "\(value)") - } - } private let _MessageTimer_Hours_zero: String private let _MessageTimer_Hours_one: String private let _MessageTimer_Hours_two: String @@ -3899,160 +3636,6 @@ public final class PresentationStrings { return String(format: self._MessageTimer_Hours_other, "\(value)") } } - private let _Media_SharePhoto_zero: String - private let _Media_SharePhoto_one: String - private let _Media_SharePhoto_two: String - private let _Media_SharePhoto_few: String - private let _Media_SharePhoto_many: String - private let _Media_SharePhoto_other: String - public func Media_SharePhoto(_ value: Int32) -> String { - switch presentationStringsPluralizationForm(self.lc, value) { - case .zero: - return String(format: self._Media_SharePhoto_zero, "\(value)") - case .one: - return String(format: self._Media_SharePhoto_one, "\(value)") - case .two: - return String(format: self._Media_SharePhoto_two, "\(value)") - case .few: - return String(format: self._Media_SharePhoto_few, "\(value)") - case .many: - return String(format: self._Media_SharePhoto_many, "\(value)") - case .other: - return String(format: self._Media_SharePhoto_other, "\(value)") - } - } - private let _Map_ETAHours_zero: String - private let _Map_ETAHours_one: String - private let _Map_ETAHours_two: String - private let _Map_ETAHours_few: String - private let _Map_ETAHours_many: String - private let _Map_ETAHours_other: String - public func Map_ETAHours(_ value: Int32) -> String { - switch presentationStringsPluralizationForm(self.lc, value) { - case .zero: - return String(format: self._Map_ETAHours_zero, "\(value)") - case .one: - return String(format: self._Map_ETAHours_one, "\(value)") - case .two: - return String(format: self._Map_ETAHours_two, "\(value)") - case .few: - return String(format: self._Map_ETAHours_few, "\(value)") - case .many: - return String(format: self._Map_ETAHours_many, "\(value)") - case .other: - return String(format: self._Map_ETAHours_other, "\(value)") - } - } - private let _Conversation_StatusMembers_zero: String - private let _Conversation_StatusMembers_one: String - private let _Conversation_StatusMembers_two: String - private let _Conversation_StatusMembers_few: String - private let _Conversation_StatusMembers_many: String - private let _Conversation_StatusMembers_other: String - public func Conversation_StatusMembers(_ value: Int32) -> String { - switch presentationStringsPluralizationForm(self.lc, value) { - case .zero: - return String(format: self._Conversation_StatusMembers_zero, "\(value)") - case .one: - return String(format: self._Conversation_StatusMembers_one, "\(value)") - case .two: - return String(format: self._Conversation_StatusMembers_two, "\(value)") - case .few: - return String(format: self._Conversation_StatusMembers_few, "\(value)") - case .many: - return String(format: self._Conversation_StatusMembers_many, "\(value)") - case .other: - return String(format: self._Conversation_StatusMembers_other, "\(value)") - } - } - private let _ServiceMessage_GameScoreSelfExtended_zero: String - private let _ServiceMessage_GameScoreSelfExtended_one: String - private let _ServiceMessage_GameScoreSelfExtended_two: String - private let _ServiceMessage_GameScoreSelfExtended_few: String - private let _ServiceMessage_GameScoreSelfExtended_many: String - private let _ServiceMessage_GameScoreSelfExtended_other: String - public func ServiceMessage_GameScoreSelfExtended(_ value: Int32) -> String { - switch presentationStringsPluralizationForm(self.lc, value) { - case .zero: - return String(format: self._ServiceMessage_GameScoreSelfExtended_zero, "\(value)") - case .one: - return String(format: self._ServiceMessage_GameScoreSelfExtended_one, "\(value)") - case .two: - return String(format: self._ServiceMessage_GameScoreSelfExtended_two, "\(value)") - case .few: - return String(format: self._ServiceMessage_GameScoreSelfExtended_few, "\(value)") - case .many: - return String(format: self._ServiceMessage_GameScoreSelfExtended_many, "\(value)") - case .other: - return String(format: self._ServiceMessage_GameScoreSelfExtended_other, "\(value)") - } - } - private let _SharedMedia_Link_zero: String - private let _SharedMedia_Link_one: String - private let _SharedMedia_Link_two: String - private let _SharedMedia_Link_few: String - private let _SharedMedia_Link_many: String - private let _SharedMedia_Link_other: String - public func SharedMedia_Link(_ value: Int32) -> String { - switch presentationStringsPluralizationForm(self.lc, value) { - case .zero: - return String(format: self._SharedMedia_Link_zero, "\(value)") - case .one: - return String(format: self._SharedMedia_Link_one, "\(value)") - case .two: - return String(format: self._SharedMedia_Link_two, "\(value)") - case .few: - return String(format: self._SharedMedia_Link_few, "\(value)") - case .many: - return String(format: self._SharedMedia_Link_many, "\(value)") - case .other: - return String(format: self._SharedMedia_Link_other, "\(value)") - } - } - private let _Media_ShareItem_zero: String - private let _Media_ShareItem_one: String - private let _Media_ShareItem_two: String - private let _Media_ShareItem_few: String - private let _Media_ShareItem_many: String - private let _Media_ShareItem_other: String - public func Media_ShareItem(_ value: Int32) -> String { - switch presentationStringsPluralizationForm(self.lc, value) { - case .zero: - return String(format: self._Media_ShareItem_zero, "\(value)") - case .one: - return String(format: self._Media_ShareItem_one, "\(value)") - case .two: - return String(format: self._Media_ShareItem_two, "\(value)") - case .few: - return String(format: self._Media_ShareItem_few, "\(value)") - case .many: - return String(format: self._Media_ShareItem_many, "\(value)") - case .other: - return String(format: self._Media_ShareItem_other, "\(value)") - } - } - private let _PasscodeSettings_FailedAttempts_zero: String - private let _PasscodeSettings_FailedAttempts_one: String - private let _PasscodeSettings_FailedAttempts_two: String - private let _PasscodeSettings_FailedAttempts_few: String - private let _PasscodeSettings_FailedAttempts_many: String - private let _PasscodeSettings_FailedAttempts_other: String - public func PasscodeSettings_FailedAttempts(_ value: Int32) -> String { - switch presentationStringsPluralizationForm(self.lc, value) { - case .zero: - return String(format: self._PasscodeSettings_FailedAttempts_zero, "\(value)") - case .one: - return String(format: self._PasscodeSettings_FailedAttempts_one, "\(value)") - case .two: - return String(format: self._PasscodeSettings_FailedAttempts_two, "\(value)") - case .few: - return String(format: self._PasscodeSettings_FailedAttempts_few, "\(value)") - case .many: - return String(format: self._PasscodeSettings_FailedAttempts_many, "\(value)") - case .other: - return String(format: self._PasscodeSettings_FailedAttempts_other, "\(value)") - } - } private let _Notification_GameScoreSelfSimple_zero: String private let _Notification_GameScoreSelfSimple_one: String private let _Notification_GameScoreSelfSimple_two: String @@ -4075,138 +3658,6 @@ public final class PresentationStrings { return String(format: self._Notification_GameScoreSelfSimple_other, "\(value)") } } - private let _Notifications_ExceptionMuteExpires_Hours_zero: String - private let _Notifications_ExceptionMuteExpires_Hours_one: String - private let _Notifications_ExceptionMuteExpires_Hours_two: String - private let _Notifications_ExceptionMuteExpires_Hours_few: String - private let _Notifications_ExceptionMuteExpires_Hours_many: String - private let _Notifications_ExceptionMuteExpires_Hours_other: String - public func Notifications_ExceptionMuteExpires_Hours(_ value: Int32) -> String { - switch presentationStringsPluralizationForm(self.lc, value) { - case .zero: - return String(format: self._Notifications_ExceptionMuteExpires_Hours_zero, "\(value)") - case .one: - return String(format: self._Notifications_ExceptionMuteExpires_Hours_one, "\(value)") - case .two: - return String(format: self._Notifications_ExceptionMuteExpires_Hours_two, "\(value)") - case .few: - return String(format: self._Notifications_ExceptionMuteExpires_Hours_few, "\(value)") - case .many: - return String(format: self._Notifications_ExceptionMuteExpires_Hours_many, "\(value)") - case .other: - return String(format: self._Notifications_ExceptionMuteExpires_Hours_other, "\(value)") - } - } - private let _LastSeen_HoursAgo_zero: String - private let _LastSeen_HoursAgo_one: String - private let _LastSeen_HoursAgo_two: String - private let _LastSeen_HoursAgo_few: String - private let _LastSeen_HoursAgo_many: String - private let _LastSeen_HoursAgo_other: String - public func LastSeen_HoursAgo(_ value: Int32) -> String { - switch presentationStringsPluralizationForm(self.lc, value) { - case .zero: - return String(format: self._LastSeen_HoursAgo_zero, "\(value)") - case .one: - return String(format: self._LastSeen_HoursAgo_one, "\(value)") - case .two: - return String(format: self._LastSeen_HoursAgo_two, "\(value)") - case .few: - return String(format: self._LastSeen_HoursAgo_few, "\(value)") - case .many: - return String(format: self._LastSeen_HoursAgo_many, "\(value)") - case .other: - return String(format: self._LastSeen_HoursAgo_other, "\(value)") - } - } - private let _MessageTimer_Seconds_zero: String - private let _MessageTimer_Seconds_one: String - private let _MessageTimer_Seconds_two: String - private let _MessageTimer_Seconds_few: String - private let _MessageTimer_Seconds_many: String - private let _MessageTimer_Seconds_other: String - public func MessageTimer_Seconds(_ value: Int32) -> String { - switch presentationStringsPluralizationForm(self.lc, value) { - case .zero: - return String(format: self._MessageTimer_Seconds_zero, "\(value)") - case .one: - return String(format: self._MessageTimer_Seconds_one, "\(value)") - case .two: - return String(format: self._MessageTimer_Seconds_two, "\(value)") - case .few: - return String(format: self._MessageTimer_Seconds_few, "\(value)") - case .many: - return String(format: self._MessageTimer_Seconds_many, "\(value)") - case .other: - return String(format: self._MessageTimer_Seconds_other, "\(value)") - } - } - private let _Notification_GameScoreSelfExtended_zero: String - private let _Notification_GameScoreSelfExtended_one: String - private let _Notification_GameScoreSelfExtended_two: String - private let _Notification_GameScoreSelfExtended_few: String - private let _Notification_GameScoreSelfExtended_many: String - private let _Notification_GameScoreSelfExtended_other: String - public func Notification_GameScoreSelfExtended(_ value: Int32) -> String { - switch presentationStringsPluralizationForm(self.lc, value) { - case .zero: - return String(format: self._Notification_GameScoreSelfExtended_zero, "\(value)") - case .one: - return String(format: self._Notification_GameScoreSelfExtended_one, "\(value)") - case .two: - return String(format: self._Notification_GameScoreSelfExtended_two, "\(value)") - case .few: - return String(format: self._Notification_GameScoreSelfExtended_few, "\(value)") - case .many: - return String(format: self._Notification_GameScoreSelfExtended_many, "\(value)") - case .other: - return String(format: self._Notification_GameScoreSelfExtended_other, "\(value)") - } - } - private let _Notification_GameScoreSimple_zero: String - private let _Notification_GameScoreSimple_one: String - private let _Notification_GameScoreSimple_two: String - private let _Notification_GameScoreSimple_few: String - private let _Notification_GameScoreSimple_many: String - private let _Notification_GameScoreSimple_other: String - public func Notification_GameScoreSimple(_ value: Int32) -> String { - switch presentationStringsPluralizationForm(self.lc, value) { - case .zero: - return String(format: self._Notification_GameScoreSimple_zero, "\(value)") - case .one: - return String(format: self._Notification_GameScoreSimple_one, "\(value)") - case .two: - return String(format: self._Notification_GameScoreSimple_two, "\(value)") - case .few: - return String(format: self._Notification_GameScoreSimple_few, "\(value)") - case .many: - return String(format: self._Notification_GameScoreSimple_many, "\(value)") - case .other: - return String(format: self._Notification_GameScoreSimple_other, "\(value)") - } - } - private let _MuteFor_Days_zero: String - private let _MuteFor_Days_one: String - private let _MuteFor_Days_two: String - private let _MuteFor_Days_few: String - private let _MuteFor_Days_many: String - private let _MuteFor_Days_other: String - public func MuteFor_Days(_ value: Int32) -> String { - switch presentationStringsPluralizationForm(self.lc, value) { - case .zero: - return String(format: self._MuteFor_Days_zero, "\(value)") - case .one: - return String(format: self._MuteFor_Days_one, "\(value)") - case .two: - return String(format: self._MuteFor_Days_two, "\(value)") - case .few: - return String(format: self._MuteFor_Days_few, "\(value)") - case .many: - return String(format: self._MuteFor_Days_many, "\(value)") - case .other: - return String(format: self._MuteFor_Days_other, "\(value)") - } - } private let _Conversation_StatusSubscribers_zero: String private let _Conversation_StatusSubscribers_one: String private let _Conversation_StatusSubscribers_two: String @@ -4229,268 +3680,26 @@ public final class PresentationStrings { return String(format: self._Conversation_StatusSubscribers_other, "\(value)") } } - private let _ForwardedGifs_zero: String - private let _ForwardedGifs_one: String - private let _ForwardedGifs_two: String - private let _ForwardedGifs_few: String - private let _ForwardedGifs_many: String - private let _ForwardedGifs_other: String - public func ForwardedGifs(_ value: Int32) -> String { + private let _Watch_LastSeen_HoursAgo_zero: String + private let _Watch_LastSeen_HoursAgo_one: String + private let _Watch_LastSeen_HoursAgo_two: String + private let _Watch_LastSeen_HoursAgo_few: String + private let _Watch_LastSeen_HoursAgo_many: String + private let _Watch_LastSeen_HoursAgo_other: String + public func Watch_LastSeen_HoursAgo(_ value: Int32) -> String { switch presentationStringsPluralizationForm(self.lc, value) { case .zero: - return String(format: self._ForwardedGifs_zero, "\(value)") + return String(format: self._Watch_LastSeen_HoursAgo_zero, "\(value)") case .one: - return String(format: self._ForwardedGifs_one, "\(value)") + return String(format: self._Watch_LastSeen_HoursAgo_one, "\(value)") case .two: - return String(format: self._ForwardedGifs_two, "\(value)") + return String(format: self._Watch_LastSeen_HoursAgo_two, "\(value)") case .few: - return String(format: self._ForwardedGifs_few, "\(value)") + return String(format: self._Watch_LastSeen_HoursAgo_few, "\(value)") case .many: - return String(format: self._ForwardedGifs_many, "\(value)") + return String(format: self._Watch_LastSeen_HoursAgo_many, "\(value)") case .other: - return String(format: self._ForwardedGifs_other, "\(value)") - } - } - private let _StickerPack_AddMaskCount_zero: String - private let _StickerPack_AddMaskCount_one: String - private let _StickerPack_AddMaskCount_two: String - private let _StickerPack_AddMaskCount_few: String - private let _StickerPack_AddMaskCount_many: String - private let _StickerPack_AddMaskCount_other: String - public func StickerPack_AddMaskCount(_ value: Int32) -> String { - switch presentationStringsPluralizationForm(self.lc, value) { - case .zero: - return String(format: self._StickerPack_AddMaskCount_zero, "\(value)") - case .one: - return String(format: self._StickerPack_AddMaskCount_one, "\(value)") - case .two: - return String(format: self._StickerPack_AddMaskCount_two, "\(value)") - case .few: - return String(format: self._StickerPack_AddMaskCount_few, "\(value)") - case .many: - return String(format: self._StickerPack_AddMaskCount_many, "\(value)") - case .other: - return String(format: self._StickerPack_AddMaskCount_other, "\(value)") - } - } - private let _MessageTimer_ShortSeconds_zero: String - private let _MessageTimer_ShortSeconds_one: String - private let _MessageTimer_ShortSeconds_two: String - private let _MessageTimer_ShortSeconds_few: String - private let _MessageTimer_ShortSeconds_many: String - private let _MessageTimer_ShortSeconds_other: String - public func MessageTimer_ShortSeconds(_ value: Int32) -> String { - switch presentationStringsPluralizationForm(self.lc, value) { - case .zero: - return String(format: self._MessageTimer_ShortSeconds_zero, "\(value)") - case .one: - return String(format: self._MessageTimer_ShortSeconds_one, "\(value)") - case .two: - return String(format: self._MessageTimer_ShortSeconds_two, "\(value)") - case .few: - return String(format: self._MessageTimer_ShortSeconds_few, "\(value)") - case .many: - return String(format: self._MessageTimer_ShortSeconds_many, "\(value)") - case .other: - return String(format: self._MessageTimer_ShortSeconds_other, "\(value)") - } - } - private let _AttachmentMenu_SendPhoto_zero: String - private let _AttachmentMenu_SendPhoto_one: String - private let _AttachmentMenu_SendPhoto_two: String - private let _AttachmentMenu_SendPhoto_few: String - private let _AttachmentMenu_SendPhoto_many: String - private let _AttachmentMenu_SendPhoto_other: String - public func AttachmentMenu_SendPhoto(_ value: Int32) -> String { - switch presentationStringsPluralizationForm(self.lc, value) { - case .zero: - return String(format: self._AttachmentMenu_SendPhoto_zero, "\(value)") - case .one: - return String(format: self._AttachmentMenu_SendPhoto_one, "\(value)") - case .two: - return String(format: self._AttachmentMenu_SendPhoto_two, "\(value)") - case .few: - return String(format: self._AttachmentMenu_SendPhoto_few, "\(value)") - case .many: - return String(format: self._AttachmentMenu_SendPhoto_many, "\(value)") - case .other: - return String(format: self._AttachmentMenu_SendPhoto_other, "\(value)") - } - } - private let _Notifications_ExceptionMuteExpires_Minutes_zero: String - private let _Notifications_ExceptionMuteExpires_Minutes_one: String - private let _Notifications_ExceptionMuteExpires_Minutes_two: String - private let _Notifications_ExceptionMuteExpires_Minutes_few: String - private let _Notifications_ExceptionMuteExpires_Minutes_many: String - private let _Notifications_ExceptionMuteExpires_Minutes_other: String - public func Notifications_ExceptionMuteExpires_Minutes(_ value: Int32) -> String { - switch presentationStringsPluralizationForm(self.lc, value) { - case .zero: - return String(format: self._Notifications_ExceptionMuteExpires_Minutes_zero, "\(value)") - case .one: - return String(format: self._Notifications_ExceptionMuteExpires_Minutes_one, "\(value)") - case .two: - return String(format: self._Notifications_ExceptionMuteExpires_Minutes_two, "\(value)") - case .few: - return String(format: self._Notifications_ExceptionMuteExpires_Minutes_few, "\(value)") - case .many: - return String(format: self._Notifications_ExceptionMuteExpires_Minutes_many, "\(value)") - case .other: - return String(format: self._Notifications_ExceptionMuteExpires_Minutes_other, "\(value)") - } - } - private let _MessageTimer_ShortWeeks_zero: String - private let _MessageTimer_ShortWeeks_one: String - private let _MessageTimer_ShortWeeks_two: String - private let _MessageTimer_ShortWeeks_few: String - private let _MessageTimer_ShortWeeks_many: String - private let _MessageTimer_ShortWeeks_other: String - public func MessageTimer_ShortWeeks(_ value: Int32) -> String { - switch presentationStringsPluralizationForm(self.lc, value) { - case .zero: - return String(format: self._MessageTimer_ShortWeeks_zero, "\(value)") - case .one: - return String(format: self._MessageTimer_ShortWeeks_one, "\(value)") - case .two: - return String(format: self._MessageTimer_ShortWeeks_two, "\(value)") - case .few: - return String(format: self._MessageTimer_ShortWeeks_few, "\(value)") - case .many: - return String(format: self._MessageTimer_ShortWeeks_many, "\(value)") - case .other: - return String(format: self._MessageTimer_ShortWeeks_other, "\(value)") - } - } - private let _ForwardedStickers_zero: String - private let _ForwardedStickers_one: String - private let _ForwardedStickers_two: String - private let _ForwardedStickers_few: String - private let _ForwardedStickers_many: String - private let _ForwardedStickers_other: String - public func ForwardedStickers(_ value: Int32) -> String { - switch presentationStringsPluralizationForm(self.lc, value) { - case .zero: - return String(format: self._ForwardedStickers_zero, "\(value)") - case .one: - return String(format: self._ForwardedStickers_one, "\(value)") - case .two: - return String(format: self._ForwardedStickers_two, "\(value)") - case .few: - return String(format: self._ForwardedStickers_few, "\(value)") - case .many: - return String(format: self._ForwardedStickers_many, "\(value)") - case .other: - return String(format: self._ForwardedStickers_other, "\(value)") - } - } - private let _Forward_ConfirmMultipleFiles_zero: String - private let _Forward_ConfirmMultipleFiles_one: String - private let _Forward_ConfirmMultipleFiles_two: String - private let _Forward_ConfirmMultipleFiles_few: String - private let _Forward_ConfirmMultipleFiles_many: String - private let _Forward_ConfirmMultipleFiles_other: String - public func Forward_ConfirmMultipleFiles(_ value: Int32) -> String { - switch presentationStringsPluralizationForm(self.lc, value) { - case .zero: - return String(format: self._Forward_ConfirmMultipleFiles_zero, "\(value)") - case .one: - return String(format: self._Forward_ConfirmMultipleFiles_one, "\(value)") - case .two: - return String(format: self._Forward_ConfirmMultipleFiles_two, "\(value)") - case .few: - return String(format: self._Forward_ConfirmMultipleFiles_few, "\(value)") - case .many: - return String(format: self._Forward_ConfirmMultipleFiles_many, "\(value)") - case .other: - return String(format: self._Forward_ConfirmMultipleFiles_other, "\(value)") - } - } - private let _SharedMedia_Photo_zero: String - private let _SharedMedia_Photo_one: String - private let _SharedMedia_Photo_two: String - private let _SharedMedia_Photo_few: String - private let _SharedMedia_Photo_many: String - private let _SharedMedia_Photo_other: String - public func SharedMedia_Photo(_ value: Int32) -> String { - switch presentationStringsPluralizationForm(self.lc, value) { - case .zero: - return String(format: self._SharedMedia_Photo_zero, "\(value)") - case .one: - return String(format: self._SharedMedia_Photo_one, "\(value)") - case .two: - return String(format: self._SharedMedia_Photo_two, "\(value)") - case .few: - return String(format: self._SharedMedia_Photo_few, "\(value)") - case .many: - return String(format: self._SharedMedia_Photo_many, "\(value)") - case .other: - return String(format: self._SharedMedia_Photo_other, "\(value)") - } - } - private let _StickerPack_RemoveStickerCount_zero: String - private let _StickerPack_RemoveStickerCount_one: String - private let _StickerPack_RemoveStickerCount_two: String - private let _StickerPack_RemoveStickerCount_few: String - private let _StickerPack_RemoveStickerCount_many: String - private let _StickerPack_RemoveStickerCount_other: String - public func StickerPack_RemoveStickerCount(_ value: Int32) -> String { - switch presentationStringsPluralizationForm(self.lc, value) { - case .zero: - return String(format: self._StickerPack_RemoveStickerCount_zero, "\(value)") - case .one: - return String(format: self._StickerPack_RemoveStickerCount_one, "\(value)") - case .two: - return String(format: self._StickerPack_RemoveStickerCount_two, "\(value)") - case .few: - return String(format: self._StickerPack_RemoveStickerCount_few, "\(value)") - case .many: - return String(format: self._StickerPack_RemoveStickerCount_many, "\(value)") - case .other: - return String(format: self._StickerPack_RemoveStickerCount_other, "\(value)") - } - } - private let _InviteText_ContactsCountText_zero: String - private let _InviteText_ContactsCountText_one: String - private let _InviteText_ContactsCountText_two: String - private let _InviteText_ContactsCountText_few: String - private let _InviteText_ContactsCountText_many: String - private let _InviteText_ContactsCountText_other: String - public func InviteText_ContactsCountText(_ value: Int32) -> String { - switch presentationStringsPluralizationForm(self.lc, value) { - case .zero: - return String(format: self._InviteText_ContactsCountText_zero, "\(value)") - case .one: - return String(format: self._InviteText_ContactsCountText_one, "\(value)") - case .two: - return String(format: self._InviteText_ContactsCountText_two, "\(value)") - case .few: - return String(format: self._InviteText_ContactsCountText_few, "\(value)") - case .many: - return String(format: self._InviteText_ContactsCountText_many, "\(value)") - case .other: - return String(format: self._InviteText_ContactsCountText_other, "\(value)") - } - } - private let _MuteExpires_Hours_zero: String - private let _MuteExpires_Hours_one: String - private let _MuteExpires_Hours_two: String - private let _MuteExpires_Hours_few: String - private let _MuteExpires_Hours_many: String - private let _MuteExpires_Hours_other: String - public func MuteExpires_Hours(_ value: Int32) -> String { - switch presentationStringsPluralizationForm(self.lc, value) { - case .zero: - return String(format: self._MuteExpires_Hours_zero, "\(value)") - case .one: - return String(format: self._MuteExpires_Hours_one, "\(value)") - case .two: - return String(format: self._MuteExpires_Hours_two, "\(value)") - case .few: - return String(format: self._MuteExpires_Hours_few, "\(value)") - case .many: - return String(format: self._MuteExpires_Hours_many, "\(value)") - case .other: - return String(format: self._MuteExpires_Hours_other, "\(value)") + return String(format: self._Watch_LastSeen_HoursAgo_other, "\(value)") } } private let _Notification_GameScoreExtended_zero: String @@ -4515,136 +3724,92 @@ public final class PresentationStrings { return String(format: self._Notification_GameScoreExtended_other, "\(value)") } } - private let _MuteExpires_Minutes_zero: String - private let _MuteExpires_Minutes_one: String - private let _MuteExpires_Minutes_two: String - private let _MuteExpires_Minutes_few: String - private let _MuteExpires_Minutes_many: String - private let _MuteExpires_Minutes_other: String - public func MuteExpires_Minutes(_ value: Int32) -> String { + private let _InviteText_ContactsCountText_zero: String + private let _InviteText_ContactsCountText_one: String + private let _InviteText_ContactsCountText_two: String + private let _InviteText_ContactsCountText_few: String + private let _InviteText_ContactsCountText_many: String + private let _InviteText_ContactsCountText_other: String + public func InviteText_ContactsCountText(_ value: Int32) -> String { switch presentationStringsPluralizationForm(self.lc, value) { case .zero: - return String(format: self._MuteExpires_Minutes_zero, "\(value)") + return String(format: self._InviteText_ContactsCountText_zero, "\(value)") case .one: - return String(format: self._MuteExpires_Minutes_one, "\(value)") + return String(format: self._InviteText_ContactsCountText_one, "\(value)") case .two: - return String(format: self._MuteExpires_Minutes_two, "\(value)") + return String(format: self._InviteText_ContactsCountText_two, "\(value)") case .few: - return String(format: self._MuteExpires_Minutes_few, "\(value)") + return String(format: self._InviteText_ContactsCountText_few, "\(value)") case .many: - return String(format: self._MuteExpires_Minutes_many, "\(value)") + return String(format: self._InviteText_ContactsCountText_many, "\(value)") case .other: - return String(format: self._MuteExpires_Minutes_other, "\(value)") + return String(format: self._InviteText_ContactsCountText_other, "\(value)") } } - private let _AttachmentMenu_SendItem_zero: String - private let _AttachmentMenu_SendItem_one: String - private let _AttachmentMenu_SendItem_two: String - private let _AttachmentMenu_SendItem_few: String - private let _AttachmentMenu_SendItem_many: String - private let _AttachmentMenu_SendItem_other: String - public func AttachmentMenu_SendItem(_ value: Int32) -> String { + private let _StickerPack_RemoveMaskCount_zero: String + private let _StickerPack_RemoveMaskCount_one: String + private let _StickerPack_RemoveMaskCount_two: String + private let _StickerPack_RemoveMaskCount_few: String + private let _StickerPack_RemoveMaskCount_many: String + private let _StickerPack_RemoveMaskCount_other: String + public func StickerPack_RemoveMaskCount(_ value: Int32) -> String { switch presentationStringsPluralizationForm(self.lc, value) { case .zero: - return String(format: self._AttachmentMenu_SendItem_zero, "\(value)") + return String(format: self._StickerPack_RemoveMaskCount_zero, "\(value)") case .one: - return String(format: self._AttachmentMenu_SendItem_one, "\(value)") + return String(format: self._StickerPack_RemoveMaskCount_one, "\(value)") case .two: - return String(format: self._AttachmentMenu_SendItem_two, "\(value)") + return String(format: self._StickerPack_RemoveMaskCount_two, "\(value)") case .few: - return String(format: self._AttachmentMenu_SendItem_few, "\(value)") + return String(format: self._StickerPack_RemoveMaskCount_few, "\(value)") case .many: - return String(format: self._AttachmentMenu_SendItem_many, "\(value)") + return String(format: self._StickerPack_RemoveMaskCount_many, "\(value)") case .other: - return String(format: self._AttachmentMenu_SendItem_other, "\(value)") + return String(format: self._StickerPack_RemoveMaskCount_other, "\(value)") } } - private let _ForwardedAudios_zero: String - private let _ForwardedAudios_one: String - private let _ForwardedAudios_two: String - private let _ForwardedAudios_few: String - private let _ForwardedAudios_many: String - private let _ForwardedAudios_other: String - public func ForwardedAudios(_ value: Int32) -> String { + private let _Map_ETAMinutes_zero: String + private let _Map_ETAMinutes_one: String + private let _Map_ETAMinutes_two: String + private let _Map_ETAMinutes_few: String + private let _Map_ETAMinutes_many: String + private let _Map_ETAMinutes_other: String + public func Map_ETAMinutes(_ value: Int32) -> String { switch presentationStringsPluralizationForm(self.lc, value) { case .zero: - return String(format: self._ForwardedAudios_zero, "\(value)") + return String(format: self._Map_ETAMinutes_zero, "\(value)") case .one: - return String(format: self._ForwardedAudios_one, "\(value)") + return String(format: self._Map_ETAMinutes_one, "\(value)") case .two: - return String(format: self._ForwardedAudios_two, "\(value)") + return String(format: self._Map_ETAMinutes_two, "\(value)") case .few: - return String(format: self._ForwardedAudios_few, "\(value)") + return String(format: self._Map_ETAMinutes_few, "\(value)") case .many: - return String(format: self._ForwardedAudios_many, "\(value)") + return String(format: self._Map_ETAMinutes_many, "\(value)") case .other: - return String(format: self._ForwardedAudios_other, "\(value)") + return String(format: self._Map_ETAMinutes_other, "\(value)") } } - private let _SharedMedia_File_zero: String - private let _SharedMedia_File_one: String - private let _SharedMedia_File_two: String - private let _SharedMedia_File_few: String - private let _SharedMedia_File_many: String - private let _SharedMedia_File_other: String - public func SharedMedia_File(_ value: Int32) -> String { + private let _MessageTimer_Months_zero: String + private let _MessageTimer_Months_one: String + private let _MessageTimer_Months_two: String + private let _MessageTimer_Months_few: String + private let _MessageTimer_Months_many: String + private let _MessageTimer_Months_other: String + public func MessageTimer_Months(_ value: Int32) -> String { switch presentationStringsPluralizationForm(self.lc, value) { case .zero: - return String(format: self._SharedMedia_File_zero, "\(value)") + return String(format: self._MessageTimer_Months_zero, "\(value)") case .one: - return String(format: self._SharedMedia_File_one, "\(value)") + return String(format: self._MessageTimer_Months_one, "\(value)") case .two: - return String(format: self._SharedMedia_File_two, "\(value)") + return String(format: self._MessageTimer_Months_two, "\(value)") case .few: - return String(format: self._SharedMedia_File_few, "\(value)") + return String(format: self._MessageTimer_Months_few, "\(value)") case .many: - return String(format: self._SharedMedia_File_many, "\(value)") + return String(format: self._MessageTimer_Months_many, "\(value)") case .other: - return String(format: self._SharedMedia_File_other, "\(value)") - } - } - private let _Call_ShortMinutes_zero: String - private let _Call_ShortMinutes_one: String - private let _Call_ShortMinutes_two: String - private let _Call_ShortMinutes_few: String - private let _Call_ShortMinutes_many: String - private let _Call_ShortMinutes_other: String - public func Call_ShortMinutes(_ value: Int32) -> String { - switch presentationStringsPluralizationForm(self.lc, value) { - case .zero: - return String(format: self._Call_ShortMinutes_zero, "\(value)") - case .one: - return String(format: self._Call_ShortMinutes_one, "\(value)") - case .two: - return String(format: self._Call_ShortMinutes_two, "\(value)") - case .few: - return String(format: self._Call_ShortMinutes_few, "\(value)") - case .many: - return String(format: self._Call_ShortMinutes_many, "\(value)") - case .other: - return String(format: self._Call_ShortMinutes_other, "\(value)") - } - } - private let _Call_Minutes_zero: String - private let _Call_Minutes_one: String - private let _Call_Minutes_two: String - private let _Call_Minutes_few: String - private let _Call_Minutes_many: String - private let _Call_Minutes_other: String - public func Call_Minutes(_ value: Int32) -> String { - switch presentationStringsPluralizationForm(self.lc, value) { - case .zero: - return String(format: self._Call_Minutes_zero, "\(value)") - case .one: - return String(format: self._Call_Minutes_one, "\(value)") - case .two: - return String(format: self._Call_Minutes_two, "\(value)") - case .few: - return String(format: self._Call_Minutes_few, "\(value)") - case .many: - return String(format: self._Call_Minutes_many, "\(value)") - case .other: - return String(format: self._Call_Minutes_other, "\(value)") + return String(format: self._MessageTimer_Months_other, "\(value)") } } private let _Call_ShortSeconds_zero: String @@ -4669,48 +3834,356 @@ public final class PresentationStrings { return String(format: self._Call_ShortSeconds_other, "\(value)") } } - private let _Invitation_Members_zero: String - private let _Invitation_Members_one: String - private let _Invitation_Members_two: String - private let _Invitation_Members_few: String - private let _Invitation_Members_many: String - private let _Invitation_Members_other: String - public func Invitation_Members(_ value: Int32) -> String { + private let _Media_ShareVideo_zero: String + private let _Media_ShareVideo_one: String + private let _Media_ShareVideo_two: String + private let _Media_ShareVideo_few: String + private let _Media_ShareVideo_many: String + private let _Media_ShareVideo_other: String + public func Media_ShareVideo(_ value: Int32) -> String { switch presentationStringsPluralizationForm(self.lc, value) { case .zero: - return String(format: self._Invitation_Members_zero, "\(value)") + return String(format: self._Media_ShareVideo_zero, "\(value)") case .one: - return String(format: self._Invitation_Members_one, "\(value)") + return String(format: self._Media_ShareVideo_one, "\(value)") case .two: - return String(format: self._Invitation_Members_two, "\(value)") + return String(format: self._Media_ShareVideo_two, "\(value)") case .few: - return String(format: self._Invitation_Members_few, "\(value)") + return String(format: self._Media_ShareVideo_few, "\(value)") case .many: - return String(format: self._Invitation_Members_many, "\(value)") + return String(format: self._Media_ShareVideo_many, "\(value)") case .other: - return String(format: self._Invitation_Members_other, "\(value)") + return String(format: self._Media_ShareVideo_other, "\(value)") } } - private let _AttachmentMenu_SendGif_zero: String - private let _AttachmentMenu_SendGif_one: String - private let _AttachmentMenu_SendGif_two: String - private let _AttachmentMenu_SendGif_few: String - private let _AttachmentMenu_SendGif_many: String - private let _AttachmentMenu_SendGif_other: String - public func AttachmentMenu_SendGif(_ value: Int32) -> String { + private let _StickerPack_AddStickerCount_zero: String + private let _StickerPack_AddStickerCount_one: String + private let _StickerPack_AddStickerCount_two: String + private let _StickerPack_AddStickerCount_few: String + private let _StickerPack_AddStickerCount_many: String + private let _StickerPack_AddStickerCount_other: String + public func StickerPack_AddStickerCount(_ value: Int32) -> String { switch presentationStringsPluralizationForm(self.lc, value) { case .zero: - return String(format: self._AttachmentMenu_SendGif_zero, "\(value)") + return String(format: self._StickerPack_AddStickerCount_zero, "\(value)") case .one: - return String(format: self._AttachmentMenu_SendGif_one, "\(value)") + return String(format: self._StickerPack_AddStickerCount_one, "\(value)") case .two: - return String(format: self._AttachmentMenu_SendGif_two, "\(value)") + return String(format: self._StickerPack_AddStickerCount_two, "\(value)") case .few: - return String(format: self._AttachmentMenu_SendGif_few, "\(value)") + return String(format: self._StickerPack_AddStickerCount_few, "\(value)") case .many: - return String(format: self._AttachmentMenu_SendGif_many, "\(value)") + return String(format: self._StickerPack_AddStickerCount_many, "\(value)") case .other: - return String(format: self._AttachmentMenu_SendGif_other, "\(value)") + return String(format: self._StickerPack_AddStickerCount_other, "\(value)") + } + } + private let _SharedMedia_File_zero: String + private let _SharedMedia_File_one: String + private let _SharedMedia_File_two: String + private let _SharedMedia_File_few: String + private let _SharedMedia_File_many: String + private let _SharedMedia_File_other: String + public func SharedMedia_File(_ value: Int32) -> String { + switch presentationStringsPluralizationForm(self.lc, value) { + case .zero: + return String(format: self._SharedMedia_File_zero, "\(value)") + case .one: + return String(format: self._SharedMedia_File_one, "\(value)") + case .two: + return String(format: self._SharedMedia_File_two, "\(value)") + case .few: + return String(format: self._SharedMedia_File_few, "\(value)") + case .many: + return String(format: self._SharedMedia_File_many, "\(value)") + case .other: + return String(format: self._SharedMedia_File_other, "\(value)") + } + } + private let _PasscodeSettings_FailedAttempts_zero: String + private let _PasscodeSettings_FailedAttempts_one: String + private let _PasscodeSettings_FailedAttempts_two: String + private let _PasscodeSettings_FailedAttempts_few: String + private let _PasscodeSettings_FailedAttempts_many: String + private let _PasscodeSettings_FailedAttempts_other: String + public func PasscodeSettings_FailedAttempts(_ value: Int32) -> String { + switch presentationStringsPluralizationForm(self.lc, value) { + case .zero: + return String(format: self._PasscodeSettings_FailedAttempts_zero, "\(value)") + case .one: + return String(format: self._PasscodeSettings_FailedAttempts_one, "\(value)") + case .two: + return String(format: self._PasscodeSettings_FailedAttempts_two, "\(value)") + case .few: + return String(format: self._PasscodeSettings_FailedAttempts_few, "\(value)") + case .many: + return String(format: self._PasscodeSettings_FailedAttempts_many, "\(value)") + case .other: + return String(format: self._PasscodeSettings_FailedAttempts_other, "\(value)") + } + } + private let _Call_Minutes_zero: String + private let _Call_Minutes_one: String + private let _Call_Minutes_two: String + private let _Call_Minutes_few: String + private let _Call_Minutes_many: String + private let _Call_Minutes_other: String + public func Call_Minutes(_ value: Int32) -> String { + switch presentationStringsPluralizationForm(self.lc, value) { + case .zero: + return String(format: self._Call_Minutes_zero, "\(value)") + case .one: + return String(format: self._Call_Minutes_one, "\(value)") + case .two: + return String(format: self._Call_Minutes_two, "\(value)") + case .few: + return String(format: self._Call_Minutes_few, "\(value)") + case .many: + return String(format: self._Call_Minutes_many, "\(value)") + case .other: + return String(format: self._Call_Minutes_other, "\(value)") + } + } + private let _ForwardedFiles_zero: String + private let _ForwardedFiles_one: String + private let _ForwardedFiles_two: String + private let _ForwardedFiles_few: String + private let _ForwardedFiles_many: String + private let _ForwardedFiles_other: String + public func ForwardedFiles(_ value: Int32) -> String { + switch presentationStringsPluralizationForm(self.lc, value) { + case .zero: + return String(format: self._ForwardedFiles_zero, "\(value)") + case .one: + return String(format: self._ForwardedFiles_one, "\(value)") + case .two: + return String(format: self._ForwardedFiles_two, "\(value)") + case .few: + return String(format: self._ForwardedFiles_few, "\(value)") + case .many: + return String(format: self._ForwardedFiles_many, "\(value)") + case .other: + return String(format: self._ForwardedFiles_other, "\(value)") + } + } + private let _ForwardedLocations_zero: String + private let _ForwardedLocations_one: String + private let _ForwardedLocations_two: String + private let _ForwardedLocations_few: String + private let _ForwardedLocations_many: String + private let _ForwardedLocations_other: String + public func ForwardedLocations(_ value: Int32) -> String { + switch presentationStringsPluralizationForm(self.lc, value) { + case .zero: + return String(format: self._ForwardedLocations_zero, "\(value)") + case .one: + return String(format: self._ForwardedLocations_one, "\(value)") + case .two: + return String(format: self._ForwardedLocations_two, "\(value)") + case .few: + return String(format: self._ForwardedLocations_few, "\(value)") + case .many: + return String(format: self._ForwardedLocations_many, "\(value)") + case .other: + return String(format: self._ForwardedLocations_other, "\(value)") + } + } + private let _MuteExpires_Days_zero: String + private let _MuteExpires_Days_one: String + private let _MuteExpires_Days_two: String + private let _MuteExpires_Days_few: String + private let _MuteExpires_Days_many: String + private let _MuteExpires_Days_other: String + public func MuteExpires_Days(_ value: Int32) -> String { + switch presentationStringsPluralizationForm(self.lc, value) { + case .zero: + return String(format: self._MuteExpires_Days_zero, "\(value)") + case .one: + return String(format: self._MuteExpires_Days_one, "\(value)") + case .two: + return String(format: self._MuteExpires_Days_two, "\(value)") + case .few: + return String(format: self._MuteExpires_Days_few, "\(value)") + case .many: + return String(format: self._MuteExpires_Days_many, "\(value)") + case .other: + return String(format: self._MuteExpires_Days_other, "\(value)") + } + } + private let _Media_SharePhoto_zero: String + private let _Media_SharePhoto_one: String + private let _Media_SharePhoto_two: String + private let _Media_SharePhoto_few: String + private let _Media_SharePhoto_many: String + private let _Media_SharePhoto_other: String + public func Media_SharePhoto(_ value: Int32) -> String { + switch presentationStringsPluralizationForm(self.lc, value) { + case .zero: + return String(format: self._Media_SharePhoto_zero, "\(value)") + case .one: + return String(format: self._Media_SharePhoto_one, "\(value)") + case .two: + return String(format: self._Media_SharePhoto_two, "\(value)") + case .few: + return String(format: self._Media_SharePhoto_few, "\(value)") + case .many: + return String(format: self._Media_SharePhoto_many, "\(value)") + case .other: + return String(format: self._Media_SharePhoto_other, "\(value)") + } + } + private let _ForwardedPhotos_zero: String + private let _ForwardedPhotos_one: String + private let _ForwardedPhotos_two: String + private let _ForwardedPhotos_few: String + private let _ForwardedPhotos_many: String + private let _ForwardedPhotos_other: String + public func ForwardedPhotos(_ value: Int32) -> String { + switch presentationStringsPluralizationForm(self.lc, value) { + case .zero: + return String(format: self._ForwardedPhotos_zero, "\(value)") + case .one: + return String(format: self._ForwardedPhotos_one, "\(value)") + case .two: + return String(format: self._ForwardedPhotos_two, "\(value)") + case .few: + return String(format: self._ForwardedPhotos_few, "\(value)") + case .many: + return String(format: self._ForwardedPhotos_many, "\(value)") + case .other: + return String(format: self._ForwardedPhotos_other, "\(value)") + } + } + private let _MessageTimer_Years_zero: String + private let _MessageTimer_Years_one: String + private let _MessageTimer_Years_two: String + private let _MessageTimer_Years_few: String + private let _MessageTimer_Years_many: String + private let _MessageTimer_Years_other: String + public func MessageTimer_Years(_ value: Int32) -> String { + switch presentationStringsPluralizationForm(self.lc, value) { + case .zero: + return String(format: self._MessageTimer_Years_zero, "\(value)") + case .one: + return String(format: self._MessageTimer_Years_one, "\(value)") + case .two: + return String(format: self._MessageTimer_Years_two, "\(value)") + case .few: + return String(format: self._MessageTimer_Years_few, "\(value)") + case .many: + return String(format: self._MessageTimer_Years_many, "\(value)") + case .other: + return String(format: self._MessageTimer_Years_other, "\(value)") + } + } + private let _MuteExpires_Minutes_zero: String + private let _MuteExpires_Minutes_one: String + private let _MuteExpires_Minutes_two: String + private let _MuteExpires_Minutes_few: String + private let _MuteExpires_Minutes_many: String + private let _MuteExpires_Minutes_other: String + public func MuteExpires_Minutes(_ value: Int32) -> String { + switch presentationStringsPluralizationForm(self.lc, value) { + case .zero: + return String(format: self._MuteExpires_Minutes_zero, "\(value)") + case .one: + return String(format: self._MuteExpires_Minutes_one, "\(value)") + case .two: + return String(format: self._MuteExpires_Minutes_two, "\(value)") + case .few: + return String(format: self._MuteExpires_Minutes_few, "\(value)") + case .many: + return String(format: self._MuteExpires_Minutes_many, "\(value)") + case .other: + return String(format: self._MuteExpires_Minutes_other, "\(value)") + } + } + private let _MessageTimer_Seconds_zero: String + private let _MessageTimer_Seconds_one: String + private let _MessageTimer_Seconds_two: String + private let _MessageTimer_Seconds_few: String + private let _MessageTimer_Seconds_many: String + private let _MessageTimer_Seconds_other: String + public func MessageTimer_Seconds(_ value: Int32) -> String { + switch presentationStringsPluralizationForm(self.lc, value) { + case .zero: + return String(format: self._MessageTimer_Seconds_zero, "\(value)") + case .one: + return String(format: self._MessageTimer_Seconds_one, "\(value)") + case .two: + return String(format: self._MessageTimer_Seconds_two, "\(value)") + case .few: + return String(format: self._MessageTimer_Seconds_few, "\(value)") + case .many: + return String(format: self._MessageTimer_Seconds_many, "\(value)") + case .other: + return String(format: self._MessageTimer_Seconds_other, "\(value)") + } + } + private let _ForwardedContacts_zero: String + private let _ForwardedContacts_one: String + private let _ForwardedContacts_two: String + private let _ForwardedContacts_few: String + private let _ForwardedContacts_many: String + private let _ForwardedContacts_other: String + public func ForwardedContacts(_ value: Int32) -> String { + switch presentationStringsPluralizationForm(self.lc, value) { + case .zero: + return String(format: self._ForwardedContacts_zero, "\(value)") + case .one: + return String(format: self._ForwardedContacts_one, "\(value)") + case .two: + return String(format: self._ForwardedContacts_two, "\(value)") + case .few: + return String(format: self._ForwardedContacts_few, "\(value)") + case .many: + return String(format: self._ForwardedContacts_many, "\(value)") + case .other: + return String(format: self._ForwardedContacts_other, "\(value)") + } + } + private let _GroupInfo_ParticipantCount_zero: String + private let _GroupInfo_ParticipantCount_one: String + private let _GroupInfo_ParticipantCount_two: String + private let _GroupInfo_ParticipantCount_few: String + private let _GroupInfo_ParticipantCount_many: String + private let _GroupInfo_ParticipantCount_other: String + public func GroupInfo_ParticipantCount(_ value: Int32) -> String { + switch presentationStringsPluralizationForm(self.lc, value) { + case .zero: + return String(format: self._GroupInfo_ParticipantCount_zero, "\(value)") + case .one: + return String(format: self._GroupInfo_ParticipantCount_one, "\(value)") + case .two: + return String(format: self._GroupInfo_ParticipantCount_two, "\(value)") + case .few: + return String(format: self._GroupInfo_ParticipantCount_few, "\(value)") + case .many: + return String(format: self._GroupInfo_ParticipantCount_many, "\(value)") + case .other: + return String(format: self._GroupInfo_ParticipantCount_other, "\(value)") + } + } + private let _MessageTimer_Minutes_zero: String + private let _MessageTimer_Minutes_one: String + private let _MessageTimer_Minutes_two: String + private let _MessageTimer_Minutes_few: String + private let _MessageTimer_Minutes_many: String + private let _MessageTimer_Minutes_other: String + public func MessageTimer_Minutes(_ value: Int32) -> String { + switch presentationStringsPluralizationForm(self.lc, value) { + case .zero: + return String(format: self._MessageTimer_Minutes_zero, "\(value)") + case .one: + return String(format: self._MessageTimer_Minutes_one, "\(value)") + case .two: + return String(format: self._MessageTimer_Minutes_two, "\(value)") + case .few: + return String(format: self._MessageTimer_Minutes_few, "\(value)") + case .many: + return String(format: self._MessageTimer_Minutes_many, "\(value)") + case .other: + return String(format: self._MessageTimer_Minutes_other, "\(value)") } } private let _Conversation_LiveLocationMembersCount_zero: String @@ -4757,114 +4230,92 @@ public final class PresentationStrings { return String(format: self._Watch_LastSeen_MinutesAgo_other, "\(value)") } } - private let _MessageTimer_Months_zero: String - private let _MessageTimer_Months_one: String - private let _MessageTimer_Months_two: String - private let _MessageTimer_Months_few: String - private let _MessageTimer_Months_many: String - private let _MessageTimer_Months_other: String - public func MessageTimer_Months(_ value: Int32) -> String { + private let _Notifications_ExceptionMuteExpires_Minutes_zero: String + private let _Notifications_ExceptionMuteExpires_Minutes_one: String + private let _Notifications_ExceptionMuteExpires_Minutes_two: String + private let _Notifications_ExceptionMuteExpires_Minutes_few: String + private let _Notifications_ExceptionMuteExpires_Minutes_many: String + private let _Notifications_ExceptionMuteExpires_Minutes_other: String + public func Notifications_ExceptionMuteExpires_Minutes(_ value: Int32) -> String { switch presentationStringsPluralizationForm(self.lc, value) { case .zero: - return String(format: self._MessageTimer_Months_zero, "\(value)") + return String(format: self._Notifications_ExceptionMuteExpires_Minutes_zero, "\(value)") case .one: - return String(format: self._MessageTimer_Months_one, "\(value)") + return String(format: self._Notifications_ExceptionMuteExpires_Minutes_one, "\(value)") case .two: - return String(format: self._MessageTimer_Months_two, "\(value)") + return String(format: self._Notifications_ExceptionMuteExpires_Minutes_two, "\(value)") case .few: - return String(format: self._MessageTimer_Months_few, "\(value)") + return String(format: self._Notifications_ExceptionMuteExpires_Minutes_few, "\(value)") case .many: - return String(format: self._MessageTimer_Months_many, "\(value)") + return String(format: self._Notifications_ExceptionMuteExpires_Minutes_many, "\(value)") case .other: - return String(format: self._MessageTimer_Months_other, "\(value)") + return String(format: self._Notifications_ExceptionMuteExpires_Minutes_other, "\(value)") } } - private let _ForwardedMessages_zero: String - private let _ForwardedMessages_one: String - private let _ForwardedMessages_two: String - private let _ForwardedMessages_few: String - private let _ForwardedMessages_many: String - private let _ForwardedMessages_other: String - public func ForwardedMessages(_ value: Int32) -> String { + private let _Watch_UserInfo_Mute_zero: String + private let _Watch_UserInfo_Mute_one: String + private let _Watch_UserInfo_Mute_two: String + private let _Watch_UserInfo_Mute_few: String + private let _Watch_UserInfo_Mute_many: String + private let _Watch_UserInfo_Mute_other: String + public func Watch_UserInfo_Mute(_ value: Int32) -> String { switch presentationStringsPluralizationForm(self.lc, value) { case .zero: - return String(format: self._ForwardedMessages_zero, "\(value)") + return String(format: self._Watch_UserInfo_Mute_zero, "\(value)") case .one: - return String(format: self._ForwardedMessages_one, "\(value)") + return String(format: self._Watch_UserInfo_Mute_one, "\(value)") case .two: - return String(format: self._ForwardedMessages_two, "\(value)") + return String(format: self._Watch_UserInfo_Mute_two, "\(value)") case .few: - return String(format: self._ForwardedMessages_few, "\(value)") + return String(format: self._Watch_UserInfo_Mute_few, "\(value)") case .many: - return String(format: self._ForwardedMessages_many, "\(value)") + return String(format: self._Watch_UserInfo_Mute_many, "\(value)") case .other: - return String(format: self._ForwardedMessages_other, "\(value)") + return String(format: self._Watch_UserInfo_Mute_other, "\(value)") } } - private let _ForwardedVideos_zero: String - private let _ForwardedVideos_one: String - private let _ForwardedVideos_two: String - private let _ForwardedVideos_few: String - private let _ForwardedVideos_many: String - private let _ForwardedVideos_other: String - public func ForwardedVideos(_ value: Int32) -> String { + private let _MessageTimer_ShortWeeks_zero: String + private let _MessageTimer_ShortWeeks_one: String + private let _MessageTimer_ShortWeeks_two: String + private let _MessageTimer_ShortWeeks_few: String + private let _MessageTimer_ShortWeeks_many: String + private let _MessageTimer_ShortWeeks_other: String + public func MessageTimer_ShortWeeks(_ value: Int32) -> String { switch presentationStringsPluralizationForm(self.lc, value) { case .zero: - return String(format: self._ForwardedVideos_zero, "\(value)") + return String(format: self._MessageTimer_ShortWeeks_zero, "\(value)") case .one: - return String(format: self._ForwardedVideos_one, "\(value)") + return String(format: self._MessageTimer_ShortWeeks_one, "\(value)") case .two: - return String(format: self._ForwardedVideos_two, "\(value)") + return String(format: self._MessageTimer_ShortWeeks_two, "\(value)") case .few: - return String(format: self._ForwardedVideos_few, "\(value)") + return String(format: self._MessageTimer_ShortWeeks_few, "\(value)") case .many: - return String(format: self._ForwardedVideos_many, "\(value)") + return String(format: self._MessageTimer_ShortWeeks_many, "\(value)") case .other: - return String(format: self._ForwardedVideos_other, "\(value)") + return String(format: self._MessageTimer_ShortWeeks_other, "\(value)") } } - private let _MessageTimer_Minutes_zero: String - private let _MessageTimer_Minutes_one: String - private let _MessageTimer_Minutes_two: String - private let _MessageTimer_Minutes_few: String - private let _MessageTimer_Minutes_many: String - private let _MessageTimer_Minutes_other: String - public func MessageTimer_Minutes(_ value: Int32) -> String { + private let _LastSeen_HoursAgo_zero: String + private let _LastSeen_HoursAgo_one: String + private let _LastSeen_HoursAgo_two: String + private let _LastSeen_HoursAgo_few: String + private let _LastSeen_HoursAgo_many: String + private let _LastSeen_HoursAgo_other: String + public func LastSeen_HoursAgo(_ value: Int32) -> String { switch presentationStringsPluralizationForm(self.lc, value) { case .zero: - return String(format: self._MessageTimer_Minutes_zero, "\(value)") + return String(format: self._LastSeen_HoursAgo_zero, "\(value)") case .one: - return String(format: self._MessageTimer_Minutes_one, "\(value)") + return String(format: self._LastSeen_HoursAgo_one, "\(value)") case .two: - return String(format: self._MessageTimer_Minutes_two, "\(value)") + return String(format: self._LastSeen_HoursAgo_two, "\(value)") case .few: - return String(format: self._MessageTimer_Minutes_few, "\(value)") + return String(format: self._LastSeen_HoursAgo_few, "\(value)") case .many: - return String(format: self._MessageTimer_Minutes_many, "\(value)") + return String(format: self._LastSeen_HoursAgo_many, "\(value)") case .other: - return String(format: self._MessageTimer_Minutes_other, "\(value)") - } - } - private let _Conversation_StatusOnline_zero: String - private let _Conversation_StatusOnline_one: String - private let _Conversation_StatusOnline_two: String - private let _Conversation_StatusOnline_few: String - private let _Conversation_StatusOnline_many: String - private let _Conversation_StatusOnline_other: String - public func Conversation_StatusOnline(_ value: Int32) -> String { - switch presentationStringsPluralizationForm(self.lc, value) { - case .zero: - return String(format: self._Conversation_StatusOnline_zero, "\(value)") - case .one: - return String(format: self._Conversation_StatusOnline_one, "\(value)") - case .two: - return String(format: self._Conversation_StatusOnline_two, "\(value)") - case .few: - return String(format: self._Conversation_StatusOnline_few, "\(value)") - case .many: - return String(format: self._Conversation_StatusOnline_many, "\(value)") - case .other: - return String(format: self._Conversation_StatusOnline_other, "\(value)") + return String(format: self._LastSeen_HoursAgo_other, "\(value)") } } private let _MessageTimer_ShortMinutes_zero: String @@ -4889,576 +4340,92 @@ public final class PresentationStrings { return String(format: self._MessageTimer_ShortMinutes_other, "\(value)") } } - private let _QuickSend_Photos_zero: String - private let _QuickSend_Photos_one: String - private let _QuickSend_Photos_two: String - private let _QuickSend_Photos_few: String - private let _QuickSend_Photos_many: String - private let _QuickSend_Photos_other: String - public func QuickSend_Photos(_ value: Int32) -> String { + private let _AttachmentMenu_SendItem_zero: String + private let _AttachmentMenu_SendItem_one: String + private let _AttachmentMenu_SendItem_two: String + private let _AttachmentMenu_SendItem_few: String + private let _AttachmentMenu_SendItem_many: String + private let _AttachmentMenu_SendItem_other: String + public func AttachmentMenu_SendItem(_ value: Int32) -> String { switch presentationStringsPluralizationForm(self.lc, value) { case .zero: - return String(format: self._QuickSend_Photos_zero, "\(value)") + return String(format: self._AttachmentMenu_SendItem_zero, "\(value)") case .one: - return String(format: self._QuickSend_Photos_one, "\(value)") + return String(format: self._AttachmentMenu_SendItem_one, "\(value)") case .two: - return String(format: self._QuickSend_Photos_two, "\(value)") + return String(format: self._AttachmentMenu_SendItem_two, "\(value)") case .few: - return String(format: self._QuickSend_Photos_few, "\(value)") + return String(format: self._AttachmentMenu_SendItem_few, "\(value)") case .many: - return String(format: self._QuickSend_Photos_many, "\(value)") + return String(format: self._AttachmentMenu_SendItem_many, "\(value)") case .other: - return String(format: self._QuickSend_Photos_other, "\(value)") + return String(format: self._AttachmentMenu_SendItem_other, "\(value)") } } - private let _ServiceMessage_GameScoreExtended_zero: String - private let _ServiceMessage_GameScoreExtended_one: String - private let _ServiceMessage_GameScoreExtended_two: String - private let _ServiceMessage_GameScoreExtended_few: String - private let _ServiceMessage_GameScoreExtended_many: String - private let _ServiceMessage_GameScoreExtended_other: String - public func ServiceMessage_GameScoreExtended(_ value: Int32) -> String { + private let _ForwardedAudios_zero: String + private let _ForwardedAudios_one: String + private let _ForwardedAudios_two: String + private let _ForwardedAudios_few: String + private let _ForwardedAudios_many: String + private let _ForwardedAudios_other: String + public func ForwardedAudios(_ value: Int32) -> String { switch presentationStringsPluralizationForm(self.lc, value) { case .zero: - return String(format: self._ServiceMessage_GameScoreExtended_zero, "\(value)") + return String(format: self._ForwardedAudios_zero, "\(value)") case .one: - return String(format: self._ServiceMessage_GameScoreExtended_one, "\(value)") + return String(format: self._ForwardedAudios_one, "\(value)") case .two: - return String(format: self._ServiceMessage_GameScoreExtended_two, "\(value)") + return String(format: self._ForwardedAudios_two, "\(value)") case .few: - return String(format: self._ServiceMessage_GameScoreExtended_few, "\(value)") + return String(format: self._ForwardedAudios_few, "\(value)") case .many: - return String(format: self._ServiceMessage_GameScoreExtended_many, "\(value)") + return String(format: self._ForwardedAudios_many, "\(value)") case .other: - return String(format: self._ServiceMessage_GameScoreExtended_other, "\(value)") + return String(format: self._ForwardedAudios_other, "\(value)") } } - private let _SharedMedia_Generic_zero: String - private let _SharedMedia_Generic_one: String - private let _SharedMedia_Generic_two: String - private let _SharedMedia_Generic_few: String - private let _SharedMedia_Generic_many: String - private let _SharedMedia_Generic_other: String - public func SharedMedia_Generic(_ value: Int32) -> String { + private let _Notification_GameScoreSelfExtended_zero: String + private let _Notification_GameScoreSelfExtended_one: String + private let _Notification_GameScoreSelfExtended_two: String + private let _Notification_GameScoreSelfExtended_few: String + private let _Notification_GameScoreSelfExtended_many: String + private let _Notification_GameScoreSelfExtended_other: String + public func Notification_GameScoreSelfExtended(_ value: Int32) -> String { switch presentationStringsPluralizationForm(self.lc, value) { case .zero: - return String(format: self._SharedMedia_Generic_zero, "\(value)") + return String(format: self._Notification_GameScoreSelfExtended_zero, "\(value)") case .one: - return String(format: self._SharedMedia_Generic_one, "\(value)") + return String(format: self._Notification_GameScoreSelfExtended_one, "\(value)") case .two: - return String(format: self._SharedMedia_Generic_two, "\(value)") + return String(format: self._Notification_GameScoreSelfExtended_two, "\(value)") case .few: - return String(format: self._SharedMedia_Generic_few, "\(value)") + return String(format: self._Notification_GameScoreSelfExtended_few, "\(value)") case .many: - return String(format: self._SharedMedia_Generic_many, "\(value)") + return String(format: self._Notification_GameScoreSelfExtended_many, "\(value)") case .other: - return String(format: self._SharedMedia_Generic_other, "\(value)") + return String(format: self._Notification_GameScoreSelfExtended_other, "\(value)") } } - private let _SharedMedia_Video_zero: String - private let _SharedMedia_Video_one: String - private let _SharedMedia_Video_two: String - private let _SharedMedia_Video_few: String - private let _SharedMedia_Video_many: String - private let _SharedMedia_Video_other: String - public func SharedMedia_Video(_ value: Int32) -> String { + private let _Call_ShortMinutes_zero: String + private let _Call_ShortMinutes_one: String + private let _Call_ShortMinutes_two: String + private let _Call_ShortMinutes_few: String + private let _Call_ShortMinutes_many: String + private let _Call_ShortMinutes_other: String + public func Call_ShortMinutes(_ value: Int32) -> String { switch presentationStringsPluralizationForm(self.lc, value) { case .zero: - return String(format: self._SharedMedia_Video_zero, "\(value)") + return String(format: self._Call_ShortMinutes_zero, "\(value)") case .one: - return String(format: self._SharedMedia_Video_one, "\(value)") + return String(format: self._Call_ShortMinutes_one, "\(value)") case .two: - return String(format: self._SharedMedia_Video_two, "\(value)") + return String(format: self._Call_ShortMinutes_two, "\(value)") case .few: - return String(format: self._SharedMedia_Video_few, "\(value)") + return String(format: self._Call_ShortMinutes_few, "\(value)") case .many: - return String(format: self._SharedMedia_Video_many, "\(value)") + return String(format: self._Call_ShortMinutes_many, "\(value)") case .other: - return String(format: self._SharedMedia_Video_other, "\(value)") - } - } - private let _ServiceMessage_GameScoreSelfSimple_zero: String - private let _ServiceMessage_GameScoreSelfSimple_one: String - private let _ServiceMessage_GameScoreSelfSimple_two: String - private let _ServiceMessage_GameScoreSelfSimple_few: String - private let _ServiceMessage_GameScoreSelfSimple_many: String - private let _ServiceMessage_GameScoreSelfSimple_other: String - public func ServiceMessage_GameScoreSelfSimple(_ value: Int32) -> String { - switch presentationStringsPluralizationForm(self.lc, value) { - case .zero: - return String(format: self._ServiceMessage_GameScoreSelfSimple_zero, "\(value)") - case .one: - return String(format: self._ServiceMessage_GameScoreSelfSimple_one, "\(value)") - case .two: - return String(format: self._ServiceMessage_GameScoreSelfSimple_two, "\(value)") - case .few: - return String(format: self._ServiceMessage_GameScoreSelfSimple_few, "\(value)") - case .many: - return String(format: self._ServiceMessage_GameScoreSelfSimple_many, "\(value)") - case .other: - return String(format: self._ServiceMessage_GameScoreSelfSimple_other, "\(value)") - } - } - private let _Media_ShareVideo_zero: String - private let _Media_ShareVideo_one: String - private let _Media_ShareVideo_two: String - private let _Media_ShareVideo_few: String - private let _Media_ShareVideo_many: String - private let _Media_ShareVideo_other: String - public func Media_ShareVideo(_ value: Int32) -> String { - switch presentationStringsPluralizationForm(self.lc, value) { - case .zero: - return String(format: self._Media_ShareVideo_zero, "\(value)") - case .one: - return String(format: self._Media_ShareVideo_one, "\(value)") - case .two: - return String(format: self._Media_ShareVideo_two, "\(value)") - case .few: - return String(format: self._Media_ShareVideo_few, "\(value)") - case .many: - return String(format: self._Media_ShareVideo_many, "\(value)") - case .other: - return String(format: self._Media_ShareVideo_other, "\(value)") - } - } - private let _PrivacyLastSeenSettings_AddUsers_zero: String - private let _PrivacyLastSeenSettings_AddUsers_one: String - private let _PrivacyLastSeenSettings_AddUsers_two: String - private let _PrivacyLastSeenSettings_AddUsers_few: String - private let _PrivacyLastSeenSettings_AddUsers_many: String - private let _PrivacyLastSeenSettings_AddUsers_other: String - public func PrivacyLastSeenSettings_AddUsers(_ value: Int32) -> String { - switch presentationStringsPluralizationForm(self.lc, value) { - case .zero: - return String(format: self._PrivacyLastSeenSettings_AddUsers_zero, "\(value)") - case .one: - return String(format: self._PrivacyLastSeenSettings_AddUsers_one, "\(value)") - case .two: - return String(format: self._PrivacyLastSeenSettings_AddUsers_two, "\(value)") - case .few: - return String(format: self._PrivacyLastSeenSettings_AddUsers_few, "\(value)") - case .many: - return String(format: self._PrivacyLastSeenSettings_AddUsers_many, "\(value)") - case .other: - return String(format: self._PrivacyLastSeenSettings_AddUsers_other, "\(value)") - } - } - private let _ForwardedContacts_zero: String - private let _ForwardedContacts_one: String - private let _ForwardedContacts_two: String - private let _ForwardedContacts_few: String - private let _ForwardedContacts_many: String - private let _ForwardedContacts_other: String - public func ForwardedContacts(_ value: Int32) -> String { - switch presentationStringsPluralizationForm(self.lc, value) { - case .zero: - return String(format: self._ForwardedContacts_zero, "\(value)") - case .one: - return String(format: self._ForwardedContacts_one, "\(value)") - case .two: - return String(format: self._ForwardedContacts_two, "\(value)") - case .few: - return String(format: self._ForwardedContacts_few, "\(value)") - case .many: - return String(format: self._ForwardedContacts_many, "\(value)") - case .other: - return String(format: self._ForwardedContacts_other, "\(value)") - } - } - private let _Call_Seconds_zero: String - private let _Call_Seconds_one: String - private let _Call_Seconds_two: String - private let _Call_Seconds_few: String - private let _Call_Seconds_many: String - private let _Call_Seconds_other: String - public func Call_Seconds(_ value: Int32) -> String { - switch presentationStringsPluralizationForm(self.lc, value) { - case .zero: - return String(format: self._Call_Seconds_zero, "\(value)") - case .one: - return String(format: self._Call_Seconds_one, "\(value)") - case .two: - return String(format: self._Call_Seconds_two, "\(value)") - case .few: - return String(format: self._Call_Seconds_few, "\(value)") - case .many: - return String(format: self._Call_Seconds_many, "\(value)") - case .other: - return String(format: self._Call_Seconds_other, "\(value)") - } - } - private let _ForwardedAuthorsOthers_zero: String - private let _ForwardedAuthorsOthers_one: String - private let _ForwardedAuthorsOthers_two: String - private let _ForwardedAuthorsOthers_few: String - private let _ForwardedAuthorsOthers_many: String - private let _ForwardedAuthorsOthers_other: String - public func ForwardedAuthorsOthers(_ value: Int32) -> String { - switch presentationStringsPluralizationForm(self.lc, value) { - case .zero: - return String(format: self._ForwardedAuthorsOthers_zero, "\(value)") - case .one: - return String(format: self._ForwardedAuthorsOthers_one, "\(value)") - case .two: - return String(format: self._ForwardedAuthorsOthers_two, "\(value)") - case .few: - return String(format: self._ForwardedAuthorsOthers_few, "\(value)") - case .many: - return String(format: self._ForwardedAuthorsOthers_many, "\(value)") - case .other: - return String(format: self._ForwardedAuthorsOthers_other, "\(value)") - } - } - private let _DialogList_LiveLocationChatsCount_zero: String - private let _DialogList_LiveLocationChatsCount_one: String - private let _DialogList_LiveLocationChatsCount_two: String - private let _DialogList_LiveLocationChatsCount_few: String - private let _DialogList_LiveLocationChatsCount_many: String - private let _DialogList_LiveLocationChatsCount_other: String - public func DialogList_LiveLocationChatsCount(_ value: Int32) -> String { - switch presentationStringsPluralizationForm(self.lc, value) { - case .zero: - return String(format: self._DialogList_LiveLocationChatsCount_zero, "\(value)") - case .one: - return String(format: self._DialogList_LiveLocationChatsCount_one, "\(value)") - case .two: - return String(format: self._DialogList_LiveLocationChatsCount_two, "\(value)") - case .few: - return String(format: self._DialogList_LiveLocationChatsCount_few, "\(value)") - case .many: - return String(format: self._DialogList_LiveLocationChatsCount_many, "\(value)") - case .other: - return String(format: self._DialogList_LiveLocationChatsCount_other, "\(value)") - } - } - private let _MessageTimer_Years_zero: String - private let _MessageTimer_Years_one: String - private let _MessageTimer_Years_two: String - private let _MessageTimer_Years_few: String - private let _MessageTimer_Years_many: String - private let _MessageTimer_Years_other: String - public func MessageTimer_Years(_ value: Int32) -> String { - switch presentationStringsPluralizationForm(self.lc, value) { - case .zero: - return String(format: self._MessageTimer_Years_zero, "\(value)") - case .one: - return String(format: self._MessageTimer_Years_one, "\(value)") - case .two: - return String(format: self._MessageTimer_Years_two, "\(value)") - case .few: - return String(format: self._MessageTimer_Years_few, "\(value)") - case .many: - return String(format: self._MessageTimer_Years_many, "\(value)") - case .other: - return String(format: self._MessageTimer_Years_other, "\(value)") - } - } - private let _LiveLocationUpdated_MinutesAgo_zero: String - private let _LiveLocationUpdated_MinutesAgo_one: String - private let _LiveLocationUpdated_MinutesAgo_two: String - private let _LiveLocationUpdated_MinutesAgo_few: String - private let _LiveLocationUpdated_MinutesAgo_many: String - private let _LiveLocationUpdated_MinutesAgo_other: String - public func LiveLocationUpdated_MinutesAgo(_ value: Int32) -> String { - switch presentationStringsPluralizationForm(self.lc, value) { - case .zero: - return String(format: self._LiveLocationUpdated_MinutesAgo_zero, "\(value)") - case .one: - return String(format: self._LiveLocationUpdated_MinutesAgo_one, "\(value)") - case .two: - return String(format: self._LiveLocationUpdated_MinutesAgo_two, "\(value)") - case .few: - return String(format: self._LiveLocationUpdated_MinutesAgo_few, "\(value)") - case .many: - return String(format: self._LiveLocationUpdated_MinutesAgo_many, "\(value)") - case .other: - return String(format: self._LiveLocationUpdated_MinutesAgo_other, "\(value)") - } - } - private let _StickerPack_RemoveMaskCount_zero: String - private let _StickerPack_RemoveMaskCount_one: String - private let _StickerPack_RemoveMaskCount_two: String - private let _StickerPack_RemoveMaskCount_few: String - private let _StickerPack_RemoveMaskCount_many: String - private let _StickerPack_RemoveMaskCount_other: String - public func StickerPack_RemoveMaskCount(_ value: Int32) -> String { - switch presentationStringsPluralizationForm(self.lc, value) { - case .zero: - return String(format: self._StickerPack_RemoveMaskCount_zero, "\(value)") - case .one: - return String(format: self._StickerPack_RemoveMaskCount_one, "\(value)") - case .two: - return String(format: self._StickerPack_RemoveMaskCount_two, "\(value)") - case .few: - return String(format: self._StickerPack_RemoveMaskCount_few, "\(value)") - case .many: - return String(format: self._StickerPack_RemoveMaskCount_many, "\(value)") - case .other: - return String(format: self._StickerPack_RemoveMaskCount_other, "\(value)") - } - } - private let _ForwardedVideoMessages_zero: String - private let _ForwardedVideoMessages_one: String - private let _ForwardedVideoMessages_two: String - private let _ForwardedVideoMessages_few: String - private let _ForwardedVideoMessages_many: String - private let _ForwardedVideoMessages_other: String - public func ForwardedVideoMessages(_ value: Int32) -> String { - switch presentationStringsPluralizationForm(self.lc, value) { - case .zero: - return String(format: self._ForwardedVideoMessages_zero, "\(value)") - case .one: - return String(format: self._ForwardedVideoMessages_one, "\(value)") - case .two: - return String(format: self._ForwardedVideoMessages_two, "\(value)") - case .few: - return String(format: self._ForwardedVideoMessages_few, "\(value)") - case .many: - return String(format: self._ForwardedVideoMessages_many, "\(value)") - case .other: - return String(format: self._ForwardedVideoMessages_other, "\(value)") - } - } - private let _MessageTimer_ShortHours_zero: String - private let _MessageTimer_ShortHours_one: String - private let _MessageTimer_ShortHours_two: String - private let _MessageTimer_ShortHours_few: String - private let _MessageTimer_ShortHours_many: String - private let _MessageTimer_ShortHours_other: String - public func MessageTimer_ShortHours(_ value: Int32) -> String { - switch presentationStringsPluralizationForm(self.lc, value) { - case .zero: - return String(format: self._MessageTimer_ShortHours_zero, "\(value)") - case .one: - return String(format: self._MessageTimer_ShortHours_one, "\(value)") - case .two: - return String(format: self._MessageTimer_ShortHours_two, "\(value)") - case .few: - return String(format: self._MessageTimer_ShortHours_few, "\(value)") - case .many: - return String(format: self._MessageTimer_ShortHours_many, "\(value)") - case .other: - return String(format: self._MessageTimer_ShortHours_other, "\(value)") - } - } - private let _Watch_LastSeen_HoursAgo_zero: String - private let _Watch_LastSeen_HoursAgo_one: String - private let _Watch_LastSeen_HoursAgo_two: String - private let _Watch_LastSeen_HoursAgo_few: String - private let _Watch_LastSeen_HoursAgo_many: String - private let _Watch_LastSeen_HoursAgo_other: String - public func Watch_LastSeen_HoursAgo(_ value: Int32) -> String { - switch presentationStringsPluralizationForm(self.lc, value) { - case .zero: - return String(format: self._Watch_LastSeen_HoursAgo_zero, "\(value)") - case .one: - return String(format: self._Watch_LastSeen_HoursAgo_one, "\(value)") - case .two: - return String(format: self._Watch_LastSeen_HoursAgo_two, "\(value)") - case .few: - return String(format: self._Watch_LastSeen_HoursAgo_few, "\(value)") - case .many: - return String(format: self._Watch_LastSeen_HoursAgo_many, "\(value)") - case .other: - return String(format: self._Watch_LastSeen_HoursAgo_other, "\(value)") - } - } - private let _AttachmentMenu_SendVideo_zero: String - private let _AttachmentMenu_SendVideo_one: String - private let _AttachmentMenu_SendVideo_two: String - private let _AttachmentMenu_SendVideo_few: String - private let _AttachmentMenu_SendVideo_many: String - private let _AttachmentMenu_SendVideo_other: String - public func AttachmentMenu_SendVideo(_ value: Int32) -> String { - switch presentationStringsPluralizationForm(self.lc, value) { - case .zero: - return String(format: self._AttachmentMenu_SendVideo_zero, "\(value)") - case .one: - return String(format: self._AttachmentMenu_SendVideo_one, "\(value)") - case .two: - return String(format: self._AttachmentMenu_SendVideo_two, "\(value)") - case .few: - return String(format: self._AttachmentMenu_SendVideo_few, "\(value)") - case .many: - return String(format: self._AttachmentMenu_SendVideo_many, "\(value)") - case .other: - return String(format: self._AttachmentMenu_SendVideo_other, "\(value)") - } - } - private let _MessageTimer_Weeks_zero: String - private let _MessageTimer_Weeks_one: String - private let _MessageTimer_Weeks_two: String - private let _MessageTimer_Weeks_few: String - private let _MessageTimer_Weeks_many: String - private let _MessageTimer_Weeks_other: String - public func MessageTimer_Weeks(_ value: Int32) -> String { - switch presentationStringsPluralizationForm(self.lc, value) { - case .zero: - return String(format: self._MessageTimer_Weeks_zero, "\(value)") - case .one: - return String(format: self._MessageTimer_Weeks_one, "\(value)") - case .two: - return String(format: self._MessageTimer_Weeks_two, "\(value)") - case .few: - return String(format: self._MessageTimer_Weeks_few, "\(value)") - case .many: - return String(format: self._MessageTimer_Weeks_many, "\(value)") - case .other: - return String(format: self._MessageTimer_Weeks_other, "\(value)") - } - } - private let _StickerPack_StickerCount_zero: String - private let _StickerPack_StickerCount_one: String - private let _StickerPack_StickerCount_two: String - private let _StickerPack_StickerCount_few: String - private let _StickerPack_StickerCount_many: String - private let _StickerPack_StickerCount_other: String - public func StickerPack_StickerCount(_ value: Int32) -> String { - switch presentationStringsPluralizationForm(self.lc, value) { - case .zero: - return String(format: self._StickerPack_StickerCount_zero, "\(value)") - case .one: - return String(format: self._StickerPack_StickerCount_one, "\(value)") - case .two: - return String(format: self._StickerPack_StickerCount_two, "\(value)") - case .few: - return String(format: self._StickerPack_StickerCount_few, "\(value)") - case .many: - return String(format: self._StickerPack_StickerCount_many, "\(value)") - case .other: - return String(format: self._StickerPack_StickerCount_other, "\(value)") - } - } - private let _ServiceMessage_GameScoreSimple_zero: String - private let _ServiceMessage_GameScoreSimple_one: String - private let _ServiceMessage_GameScoreSimple_two: String - private let _ServiceMessage_GameScoreSimple_few: String - private let _ServiceMessage_GameScoreSimple_many: String - private let _ServiceMessage_GameScoreSimple_other: String - public func ServiceMessage_GameScoreSimple(_ value: Int32) -> String { - switch presentationStringsPluralizationForm(self.lc, value) { - case .zero: - return String(format: self._ServiceMessage_GameScoreSimple_zero, "\(value)") - case .one: - return String(format: self._ServiceMessage_GameScoreSimple_one, "\(value)") - case .two: - return String(format: self._ServiceMessage_GameScoreSimple_two, "\(value)") - case .few: - return String(format: self._ServiceMessage_GameScoreSimple_few, "\(value)") - case .many: - return String(format: self._ServiceMessage_GameScoreSimple_many, "\(value)") - case .other: - return String(format: self._ServiceMessage_GameScoreSimple_other, "\(value)") - } - } - private let _ForwardedFiles_zero: String - private let _ForwardedFiles_one: String - private let _ForwardedFiles_two: String - private let _ForwardedFiles_few: String - private let _ForwardedFiles_many: String - private let _ForwardedFiles_other: String - public func ForwardedFiles(_ value: Int32) -> String { - switch presentationStringsPluralizationForm(self.lc, value) { - case .zero: - return String(format: self._ForwardedFiles_zero, "\(value)") - case .one: - return String(format: self._ForwardedFiles_one, "\(value)") - case .two: - return String(format: self._ForwardedFiles_two, "\(value)") - case .few: - return String(format: self._ForwardedFiles_few, "\(value)") - case .many: - return String(format: self._ForwardedFiles_many, "\(value)") - case .other: - return String(format: self._ForwardedFiles_other, "\(value)") - } - } - private let _GroupInfo_ParticipantCount_zero: String - private let _GroupInfo_ParticipantCount_one: String - private let _GroupInfo_ParticipantCount_two: String - private let _GroupInfo_ParticipantCount_few: String - private let _GroupInfo_ParticipantCount_many: String - private let _GroupInfo_ParticipantCount_other: String - public func GroupInfo_ParticipantCount(_ value: Int32) -> String { - switch presentationStringsPluralizationForm(self.lc, value) { - case .zero: - return String(format: self._GroupInfo_ParticipantCount_zero, "\(value)") - case .one: - return String(format: self._GroupInfo_ParticipantCount_one, "\(value)") - case .two: - return String(format: self._GroupInfo_ParticipantCount_two, "\(value)") - case .few: - return String(format: self._GroupInfo_ParticipantCount_few, "\(value)") - case .many: - return String(format: self._GroupInfo_ParticipantCount_many, "\(value)") - case .other: - return String(format: self._GroupInfo_ParticipantCount_other, "\(value)") - } - } - private let _ForwardedLocations_zero: String - private let _ForwardedLocations_one: String - private let _ForwardedLocations_two: String - private let _ForwardedLocations_few: String - private let _ForwardedLocations_many: String - private let _ForwardedLocations_other: String - public func ForwardedLocations(_ value: Int32) -> String { - switch presentationStringsPluralizationForm(self.lc, value) { - case .zero: - return String(format: self._ForwardedLocations_zero, "\(value)") - case .one: - return String(format: self._ForwardedLocations_one, "\(value)") - case .two: - return String(format: self._ForwardedLocations_two, "\(value)") - case .few: - return String(format: self._ForwardedLocations_few, "\(value)") - case .many: - return String(format: self._ForwardedLocations_many, "\(value)") - case .other: - return String(format: self._ForwardedLocations_other, "\(value)") - } - } - private let _SharedMedia_DeleteItemsConfirmation_zero: String - private let _SharedMedia_DeleteItemsConfirmation_one: String - private let _SharedMedia_DeleteItemsConfirmation_two: String - private let _SharedMedia_DeleteItemsConfirmation_few: String - private let _SharedMedia_DeleteItemsConfirmation_many: String - private let _SharedMedia_DeleteItemsConfirmation_other: String - public func SharedMedia_DeleteItemsConfirmation(_ value: Int32) -> String { - switch presentationStringsPluralizationForm(self.lc, value) { - case .zero: - return String(format: self._SharedMedia_DeleteItemsConfirmation_zero, "\(value)") - case .one: - return String(format: self._SharedMedia_DeleteItemsConfirmation_one, "\(value)") - case .two: - return String(format: self._SharedMedia_DeleteItemsConfirmation_two, "\(value)") - case .few: - return String(format: self._SharedMedia_DeleteItemsConfirmation_few, "\(value)") - case .many: - return String(format: self._SharedMedia_DeleteItemsConfirmation_many, "\(value)") - case .other: - return String(format: self._SharedMedia_DeleteItemsConfirmation_other, "\(value)") - } - } - private let _ForwardedPhotos_zero: String - private let _ForwardedPhotos_one: String - private let _ForwardedPhotos_two: String - private let _ForwardedPhotos_few: String - private let _ForwardedPhotos_many: String - private let _ForwardedPhotos_other: String - public func ForwardedPhotos(_ value: Int32) -> String { - switch presentationStringsPluralizationForm(self.lc, value) { - case .zero: - return String(format: self._ForwardedPhotos_zero, "\(value)") - case .one: - return String(format: self._ForwardedPhotos_one, "\(value)") - case .two: - return String(format: self._ForwardedPhotos_two, "\(value)") - case .few: - return String(format: self._ForwardedPhotos_few, "\(value)") - case .many: - return String(format: self._ForwardedPhotos_many, "\(value)") - case .other: - return String(format: self._ForwardedPhotos_other, "\(value)") + return String(format: self._Call_ShortMinutes_other, "\(value)") } } private let _MessageTimer_ShortDays_zero: String @@ -5505,6 +4472,1040 @@ public final class PresentationStrings { return String(format: self._LiveLocation_MenuChatsCount_other, "\(value)") } } + private let _ForwardedVideoMessages_zero: String + private let _ForwardedVideoMessages_one: String + private let _ForwardedVideoMessages_two: String + private let _ForwardedVideoMessages_few: String + private let _ForwardedVideoMessages_many: String + private let _ForwardedVideoMessages_other: String + public func ForwardedVideoMessages(_ value: Int32) -> String { + switch presentationStringsPluralizationForm(self.lc, value) { + case .zero: + return String(format: self._ForwardedVideoMessages_zero, "\(value)") + case .one: + return String(format: self._ForwardedVideoMessages_one, "\(value)") + case .two: + return String(format: self._ForwardedVideoMessages_two, "\(value)") + case .few: + return String(format: self._ForwardedVideoMessages_few, "\(value)") + case .many: + return String(format: self._ForwardedVideoMessages_many, "\(value)") + case .other: + return String(format: self._ForwardedVideoMessages_other, "\(value)") + } + } + private let _MessageTimer_ShortSeconds_zero: String + private let _MessageTimer_ShortSeconds_one: String + private let _MessageTimer_ShortSeconds_two: String + private let _MessageTimer_ShortSeconds_few: String + private let _MessageTimer_ShortSeconds_many: String + private let _MessageTimer_ShortSeconds_other: String + public func MessageTimer_ShortSeconds(_ value: Int32) -> String { + switch presentationStringsPluralizationForm(self.lc, value) { + case .zero: + return String(format: self._MessageTimer_ShortSeconds_zero, "\(value)") + case .one: + return String(format: self._MessageTimer_ShortSeconds_one, "\(value)") + case .two: + return String(format: self._MessageTimer_ShortSeconds_two, "\(value)") + case .few: + return String(format: self._MessageTimer_ShortSeconds_few, "\(value)") + case .many: + return String(format: self._MessageTimer_ShortSeconds_many, "\(value)") + case .other: + return String(format: self._MessageTimer_ShortSeconds_other, "\(value)") + } + } + private let _Invitation_Members_zero: String + private let _Invitation_Members_one: String + private let _Invitation_Members_two: String + private let _Invitation_Members_few: String + private let _Invitation_Members_many: String + private let _Invitation_Members_other: String + public func Invitation_Members(_ value: Int32) -> String { + switch presentationStringsPluralizationForm(self.lc, value) { + case .zero: + return String(format: self._Invitation_Members_zero, "\(value)") + case .one: + return String(format: self._Invitation_Members_one, "\(value)") + case .two: + return String(format: self._Invitation_Members_two, "\(value)") + case .few: + return String(format: self._Invitation_Members_few, "\(value)") + case .many: + return String(format: self._Invitation_Members_many, "\(value)") + case .other: + return String(format: self._Invitation_Members_other, "\(value)") + } + } + private let _AttachmentMenu_SendPhoto_zero: String + private let _AttachmentMenu_SendPhoto_one: String + private let _AttachmentMenu_SendPhoto_two: String + private let _AttachmentMenu_SendPhoto_few: String + private let _AttachmentMenu_SendPhoto_many: String + private let _AttachmentMenu_SendPhoto_other: String + public func AttachmentMenu_SendPhoto(_ value: Int32) -> String { + switch presentationStringsPluralizationForm(self.lc, value) { + case .zero: + return String(format: self._AttachmentMenu_SendPhoto_zero, "\(value)") + case .one: + return String(format: self._AttachmentMenu_SendPhoto_one, "\(value)") + case .two: + return String(format: self._AttachmentMenu_SendPhoto_two, "\(value)") + case .few: + return String(format: self._AttachmentMenu_SendPhoto_few, "\(value)") + case .many: + return String(format: self._AttachmentMenu_SendPhoto_many, "\(value)") + case .other: + return String(format: self._AttachmentMenu_SendPhoto_other, "\(value)") + } + } + private let _SharedMedia_Link_zero: String + private let _SharedMedia_Link_one: String + private let _SharedMedia_Link_two: String + private let _SharedMedia_Link_few: String + private let _SharedMedia_Link_many: String + private let _SharedMedia_Link_other: String + public func SharedMedia_Link(_ value: Int32) -> String { + switch presentationStringsPluralizationForm(self.lc, value) { + case .zero: + return String(format: self._SharedMedia_Link_zero, "\(value)") + case .one: + return String(format: self._SharedMedia_Link_one, "\(value)") + case .two: + return String(format: self._SharedMedia_Link_two, "\(value)") + case .few: + return String(format: self._SharedMedia_Link_few, "\(value)") + case .many: + return String(format: self._SharedMedia_Link_many, "\(value)") + case .other: + return String(format: self._SharedMedia_Link_other, "\(value)") + } + } + private let _ServiceMessage_GameScoreSelfExtended_zero: String + private let _ServiceMessage_GameScoreSelfExtended_one: String + private let _ServiceMessage_GameScoreSelfExtended_two: String + private let _ServiceMessage_GameScoreSelfExtended_few: String + private let _ServiceMessage_GameScoreSelfExtended_many: String + private let _ServiceMessage_GameScoreSelfExtended_other: String + public func ServiceMessage_GameScoreSelfExtended(_ value: Int32) -> String { + switch presentationStringsPluralizationForm(self.lc, value) { + case .zero: + return String(format: self._ServiceMessage_GameScoreSelfExtended_zero, "\(value)") + case .one: + return String(format: self._ServiceMessage_GameScoreSelfExtended_one, "\(value)") + case .two: + return String(format: self._ServiceMessage_GameScoreSelfExtended_two, "\(value)") + case .few: + return String(format: self._ServiceMessage_GameScoreSelfExtended_few, "\(value)") + case .many: + return String(format: self._ServiceMessage_GameScoreSelfExtended_many, "\(value)") + case .other: + return String(format: self._ServiceMessage_GameScoreSelfExtended_other, "\(value)") + } + } + private let _QuickSend_Photos_zero: String + private let _QuickSend_Photos_one: String + private let _QuickSend_Photos_two: String + private let _QuickSend_Photos_few: String + private let _QuickSend_Photos_many: String + private let _QuickSend_Photos_other: String + public func QuickSend_Photos(_ value: Int32) -> String { + switch presentationStringsPluralizationForm(self.lc, value) { + case .zero: + return String(format: self._QuickSend_Photos_zero, "\(value)") + case .one: + return String(format: self._QuickSend_Photos_one, "\(value)") + case .two: + return String(format: self._QuickSend_Photos_two, "\(value)") + case .few: + return String(format: self._QuickSend_Photos_few, "\(value)") + case .many: + return String(format: self._QuickSend_Photos_many, "\(value)") + case .other: + return String(format: self._QuickSend_Photos_other, "\(value)") + } + } + private let _StickerPack_StickerCount_zero: String + private let _StickerPack_StickerCount_one: String + private let _StickerPack_StickerCount_two: String + private let _StickerPack_StickerCount_few: String + private let _StickerPack_StickerCount_many: String + private let _StickerPack_StickerCount_other: String + public func StickerPack_StickerCount(_ value: Int32) -> String { + switch presentationStringsPluralizationForm(self.lc, value) { + case .zero: + return String(format: self._StickerPack_StickerCount_zero, "\(value)") + case .one: + return String(format: self._StickerPack_StickerCount_one, "\(value)") + case .two: + return String(format: self._StickerPack_StickerCount_two, "\(value)") + case .few: + return String(format: self._StickerPack_StickerCount_few, "\(value)") + case .many: + return String(format: self._StickerPack_StickerCount_many, "\(value)") + case .other: + return String(format: self._StickerPack_StickerCount_other, "\(value)") + } + } + private let _Media_ShareItem_zero: String + private let _Media_ShareItem_one: String + private let _Media_ShareItem_two: String + private let _Media_ShareItem_few: String + private let _Media_ShareItem_many: String + private let _Media_ShareItem_other: String + public func Media_ShareItem(_ value: Int32) -> String { + switch presentationStringsPluralizationForm(self.lc, value) { + case .zero: + return String(format: self._Media_ShareItem_zero, "\(value)") + case .one: + return String(format: self._Media_ShareItem_one, "\(value)") + case .two: + return String(format: self._Media_ShareItem_two, "\(value)") + case .few: + return String(format: self._Media_ShareItem_few, "\(value)") + case .many: + return String(format: self._Media_ShareItem_many, "\(value)") + case .other: + return String(format: self._Media_ShareItem_other, "\(value)") + } + } + private let _Conversation_StatusOnline_zero: String + private let _Conversation_StatusOnline_one: String + private let _Conversation_StatusOnline_two: String + private let _Conversation_StatusOnline_few: String + private let _Conversation_StatusOnline_many: String + private let _Conversation_StatusOnline_other: String + public func Conversation_StatusOnline(_ value: Int32) -> String { + switch presentationStringsPluralizationForm(self.lc, value) { + case .zero: + return String(format: self._Conversation_StatusOnline_zero, "\(value)") + case .one: + return String(format: self._Conversation_StatusOnline_one, "\(value)") + case .two: + return String(format: self._Conversation_StatusOnline_two, "\(value)") + case .few: + return String(format: self._Conversation_StatusOnline_few, "\(value)") + case .many: + return String(format: self._Conversation_StatusOnline_many, "\(value)") + case .other: + return String(format: self._Conversation_StatusOnline_other, "\(value)") + } + } + private let _MessageTimer_Days_zero: String + private let _MessageTimer_Days_one: String + private let _MessageTimer_Days_two: String + private let _MessageTimer_Days_few: String + private let _MessageTimer_Days_many: String + private let _MessageTimer_Days_other: String + public func MessageTimer_Days(_ value: Int32) -> String { + switch presentationStringsPluralizationForm(self.lc, value) { + case .zero: + return String(format: self._MessageTimer_Days_zero, "\(value)") + case .one: + return String(format: self._MessageTimer_Days_one, "\(value)") + case .two: + return String(format: self._MessageTimer_Days_two, "\(value)") + case .few: + return String(format: self._MessageTimer_Days_few, "\(value)") + case .many: + return String(format: self._MessageTimer_Days_many, "\(value)") + case .other: + return String(format: self._MessageTimer_Days_other, "\(value)") + } + } + private let _SharedMedia_Video_zero: String + private let _SharedMedia_Video_one: String + private let _SharedMedia_Video_two: String + private let _SharedMedia_Video_few: String + private let _SharedMedia_Video_many: String + private let _SharedMedia_Video_other: String + public func SharedMedia_Video(_ value: Int32) -> String { + switch presentationStringsPluralizationForm(self.lc, value) { + case .zero: + return String(format: self._SharedMedia_Video_zero, "\(value)") + case .one: + return String(format: self._SharedMedia_Video_one, "\(value)") + case .two: + return String(format: self._SharedMedia_Video_two, "\(value)") + case .few: + return String(format: self._SharedMedia_Video_few, "\(value)") + case .many: + return String(format: self._SharedMedia_Video_many, "\(value)") + case .other: + return String(format: self._SharedMedia_Video_other, "\(value)") + } + } + private let _ForwardedVideos_zero: String + private let _ForwardedVideos_one: String + private let _ForwardedVideos_two: String + private let _ForwardedVideos_few: String + private let _ForwardedVideos_many: String + private let _ForwardedVideos_other: String + public func ForwardedVideos(_ value: Int32) -> String { + switch presentationStringsPluralizationForm(self.lc, value) { + case .zero: + return String(format: self._ForwardedVideos_zero, "\(value)") + case .one: + return String(format: self._ForwardedVideos_one, "\(value)") + case .two: + return String(format: self._ForwardedVideos_two, "\(value)") + case .few: + return String(format: self._ForwardedVideos_few, "\(value)") + case .many: + return String(format: self._ForwardedVideos_many, "\(value)") + case .other: + return String(format: self._ForwardedVideos_other, "\(value)") + } + } + private let _MuteFor_Days_zero: String + private let _MuteFor_Days_one: String + private let _MuteFor_Days_two: String + private let _MuteFor_Days_few: String + private let _MuteFor_Days_many: String + private let _MuteFor_Days_other: String + public func MuteFor_Days(_ value: Int32) -> String { + switch presentationStringsPluralizationForm(self.lc, value) { + case .zero: + return String(format: self._MuteFor_Days_zero, "\(value)") + case .one: + return String(format: self._MuteFor_Days_one, "\(value)") + case .two: + return String(format: self._MuteFor_Days_two, "\(value)") + case .few: + return String(format: self._MuteFor_Days_few, "\(value)") + case .many: + return String(format: self._MuteFor_Days_many, "\(value)") + case .other: + return String(format: self._MuteFor_Days_other, "\(value)") + } + } + private let _ForwardedGifs_zero: String + private let _ForwardedGifs_one: String + private let _ForwardedGifs_two: String + private let _ForwardedGifs_few: String + private let _ForwardedGifs_many: String + private let _ForwardedGifs_other: String + public func ForwardedGifs(_ value: Int32) -> String { + switch presentationStringsPluralizationForm(self.lc, value) { + case .zero: + return String(format: self._ForwardedGifs_zero, "\(value)") + case .one: + return String(format: self._ForwardedGifs_one, "\(value)") + case .two: + return String(format: self._ForwardedGifs_two, "\(value)") + case .few: + return String(format: self._ForwardedGifs_few, "\(value)") + case .many: + return String(format: self._ForwardedGifs_many, "\(value)") + case .other: + return String(format: self._ForwardedGifs_other, "\(value)") + } + } + private let _MessageTimer_ShortHours_zero: String + private let _MessageTimer_ShortHours_one: String + private let _MessageTimer_ShortHours_two: String + private let _MessageTimer_ShortHours_few: String + private let _MessageTimer_ShortHours_many: String + private let _MessageTimer_ShortHours_other: String + public func MessageTimer_ShortHours(_ value: Int32) -> String { + switch presentationStringsPluralizationForm(self.lc, value) { + case .zero: + return String(format: self._MessageTimer_ShortHours_zero, "\(value)") + case .one: + return String(format: self._MessageTimer_ShortHours_one, "\(value)") + case .two: + return String(format: self._MessageTimer_ShortHours_two, "\(value)") + case .few: + return String(format: self._MessageTimer_ShortHours_few, "\(value)") + case .many: + return String(format: self._MessageTimer_ShortHours_many, "\(value)") + case .other: + return String(format: self._MessageTimer_ShortHours_other, "\(value)") + } + } + private let _Contacts_ImportersCount_zero: String + private let _Contacts_ImportersCount_one: String + private let _Contacts_ImportersCount_two: String + private let _Contacts_ImportersCount_few: String + private let _Contacts_ImportersCount_many: String + private let _Contacts_ImportersCount_other: String + public func Contacts_ImportersCount(_ value: Int32) -> String { + switch presentationStringsPluralizationForm(self.lc, value) { + case .zero: + return String(format: self._Contacts_ImportersCount_zero, "\(value)") + case .one: + return String(format: self._Contacts_ImportersCount_one, "\(value)") + case .two: + return String(format: self._Contacts_ImportersCount_two, "\(value)") + case .few: + return String(format: self._Contacts_ImportersCount_few, "\(value)") + case .many: + return String(format: self._Contacts_ImportersCount_many, "\(value)") + case .other: + return String(format: self._Contacts_ImportersCount_other, "\(value)") + } + } + private let _ForwardedAuthorsOthers_zero: String + private let _ForwardedAuthorsOthers_one: String + private let _ForwardedAuthorsOthers_two: String + private let _ForwardedAuthorsOthers_few: String + private let _ForwardedAuthorsOthers_many: String + private let _ForwardedAuthorsOthers_other: String + public func ForwardedAuthorsOthers(_ value: Int32) -> String { + switch presentationStringsPluralizationForm(self.lc, value) { + case .zero: + return String(format: self._ForwardedAuthorsOthers_zero, "\(value)") + case .one: + return String(format: self._ForwardedAuthorsOthers_one, "\(value)") + case .two: + return String(format: self._ForwardedAuthorsOthers_two, "\(value)") + case .few: + return String(format: self._ForwardedAuthorsOthers_few, "\(value)") + case .many: + return String(format: self._ForwardedAuthorsOthers_many, "\(value)") + case .other: + return String(format: self._ForwardedAuthorsOthers_other, "\(value)") + } + } + private let _UserCount_zero: String + private let _UserCount_one: String + private let _UserCount_two: String + private let _UserCount_few: String + private let _UserCount_many: String + private let _UserCount_other: String + public func UserCount(_ value: Int32) -> String { + switch presentationStringsPluralizationForm(self.lc, value) { + case .zero: + return String(format: self._UserCount_zero, "\(value)") + case .one: + return String(format: self._UserCount_one, "\(value)") + case .two: + return String(format: self._UserCount_two, "\(value)") + case .few: + return String(format: self._UserCount_few, "\(value)") + case .many: + return String(format: self._UserCount_many, "\(value)") + case .other: + return String(format: self._UserCount_other, "\(value)") + } + } + private let _AttachmentMenu_SendGif_zero: String + private let _AttachmentMenu_SendGif_one: String + private let _AttachmentMenu_SendGif_two: String + private let _AttachmentMenu_SendGif_few: String + private let _AttachmentMenu_SendGif_many: String + private let _AttachmentMenu_SendGif_other: String + public func AttachmentMenu_SendGif(_ value: Int32) -> String { + switch presentationStringsPluralizationForm(self.lc, value) { + case .zero: + return String(format: self._AttachmentMenu_SendGif_zero, "\(value)") + case .one: + return String(format: self._AttachmentMenu_SendGif_one, "\(value)") + case .two: + return String(format: self._AttachmentMenu_SendGif_two, "\(value)") + case .few: + return String(format: self._AttachmentMenu_SendGif_few, "\(value)") + case .many: + return String(format: self._AttachmentMenu_SendGif_many, "\(value)") + case .other: + return String(format: self._AttachmentMenu_SendGif_other, "\(value)") + } + } + private let _StickerPack_AddMaskCount_zero: String + private let _StickerPack_AddMaskCount_one: String + private let _StickerPack_AddMaskCount_two: String + private let _StickerPack_AddMaskCount_few: String + private let _StickerPack_AddMaskCount_many: String + private let _StickerPack_AddMaskCount_other: String + public func StickerPack_AddMaskCount(_ value: Int32) -> String { + switch presentationStringsPluralizationForm(self.lc, value) { + case .zero: + return String(format: self._StickerPack_AddMaskCount_zero, "\(value)") + case .one: + return String(format: self._StickerPack_AddMaskCount_one, "\(value)") + case .two: + return String(format: self._StickerPack_AddMaskCount_two, "\(value)") + case .few: + return String(format: self._StickerPack_AddMaskCount_few, "\(value)") + case .many: + return String(format: self._StickerPack_AddMaskCount_many, "\(value)") + case .other: + return String(format: self._StickerPack_AddMaskCount_other, "\(value)") + } + } + private let _Forward_ConfirmMultipleFiles_zero: String + private let _Forward_ConfirmMultipleFiles_one: String + private let _Forward_ConfirmMultipleFiles_two: String + private let _Forward_ConfirmMultipleFiles_few: String + private let _Forward_ConfirmMultipleFiles_many: String + private let _Forward_ConfirmMultipleFiles_other: String + public func Forward_ConfirmMultipleFiles(_ value: Int32) -> String { + switch presentationStringsPluralizationForm(self.lc, value) { + case .zero: + return String(format: self._Forward_ConfirmMultipleFiles_zero, "\(value)") + case .one: + return String(format: self._Forward_ConfirmMultipleFiles_one, "\(value)") + case .two: + return String(format: self._Forward_ConfirmMultipleFiles_two, "\(value)") + case .few: + return String(format: self._Forward_ConfirmMultipleFiles_few, "\(value)") + case .many: + return String(format: self._Forward_ConfirmMultipleFiles_many, "\(value)") + case .other: + return String(format: self._Forward_ConfirmMultipleFiles_other, "\(value)") + } + } + private let _Conversation_StatusMembers_zero: String + private let _Conversation_StatusMembers_one: String + private let _Conversation_StatusMembers_two: String + private let _Conversation_StatusMembers_few: String + private let _Conversation_StatusMembers_many: String + private let _Conversation_StatusMembers_other: String + public func Conversation_StatusMembers(_ value: Int32) -> String { + switch presentationStringsPluralizationForm(self.lc, value) { + case .zero: + return String(format: self._Conversation_StatusMembers_zero, "\(value)") + case .one: + return String(format: self._Conversation_StatusMembers_one, "\(value)") + case .two: + return String(format: self._Conversation_StatusMembers_two, "\(value)") + case .few: + return String(format: self._Conversation_StatusMembers_few, "\(value)") + case .many: + return String(format: self._Conversation_StatusMembers_many, "\(value)") + case .other: + return String(format: self._Conversation_StatusMembers_other, "\(value)") + } + } + private let _MuteExpires_Hours_zero: String + private let _MuteExpires_Hours_one: String + private let _MuteExpires_Hours_two: String + private let _MuteExpires_Hours_few: String + private let _MuteExpires_Hours_many: String + private let _MuteExpires_Hours_other: String + public func MuteExpires_Hours(_ value: Int32) -> String { + switch presentationStringsPluralizationForm(self.lc, value) { + case .zero: + return String(format: self._MuteExpires_Hours_zero, "\(value)") + case .one: + return String(format: self._MuteExpires_Hours_one, "\(value)") + case .two: + return String(format: self._MuteExpires_Hours_two, "\(value)") + case .few: + return String(format: self._MuteExpires_Hours_few, "\(value)") + case .many: + return String(format: self._MuteExpires_Hours_many, "\(value)") + case .other: + return String(format: self._MuteExpires_Hours_other, "\(value)") + } + } + private let _LastSeen_MinutesAgo_zero: String + private let _LastSeen_MinutesAgo_one: String + private let _LastSeen_MinutesAgo_two: String + private let _LastSeen_MinutesAgo_few: String + private let _LastSeen_MinutesAgo_many: String + private let _LastSeen_MinutesAgo_other: String + public func LastSeen_MinutesAgo(_ value: Int32) -> String { + switch presentationStringsPluralizationForm(self.lc, value) { + case .zero: + return String(format: self._LastSeen_MinutesAgo_zero, "\(value)") + case .one: + return String(format: self._LastSeen_MinutesAgo_one, "\(value)") + case .two: + return String(format: self._LastSeen_MinutesAgo_two, "\(value)") + case .few: + return String(format: self._LastSeen_MinutesAgo_few, "\(value)") + case .many: + return String(format: self._LastSeen_MinutesAgo_many, "\(value)") + case .other: + return String(format: self._LastSeen_MinutesAgo_other, "\(value)") + } + } + private let _Notification_GameScoreSimple_zero: String + private let _Notification_GameScoreSimple_one: String + private let _Notification_GameScoreSimple_two: String + private let _Notification_GameScoreSimple_few: String + private let _Notification_GameScoreSimple_many: String + private let _Notification_GameScoreSimple_other: String + public func Notification_GameScoreSimple(_ value: Int32) -> String { + switch presentationStringsPluralizationForm(self.lc, value) { + case .zero: + return String(format: self._Notification_GameScoreSimple_zero, "\(value)") + case .one: + return String(format: self._Notification_GameScoreSimple_one, "\(value)") + case .two: + return String(format: self._Notification_GameScoreSimple_two, "\(value)") + case .few: + return String(format: self._Notification_GameScoreSimple_few, "\(value)") + case .many: + return String(format: self._Notification_GameScoreSimple_many, "\(value)") + case .other: + return String(format: self._Notification_GameScoreSimple_other, "\(value)") + } + } + private let _MuteFor_Hours_zero: String + private let _MuteFor_Hours_one: String + private let _MuteFor_Hours_two: String + private let _MuteFor_Hours_few: String + private let _MuteFor_Hours_many: String + private let _MuteFor_Hours_other: String + public func MuteFor_Hours(_ value: Int32) -> String { + switch presentationStringsPluralizationForm(self.lc, value) { + case .zero: + return String(format: self._MuteFor_Hours_zero, "\(value)") + case .one: + return String(format: self._MuteFor_Hours_one, "\(value)") + case .two: + return String(format: self._MuteFor_Hours_two, "\(value)") + case .few: + return String(format: self._MuteFor_Hours_few, "\(value)") + case .many: + return String(format: self._MuteFor_Hours_many, "\(value)") + case .other: + return String(format: self._MuteFor_Hours_other, "\(value)") + } + } + private let _SharedMedia_DeleteItemsConfirmation_zero: String + private let _SharedMedia_DeleteItemsConfirmation_one: String + private let _SharedMedia_DeleteItemsConfirmation_two: String + private let _SharedMedia_DeleteItemsConfirmation_few: String + private let _SharedMedia_DeleteItemsConfirmation_many: String + private let _SharedMedia_DeleteItemsConfirmation_other: String + public func SharedMedia_DeleteItemsConfirmation(_ value: Int32) -> String { + switch presentationStringsPluralizationForm(self.lc, value) { + case .zero: + return String(format: self._SharedMedia_DeleteItemsConfirmation_zero, "\(value)") + case .one: + return String(format: self._SharedMedia_DeleteItemsConfirmation_one, "\(value)") + case .two: + return String(format: self._SharedMedia_DeleteItemsConfirmation_two, "\(value)") + case .few: + return String(format: self._SharedMedia_DeleteItemsConfirmation_few, "\(value)") + case .many: + return String(format: self._SharedMedia_DeleteItemsConfirmation_many, "\(value)") + case .other: + return String(format: self._SharedMedia_DeleteItemsConfirmation_other, "\(value)") + } + } + private let _ForwardedStickers_zero: String + private let _ForwardedStickers_one: String + private let _ForwardedStickers_two: String + private let _ForwardedStickers_few: String + private let _ForwardedStickers_many: String + private let _ForwardedStickers_other: String + public func ForwardedStickers(_ value: Int32) -> String { + switch presentationStringsPluralizationForm(self.lc, value) { + case .zero: + return String(format: self._ForwardedStickers_zero, "\(value)") + case .one: + return String(format: self._ForwardedStickers_one, "\(value)") + case .two: + return String(format: self._ForwardedStickers_two, "\(value)") + case .few: + return String(format: self._ForwardedStickers_few, "\(value)") + case .many: + return String(format: self._ForwardedStickers_many, "\(value)") + case .other: + return String(format: self._ForwardedStickers_other, "\(value)") + } + } + private let _Call_Seconds_zero: String + private let _Call_Seconds_one: String + private let _Call_Seconds_two: String + private let _Call_Seconds_few: String + private let _Call_Seconds_many: String + private let _Call_Seconds_other: String + public func Call_Seconds(_ value: Int32) -> String { + switch presentationStringsPluralizationForm(self.lc, value) { + case .zero: + return String(format: self._Call_Seconds_zero, "\(value)") + case .one: + return String(format: self._Call_Seconds_one, "\(value)") + case .two: + return String(format: self._Call_Seconds_two, "\(value)") + case .few: + return String(format: self._Call_Seconds_few, "\(value)") + case .many: + return String(format: self._Call_Seconds_many, "\(value)") + case .other: + return String(format: self._Call_Seconds_other, "\(value)") + } + } + private let _Map_ETAHours_zero: String + private let _Map_ETAHours_one: String + private let _Map_ETAHours_two: String + private let _Map_ETAHours_few: String + private let _Map_ETAHours_many: String + private let _Map_ETAHours_other: String + public func Map_ETAHours(_ value: Int32) -> String { + switch presentationStringsPluralizationForm(self.lc, value) { + case .zero: + return String(format: self._Map_ETAHours_zero, "\(value)") + case .one: + return String(format: self._Map_ETAHours_one, "\(value)") + case .two: + return String(format: self._Map_ETAHours_two, "\(value)") + case .few: + return String(format: self._Map_ETAHours_few, "\(value)") + case .many: + return String(format: self._Map_ETAHours_many, "\(value)") + case .other: + return String(format: self._Map_ETAHours_other, "\(value)") + } + } + private let _PrivacyLastSeenSettings_AddUsers_zero: String + private let _PrivacyLastSeenSettings_AddUsers_one: String + private let _PrivacyLastSeenSettings_AddUsers_two: String + private let _PrivacyLastSeenSettings_AddUsers_few: String + private let _PrivacyLastSeenSettings_AddUsers_many: String + private let _PrivacyLastSeenSettings_AddUsers_other: String + public func PrivacyLastSeenSettings_AddUsers(_ value: Int32) -> String { + switch presentationStringsPluralizationForm(self.lc, value) { + case .zero: + return String(format: self._PrivacyLastSeenSettings_AddUsers_zero, "\(value)") + case .one: + return String(format: self._PrivacyLastSeenSettings_AddUsers_one, "\(value)") + case .two: + return String(format: self._PrivacyLastSeenSettings_AddUsers_two, "\(value)") + case .few: + return String(format: self._PrivacyLastSeenSettings_AddUsers_few, "\(value)") + case .many: + return String(format: self._PrivacyLastSeenSettings_AddUsers_many, "\(value)") + case .other: + return String(format: self._PrivacyLastSeenSettings_AddUsers_other, "\(value)") + } + } + private let _Notifications_ExceptionMuteExpires_Days_zero: String + private let _Notifications_ExceptionMuteExpires_Days_one: String + private let _Notifications_ExceptionMuteExpires_Days_two: String + private let _Notifications_ExceptionMuteExpires_Days_few: String + private let _Notifications_ExceptionMuteExpires_Days_many: String + private let _Notifications_ExceptionMuteExpires_Days_other: String + public func Notifications_ExceptionMuteExpires_Days(_ value: Int32) -> String { + switch presentationStringsPluralizationForm(self.lc, value) { + case .zero: + return String(format: self._Notifications_ExceptionMuteExpires_Days_zero, "\(value)") + case .one: + return String(format: self._Notifications_ExceptionMuteExpires_Days_one, "\(value)") + case .two: + return String(format: self._Notifications_ExceptionMuteExpires_Days_two, "\(value)") + case .few: + return String(format: self._Notifications_ExceptionMuteExpires_Days_few, "\(value)") + case .many: + return String(format: self._Notifications_ExceptionMuteExpires_Days_many, "\(value)") + case .other: + return String(format: self._Notifications_ExceptionMuteExpires_Days_other, "\(value)") + } + } + private let _ServiceMessage_GameScoreSimple_zero: String + private let _ServiceMessage_GameScoreSimple_one: String + private let _ServiceMessage_GameScoreSimple_two: String + private let _ServiceMessage_GameScoreSimple_few: String + private let _ServiceMessage_GameScoreSimple_many: String + private let _ServiceMessage_GameScoreSimple_other: String + public func ServiceMessage_GameScoreSimple(_ value: Int32) -> String { + switch presentationStringsPluralizationForm(self.lc, value) { + case .zero: + return String(format: self._ServiceMessage_GameScoreSimple_zero, "\(value)") + case .one: + return String(format: self._ServiceMessage_GameScoreSimple_one, "\(value)") + case .two: + return String(format: self._ServiceMessage_GameScoreSimple_two, "\(value)") + case .few: + return String(format: self._ServiceMessage_GameScoreSimple_few, "\(value)") + case .many: + return String(format: self._ServiceMessage_GameScoreSimple_many, "\(value)") + case .other: + return String(format: self._ServiceMessage_GameScoreSimple_other, "\(value)") + } + } + private let _MessageTimer_Weeks_zero: String + private let _MessageTimer_Weeks_one: String + private let _MessageTimer_Weeks_two: String + private let _MessageTimer_Weeks_few: String + private let _MessageTimer_Weeks_many: String + private let _MessageTimer_Weeks_other: String + public func MessageTimer_Weeks(_ value: Int32) -> String { + switch presentationStringsPluralizationForm(self.lc, value) { + case .zero: + return String(format: self._MessageTimer_Weeks_zero, "\(value)") + case .one: + return String(format: self._MessageTimer_Weeks_one, "\(value)") + case .two: + return String(format: self._MessageTimer_Weeks_two, "\(value)") + case .few: + return String(format: self._MessageTimer_Weeks_few, "\(value)") + case .many: + return String(format: self._MessageTimer_Weeks_many, "\(value)") + case .other: + return String(format: self._MessageTimer_Weeks_other, "\(value)") + } + } + private let _SharedMedia_Generic_zero: String + private let _SharedMedia_Generic_one: String + private let _SharedMedia_Generic_two: String + private let _SharedMedia_Generic_few: String + private let _SharedMedia_Generic_many: String + private let _SharedMedia_Generic_other: String + public func SharedMedia_Generic(_ value: Int32) -> String { + switch presentationStringsPluralizationForm(self.lc, value) { + case .zero: + return String(format: self._SharedMedia_Generic_zero, "\(value)") + case .one: + return String(format: self._SharedMedia_Generic_one, "\(value)") + case .two: + return String(format: self._SharedMedia_Generic_two, "\(value)") + case .few: + return String(format: self._SharedMedia_Generic_few, "\(value)") + case .many: + return String(format: self._SharedMedia_Generic_many, "\(value)") + case .other: + return String(format: self._SharedMedia_Generic_other, "\(value)") + } + } + private let _DialogList_LiveLocationChatsCount_zero: String + private let _DialogList_LiveLocationChatsCount_one: String + private let _DialogList_LiveLocationChatsCount_two: String + private let _DialogList_LiveLocationChatsCount_few: String + private let _DialogList_LiveLocationChatsCount_many: String + private let _DialogList_LiveLocationChatsCount_other: String + public func DialogList_LiveLocationChatsCount(_ value: Int32) -> String { + switch presentationStringsPluralizationForm(self.lc, value) { + case .zero: + return String(format: self._DialogList_LiveLocationChatsCount_zero, "\(value)") + case .one: + return String(format: self._DialogList_LiveLocationChatsCount_one, "\(value)") + case .two: + return String(format: self._DialogList_LiveLocationChatsCount_two, "\(value)") + case .few: + return String(format: self._DialogList_LiveLocationChatsCount_few, "\(value)") + case .many: + return String(format: self._DialogList_LiveLocationChatsCount_many, "\(value)") + case .other: + return String(format: self._DialogList_LiveLocationChatsCount_other, "\(value)") + } + } + private let _AttachmentMenu_SendVideo_zero: String + private let _AttachmentMenu_SendVideo_one: String + private let _AttachmentMenu_SendVideo_two: String + private let _AttachmentMenu_SendVideo_few: String + private let _AttachmentMenu_SendVideo_many: String + private let _AttachmentMenu_SendVideo_other: String + public func AttachmentMenu_SendVideo(_ value: Int32) -> String { + switch presentationStringsPluralizationForm(self.lc, value) { + case .zero: + return String(format: self._AttachmentMenu_SendVideo_zero, "\(value)") + case .one: + return String(format: self._AttachmentMenu_SendVideo_one, "\(value)") + case .two: + return String(format: self._AttachmentMenu_SendVideo_two, "\(value)") + case .few: + return String(format: self._AttachmentMenu_SendVideo_few, "\(value)") + case .many: + return String(format: self._AttachmentMenu_SendVideo_many, "\(value)") + case .other: + return String(format: self._AttachmentMenu_SendVideo_other, "\(value)") + } + } + private let _LiveLocationUpdated_MinutesAgo_zero: String + private let _LiveLocationUpdated_MinutesAgo_one: String + private let _LiveLocationUpdated_MinutesAgo_two: String + private let _LiveLocationUpdated_MinutesAgo_few: String + private let _LiveLocationUpdated_MinutesAgo_many: String + private let _LiveLocationUpdated_MinutesAgo_other: String + public func LiveLocationUpdated_MinutesAgo(_ value: Int32) -> String { + switch presentationStringsPluralizationForm(self.lc, value) { + case .zero: + return String(format: self._LiveLocationUpdated_MinutesAgo_zero, "\(value)") + case .one: + return String(format: self._LiveLocationUpdated_MinutesAgo_one, "\(value)") + case .two: + return String(format: self._LiveLocationUpdated_MinutesAgo_two, "\(value)") + case .few: + return String(format: self._LiveLocationUpdated_MinutesAgo_few, "\(value)") + case .many: + return String(format: self._LiveLocationUpdated_MinutesAgo_many, "\(value)") + case .other: + return String(format: self._LiveLocationUpdated_MinutesAgo_other, "\(value)") + } + } + private let _SharedMedia_Photo_zero: String + private let _SharedMedia_Photo_one: String + private let _SharedMedia_Photo_two: String + private let _SharedMedia_Photo_few: String + private let _SharedMedia_Photo_many: String + private let _SharedMedia_Photo_other: String + public func SharedMedia_Photo(_ value: Int32) -> String { + switch presentationStringsPluralizationForm(self.lc, value) { + case .zero: + return String(format: self._SharedMedia_Photo_zero, "\(value)") + case .one: + return String(format: self._SharedMedia_Photo_one, "\(value)") + case .two: + return String(format: self._SharedMedia_Photo_two, "\(value)") + case .few: + return String(format: self._SharedMedia_Photo_few, "\(value)") + case .many: + return String(format: self._SharedMedia_Photo_many, "\(value)") + case .other: + return String(format: self._SharedMedia_Photo_other, "\(value)") + } + } + private let _StickerPack_RemoveStickerCount_zero: String + private let _StickerPack_RemoveStickerCount_one: String + private let _StickerPack_RemoveStickerCount_two: String + private let _StickerPack_RemoveStickerCount_few: String + private let _StickerPack_RemoveStickerCount_many: String + private let _StickerPack_RemoveStickerCount_other: String + public func StickerPack_RemoveStickerCount(_ value: Int32) -> String { + switch presentationStringsPluralizationForm(self.lc, value) { + case .zero: + return String(format: self._StickerPack_RemoveStickerCount_zero, "\(value)") + case .one: + return String(format: self._StickerPack_RemoveStickerCount_one, "\(value)") + case .two: + return String(format: self._StickerPack_RemoveStickerCount_two, "\(value)") + case .few: + return String(format: self._StickerPack_RemoveStickerCount_few, "\(value)") + case .many: + return String(format: self._StickerPack_RemoveStickerCount_many, "\(value)") + case .other: + return String(format: self._StickerPack_RemoveStickerCount_other, "\(value)") + } + } + private let _ServiceMessage_GameScoreSelfSimple_zero: String + private let _ServiceMessage_GameScoreSelfSimple_one: String + private let _ServiceMessage_GameScoreSelfSimple_two: String + private let _ServiceMessage_GameScoreSelfSimple_few: String + private let _ServiceMessage_GameScoreSelfSimple_many: String + private let _ServiceMessage_GameScoreSelfSimple_other: String + public func ServiceMessage_GameScoreSelfSimple(_ value: Int32) -> String { + switch presentationStringsPluralizationForm(self.lc, value) { + case .zero: + return String(format: self._ServiceMessage_GameScoreSelfSimple_zero, "\(value)") + case .one: + return String(format: self._ServiceMessage_GameScoreSelfSimple_one, "\(value)") + case .two: + return String(format: self._ServiceMessage_GameScoreSelfSimple_two, "\(value)") + case .few: + return String(format: self._ServiceMessage_GameScoreSelfSimple_few, "\(value)") + case .many: + return String(format: self._ServiceMessage_GameScoreSelfSimple_many, "\(value)") + case .other: + return String(format: self._ServiceMessage_GameScoreSelfSimple_other, "\(value)") + } + } + private let _Notifications_ExceptionMuteExpires_Hours_zero: String + private let _Notifications_ExceptionMuteExpires_Hours_one: String + private let _Notifications_ExceptionMuteExpires_Hours_two: String + private let _Notifications_ExceptionMuteExpires_Hours_few: String + private let _Notifications_ExceptionMuteExpires_Hours_many: String + private let _Notifications_ExceptionMuteExpires_Hours_other: String + public func Notifications_ExceptionMuteExpires_Hours(_ value: Int32) -> String { + switch presentationStringsPluralizationForm(self.lc, value) { + case .zero: + return String(format: self._Notifications_ExceptionMuteExpires_Hours_zero, "\(value)") + case .one: + return String(format: self._Notifications_ExceptionMuteExpires_Hours_one, "\(value)") + case .two: + return String(format: self._Notifications_ExceptionMuteExpires_Hours_two, "\(value)") + case .few: + return String(format: self._Notifications_ExceptionMuteExpires_Hours_few, "\(value)") + case .many: + return String(format: self._Notifications_ExceptionMuteExpires_Hours_many, "\(value)") + case .other: + return String(format: self._Notifications_ExceptionMuteExpires_Hours_other, "\(value)") + } + } + private let _ForwardedMessages_zero: String + private let _ForwardedMessages_one: String + private let _ForwardedMessages_two: String + private let _ForwardedMessages_few: String + private let _ForwardedMessages_many: String + private let _ForwardedMessages_other: String + public func ForwardedMessages(_ value: Int32) -> String { + switch presentationStringsPluralizationForm(self.lc, value) { + case .zero: + return String(format: self._ForwardedMessages_zero, "\(value)") + case .one: + return String(format: self._ForwardedMessages_one, "\(value)") + case .two: + return String(format: self._ForwardedMessages_two, "\(value)") + case .few: + return String(format: self._ForwardedMessages_few, "\(value)") + case .many: + return String(format: self._ForwardedMessages_many, "\(value)") + case .other: + return String(format: self._ForwardedMessages_other, "\(value)") + } + } + private let _ServiceMessage_GameScoreExtended_zero: String + private let _ServiceMessage_GameScoreExtended_one: String + private let _ServiceMessage_GameScoreExtended_two: String + private let _ServiceMessage_GameScoreExtended_few: String + private let _ServiceMessage_GameScoreExtended_many: String + private let _ServiceMessage_GameScoreExtended_other: String + public func ServiceMessage_GameScoreExtended(_ value: Int32) -> String { + switch presentationStringsPluralizationForm(self.lc, value) { + case .zero: + return String(format: self._ServiceMessage_GameScoreExtended_zero, "\(value)") + case .one: + return String(format: self._ServiceMessage_GameScoreExtended_one, "\(value)") + case .two: + return String(format: self._ServiceMessage_GameScoreExtended_two, "\(value)") + case .few: + return String(format: self._ServiceMessage_GameScoreExtended_few, "\(value)") + case .many: + return String(format: self._ServiceMessage_GameScoreExtended_many, "\(value)") + case .other: + return String(format: self._ServiceMessage_GameScoreExtended_other, "\(value)") + } + } + private let _Notifications_Exceptions_zero: String + private let _Notifications_Exceptions_one: String + private let _Notifications_Exceptions_two: String + private let _Notifications_Exceptions_few: String + private let _Notifications_Exceptions_many: String + private let _Notifications_Exceptions_other: String + public func Notifications_Exceptions(_ value: Int32) -> String { + switch presentationStringsPluralizationForm(self.lc, value) { + case .zero: + return String(format: self._Notifications_Exceptions_zero, "\(value)") + case .one: + return String(format: self._Notifications_Exceptions_one, "\(value)") + case .two: + return String(format: self._Notifications_Exceptions_two, "\(value)") + case .few: + return String(format: self._Notifications_Exceptions_few, "\(value)") + case .many: + return String(format: self._Notifications_Exceptions_many, "\(value)") + case .other: + return String(format: self._Notifications_Exceptions_other, "\(value)") + } + } + private let _Passport_Scans_zero: String + private let _Passport_Scans_one: String + private let _Passport_Scans_two: String + private let _Passport_Scans_few: String + private let _Passport_Scans_many: String + private let _Passport_Scans_other: String + public func Passport_Scans(_ value: Int32) -> String { + switch presentationStringsPluralizationForm(self.lc, value) { + case .zero: + return String(format: self._Passport_Scans_zero, "\(value)") + case .one: + return String(format: self._Passport_Scans_one, "\(value)") + case .two: + return String(format: self._Passport_Scans_two, "\(value)") + case .few: + return String(format: self._Passport_Scans_few, "\(value)") + case .many: + return String(format: self._Passport_Scans_many, "\(value)") + case .other: + return String(format: self._Passport_Scans_other, "\(value)") + } + } init(languageCode: String, dict: [String: String]) { @@ -5540,6 +5541,7 @@ public final class PresentationStrings { self._DialogList_PinLimitError_r = extractArgumentRanges(self._DialogList_PinLimitError) self.FastTwoStepSetup_PasswordSection = getValue(dict, "FastTwoStepSetup.PasswordSection") self.FastTwoStepSetup_EmailSection = getValue(dict, "FastTwoStepSetup.EmailSection") + self.Notifications_Badge_IncludeChannels = getValue(dict, "Notifications.Badge.IncludeChannels") self.Cache_ClearCache = getValue(dict, "Cache.ClearCache") self.Common_Close = getValue(dict, "Common.Close") self.Passport_PasswordDescription = getValue(dict, "Passport.PasswordDescription") @@ -6589,7 +6591,6 @@ public final class PresentationStrings { self._LOCKED_MESSAGE_r = extractArgumentRanges(self._LOCKED_MESSAGE) self.Conversation_ClearPrivateHistory = getValue(dict, "Conversation.ClearPrivateHistory") self.Conversation_ContextMenuShare = getValue(dict, "Conversation.ContextMenuShare") - self.Notifications_ExceptionsResetToDefaults = getValue(dict, "Notifications.ExceptionsResetToDefaults") self.Notifications_ExceptionsNone = getValue(dict, "Notifications.ExceptionsNone") self._Time_MonthOfYear_m6 = getValue(dict, "Time.MonthOfYear_m6") self._Time_MonthOfYear_m6_r = extractArgumentRanges(self._Time_MonthOfYear_m6) @@ -7726,6 +7727,7 @@ public final class PresentationStrings { self.LastSeen_Offline = getValue(dict, "LastSeen.Offline") self.Login_CodeFloodError = getValue(dict, "Login.CodeFloodError") self.Conversation_EncryptedDescription3 = getValue(dict, "Conversation.EncryptedDescription3") + self.Notifications_Badge_IncludePublicGroups = getValue(dict, "Notifications.Badge.IncludePublicGroups") self.Conversation_EncryptedDescription4 = getValue(dict, "Conversation.EncryptedDescription4") self.AppleWatch_Title = getValue(dict, "AppleWatch.Title") self.Contacts_AccessDeniedError = getValue(dict, "Contacts.AccessDeniedError") @@ -8044,306 +8046,162 @@ public final class PresentationStrings { self.PrivacySettings_PasscodeAndFaceId = getValue(dict, "PrivacySettings.PasscodeAndFaceId") self.Settings_ChatBackground = getValue(dict, "Settings.ChatBackground") self.Login_TermsOfServiceDecline = getValue(dict, "Login.TermsOfServiceDecline") - self._LastSeen_MinutesAgo_zero = getValueWithForm(dict, "LastSeen.MinutesAgo", .zero) - self._LastSeen_MinutesAgo_one = getValueWithForm(dict, "LastSeen.MinutesAgo", .one) - self._LastSeen_MinutesAgo_two = getValueWithForm(dict, "LastSeen.MinutesAgo", .two) - self._LastSeen_MinutesAgo_few = getValueWithForm(dict, "LastSeen.MinutesAgo", .few) - self._LastSeen_MinutesAgo_many = getValueWithForm(dict, "LastSeen.MinutesAgo", .many) - self._LastSeen_MinutesAgo_other = getValueWithForm(dict, "LastSeen.MinutesAgo", .other) - self._Passport_Scans_zero = getValueWithForm(dict, "Passport.Scans", .zero) - self._Passport_Scans_one = getValueWithForm(dict, "Passport.Scans", .one) - self._Passport_Scans_two = getValueWithForm(dict, "Passport.Scans", .two) - self._Passport_Scans_few = getValueWithForm(dict, "Passport.Scans", .few) - self._Passport_Scans_many = getValueWithForm(dict, "Passport.Scans", .many) - self._Passport_Scans_other = getValueWithForm(dict, "Passport.Scans", .other) - self._Notifications_Exceptions_zero = getValueWithForm(dict, "Notifications.Exceptions", .zero) - self._Notifications_Exceptions_one = getValueWithForm(dict, "Notifications.Exceptions", .one) - self._Notifications_Exceptions_two = getValueWithForm(dict, "Notifications.Exceptions", .two) - self._Notifications_Exceptions_few = getValueWithForm(dict, "Notifications.Exceptions", .few) - self._Notifications_Exceptions_many = getValueWithForm(dict, "Notifications.Exceptions", .many) - self._Notifications_Exceptions_other = getValueWithForm(dict, "Notifications.Exceptions", .other) - self._MuteExpires_Days_zero = getValueWithForm(dict, "MuteExpires.Days", .zero) - self._MuteExpires_Days_one = getValueWithForm(dict, "MuteExpires.Days", .one) - self._MuteExpires_Days_two = getValueWithForm(dict, "MuteExpires.Days", .two) - self._MuteExpires_Days_few = getValueWithForm(dict, "MuteExpires.Days", .few) - self._MuteExpires_Days_many = getValueWithForm(dict, "MuteExpires.Days", .many) - self._MuteExpires_Days_other = getValueWithForm(dict, "MuteExpires.Days", .other) - self._Watch_UserInfo_Mute_zero = getValueWithForm(dict, "Watch.UserInfo.Mute", .zero) - self._Watch_UserInfo_Mute_one = getValueWithForm(dict, "Watch.UserInfo.Mute", .one) - self._Watch_UserInfo_Mute_two = getValueWithForm(dict, "Watch.UserInfo.Mute", .two) - self._Watch_UserInfo_Mute_few = getValueWithForm(dict, "Watch.UserInfo.Mute", .few) - self._Watch_UserInfo_Mute_many = getValueWithForm(dict, "Watch.UserInfo.Mute", .many) - self._Watch_UserInfo_Mute_other = getValueWithForm(dict, "Watch.UserInfo.Mute", .other) - self._MessageTimer_Days_zero = getValueWithForm(dict, "MessageTimer.Days", .zero) - self._MessageTimer_Days_one = getValueWithForm(dict, "MessageTimer.Days", .one) - self._MessageTimer_Days_two = getValueWithForm(dict, "MessageTimer.Days", .two) - self._MessageTimer_Days_few = getValueWithForm(dict, "MessageTimer.Days", .few) - self._MessageTimer_Days_many = getValueWithForm(dict, "MessageTimer.Days", .many) - self._MessageTimer_Days_other = getValueWithForm(dict, "MessageTimer.Days", .other) - self._Map_ETAMinutes_zero = getValueWithForm(dict, "Map.ETAMinutes", .zero) - self._Map_ETAMinutes_one = getValueWithForm(dict, "Map.ETAMinutes", .one) - self._Map_ETAMinutes_two = getValueWithForm(dict, "Map.ETAMinutes", .two) - self._Map_ETAMinutes_few = getValueWithForm(dict, "Map.ETAMinutes", .few) - self._Map_ETAMinutes_many = getValueWithForm(dict, "Map.ETAMinutes", .many) - self._Map_ETAMinutes_other = getValueWithForm(dict, "Map.ETAMinutes", .other) - self._Contacts_ImportersCount_zero = getValueWithForm(dict, "Contacts.ImportersCount", .zero) - self._Contacts_ImportersCount_one = getValueWithForm(dict, "Contacts.ImportersCount", .one) - self._Contacts_ImportersCount_two = getValueWithForm(dict, "Contacts.ImportersCount", .two) - self._Contacts_ImportersCount_few = getValueWithForm(dict, "Contacts.ImportersCount", .few) - self._Contacts_ImportersCount_many = getValueWithForm(dict, "Contacts.ImportersCount", .many) - self._Contacts_ImportersCount_other = getValueWithForm(dict, "Contacts.ImportersCount", .other) - self._StickerPack_AddStickerCount_zero = getValueWithForm(dict, "StickerPack.AddStickerCount", .zero) - self._StickerPack_AddStickerCount_one = getValueWithForm(dict, "StickerPack.AddStickerCount", .one) - self._StickerPack_AddStickerCount_two = getValueWithForm(dict, "StickerPack.AddStickerCount", .two) - self._StickerPack_AddStickerCount_few = getValueWithForm(dict, "StickerPack.AddStickerCount", .few) - self._StickerPack_AddStickerCount_many = getValueWithForm(dict, "StickerPack.AddStickerCount", .many) - self._StickerPack_AddStickerCount_other = getValueWithForm(dict, "StickerPack.AddStickerCount", .other) - self._UserCount_zero = getValueWithForm(dict, "UserCount", .zero) - self._UserCount_one = getValueWithForm(dict, "UserCount", .one) - self._UserCount_two = getValueWithForm(dict, "UserCount", .two) - self._UserCount_few = getValueWithForm(dict, "UserCount", .few) - self._UserCount_many = getValueWithForm(dict, "UserCount", .many) - self._UserCount_other = getValueWithForm(dict, "UserCount", .other) - self._Notifications_ExceptionMuteExpires_Days_zero = getValueWithForm(dict, "Notifications.ExceptionMuteExpires.Days", .zero) - self._Notifications_ExceptionMuteExpires_Days_one = getValueWithForm(dict, "Notifications.ExceptionMuteExpires.Days", .one) - self._Notifications_ExceptionMuteExpires_Days_two = getValueWithForm(dict, "Notifications.ExceptionMuteExpires.Days", .two) - self._Notifications_ExceptionMuteExpires_Days_few = getValueWithForm(dict, "Notifications.ExceptionMuteExpires.Days", .few) - self._Notifications_ExceptionMuteExpires_Days_many = getValueWithForm(dict, "Notifications.ExceptionMuteExpires.Days", .many) - self._Notifications_ExceptionMuteExpires_Days_other = getValueWithForm(dict, "Notifications.ExceptionMuteExpires.Days", .other) - self._MuteFor_Hours_zero = getValueWithForm(dict, "MuteFor.Hours", .zero) - self._MuteFor_Hours_one = getValueWithForm(dict, "MuteFor.Hours", .one) - self._MuteFor_Hours_two = getValueWithForm(dict, "MuteFor.Hours", .two) - self._MuteFor_Hours_few = getValueWithForm(dict, "MuteFor.Hours", .few) - self._MuteFor_Hours_many = getValueWithForm(dict, "MuteFor.Hours", .many) - self._MuteFor_Hours_other = getValueWithForm(dict, "MuteFor.Hours", .other) self._MessageTimer_Hours_zero = getValueWithForm(dict, "MessageTimer.Hours", .zero) self._MessageTimer_Hours_one = getValueWithForm(dict, "MessageTimer.Hours", .one) self._MessageTimer_Hours_two = getValueWithForm(dict, "MessageTimer.Hours", .two) self._MessageTimer_Hours_few = getValueWithForm(dict, "MessageTimer.Hours", .few) self._MessageTimer_Hours_many = getValueWithForm(dict, "MessageTimer.Hours", .many) self._MessageTimer_Hours_other = getValueWithForm(dict, "MessageTimer.Hours", .other) - self._Media_SharePhoto_zero = getValueWithForm(dict, "Media.SharePhoto", .zero) - self._Media_SharePhoto_one = getValueWithForm(dict, "Media.SharePhoto", .one) - self._Media_SharePhoto_two = getValueWithForm(dict, "Media.SharePhoto", .two) - self._Media_SharePhoto_few = getValueWithForm(dict, "Media.SharePhoto", .few) - self._Media_SharePhoto_many = getValueWithForm(dict, "Media.SharePhoto", .many) - self._Media_SharePhoto_other = getValueWithForm(dict, "Media.SharePhoto", .other) - self._Map_ETAHours_zero = getValueWithForm(dict, "Map.ETAHours", .zero) - self._Map_ETAHours_one = getValueWithForm(dict, "Map.ETAHours", .one) - self._Map_ETAHours_two = getValueWithForm(dict, "Map.ETAHours", .two) - self._Map_ETAHours_few = getValueWithForm(dict, "Map.ETAHours", .few) - self._Map_ETAHours_many = getValueWithForm(dict, "Map.ETAHours", .many) - self._Map_ETAHours_other = getValueWithForm(dict, "Map.ETAHours", .other) - self._Conversation_StatusMembers_zero = getValueWithForm(dict, "Conversation.StatusMembers", .zero) - self._Conversation_StatusMembers_one = getValueWithForm(dict, "Conversation.StatusMembers", .one) - self._Conversation_StatusMembers_two = getValueWithForm(dict, "Conversation.StatusMembers", .two) - self._Conversation_StatusMembers_few = getValueWithForm(dict, "Conversation.StatusMembers", .few) - self._Conversation_StatusMembers_many = getValueWithForm(dict, "Conversation.StatusMembers", .many) - self._Conversation_StatusMembers_other = getValueWithForm(dict, "Conversation.StatusMembers", .other) - self._ServiceMessage_GameScoreSelfExtended_zero = getValueWithForm(dict, "ServiceMessage.GameScoreSelfExtended", .zero) - self._ServiceMessage_GameScoreSelfExtended_one = getValueWithForm(dict, "ServiceMessage.GameScoreSelfExtended", .one) - self._ServiceMessage_GameScoreSelfExtended_two = getValueWithForm(dict, "ServiceMessage.GameScoreSelfExtended", .two) - self._ServiceMessage_GameScoreSelfExtended_few = getValueWithForm(dict, "ServiceMessage.GameScoreSelfExtended", .few) - self._ServiceMessage_GameScoreSelfExtended_many = getValueWithForm(dict, "ServiceMessage.GameScoreSelfExtended", .many) - self._ServiceMessage_GameScoreSelfExtended_other = getValueWithForm(dict, "ServiceMessage.GameScoreSelfExtended", .other) - self._SharedMedia_Link_zero = getValueWithForm(dict, "SharedMedia.Link", .zero) - self._SharedMedia_Link_one = getValueWithForm(dict, "SharedMedia.Link", .one) - self._SharedMedia_Link_two = getValueWithForm(dict, "SharedMedia.Link", .two) - self._SharedMedia_Link_few = getValueWithForm(dict, "SharedMedia.Link", .few) - self._SharedMedia_Link_many = getValueWithForm(dict, "SharedMedia.Link", .many) - self._SharedMedia_Link_other = getValueWithForm(dict, "SharedMedia.Link", .other) - self._Media_ShareItem_zero = getValueWithForm(dict, "Media.ShareItem", .zero) - self._Media_ShareItem_one = getValueWithForm(dict, "Media.ShareItem", .one) - self._Media_ShareItem_two = getValueWithForm(dict, "Media.ShareItem", .two) - self._Media_ShareItem_few = getValueWithForm(dict, "Media.ShareItem", .few) - self._Media_ShareItem_many = getValueWithForm(dict, "Media.ShareItem", .many) - self._Media_ShareItem_other = getValueWithForm(dict, "Media.ShareItem", .other) - self._PasscodeSettings_FailedAttempts_zero = getValueWithForm(dict, "PasscodeSettings.FailedAttempts", .zero) - self._PasscodeSettings_FailedAttempts_one = getValueWithForm(dict, "PasscodeSettings.FailedAttempts", .one) - self._PasscodeSettings_FailedAttempts_two = getValueWithForm(dict, "PasscodeSettings.FailedAttempts", .two) - self._PasscodeSettings_FailedAttempts_few = getValueWithForm(dict, "PasscodeSettings.FailedAttempts", .few) - self._PasscodeSettings_FailedAttempts_many = getValueWithForm(dict, "PasscodeSettings.FailedAttempts", .many) - self._PasscodeSettings_FailedAttempts_other = getValueWithForm(dict, "PasscodeSettings.FailedAttempts", .other) self._Notification_GameScoreSelfSimple_zero = getValueWithForm(dict, "Notification.GameScoreSelfSimple", .zero) self._Notification_GameScoreSelfSimple_one = getValueWithForm(dict, "Notification.GameScoreSelfSimple", .one) self._Notification_GameScoreSelfSimple_two = getValueWithForm(dict, "Notification.GameScoreSelfSimple", .two) self._Notification_GameScoreSelfSimple_few = getValueWithForm(dict, "Notification.GameScoreSelfSimple", .few) self._Notification_GameScoreSelfSimple_many = getValueWithForm(dict, "Notification.GameScoreSelfSimple", .many) self._Notification_GameScoreSelfSimple_other = getValueWithForm(dict, "Notification.GameScoreSelfSimple", .other) - self._Notifications_ExceptionMuteExpires_Hours_zero = getValueWithForm(dict, "Notifications.ExceptionMuteExpires.Hours", .zero) - self._Notifications_ExceptionMuteExpires_Hours_one = getValueWithForm(dict, "Notifications.ExceptionMuteExpires.Hours", .one) - self._Notifications_ExceptionMuteExpires_Hours_two = getValueWithForm(dict, "Notifications.ExceptionMuteExpires.Hours", .two) - self._Notifications_ExceptionMuteExpires_Hours_few = getValueWithForm(dict, "Notifications.ExceptionMuteExpires.Hours", .few) - self._Notifications_ExceptionMuteExpires_Hours_many = getValueWithForm(dict, "Notifications.ExceptionMuteExpires.Hours", .many) - self._Notifications_ExceptionMuteExpires_Hours_other = getValueWithForm(dict, "Notifications.ExceptionMuteExpires.Hours", .other) - self._LastSeen_HoursAgo_zero = getValueWithForm(dict, "LastSeen.HoursAgo", .zero) - self._LastSeen_HoursAgo_one = getValueWithForm(dict, "LastSeen.HoursAgo", .one) - self._LastSeen_HoursAgo_two = getValueWithForm(dict, "LastSeen.HoursAgo", .two) - self._LastSeen_HoursAgo_few = getValueWithForm(dict, "LastSeen.HoursAgo", .few) - self._LastSeen_HoursAgo_many = getValueWithForm(dict, "LastSeen.HoursAgo", .many) - self._LastSeen_HoursAgo_other = getValueWithForm(dict, "LastSeen.HoursAgo", .other) - self._MessageTimer_Seconds_zero = getValueWithForm(dict, "MessageTimer.Seconds", .zero) - self._MessageTimer_Seconds_one = getValueWithForm(dict, "MessageTimer.Seconds", .one) - self._MessageTimer_Seconds_two = getValueWithForm(dict, "MessageTimer.Seconds", .two) - self._MessageTimer_Seconds_few = getValueWithForm(dict, "MessageTimer.Seconds", .few) - self._MessageTimer_Seconds_many = getValueWithForm(dict, "MessageTimer.Seconds", .many) - self._MessageTimer_Seconds_other = getValueWithForm(dict, "MessageTimer.Seconds", .other) - self._Notification_GameScoreSelfExtended_zero = getValueWithForm(dict, "Notification.GameScoreSelfExtended", .zero) - self._Notification_GameScoreSelfExtended_one = getValueWithForm(dict, "Notification.GameScoreSelfExtended", .one) - self._Notification_GameScoreSelfExtended_two = getValueWithForm(dict, "Notification.GameScoreSelfExtended", .two) - self._Notification_GameScoreSelfExtended_few = getValueWithForm(dict, "Notification.GameScoreSelfExtended", .few) - self._Notification_GameScoreSelfExtended_many = getValueWithForm(dict, "Notification.GameScoreSelfExtended", .many) - self._Notification_GameScoreSelfExtended_other = getValueWithForm(dict, "Notification.GameScoreSelfExtended", .other) - self._Notification_GameScoreSimple_zero = getValueWithForm(dict, "Notification.GameScoreSimple", .zero) - self._Notification_GameScoreSimple_one = getValueWithForm(dict, "Notification.GameScoreSimple", .one) - self._Notification_GameScoreSimple_two = getValueWithForm(dict, "Notification.GameScoreSimple", .two) - self._Notification_GameScoreSimple_few = getValueWithForm(dict, "Notification.GameScoreSimple", .few) - self._Notification_GameScoreSimple_many = getValueWithForm(dict, "Notification.GameScoreSimple", .many) - self._Notification_GameScoreSimple_other = getValueWithForm(dict, "Notification.GameScoreSimple", .other) - self._MuteFor_Days_zero = getValueWithForm(dict, "MuteFor.Days", .zero) - self._MuteFor_Days_one = getValueWithForm(dict, "MuteFor.Days", .one) - self._MuteFor_Days_two = getValueWithForm(dict, "MuteFor.Days", .two) - self._MuteFor_Days_few = getValueWithForm(dict, "MuteFor.Days", .few) - self._MuteFor_Days_many = getValueWithForm(dict, "MuteFor.Days", .many) - self._MuteFor_Days_other = getValueWithForm(dict, "MuteFor.Days", .other) self._Conversation_StatusSubscribers_zero = getValueWithForm(dict, "Conversation.StatusSubscribers", .zero) self._Conversation_StatusSubscribers_one = getValueWithForm(dict, "Conversation.StatusSubscribers", .one) self._Conversation_StatusSubscribers_two = getValueWithForm(dict, "Conversation.StatusSubscribers", .two) self._Conversation_StatusSubscribers_few = getValueWithForm(dict, "Conversation.StatusSubscribers", .few) self._Conversation_StatusSubscribers_many = getValueWithForm(dict, "Conversation.StatusSubscribers", .many) self._Conversation_StatusSubscribers_other = getValueWithForm(dict, "Conversation.StatusSubscribers", .other) - self._ForwardedGifs_zero = getValueWithForm(dict, "ForwardedGifs", .zero) - self._ForwardedGifs_one = getValueWithForm(dict, "ForwardedGifs", .one) - self._ForwardedGifs_two = getValueWithForm(dict, "ForwardedGifs", .two) - self._ForwardedGifs_few = getValueWithForm(dict, "ForwardedGifs", .few) - self._ForwardedGifs_many = getValueWithForm(dict, "ForwardedGifs", .many) - self._ForwardedGifs_other = getValueWithForm(dict, "ForwardedGifs", .other) - self._StickerPack_AddMaskCount_zero = getValueWithForm(dict, "StickerPack.AddMaskCount", .zero) - self._StickerPack_AddMaskCount_one = getValueWithForm(dict, "StickerPack.AddMaskCount", .one) - self._StickerPack_AddMaskCount_two = getValueWithForm(dict, "StickerPack.AddMaskCount", .two) - self._StickerPack_AddMaskCount_few = getValueWithForm(dict, "StickerPack.AddMaskCount", .few) - self._StickerPack_AddMaskCount_many = getValueWithForm(dict, "StickerPack.AddMaskCount", .many) - self._StickerPack_AddMaskCount_other = getValueWithForm(dict, "StickerPack.AddMaskCount", .other) - self._MessageTimer_ShortSeconds_zero = getValueWithForm(dict, "MessageTimer.ShortSeconds", .zero) - self._MessageTimer_ShortSeconds_one = getValueWithForm(dict, "MessageTimer.ShortSeconds", .one) - self._MessageTimer_ShortSeconds_two = getValueWithForm(dict, "MessageTimer.ShortSeconds", .two) - self._MessageTimer_ShortSeconds_few = getValueWithForm(dict, "MessageTimer.ShortSeconds", .few) - self._MessageTimer_ShortSeconds_many = getValueWithForm(dict, "MessageTimer.ShortSeconds", .many) - self._MessageTimer_ShortSeconds_other = getValueWithForm(dict, "MessageTimer.ShortSeconds", .other) - self._AttachmentMenu_SendPhoto_zero = getValueWithForm(dict, "AttachmentMenu.SendPhoto", .zero) - self._AttachmentMenu_SendPhoto_one = getValueWithForm(dict, "AttachmentMenu.SendPhoto", .one) - self._AttachmentMenu_SendPhoto_two = getValueWithForm(dict, "AttachmentMenu.SendPhoto", .two) - self._AttachmentMenu_SendPhoto_few = getValueWithForm(dict, "AttachmentMenu.SendPhoto", .few) - self._AttachmentMenu_SendPhoto_many = getValueWithForm(dict, "AttachmentMenu.SendPhoto", .many) - self._AttachmentMenu_SendPhoto_other = getValueWithForm(dict, "AttachmentMenu.SendPhoto", .other) - self._Notifications_ExceptionMuteExpires_Minutes_zero = getValueWithForm(dict, "Notifications.ExceptionMuteExpires.Minutes", .zero) - self._Notifications_ExceptionMuteExpires_Minutes_one = getValueWithForm(dict, "Notifications.ExceptionMuteExpires.Minutes", .one) - self._Notifications_ExceptionMuteExpires_Minutes_two = getValueWithForm(dict, "Notifications.ExceptionMuteExpires.Minutes", .two) - self._Notifications_ExceptionMuteExpires_Minutes_few = getValueWithForm(dict, "Notifications.ExceptionMuteExpires.Minutes", .few) - self._Notifications_ExceptionMuteExpires_Minutes_many = getValueWithForm(dict, "Notifications.ExceptionMuteExpires.Minutes", .many) - self._Notifications_ExceptionMuteExpires_Minutes_other = getValueWithForm(dict, "Notifications.ExceptionMuteExpires.Minutes", .other) - self._MessageTimer_ShortWeeks_zero = getValueWithForm(dict, "MessageTimer.ShortWeeks", .zero) - self._MessageTimer_ShortWeeks_one = getValueWithForm(dict, "MessageTimer.ShortWeeks", .one) - self._MessageTimer_ShortWeeks_two = getValueWithForm(dict, "MessageTimer.ShortWeeks", .two) - self._MessageTimer_ShortWeeks_few = getValueWithForm(dict, "MessageTimer.ShortWeeks", .few) - self._MessageTimer_ShortWeeks_many = getValueWithForm(dict, "MessageTimer.ShortWeeks", .many) - self._MessageTimer_ShortWeeks_other = getValueWithForm(dict, "MessageTimer.ShortWeeks", .other) - self._ForwardedStickers_zero = getValueWithForm(dict, "ForwardedStickers", .zero) - self._ForwardedStickers_one = getValueWithForm(dict, "ForwardedStickers", .one) - self._ForwardedStickers_two = getValueWithForm(dict, "ForwardedStickers", .two) - self._ForwardedStickers_few = getValueWithForm(dict, "ForwardedStickers", .few) - self._ForwardedStickers_many = getValueWithForm(dict, "ForwardedStickers", .many) - self._ForwardedStickers_other = getValueWithForm(dict, "ForwardedStickers", .other) - self._Forward_ConfirmMultipleFiles_zero = getValueWithForm(dict, "Forward.ConfirmMultipleFiles", .zero) - self._Forward_ConfirmMultipleFiles_one = getValueWithForm(dict, "Forward.ConfirmMultipleFiles", .one) - self._Forward_ConfirmMultipleFiles_two = getValueWithForm(dict, "Forward.ConfirmMultipleFiles", .two) - self._Forward_ConfirmMultipleFiles_few = getValueWithForm(dict, "Forward.ConfirmMultipleFiles", .few) - self._Forward_ConfirmMultipleFiles_many = getValueWithForm(dict, "Forward.ConfirmMultipleFiles", .many) - self._Forward_ConfirmMultipleFiles_other = getValueWithForm(dict, "Forward.ConfirmMultipleFiles", .other) - self._SharedMedia_Photo_zero = getValueWithForm(dict, "SharedMedia.Photo", .zero) - self._SharedMedia_Photo_one = getValueWithForm(dict, "SharedMedia.Photo", .one) - self._SharedMedia_Photo_two = getValueWithForm(dict, "SharedMedia.Photo", .two) - self._SharedMedia_Photo_few = getValueWithForm(dict, "SharedMedia.Photo", .few) - self._SharedMedia_Photo_many = getValueWithForm(dict, "SharedMedia.Photo", .many) - self._SharedMedia_Photo_other = getValueWithForm(dict, "SharedMedia.Photo", .other) - self._StickerPack_RemoveStickerCount_zero = getValueWithForm(dict, "StickerPack.RemoveStickerCount", .zero) - self._StickerPack_RemoveStickerCount_one = getValueWithForm(dict, "StickerPack.RemoveStickerCount", .one) - self._StickerPack_RemoveStickerCount_two = getValueWithForm(dict, "StickerPack.RemoveStickerCount", .two) - self._StickerPack_RemoveStickerCount_few = getValueWithForm(dict, "StickerPack.RemoveStickerCount", .few) - self._StickerPack_RemoveStickerCount_many = getValueWithForm(dict, "StickerPack.RemoveStickerCount", .many) - self._StickerPack_RemoveStickerCount_other = getValueWithForm(dict, "StickerPack.RemoveStickerCount", .other) - self._InviteText_ContactsCountText_zero = getValueWithForm(dict, "InviteText.ContactsCountText", .zero) - self._InviteText_ContactsCountText_one = getValueWithForm(dict, "InviteText.ContactsCountText", .one) - self._InviteText_ContactsCountText_two = getValueWithForm(dict, "InviteText.ContactsCountText", .two) - self._InviteText_ContactsCountText_few = getValueWithForm(dict, "InviteText.ContactsCountText", .few) - self._InviteText_ContactsCountText_many = getValueWithForm(dict, "InviteText.ContactsCountText", .many) - self._InviteText_ContactsCountText_other = getValueWithForm(dict, "InviteText.ContactsCountText", .other) - self._MuteExpires_Hours_zero = getValueWithForm(dict, "MuteExpires.Hours", .zero) - self._MuteExpires_Hours_one = getValueWithForm(dict, "MuteExpires.Hours", .one) - self._MuteExpires_Hours_two = getValueWithForm(dict, "MuteExpires.Hours", .two) - self._MuteExpires_Hours_few = getValueWithForm(dict, "MuteExpires.Hours", .few) - self._MuteExpires_Hours_many = getValueWithForm(dict, "MuteExpires.Hours", .many) - self._MuteExpires_Hours_other = getValueWithForm(dict, "MuteExpires.Hours", .other) + self._Watch_LastSeen_HoursAgo_zero = getValueWithForm(dict, "Watch.LastSeen.HoursAgo", .zero) + self._Watch_LastSeen_HoursAgo_one = getValueWithForm(dict, "Watch.LastSeen.HoursAgo", .one) + self._Watch_LastSeen_HoursAgo_two = getValueWithForm(dict, "Watch.LastSeen.HoursAgo", .two) + self._Watch_LastSeen_HoursAgo_few = getValueWithForm(dict, "Watch.LastSeen.HoursAgo", .few) + self._Watch_LastSeen_HoursAgo_many = getValueWithForm(dict, "Watch.LastSeen.HoursAgo", .many) + self._Watch_LastSeen_HoursAgo_other = getValueWithForm(dict, "Watch.LastSeen.HoursAgo", .other) self._Notification_GameScoreExtended_zero = getValueWithForm(dict, "Notification.GameScoreExtended", .zero) self._Notification_GameScoreExtended_one = getValueWithForm(dict, "Notification.GameScoreExtended", .one) self._Notification_GameScoreExtended_two = getValueWithForm(dict, "Notification.GameScoreExtended", .two) self._Notification_GameScoreExtended_few = getValueWithForm(dict, "Notification.GameScoreExtended", .few) self._Notification_GameScoreExtended_many = getValueWithForm(dict, "Notification.GameScoreExtended", .many) self._Notification_GameScoreExtended_other = getValueWithForm(dict, "Notification.GameScoreExtended", .other) - self._MuteExpires_Minutes_zero = getValueWithForm(dict, "MuteExpires.Minutes", .zero) - self._MuteExpires_Minutes_one = getValueWithForm(dict, "MuteExpires.Minutes", .one) - self._MuteExpires_Minutes_two = getValueWithForm(dict, "MuteExpires.Minutes", .two) - self._MuteExpires_Minutes_few = getValueWithForm(dict, "MuteExpires.Minutes", .few) - self._MuteExpires_Minutes_many = getValueWithForm(dict, "MuteExpires.Minutes", .many) - self._MuteExpires_Minutes_other = getValueWithForm(dict, "MuteExpires.Minutes", .other) - self._AttachmentMenu_SendItem_zero = getValueWithForm(dict, "AttachmentMenu.SendItem", .zero) - self._AttachmentMenu_SendItem_one = getValueWithForm(dict, "AttachmentMenu.SendItem", .one) - self._AttachmentMenu_SendItem_two = getValueWithForm(dict, "AttachmentMenu.SendItem", .two) - self._AttachmentMenu_SendItem_few = getValueWithForm(dict, "AttachmentMenu.SendItem", .few) - self._AttachmentMenu_SendItem_many = getValueWithForm(dict, "AttachmentMenu.SendItem", .many) - self._AttachmentMenu_SendItem_other = getValueWithForm(dict, "AttachmentMenu.SendItem", .other) - self._ForwardedAudios_zero = getValueWithForm(dict, "ForwardedAudios", .zero) - self._ForwardedAudios_one = getValueWithForm(dict, "ForwardedAudios", .one) - self._ForwardedAudios_two = getValueWithForm(dict, "ForwardedAudios", .two) - self._ForwardedAudios_few = getValueWithForm(dict, "ForwardedAudios", .few) - self._ForwardedAudios_many = getValueWithForm(dict, "ForwardedAudios", .many) - self._ForwardedAudios_other = getValueWithForm(dict, "ForwardedAudios", .other) - self._SharedMedia_File_zero = getValueWithForm(dict, "SharedMedia.File", .zero) - self._SharedMedia_File_one = getValueWithForm(dict, "SharedMedia.File", .one) - self._SharedMedia_File_two = getValueWithForm(dict, "SharedMedia.File", .two) - self._SharedMedia_File_few = getValueWithForm(dict, "SharedMedia.File", .few) - self._SharedMedia_File_many = getValueWithForm(dict, "SharedMedia.File", .many) - self._SharedMedia_File_other = getValueWithForm(dict, "SharedMedia.File", .other) - self._Call_ShortMinutes_zero = getValueWithForm(dict, "Call.ShortMinutes", .zero) - self._Call_ShortMinutes_one = getValueWithForm(dict, "Call.ShortMinutes", .one) - self._Call_ShortMinutes_two = getValueWithForm(dict, "Call.ShortMinutes", .two) - self._Call_ShortMinutes_few = getValueWithForm(dict, "Call.ShortMinutes", .few) - self._Call_ShortMinutes_many = getValueWithForm(dict, "Call.ShortMinutes", .many) - self._Call_ShortMinutes_other = getValueWithForm(dict, "Call.ShortMinutes", .other) - self._Call_Minutes_zero = getValueWithForm(dict, "Call.Minutes", .zero) - self._Call_Minutes_one = getValueWithForm(dict, "Call.Minutes", .one) - self._Call_Minutes_two = getValueWithForm(dict, "Call.Minutes", .two) - self._Call_Minutes_few = getValueWithForm(dict, "Call.Minutes", .few) - self._Call_Minutes_many = getValueWithForm(dict, "Call.Minutes", .many) - self._Call_Minutes_other = getValueWithForm(dict, "Call.Minutes", .other) + self._InviteText_ContactsCountText_zero = getValueWithForm(dict, "InviteText.ContactsCountText", .zero) + self._InviteText_ContactsCountText_one = getValueWithForm(dict, "InviteText.ContactsCountText", .one) + self._InviteText_ContactsCountText_two = getValueWithForm(dict, "InviteText.ContactsCountText", .two) + self._InviteText_ContactsCountText_few = getValueWithForm(dict, "InviteText.ContactsCountText", .few) + self._InviteText_ContactsCountText_many = getValueWithForm(dict, "InviteText.ContactsCountText", .many) + self._InviteText_ContactsCountText_other = getValueWithForm(dict, "InviteText.ContactsCountText", .other) + self._StickerPack_RemoveMaskCount_zero = getValueWithForm(dict, "StickerPack.RemoveMaskCount", .zero) + self._StickerPack_RemoveMaskCount_one = getValueWithForm(dict, "StickerPack.RemoveMaskCount", .one) + self._StickerPack_RemoveMaskCount_two = getValueWithForm(dict, "StickerPack.RemoveMaskCount", .two) + self._StickerPack_RemoveMaskCount_few = getValueWithForm(dict, "StickerPack.RemoveMaskCount", .few) + self._StickerPack_RemoveMaskCount_many = getValueWithForm(dict, "StickerPack.RemoveMaskCount", .many) + self._StickerPack_RemoveMaskCount_other = getValueWithForm(dict, "StickerPack.RemoveMaskCount", .other) + self._Map_ETAMinutes_zero = getValueWithForm(dict, "Map.ETAMinutes", .zero) + self._Map_ETAMinutes_one = getValueWithForm(dict, "Map.ETAMinutes", .one) + self._Map_ETAMinutes_two = getValueWithForm(dict, "Map.ETAMinutes", .two) + self._Map_ETAMinutes_few = getValueWithForm(dict, "Map.ETAMinutes", .few) + self._Map_ETAMinutes_many = getValueWithForm(dict, "Map.ETAMinutes", .many) + self._Map_ETAMinutes_other = getValueWithForm(dict, "Map.ETAMinutes", .other) + self._MessageTimer_Months_zero = getValueWithForm(dict, "MessageTimer.Months", .zero) + self._MessageTimer_Months_one = getValueWithForm(dict, "MessageTimer.Months", .one) + self._MessageTimer_Months_two = getValueWithForm(dict, "MessageTimer.Months", .two) + self._MessageTimer_Months_few = getValueWithForm(dict, "MessageTimer.Months", .few) + self._MessageTimer_Months_many = getValueWithForm(dict, "MessageTimer.Months", .many) + self._MessageTimer_Months_other = getValueWithForm(dict, "MessageTimer.Months", .other) self._Call_ShortSeconds_zero = getValueWithForm(dict, "Call.ShortSeconds", .zero) self._Call_ShortSeconds_one = getValueWithForm(dict, "Call.ShortSeconds", .one) self._Call_ShortSeconds_two = getValueWithForm(dict, "Call.ShortSeconds", .two) self._Call_ShortSeconds_few = getValueWithForm(dict, "Call.ShortSeconds", .few) self._Call_ShortSeconds_many = getValueWithForm(dict, "Call.ShortSeconds", .many) self._Call_ShortSeconds_other = getValueWithForm(dict, "Call.ShortSeconds", .other) - self._Invitation_Members_zero = getValueWithForm(dict, "Invitation.Members", .zero) - self._Invitation_Members_one = getValueWithForm(dict, "Invitation.Members", .one) - self._Invitation_Members_two = getValueWithForm(dict, "Invitation.Members", .two) - self._Invitation_Members_few = getValueWithForm(dict, "Invitation.Members", .few) - self._Invitation_Members_many = getValueWithForm(dict, "Invitation.Members", .many) - self._Invitation_Members_other = getValueWithForm(dict, "Invitation.Members", .other) - self._AttachmentMenu_SendGif_zero = getValueWithForm(dict, "AttachmentMenu.SendGif", .zero) - self._AttachmentMenu_SendGif_one = getValueWithForm(dict, "AttachmentMenu.SendGif", .one) - self._AttachmentMenu_SendGif_two = getValueWithForm(dict, "AttachmentMenu.SendGif", .two) - self._AttachmentMenu_SendGif_few = getValueWithForm(dict, "AttachmentMenu.SendGif", .few) - self._AttachmentMenu_SendGif_many = getValueWithForm(dict, "AttachmentMenu.SendGif", .many) - self._AttachmentMenu_SendGif_other = getValueWithForm(dict, "AttachmentMenu.SendGif", .other) + self._Media_ShareVideo_zero = getValueWithForm(dict, "Media.ShareVideo", .zero) + self._Media_ShareVideo_one = getValueWithForm(dict, "Media.ShareVideo", .one) + self._Media_ShareVideo_two = getValueWithForm(dict, "Media.ShareVideo", .two) + self._Media_ShareVideo_few = getValueWithForm(dict, "Media.ShareVideo", .few) + self._Media_ShareVideo_many = getValueWithForm(dict, "Media.ShareVideo", .many) + self._Media_ShareVideo_other = getValueWithForm(dict, "Media.ShareVideo", .other) + self._StickerPack_AddStickerCount_zero = getValueWithForm(dict, "StickerPack.AddStickerCount", .zero) + self._StickerPack_AddStickerCount_one = getValueWithForm(dict, "StickerPack.AddStickerCount", .one) + self._StickerPack_AddStickerCount_two = getValueWithForm(dict, "StickerPack.AddStickerCount", .two) + self._StickerPack_AddStickerCount_few = getValueWithForm(dict, "StickerPack.AddStickerCount", .few) + self._StickerPack_AddStickerCount_many = getValueWithForm(dict, "StickerPack.AddStickerCount", .many) + self._StickerPack_AddStickerCount_other = getValueWithForm(dict, "StickerPack.AddStickerCount", .other) + self._SharedMedia_File_zero = getValueWithForm(dict, "SharedMedia.File", .zero) + self._SharedMedia_File_one = getValueWithForm(dict, "SharedMedia.File", .one) + self._SharedMedia_File_two = getValueWithForm(dict, "SharedMedia.File", .two) + self._SharedMedia_File_few = getValueWithForm(dict, "SharedMedia.File", .few) + self._SharedMedia_File_many = getValueWithForm(dict, "SharedMedia.File", .many) + self._SharedMedia_File_other = getValueWithForm(dict, "SharedMedia.File", .other) + self._PasscodeSettings_FailedAttempts_zero = getValueWithForm(dict, "PasscodeSettings.FailedAttempts", .zero) + self._PasscodeSettings_FailedAttempts_one = getValueWithForm(dict, "PasscodeSettings.FailedAttempts", .one) + self._PasscodeSettings_FailedAttempts_two = getValueWithForm(dict, "PasscodeSettings.FailedAttempts", .two) + self._PasscodeSettings_FailedAttempts_few = getValueWithForm(dict, "PasscodeSettings.FailedAttempts", .few) + self._PasscodeSettings_FailedAttempts_many = getValueWithForm(dict, "PasscodeSettings.FailedAttempts", .many) + self._PasscodeSettings_FailedAttempts_other = getValueWithForm(dict, "PasscodeSettings.FailedAttempts", .other) + self._Call_Minutes_zero = getValueWithForm(dict, "Call.Minutes", .zero) + self._Call_Minutes_one = getValueWithForm(dict, "Call.Minutes", .one) + self._Call_Minutes_two = getValueWithForm(dict, "Call.Minutes", .two) + self._Call_Minutes_few = getValueWithForm(dict, "Call.Minutes", .few) + self._Call_Minutes_many = getValueWithForm(dict, "Call.Minutes", .many) + self._Call_Minutes_other = getValueWithForm(dict, "Call.Minutes", .other) + self._ForwardedFiles_zero = getValueWithForm(dict, "ForwardedFiles", .zero) + self._ForwardedFiles_one = getValueWithForm(dict, "ForwardedFiles", .one) + self._ForwardedFiles_two = getValueWithForm(dict, "ForwardedFiles", .two) + self._ForwardedFiles_few = getValueWithForm(dict, "ForwardedFiles", .few) + self._ForwardedFiles_many = getValueWithForm(dict, "ForwardedFiles", .many) + self._ForwardedFiles_other = getValueWithForm(dict, "ForwardedFiles", .other) + self._ForwardedLocations_zero = getValueWithForm(dict, "ForwardedLocations", .zero) + self._ForwardedLocations_one = getValueWithForm(dict, "ForwardedLocations", .one) + self._ForwardedLocations_two = getValueWithForm(dict, "ForwardedLocations", .two) + self._ForwardedLocations_few = getValueWithForm(dict, "ForwardedLocations", .few) + self._ForwardedLocations_many = getValueWithForm(dict, "ForwardedLocations", .many) + self._ForwardedLocations_other = getValueWithForm(dict, "ForwardedLocations", .other) + self._MuteExpires_Days_zero = getValueWithForm(dict, "MuteExpires.Days", .zero) + self._MuteExpires_Days_one = getValueWithForm(dict, "MuteExpires.Days", .one) + self._MuteExpires_Days_two = getValueWithForm(dict, "MuteExpires.Days", .two) + self._MuteExpires_Days_few = getValueWithForm(dict, "MuteExpires.Days", .few) + self._MuteExpires_Days_many = getValueWithForm(dict, "MuteExpires.Days", .many) + self._MuteExpires_Days_other = getValueWithForm(dict, "MuteExpires.Days", .other) + self._Media_SharePhoto_zero = getValueWithForm(dict, "Media.SharePhoto", .zero) + self._Media_SharePhoto_one = getValueWithForm(dict, "Media.SharePhoto", .one) + self._Media_SharePhoto_two = getValueWithForm(dict, "Media.SharePhoto", .two) + self._Media_SharePhoto_few = getValueWithForm(dict, "Media.SharePhoto", .few) + self._Media_SharePhoto_many = getValueWithForm(dict, "Media.SharePhoto", .many) + self._Media_SharePhoto_other = getValueWithForm(dict, "Media.SharePhoto", .other) + self._ForwardedPhotos_zero = getValueWithForm(dict, "ForwardedPhotos", .zero) + self._ForwardedPhotos_one = getValueWithForm(dict, "ForwardedPhotos", .one) + self._ForwardedPhotos_two = getValueWithForm(dict, "ForwardedPhotos", .two) + self._ForwardedPhotos_few = getValueWithForm(dict, "ForwardedPhotos", .few) + self._ForwardedPhotos_many = getValueWithForm(dict, "ForwardedPhotos", .many) + self._ForwardedPhotos_other = getValueWithForm(dict, "ForwardedPhotos", .other) + self._MessageTimer_Years_zero = getValueWithForm(dict, "MessageTimer.Years", .zero) + self._MessageTimer_Years_one = getValueWithForm(dict, "MessageTimer.Years", .one) + self._MessageTimer_Years_two = getValueWithForm(dict, "MessageTimer.Years", .two) + self._MessageTimer_Years_few = getValueWithForm(dict, "MessageTimer.Years", .few) + self._MessageTimer_Years_many = getValueWithForm(dict, "MessageTimer.Years", .many) + self._MessageTimer_Years_other = getValueWithForm(dict, "MessageTimer.Years", .other) + self._MuteExpires_Minutes_zero = getValueWithForm(dict, "MuteExpires.Minutes", .zero) + self._MuteExpires_Minutes_one = getValueWithForm(dict, "MuteExpires.Minutes", .one) + self._MuteExpires_Minutes_two = getValueWithForm(dict, "MuteExpires.Minutes", .two) + self._MuteExpires_Minutes_few = getValueWithForm(dict, "MuteExpires.Minutes", .few) + self._MuteExpires_Minutes_many = getValueWithForm(dict, "MuteExpires.Minutes", .many) + self._MuteExpires_Minutes_other = getValueWithForm(dict, "MuteExpires.Minutes", .other) + self._MessageTimer_Seconds_zero = getValueWithForm(dict, "MessageTimer.Seconds", .zero) + self._MessageTimer_Seconds_one = getValueWithForm(dict, "MessageTimer.Seconds", .one) + self._MessageTimer_Seconds_two = getValueWithForm(dict, "MessageTimer.Seconds", .two) + self._MessageTimer_Seconds_few = getValueWithForm(dict, "MessageTimer.Seconds", .few) + self._MessageTimer_Seconds_many = getValueWithForm(dict, "MessageTimer.Seconds", .many) + self._MessageTimer_Seconds_other = getValueWithForm(dict, "MessageTimer.Seconds", .other) + self._ForwardedContacts_zero = getValueWithForm(dict, "ForwardedContacts", .zero) + self._ForwardedContacts_one = getValueWithForm(dict, "ForwardedContacts", .one) + self._ForwardedContacts_two = getValueWithForm(dict, "ForwardedContacts", .two) + self._ForwardedContacts_few = getValueWithForm(dict, "ForwardedContacts", .few) + self._ForwardedContacts_many = getValueWithForm(dict, "ForwardedContacts", .many) + self._ForwardedContacts_other = getValueWithForm(dict, "ForwardedContacts", .other) + self._GroupInfo_ParticipantCount_zero = getValueWithForm(dict, "GroupInfo.ParticipantCount", .zero) + self._GroupInfo_ParticipantCount_one = getValueWithForm(dict, "GroupInfo.ParticipantCount", .one) + self._GroupInfo_ParticipantCount_two = getValueWithForm(dict, "GroupInfo.ParticipantCount", .two) + self._GroupInfo_ParticipantCount_few = getValueWithForm(dict, "GroupInfo.ParticipantCount", .few) + self._GroupInfo_ParticipantCount_many = getValueWithForm(dict, "GroupInfo.ParticipantCount", .many) + self._GroupInfo_ParticipantCount_other = getValueWithForm(dict, "GroupInfo.ParticipantCount", .other) + self._MessageTimer_Minutes_zero = getValueWithForm(dict, "MessageTimer.Minutes", .zero) + self._MessageTimer_Minutes_one = getValueWithForm(dict, "MessageTimer.Minutes", .one) + self._MessageTimer_Minutes_two = getValueWithForm(dict, "MessageTimer.Minutes", .two) + self._MessageTimer_Minutes_few = getValueWithForm(dict, "MessageTimer.Minutes", .few) + self._MessageTimer_Minutes_many = getValueWithForm(dict, "MessageTimer.Minutes", .many) + self._MessageTimer_Minutes_other = getValueWithForm(dict, "MessageTimer.Minutes", .other) self._Conversation_LiveLocationMembersCount_zero = getValueWithForm(dict, "Conversation.LiveLocationMembersCount", .zero) self._Conversation_LiveLocationMembersCount_one = getValueWithForm(dict, "Conversation.LiveLocationMembersCount", .one) self._Conversation_LiveLocationMembersCount_two = getValueWithForm(dict, "Conversation.LiveLocationMembersCount", .two) @@ -8356,198 +8214,60 @@ public final class PresentationStrings { self._Watch_LastSeen_MinutesAgo_few = getValueWithForm(dict, "Watch.LastSeen.MinutesAgo", .few) self._Watch_LastSeen_MinutesAgo_many = getValueWithForm(dict, "Watch.LastSeen.MinutesAgo", .many) self._Watch_LastSeen_MinutesAgo_other = getValueWithForm(dict, "Watch.LastSeen.MinutesAgo", .other) - self._MessageTimer_Months_zero = getValueWithForm(dict, "MessageTimer.Months", .zero) - self._MessageTimer_Months_one = getValueWithForm(dict, "MessageTimer.Months", .one) - self._MessageTimer_Months_two = getValueWithForm(dict, "MessageTimer.Months", .two) - self._MessageTimer_Months_few = getValueWithForm(dict, "MessageTimer.Months", .few) - self._MessageTimer_Months_many = getValueWithForm(dict, "MessageTimer.Months", .many) - self._MessageTimer_Months_other = getValueWithForm(dict, "MessageTimer.Months", .other) - self._ForwardedMessages_zero = getValueWithForm(dict, "ForwardedMessages", .zero) - self._ForwardedMessages_one = getValueWithForm(dict, "ForwardedMessages", .one) - self._ForwardedMessages_two = getValueWithForm(dict, "ForwardedMessages", .two) - self._ForwardedMessages_few = getValueWithForm(dict, "ForwardedMessages", .few) - self._ForwardedMessages_many = getValueWithForm(dict, "ForwardedMessages", .many) - self._ForwardedMessages_other = getValueWithForm(dict, "ForwardedMessages", .other) - self._ForwardedVideos_zero = getValueWithForm(dict, "ForwardedVideos", .zero) - self._ForwardedVideos_one = getValueWithForm(dict, "ForwardedVideos", .one) - self._ForwardedVideos_two = getValueWithForm(dict, "ForwardedVideos", .two) - self._ForwardedVideos_few = getValueWithForm(dict, "ForwardedVideos", .few) - self._ForwardedVideos_many = getValueWithForm(dict, "ForwardedVideos", .many) - self._ForwardedVideos_other = getValueWithForm(dict, "ForwardedVideos", .other) - self._MessageTimer_Minutes_zero = getValueWithForm(dict, "MessageTimer.Minutes", .zero) - self._MessageTimer_Minutes_one = getValueWithForm(dict, "MessageTimer.Minutes", .one) - self._MessageTimer_Minutes_two = getValueWithForm(dict, "MessageTimer.Minutes", .two) - self._MessageTimer_Minutes_few = getValueWithForm(dict, "MessageTimer.Minutes", .few) - self._MessageTimer_Minutes_many = getValueWithForm(dict, "MessageTimer.Minutes", .many) - self._MessageTimer_Minutes_other = getValueWithForm(dict, "MessageTimer.Minutes", .other) - self._Conversation_StatusOnline_zero = getValueWithForm(dict, "Conversation.StatusOnline", .zero) - self._Conversation_StatusOnline_one = getValueWithForm(dict, "Conversation.StatusOnline", .one) - self._Conversation_StatusOnline_two = getValueWithForm(dict, "Conversation.StatusOnline", .two) - self._Conversation_StatusOnline_few = getValueWithForm(dict, "Conversation.StatusOnline", .few) - self._Conversation_StatusOnline_many = getValueWithForm(dict, "Conversation.StatusOnline", .many) - self._Conversation_StatusOnline_other = getValueWithForm(dict, "Conversation.StatusOnline", .other) + self._Notifications_ExceptionMuteExpires_Minutes_zero = getValueWithForm(dict, "Notifications.ExceptionMuteExpires.Minutes", .zero) + self._Notifications_ExceptionMuteExpires_Minutes_one = getValueWithForm(dict, "Notifications.ExceptionMuteExpires.Minutes", .one) + self._Notifications_ExceptionMuteExpires_Minutes_two = getValueWithForm(dict, "Notifications.ExceptionMuteExpires.Minutes", .two) + self._Notifications_ExceptionMuteExpires_Minutes_few = getValueWithForm(dict, "Notifications.ExceptionMuteExpires.Minutes", .few) + self._Notifications_ExceptionMuteExpires_Minutes_many = getValueWithForm(dict, "Notifications.ExceptionMuteExpires.Minutes", .many) + self._Notifications_ExceptionMuteExpires_Minutes_other = getValueWithForm(dict, "Notifications.ExceptionMuteExpires.Minutes", .other) + self._Watch_UserInfo_Mute_zero = getValueWithForm(dict, "Watch.UserInfo.Mute", .zero) + self._Watch_UserInfo_Mute_one = getValueWithForm(dict, "Watch.UserInfo.Mute", .one) + self._Watch_UserInfo_Mute_two = getValueWithForm(dict, "Watch.UserInfo.Mute", .two) + self._Watch_UserInfo_Mute_few = getValueWithForm(dict, "Watch.UserInfo.Mute", .few) + self._Watch_UserInfo_Mute_many = getValueWithForm(dict, "Watch.UserInfo.Mute", .many) + self._Watch_UserInfo_Mute_other = getValueWithForm(dict, "Watch.UserInfo.Mute", .other) + self._MessageTimer_ShortWeeks_zero = getValueWithForm(dict, "MessageTimer.ShortWeeks", .zero) + self._MessageTimer_ShortWeeks_one = getValueWithForm(dict, "MessageTimer.ShortWeeks", .one) + self._MessageTimer_ShortWeeks_two = getValueWithForm(dict, "MessageTimer.ShortWeeks", .two) + self._MessageTimer_ShortWeeks_few = getValueWithForm(dict, "MessageTimer.ShortWeeks", .few) + self._MessageTimer_ShortWeeks_many = getValueWithForm(dict, "MessageTimer.ShortWeeks", .many) + self._MessageTimer_ShortWeeks_other = getValueWithForm(dict, "MessageTimer.ShortWeeks", .other) + self._LastSeen_HoursAgo_zero = getValueWithForm(dict, "LastSeen.HoursAgo", .zero) + self._LastSeen_HoursAgo_one = getValueWithForm(dict, "LastSeen.HoursAgo", .one) + self._LastSeen_HoursAgo_two = getValueWithForm(dict, "LastSeen.HoursAgo", .two) + self._LastSeen_HoursAgo_few = getValueWithForm(dict, "LastSeen.HoursAgo", .few) + self._LastSeen_HoursAgo_many = getValueWithForm(dict, "LastSeen.HoursAgo", .many) + self._LastSeen_HoursAgo_other = getValueWithForm(dict, "LastSeen.HoursAgo", .other) self._MessageTimer_ShortMinutes_zero = getValueWithForm(dict, "MessageTimer.ShortMinutes", .zero) self._MessageTimer_ShortMinutes_one = getValueWithForm(dict, "MessageTimer.ShortMinutes", .one) self._MessageTimer_ShortMinutes_two = getValueWithForm(dict, "MessageTimer.ShortMinutes", .two) self._MessageTimer_ShortMinutes_few = getValueWithForm(dict, "MessageTimer.ShortMinutes", .few) self._MessageTimer_ShortMinutes_many = getValueWithForm(dict, "MessageTimer.ShortMinutes", .many) self._MessageTimer_ShortMinutes_other = getValueWithForm(dict, "MessageTimer.ShortMinutes", .other) - self._QuickSend_Photos_zero = getValueWithForm(dict, "QuickSend.Photos", .zero) - self._QuickSend_Photos_one = getValueWithForm(dict, "QuickSend.Photos", .one) - self._QuickSend_Photos_two = getValueWithForm(dict, "QuickSend.Photos", .two) - self._QuickSend_Photos_few = getValueWithForm(dict, "QuickSend.Photos", .few) - self._QuickSend_Photos_many = getValueWithForm(dict, "QuickSend.Photos", .many) - self._QuickSend_Photos_other = getValueWithForm(dict, "QuickSend.Photos", .other) - self._ServiceMessage_GameScoreExtended_zero = getValueWithForm(dict, "ServiceMessage.GameScoreExtended", .zero) - self._ServiceMessage_GameScoreExtended_one = getValueWithForm(dict, "ServiceMessage.GameScoreExtended", .one) - self._ServiceMessage_GameScoreExtended_two = getValueWithForm(dict, "ServiceMessage.GameScoreExtended", .two) - self._ServiceMessage_GameScoreExtended_few = getValueWithForm(dict, "ServiceMessage.GameScoreExtended", .few) - self._ServiceMessage_GameScoreExtended_many = getValueWithForm(dict, "ServiceMessage.GameScoreExtended", .many) - self._ServiceMessage_GameScoreExtended_other = getValueWithForm(dict, "ServiceMessage.GameScoreExtended", .other) - self._SharedMedia_Generic_zero = getValueWithForm(dict, "SharedMedia.Generic", .zero) - self._SharedMedia_Generic_one = getValueWithForm(dict, "SharedMedia.Generic", .one) - self._SharedMedia_Generic_two = getValueWithForm(dict, "SharedMedia.Generic", .two) - self._SharedMedia_Generic_few = getValueWithForm(dict, "SharedMedia.Generic", .few) - self._SharedMedia_Generic_many = getValueWithForm(dict, "SharedMedia.Generic", .many) - self._SharedMedia_Generic_other = getValueWithForm(dict, "SharedMedia.Generic", .other) - self._SharedMedia_Video_zero = getValueWithForm(dict, "SharedMedia.Video", .zero) - self._SharedMedia_Video_one = getValueWithForm(dict, "SharedMedia.Video", .one) - self._SharedMedia_Video_two = getValueWithForm(dict, "SharedMedia.Video", .two) - self._SharedMedia_Video_few = getValueWithForm(dict, "SharedMedia.Video", .few) - self._SharedMedia_Video_many = getValueWithForm(dict, "SharedMedia.Video", .many) - self._SharedMedia_Video_other = getValueWithForm(dict, "SharedMedia.Video", .other) - self._ServiceMessage_GameScoreSelfSimple_zero = getValueWithForm(dict, "ServiceMessage.GameScoreSelfSimple", .zero) - self._ServiceMessage_GameScoreSelfSimple_one = getValueWithForm(dict, "ServiceMessage.GameScoreSelfSimple", .one) - self._ServiceMessage_GameScoreSelfSimple_two = getValueWithForm(dict, "ServiceMessage.GameScoreSelfSimple", .two) - self._ServiceMessage_GameScoreSelfSimple_few = getValueWithForm(dict, "ServiceMessage.GameScoreSelfSimple", .few) - self._ServiceMessage_GameScoreSelfSimple_many = getValueWithForm(dict, "ServiceMessage.GameScoreSelfSimple", .many) - self._ServiceMessage_GameScoreSelfSimple_other = getValueWithForm(dict, "ServiceMessage.GameScoreSelfSimple", .other) - self._Media_ShareVideo_zero = getValueWithForm(dict, "Media.ShareVideo", .zero) - self._Media_ShareVideo_one = getValueWithForm(dict, "Media.ShareVideo", .one) - self._Media_ShareVideo_two = getValueWithForm(dict, "Media.ShareVideo", .two) - self._Media_ShareVideo_few = getValueWithForm(dict, "Media.ShareVideo", .few) - self._Media_ShareVideo_many = getValueWithForm(dict, "Media.ShareVideo", .many) - self._Media_ShareVideo_other = getValueWithForm(dict, "Media.ShareVideo", .other) - self._PrivacyLastSeenSettings_AddUsers_zero = getValueWithForm(dict, "PrivacyLastSeenSettings.AddUsers", .zero) - self._PrivacyLastSeenSettings_AddUsers_one = getValueWithForm(dict, "PrivacyLastSeenSettings.AddUsers", .one) - self._PrivacyLastSeenSettings_AddUsers_two = getValueWithForm(dict, "PrivacyLastSeenSettings.AddUsers", .two) - self._PrivacyLastSeenSettings_AddUsers_few = getValueWithForm(dict, "PrivacyLastSeenSettings.AddUsers", .few) - self._PrivacyLastSeenSettings_AddUsers_many = getValueWithForm(dict, "PrivacyLastSeenSettings.AddUsers", .many) - self._PrivacyLastSeenSettings_AddUsers_other = getValueWithForm(dict, "PrivacyLastSeenSettings.AddUsers", .other) - self._ForwardedContacts_zero = getValueWithForm(dict, "ForwardedContacts", .zero) - self._ForwardedContacts_one = getValueWithForm(dict, "ForwardedContacts", .one) - self._ForwardedContacts_two = getValueWithForm(dict, "ForwardedContacts", .two) - self._ForwardedContacts_few = getValueWithForm(dict, "ForwardedContacts", .few) - self._ForwardedContacts_many = getValueWithForm(dict, "ForwardedContacts", .many) - self._ForwardedContacts_other = getValueWithForm(dict, "ForwardedContacts", .other) - self._Call_Seconds_zero = getValueWithForm(dict, "Call.Seconds", .zero) - self._Call_Seconds_one = getValueWithForm(dict, "Call.Seconds", .one) - self._Call_Seconds_two = getValueWithForm(dict, "Call.Seconds", .two) - self._Call_Seconds_few = getValueWithForm(dict, "Call.Seconds", .few) - self._Call_Seconds_many = getValueWithForm(dict, "Call.Seconds", .many) - self._Call_Seconds_other = getValueWithForm(dict, "Call.Seconds", .other) - self._ForwardedAuthorsOthers_zero = getValueWithForm(dict, "ForwardedAuthorsOthers", .zero) - self._ForwardedAuthorsOthers_one = getValueWithForm(dict, "ForwardedAuthorsOthers", .one) - self._ForwardedAuthorsOthers_two = getValueWithForm(dict, "ForwardedAuthorsOthers", .two) - self._ForwardedAuthorsOthers_few = getValueWithForm(dict, "ForwardedAuthorsOthers", .few) - self._ForwardedAuthorsOthers_many = getValueWithForm(dict, "ForwardedAuthorsOthers", .many) - self._ForwardedAuthorsOthers_other = getValueWithForm(dict, "ForwardedAuthorsOthers", .other) - self._DialogList_LiveLocationChatsCount_zero = getValueWithForm(dict, "DialogList.LiveLocationChatsCount", .zero) - self._DialogList_LiveLocationChatsCount_one = getValueWithForm(dict, "DialogList.LiveLocationChatsCount", .one) - self._DialogList_LiveLocationChatsCount_two = getValueWithForm(dict, "DialogList.LiveLocationChatsCount", .two) - self._DialogList_LiveLocationChatsCount_few = getValueWithForm(dict, "DialogList.LiveLocationChatsCount", .few) - self._DialogList_LiveLocationChatsCount_many = getValueWithForm(dict, "DialogList.LiveLocationChatsCount", .many) - self._DialogList_LiveLocationChatsCount_other = getValueWithForm(dict, "DialogList.LiveLocationChatsCount", .other) - self._MessageTimer_Years_zero = getValueWithForm(dict, "MessageTimer.Years", .zero) - self._MessageTimer_Years_one = getValueWithForm(dict, "MessageTimer.Years", .one) - self._MessageTimer_Years_two = getValueWithForm(dict, "MessageTimer.Years", .two) - self._MessageTimer_Years_few = getValueWithForm(dict, "MessageTimer.Years", .few) - self._MessageTimer_Years_many = getValueWithForm(dict, "MessageTimer.Years", .many) - self._MessageTimer_Years_other = getValueWithForm(dict, "MessageTimer.Years", .other) - self._LiveLocationUpdated_MinutesAgo_zero = getValueWithForm(dict, "LiveLocationUpdated.MinutesAgo", .zero) - self._LiveLocationUpdated_MinutesAgo_one = getValueWithForm(dict, "LiveLocationUpdated.MinutesAgo", .one) - self._LiveLocationUpdated_MinutesAgo_two = getValueWithForm(dict, "LiveLocationUpdated.MinutesAgo", .two) - self._LiveLocationUpdated_MinutesAgo_few = getValueWithForm(dict, "LiveLocationUpdated.MinutesAgo", .few) - self._LiveLocationUpdated_MinutesAgo_many = getValueWithForm(dict, "LiveLocationUpdated.MinutesAgo", .many) - self._LiveLocationUpdated_MinutesAgo_other = getValueWithForm(dict, "LiveLocationUpdated.MinutesAgo", .other) - self._StickerPack_RemoveMaskCount_zero = getValueWithForm(dict, "StickerPack.RemoveMaskCount", .zero) - self._StickerPack_RemoveMaskCount_one = getValueWithForm(dict, "StickerPack.RemoveMaskCount", .one) - self._StickerPack_RemoveMaskCount_two = getValueWithForm(dict, "StickerPack.RemoveMaskCount", .two) - self._StickerPack_RemoveMaskCount_few = getValueWithForm(dict, "StickerPack.RemoveMaskCount", .few) - self._StickerPack_RemoveMaskCount_many = getValueWithForm(dict, "StickerPack.RemoveMaskCount", .many) - self._StickerPack_RemoveMaskCount_other = getValueWithForm(dict, "StickerPack.RemoveMaskCount", .other) - self._ForwardedVideoMessages_zero = getValueWithForm(dict, "ForwardedVideoMessages", .zero) - self._ForwardedVideoMessages_one = getValueWithForm(dict, "ForwardedVideoMessages", .one) - self._ForwardedVideoMessages_two = getValueWithForm(dict, "ForwardedVideoMessages", .two) - self._ForwardedVideoMessages_few = getValueWithForm(dict, "ForwardedVideoMessages", .few) - self._ForwardedVideoMessages_many = getValueWithForm(dict, "ForwardedVideoMessages", .many) - self._ForwardedVideoMessages_other = getValueWithForm(dict, "ForwardedVideoMessages", .other) - self._MessageTimer_ShortHours_zero = getValueWithForm(dict, "MessageTimer.ShortHours", .zero) - self._MessageTimer_ShortHours_one = getValueWithForm(dict, "MessageTimer.ShortHours", .one) - self._MessageTimer_ShortHours_two = getValueWithForm(dict, "MessageTimer.ShortHours", .two) - self._MessageTimer_ShortHours_few = getValueWithForm(dict, "MessageTimer.ShortHours", .few) - self._MessageTimer_ShortHours_many = getValueWithForm(dict, "MessageTimer.ShortHours", .many) - self._MessageTimer_ShortHours_other = getValueWithForm(dict, "MessageTimer.ShortHours", .other) - self._Watch_LastSeen_HoursAgo_zero = getValueWithForm(dict, "Watch.LastSeen.HoursAgo", .zero) - self._Watch_LastSeen_HoursAgo_one = getValueWithForm(dict, "Watch.LastSeen.HoursAgo", .one) - self._Watch_LastSeen_HoursAgo_two = getValueWithForm(dict, "Watch.LastSeen.HoursAgo", .two) - self._Watch_LastSeen_HoursAgo_few = getValueWithForm(dict, "Watch.LastSeen.HoursAgo", .few) - self._Watch_LastSeen_HoursAgo_many = getValueWithForm(dict, "Watch.LastSeen.HoursAgo", .many) - self._Watch_LastSeen_HoursAgo_other = getValueWithForm(dict, "Watch.LastSeen.HoursAgo", .other) - self._AttachmentMenu_SendVideo_zero = getValueWithForm(dict, "AttachmentMenu.SendVideo", .zero) - self._AttachmentMenu_SendVideo_one = getValueWithForm(dict, "AttachmentMenu.SendVideo", .one) - self._AttachmentMenu_SendVideo_two = getValueWithForm(dict, "AttachmentMenu.SendVideo", .two) - self._AttachmentMenu_SendVideo_few = getValueWithForm(dict, "AttachmentMenu.SendVideo", .few) - self._AttachmentMenu_SendVideo_many = getValueWithForm(dict, "AttachmentMenu.SendVideo", .many) - self._AttachmentMenu_SendVideo_other = getValueWithForm(dict, "AttachmentMenu.SendVideo", .other) - self._MessageTimer_Weeks_zero = getValueWithForm(dict, "MessageTimer.Weeks", .zero) - self._MessageTimer_Weeks_one = getValueWithForm(dict, "MessageTimer.Weeks", .one) - self._MessageTimer_Weeks_two = getValueWithForm(dict, "MessageTimer.Weeks", .two) - self._MessageTimer_Weeks_few = getValueWithForm(dict, "MessageTimer.Weeks", .few) - self._MessageTimer_Weeks_many = getValueWithForm(dict, "MessageTimer.Weeks", .many) - self._MessageTimer_Weeks_other = getValueWithForm(dict, "MessageTimer.Weeks", .other) - self._StickerPack_StickerCount_zero = getValueWithForm(dict, "StickerPack.StickerCount", .zero) - self._StickerPack_StickerCount_one = getValueWithForm(dict, "StickerPack.StickerCount", .one) - self._StickerPack_StickerCount_two = getValueWithForm(dict, "StickerPack.StickerCount", .two) - self._StickerPack_StickerCount_few = getValueWithForm(dict, "StickerPack.StickerCount", .few) - self._StickerPack_StickerCount_many = getValueWithForm(dict, "StickerPack.StickerCount", .many) - self._StickerPack_StickerCount_other = getValueWithForm(dict, "StickerPack.StickerCount", .other) - self._ServiceMessage_GameScoreSimple_zero = getValueWithForm(dict, "ServiceMessage.GameScoreSimple", .zero) - self._ServiceMessage_GameScoreSimple_one = getValueWithForm(dict, "ServiceMessage.GameScoreSimple", .one) - self._ServiceMessage_GameScoreSimple_two = getValueWithForm(dict, "ServiceMessage.GameScoreSimple", .two) - self._ServiceMessage_GameScoreSimple_few = getValueWithForm(dict, "ServiceMessage.GameScoreSimple", .few) - self._ServiceMessage_GameScoreSimple_many = getValueWithForm(dict, "ServiceMessage.GameScoreSimple", .many) - self._ServiceMessage_GameScoreSimple_other = getValueWithForm(dict, "ServiceMessage.GameScoreSimple", .other) - self._ForwardedFiles_zero = getValueWithForm(dict, "ForwardedFiles", .zero) - self._ForwardedFiles_one = getValueWithForm(dict, "ForwardedFiles", .one) - self._ForwardedFiles_two = getValueWithForm(dict, "ForwardedFiles", .two) - self._ForwardedFiles_few = getValueWithForm(dict, "ForwardedFiles", .few) - self._ForwardedFiles_many = getValueWithForm(dict, "ForwardedFiles", .many) - self._ForwardedFiles_other = getValueWithForm(dict, "ForwardedFiles", .other) - self._GroupInfo_ParticipantCount_zero = getValueWithForm(dict, "GroupInfo.ParticipantCount", .zero) - self._GroupInfo_ParticipantCount_one = getValueWithForm(dict, "GroupInfo.ParticipantCount", .one) - self._GroupInfo_ParticipantCount_two = getValueWithForm(dict, "GroupInfo.ParticipantCount", .two) - self._GroupInfo_ParticipantCount_few = getValueWithForm(dict, "GroupInfo.ParticipantCount", .few) - self._GroupInfo_ParticipantCount_many = getValueWithForm(dict, "GroupInfo.ParticipantCount", .many) - self._GroupInfo_ParticipantCount_other = getValueWithForm(dict, "GroupInfo.ParticipantCount", .other) - self._ForwardedLocations_zero = getValueWithForm(dict, "ForwardedLocations", .zero) - self._ForwardedLocations_one = getValueWithForm(dict, "ForwardedLocations", .one) - self._ForwardedLocations_two = getValueWithForm(dict, "ForwardedLocations", .two) - self._ForwardedLocations_few = getValueWithForm(dict, "ForwardedLocations", .few) - self._ForwardedLocations_many = getValueWithForm(dict, "ForwardedLocations", .many) - self._ForwardedLocations_other = getValueWithForm(dict, "ForwardedLocations", .other) - self._SharedMedia_DeleteItemsConfirmation_zero = getValueWithForm(dict, "SharedMedia.DeleteItemsConfirmation", .zero) - self._SharedMedia_DeleteItemsConfirmation_one = getValueWithForm(dict, "SharedMedia.DeleteItemsConfirmation", .one) - self._SharedMedia_DeleteItemsConfirmation_two = getValueWithForm(dict, "SharedMedia.DeleteItemsConfirmation", .two) - self._SharedMedia_DeleteItemsConfirmation_few = getValueWithForm(dict, "SharedMedia.DeleteItemsConfirmation", .few) - self._SharedMedia_DeleteItemsConfirmation_many = getValueWithForm(dict, "SharedMedia.DeleteItemsConfirmation", .many) - self._SharedMedia_DeleteItemsConfirmation_other = getValueWithForm(dict, "SharedMedia.DeleteItemsConfirmation", .other) - self._ForwardedPhotos_zero = getValueWithForm(dict, "ForwardedPhotos", .zero) - self._ForwardedPhotos_one = getValueWithForm(dict, "ForwardedPhotos", .one) - self._ForwardedPhotos_two = getValueWithForm(dict, "ForwardedPhotos", .two) - self._ForwardedPhotos_few = getValueWithForm(dict, "ForwardedPhotos", .few) - self._ForwardedPhotos_many = getValueWithForm(dict, "ForwardedPhotos", .many) - self._ForwardedPhotos_other = getValueWithForm(dict, "ForwardedPhotos", .other) + self._AttachmentMenu_SendItem_zero = getValueWithForm(dict, "AttachmentMenu.SendItem", .zero) + self._AttachmentMenu_SendItem_one = getValueWithForm(dict, "AttachmentMenu.SendItem", .one) + self._AttachmentMenu_SendItem_two = getValueWithForm(dict, "AttachmentMenu.SendItem", .two) + self._AttachmentMenu_SendItem_few = getValueWithForm(dict, "AttachmentMenu.SendItem", .few) + self._AttachmentMenu_SendItem_many = getValueWithForm(dict, "AttachmentMenu.SendItem", .many) + self._AttachmentMenu_SendItem_other = getValueWithForm(dict, "AttachmentMenu.SendItem", .other) + self._ForwardedAudios_zero = getValueWithForm(dict, "ForwardedAudios", .zero) + self._ForwardedAudios_one = getValueWithForm(dict, "ForwardedAudios", .one) + self._ForwardedAudios_two = getValueWithForm(dict, "ForwardedAudios", .two) + self._ForwardedAudios_few = getValueWithForm(dict, "ForwardedAudios", .few) + self._ForwardedAudios_many = getValueWithForm(dict, "ForwardedAudios", .many) + self._ForwardedAudios_other = getValueWithForm(dict, "ForwardedAudios", .other) + self._Notification_GameScoreSelfExtended_zero = getValueWithForm(dict, "Notification.GameScoreSelfExtended", .zero) + self._Notification_GameScoreSelfExtended_one = getValueWithForm(dict, "Notification.GameScoreSelfExtended", .one) + self._Notification_GameScoreSelfExtended_two = getValueWithForm(dict, "Notification.GameScoreSelfExtended", .two) + self._Notification_GameScoreSelfExtended_few = getValueWithForm(dict, "Notification.GameScoreSelfExtended", .few) + self._Notification_GameScoreSelfExtended_many = getValueWithForm(dict, "Notification.GameScoreSelfExtended", .many) + self._Notification_GameScoreSelfExtended_other = getValueWithForm(dict, "Notification.GameScoreSelfExtended", .other) + self._Call_ShortMinutes_zero = getValueWithForm(dict, "Call.ShortMinutes", .zero) + self._Call_ShortMinutes_one = getValueWithForm(dict, "Call.ShortMinutes", .one) + self._Call_ShortMinutes_two = getValueWithForm(dict, "Call.ShortMinutes", .two) + self._Call_ShortMinutes_few = getValueWithForm(dict, "Call.ShortMinutes", .few) + self._Call_ShortMinutes_many = getValueWithForm(dict, "Call.ShortMinutes", .many) + self._Call_ShortMinutes_other = getValueWithForm(dict, "Call.ShortMinutes", .other) self._MessageTimer_ShortDays_zero = getValueWithForm(dict, "MessageTimer.ShortDays", .zero) self._MessageTimer_ShortDays_one = getValueWithForm(dict, "MessageTimer.ShortDays", .one) self._MessageTimer_ShortDays_two = getValueWithForm(dict, "MessageTimer.ShortDays", .two) @@ -8560,6 +8280,288 @@ public final class PresentationStrings { self._LiveLocation_MenuChatsCount_few = getValueWithForm(dict, "LiveLocation.MenuChatsCount", .few) self._LiveLocation_MenuChatsCount_many = getValueWithForm(dict, "LiveLocation.MenuChatsCount", .many) self._LiveLocation_MenuChatsCount_other = getValueWithForm(dict, "LiveLocation.MenuChatsCount", .other) + self._ForwardedVideoMessages_zero = getValueWithForm(dict, "ForwardedVideoMessages", .zero) + self._ForwardedVideoMessages_one = getValueWithForm(dict, "ForwardedVideoMessages", .one) + self._ForwardedVideoMessages_two = getValueWithForm(dict, "ForwardedVideoMessages", .two) + self._ForwardedVideoMessages_few = getValueWithForm(dict, "ForwardedVideoMessages", .few) + self._ForwardedVideoMessages_many = getValueWithForm(dict, "ForwardedVideoMessages", .many) + self._ForwardedVideoMessages_other = getValueWithForm(dict, "ForwardedVideoMessages", .other) + self._MessageTimer_ShortSeconds_zero = getValueWithForm(dict, "MessageTimer.ShortSeconds", .zero) + self._MessageTimer_ShortSeconds_one = getValueWithForm(dict, "MessageTimer.ShortSeconds", .one) + self._MessageTimer_ShortSeconds_two = getValueWithForm(dict, "MessageTimer.ShortSeconds", .two) + self._MessageTimer_ShortSeconds_few = getValueWithForm(dict, "MessageTimer.ShortSeconds", .few) + self._MessageTimer_ShortSeconds_many = getValueWithForm(dict, "MessageTimer.ShortSeconds", .many) + self._MessageTimer_ShortSeconds_other = getValueWithForm(dict, "MessageTimer.ShortSeconds", .other) + self._Invitation_Members_zero = getValueWithForm(dict, "Invitation.Members", .zero) + self._Invitation_Members_one = getValueWithForm(dict, "Invitation.Members", .one) + self._Invitation_Members_two = getValueWithForm(dict, "Invitation.Members", .two) + self._Invitation_Members_few = getValueWithForm(dict, "Invitation.Members", .few) + self._Invitation_Members_many = getValueWithForm(dict, "Invitation.Members", .many) + self._Invitation_Members_other = getValueWithForm(dict, "Invitation.Members", .other) + self._AttachmentMenu_SendPhoto_zero = getValueWithForm(dict, "AttachmentMenu.SendPhoto", .zero) + self._AttachmentMenu_SendPhoto_one = getValueWithForm(dict, "AttachmentMenu.SendPhoto", .one) + self._AttachmentMenu_SendPhoto_two = getValueWithForm(dict, "AttachmentMenu.SendPhoto", .two) + self._AttachmentMenu_SendPhoto_few = getValueWithForm(dict, "AttachmentMenu.SendPhoto", .few) + self._AttachmentMenu_SendPhoto_many = getValueWithForm(dict, "AttachmentMenu.SendPhoto", .many) + self._AttachmentMenu_SendPhoto_other = getValueWithForm(dict, "AttachmentMenu.SendPhoto", .other) + self._SharedMedia_Link_zero = getValueWithForm(dict, "SharedMedia.Link", .zero) + self._SharedMedia_Link_one = getValueWithForm(dict, "SharedMedia.Link", .one) + self._SharedMedia_Link_two = getValueWithForm(dict, "SharedMedia.Link", .two) + self._SharedMedia_Link_few = getValueWithForm(dict, "SharedMedia.Link", .few) + self._SharedMedia_Link_many = getValueWithForm(dict, "SharedMedia.Link", .many) + self._SharedMedia_Link_other = getValueWithForm(dict, "SharedMedia.Link", .other) + self._ServiceMessage_GameScoreSelfExtended_zero = getValueWithForm(dict, "ServiceMessage.GameScoreSelfExtended", .zero) + self._ServiceMessage_GameScoreSelfExtended_one = getValueWithForm(dict, "ServiceMessage.GameScoreSelfExtended", .one) + self._ServiceMessage_GameScoreSelfExtended_two = getValueWithForm(dict, "ServiceMessage.GameScoreSelfExtended", .two) + self._ServiceMessage_GameScoreSelfExtended_few = getValueWithForm(dict, "ServiceMessage.GameScoreSelfExtended", .few) + self._ServiceMessage_GameScoreSelfExtended_many = getValueWithForm(dict, "ServiceMessage.GameScoreSelfExtended", .many) + self._ServiceMessage_GameScoreSelfExtended_other = getValueWithForm(dict, "ServiceMessage.GameScoreSelfExtended", .other) + self._QuickSend_Photos_zero = getValueWithForm(dict, "QuickSend.Photos", .zero) + self._QuickSend_Photos_one = getValueWithForm(dict, "QuickSend.Photos", .one) + self._QuickSend_Photos_two = getValueWithForm(dict, "QuickSend.Photos", .two) + self._QuickSend_Photos_few = getValueWithForm(dict, "QuickSend.Photos", .few) + self._QuickSend_Photos_many = getValueWithForm(dict, "QuickSend.Photos", .many) + self._QuickSend_Photos_other = getValueWithForm(dict, "QuickSend.Photos", .other) + self._StickerPack_StickerCount_zero = getValueWithForm(dict, "StickerPack.StickerCount", .zero) + self._StickerPack_StickerCount_one = getValueWithForm(dict, "StickerPack.StickerCount", .one) + self._StickerPack_StickerCount_two = getValueWithForm(dict, "StickerPack.StickerCount", .two) + self._StickerPack_StickerCount_few = getValueWithForm(dict, "StickerPack.StickerCount", .few) + self._StickerPack_StickerCount_many = getValueWithForm(dict, "StickerPack.StickerCount", .many) + self._StickerPack_StickerCount_other = getValueWithForm(dict, "StickerPack.StickerCount", .other) + self._Media_ShareItem_zero = getValueWithForm(dict, "Media.ShareItem", .zero) + self._Media_ShareItem_one = getValueWithForm(dict, "Media.ShareItem", .one) + self._Media_ShareItem_two = getValueWithForm(dict, "Media.ShareItem", .two) + self._Media_ShareItem_few = getValueWithForm(dict, "Media.ShareItem", .few) + self._Media_ShareItem_many = getValueWithForm(dict, "Media.ShareItem", .many) + self._Media_ShareItem_other = getValueWithForm(dict, "Media.ShareItem", .other) + self._Conversation_StatusOnline_zero = getValueWithForm(dict, "Conversation.StatusOnline", .zero) + self._Conversation_StatusOnline_one = getValueWithForm(dict, "Conversation.StatusOnline", .one) + self._Conversation_StatusOnline_two = getValueWithForm(dict, "Conversation.StatusOnline", .two) + self._Conversation_StatusOnline_few = getValueWithForm(dict, "Conversation.StatusOnline", .few) + self._Conversation_StatusOnline_many = getValueWithForm(dict, "Conversation.StatusOnline", .many) + self._Conversation_StatusOnline_other = getValueWithForm(dict, "Conversation.StatusOnline", .other) + self._MessageTimer_Days_zero = getValueWithForm(dict, "MessageTimer.Days", .zero) + self._MessageTimer_Days_one = getValueWithForm(dict, "MessageTimer.Days", .one) + self._MessageTimer_Days_two = getValueWithForm(dict, "MessageTimer.Days", .two) + self._MessageTimer_Days_few = getValueWithForm(dict, "MessageTimer.Days", .few) + self._MessageTimer_Days_many = getValueWithForm(dict, "MessageTimer.Days", .many) + self._MessageTimer_Days_other = getValueWithForm(dict, "MessageTimer.Days", .other) + self._SharedMedia_Video_zero = getValueWithForm(dict, "SharedMedia.Video", .zero) + self._SharedMedia_Video_one = getValueWithForm(dict, "SharedMedia.Video", .one) + self._SharedMedia_Video_two = getValueWithForm(dict, "SharedMedia.Video", .two) + self._SharedMedia_Video_few = getValueWithForm(dict, "SharedMedia.Video", .few) + self._SharedMedia_Video_many = getValueWithForm(dict, "SharedMedia.Video", .many) + self._SharedMedia_Video_other = getValueWithForm(dict, "SharedMedia.Video", .other) + self._ForwardedVideos_zero = getValueWithForm(dict, "ForwardedVideos", .zero) + self._ForwardedVideos_one = getValueWithForm(dict, "ForwardedVideos", .one) + self._ForwardedVideos_two = getValueWithForm(dict, "ForwardedVideos", .two) + self._ForwardedVideos_few = getValueWithForm(dict, "ForwardedVideos", .few) + self._ForwardedVideos_many = getValueWithForm(dict, "ForwardedVideos", .many) + self._ForwardedVideos_other = getValueWithForm(dict, "ForwardedVideos", .other) + self._MuteFor_Days_zero = getValueWithForm(dict, "MuteFor.Days", .zero) + self._MuteFor_Days_one = getValueWithForm(dict, "MuteFor.Days", .one) + self._MuteFor_Days_two = getValueWithForm(dict, "MuteFor.Days", .two) + self._MuteFor_Days_few = getValueWithForm(dict, "MuteFor.Days", .few) + self._MuteFor_Days_many = getValueWithForm(dict, "MuteFor.Days", .many) + self._MuteFor_Days_other = getValueWithForm(dict, "MuteFor.Days", .other) + self._ForwardedGifs_zero = getValueWithForm(dict, "ForwardedGifs", .zero) + self._ForwardedGifs_one = getValueWithForm(dict, "ForwardedGifs", .one) + self._ForwardedGifs_two = getValueWithForm(dict, "ForwardedGifs", .two) + self._ForwardedGifs_few = getValueWithForm(dict, "ForwardedGifs", .few) + self._ForwardedGifs_many = getValueWithForm(dict, "ForwardedGifs", .many) + self._ForwardedGifs_other = getValueWithForm(dict, "ForwardedGifs", .other) + self._MessageTimer_ShortHours_zero = getValueWithForm(dict, "MessageTimer.ShortHours", .zero) + self._MessageTimer_ShortHours_one = getValueWithForm(dict, "MessageTimer.ShortHours", .one) + self._MessageTimer_ShortHours_two = getValueWithForm(dict, "MessageTimer.ShortHours", .two) + self._MessageTimer_ShortHours_few = getValueWithForm(dict, "MessageTimer.ShortHours", .few) + self._MessageTimer_ShortHours_many = getValueWithForm(dict, "MessageTimer.ShortHours", .many) + self._MessageTimer_ShortHours_other = getValueWithForm(dict, "MessageTimer.ShortHours", .other) + self._Contacts_ImportersCount_zero = getValueWithForm(dict, "Contacts.ImportersCount", .zero) + self._Contacts_ImportersCount_one = getValueWithForm(dict, "Contacts.ImportersCount", .one) + self._Contacts_ImportersCount_two = getValueWithForm(dict, "Contacts.ImportersCount", .two) + self._Contacts_ImportersCount_few = getValueWithForm(dict, "Contacts.ImportersCount", .few) + self._Contacts_ImportersCount_many = getValueWithForm(dict, "Contacts.ImportersCount", .many) + self._Contacts_ImportersCount_other = getValueWithForm(dict, "Contacts.ImportersCount", .other) + self._ForwardedAuthorsOthers_zero = getValueWithForm(dict, "ForwardedAuthorsOthers", .zero) + self._ForwardedAuthorsOthers_one = getValueWithForm(dict, "ForwardedAuthorsOthers", .one) + self._ForwardedAuthorsOthers_two = getValueWithForm(dict, "ForwardedAuthorsOthers", .two) + self._ForwardedAuthorsOthers_few = getValueWithForm(dict, "ForwardedAuthorsOthers", .few) + self._ForwardedAuthorsOthers_many = getValueWithForm(dict, "ForwardedAuthorsOthers", .many) + self._ForwardedAuthorsOthers_other = getValueWithForm(dict, "ForwardedAuthorsOthers", .other) + self._UserCount_zero = getValueWithForm(dict, "UserCount", .zero) + self._UserCount_one = getValueWithForm(dict, "UserCount", .one) + self._UserCount_two = getValueWithForm(dict, "UserCount", .two) + self._UserCount_few = getValueWithForm(dict, "UserCount", .few) + self._UserCount_many = getValueWithForm(dict, "UserCount", .many) + self._UserCount_other = getValueWithForm(dict, "UserCount", .other) + self._AttachmentMenu_SendGif_zero = getValueWithForm(dict, "AttachmentMenu.SendGif", .zero) + self._AttachmentMenu_SendGif_one = getValueWithForm(dict, "AttachmentMenu.SendGif", .one) + self._AttachmentMenu_SendGif_two = getValueWithForm(dict, "AttachmentMenu.SendGif", .two) + self._AttachmentMenu_SendGif_few = getValueWithForm(dict, "AttachmentMenu.SendGif", .few) + self._AttachmentMenu_SendGif_many = getValueWithForm(dict, "AttachmentMenu.SendGif", .many) + self._AttachmentMenu_SendGif_other = getValueWithForm(dict, "AttachmentMenu.SendGif", .other) + self._StickerPack_AddMaskCount_zero = getValueWithForm(dict, "StickerPack.AddMaskCount", .zero) + self._StickerPack_AddMaskCount_one = getValueWithForm(dict, "StickerPack.AddMaskCount", .one) + self._StickerPack_AddMaskCount_two = getValueWithForm(dict, "StickerPack.AddMaskCount", .two) + self._StickerPack_AddMaskCount_few = getValueWithForm(dict, "StickerPack.AddMaskCount", .few) + self._StickerPack_AddMaskCount_many = getValueWithForm(dict, "StickerPack.AddMaskCount", .many) + self._StickerPack_AddMaskCount_other = getValueWithForm(dict, "StickerPack.AddMaskCount", .other) + self._Forward_ConfirmMultipleFiles_zero = getValueWithForm(dict, "Forward.ConfirmMultipleFiles", .zero) + self._Forward_ConfirmMultipleFiles_one = getValueWithForm(dict, "Forward.ConfirmMultipleFiles", .one) + self._Forward_ConfirmMultipleFiles_two = getValueWithForm(dict, "Forward.ConfirmMultipleFiles", .two) + self._Forward_ConfirmMultipleFiles_few = getValueWithForm(dict, "Forward.ConfirmMultipleFiles", .few) + self._Forward_ConfirmMultipleFiles_many = getValueWithForm(dict, "Forward.ConfirmMultipleFiles", .many) + self._Forward_ConfirmMultipleFiles_other = getValueWithForm(dict, "Forward.ConfirmMultipleFiles", .other) + self._Conversation_StatusMembers_zero = getValueWithForm(dict, "Conversation.StatusMembers", .zero) + self._Conversation_StatusMembers_one = getValueWithForm(dict, "Conversation.StatusMembers", .one) + self._Conversation_StatusMembers_two = getValueWithForm(dict, "Conversation.StatusMembers", .two) + self._Conversation_StatusMembers_few = getValueWithForm(dict, "Conversation.StatusMembers", .few) + self._Conversation_StatusMembers_many = getValueWithForm(dict, "Conversation.StatusMembers", .many) + self._Conversation_StatusMembers_other = getValueWithForm(dict, "Conversation.StatusMembers", .other) + self._MuteExpires_Hours_zero = getValueWithForm(dict, "MuteExpires.Hours", .zero) + self._MuteExpires_Hours_one = getValueWithForm(dict, "MuteExpires.Hours", .one) + self._MuteExpires_Hours_two = getValueWithForm(dict, "MuteExpires.Hours", .two) + self._MuteExpires_Hours_few = getValueWithForm(dict, "MuteExpires.Hours", .few) + self._MuteExpires_Hours_many = getValueWithForm(dict, "MuteExpires.Hours", .many) + self._MuteExpires_Hours_other = getValueWithForm(dict, "MuteExpires.Hours", .other) + self._LastSeen_MinutesAgo_zero = getValueWithForm(dict, "LastSeen.MinutesAgo", .zero) + self._LastSeen_MinutesAgo_one = getValueWithForm(dict, "LastSeen.MinutesAgo", .one) + self._LastSeen_MinutesAgo_two = getValueWithForm(dict, "LastSeen.MinutesAgo", .two) + self._LastSeen_MinutesAgo_few = getValueWithForm(dict, "LastSeen.MinutesAgo", .few) + self._LastSeen_MinutesAgo_many = getValueWithForm(dict, "LastSeen.MinutesAgo", .many) + self._LastSeen_MinutesAgo_other = getValueWithForm(dict, "LastSeen.MinutesAgo", .other) + self._Notification_GameScoreSimple_zero = getValueWithForm(dict, "Notification.GameScoreSimple", .zero) + self._Notification_GameScoreSimple_one = getValueWithForm(dict, "Notification.GameScoreSimple", .one) + self._Notification_GameScoreSimple_two = getValueWithForm(dict, "Notification.GameScoreSimple", .two) + self._Notification_GameScoreSimple_few = getValueWithForm(dict, "Notification.GameScoreSimple", .few) + self._Notification_GameScoreSimple_many = getValueWithForm(dict, "Notification.GameScoreSimple", .many) + self._Notification_GameScoreSimple_other = getValueWithForm(dict, "Notification.GameScoreSimple", .other) + self._MuteFor_Hours_zero = getValueWithForm(dict, "MuteFor.Hours", .zero) + self._MuteFor_Hours_one = getValueWithForm(dict, "MuteFor.Hours", .one) + self._MuteFor_Hours_two = getValueWithForm(dict, "MuteFor.Hours", .two) + self._MuteFor_Hours_few = getValueWithForm(dict, "MuteFor.Hours", .few) + self._MuteFor_Hours_many = getValueWithForm(dict, "MuteFor.Hours", .many) + self._MuteFor_Hours_other = getValueWithForm(dict, "MuteFor.Hours", .other) + self._SharedMedia_DeleteItemsConfirmation_zero = getValueWithForm(dict, "SharedMedia.DeleteItemsConfirmation", .zero) + self._SharedMedia_DeleteItemsConfirmation_one = getValueWithForm(dict, "SharedMedia.DeleteItemsConfirmation", .one) + self._SharedMedia_DeleteItemsConfirmation_two = getValueWithForm(dict, "SharedMedia.DeleteItemsConfirmation", .two) + self._SharedMedia_DeleteItemsConfirmation_few = getValueWithForm(dict, "SharedMedia.DeleteItemsConfirmation", .few) + self._SharedMedia_DeleteItemsConfirmation_many = getValueWithForm(dict, "SharedMedia.DeleteItemsConfirmation", .many) + self._SharedMedia_DeleteItemsConfirmation_other = getValueWithForm(dict, "SharedMedia.DeleteItemsConfirmation", .other) + self._ForwardedStickers_zero = getValueWithForm(dict, "ForwardedStickers", .zero) + self._ForwardedStickers_one = getValueWithForm(dict, "ForwardedStickers", .one) + self._ForwardedStickers_two = getValueWithForm(dict, "ForwardedStickers", .two) + self._ForwardedStickers_few = getValueWithForm(dict, "ForwardedStickers", .few) + self._ForwardedStickers_many = getValueWithForm(dict, "ForwardedStickers", .many) + self._ForwardedStickers_other = getValueWithForm(dict, "ForwardedStickers", .other) + self._Call_Seconds_zero = getValueWithForm(dict, "Call.Seconds", .zero) + self._Call_Seconds_one = getValueWithForm(dict, "Call.Seconds", .one) + self._Call_Seconds_two = getValueWithForm(dict, "Call.Seconds", .two) + self._Call_Seconds_few = getValueWithForm(dict, "Call.Seconds", .few) + self._Call_Seconds_many = getValueWithForm(dict, "Call.Seconds", .many) + self._Call_Seconds_other = getValueWithForm(dict, "Call.Seconds", .other) + self._Map_ETAHours_zero = getValueWithForm(dict, "Map.ETAHours", .zero) + self._Map_ETAHours_one = getValueWithForm(dict, "Map.ETAHours", .one) + self._Map_ETAHours_two = getValueWithForm(dict, "Map.ETAHours", .two) + self._Map_ETAHours_few = getValueWithForm(dict, "Map.ETAHours", .few) + self._Map_ETAHours_many = getValueWithForm(dict, "Map.ETAHours", .many) + self._Map_ETAHours_other = getValueWithForm(dict, "Map.ETAHours", .other) + self._PrivacyLastSeenSettings_AddUsers_zero = getValueWithForm(dict, "PrivacyLastSeenSettings.AddUsers", .zero) + self._PrivacyLastSeenSettings_AddUsers_one = getValueWithForm(dict, "PrivacyLastSeenSettings.AddUsers", .one) + self._PrivacyLastSeenSettings_AddUsers_two = getValueWithForm(dict, "PrivacyLastSeenSettings.AddUsers", .two) + self._PrivacyLastSeenSettings_AddUsers_few = getValueWithForm(dict, "PrivacyLastSeenSettings.AddUsers", .few) + self._PrivacyLastSeenSettings_AddUsers_many = getValueWithForm(dict, "PrivacyLastSeenSettings.AddUsers", .many) + self._PrivacyLastSeenSettings_AddUsers_other = getValueWithForm(dict, "PrivacyLastSeenSettings.AddUsers", .other) + self._Notifications_ExceptionMuteExpires_Days_zero = getValueWithForm(dict, "Notifications.ExceptionMuteExpires.Days", .zero) + self._Notifications_ExceptionMuteExpires_Days_one = getValueWithForm(dict, "Notifications.ExceptionMuteExpires.Days", .one) + self._Notifications_ExceptionMuteExpires_Days_two = getValueWithForm(dict, "Notifications.ExceptionMuteExpires.Days", .two) + self._Notifications_ExceptionMuteExpires_Days_few = getValueWithForm(dict, "Notifications.ExceptionMuteExpires.Days", .few) + self._Notifications_ExceptionMuteExpires_Days_many = getValueWithForm(dict, "Notifications.ExceptionMuteExpires.Days", .many) + self._Notifications_ExceptionMuteExpires_Days_other = getValueWithForm(dict, "Notifications.ExceptionMuteExpires.Days", .other) + self._ServiceMessage_GameScoreSimple_zero = getValueWithForm(dict, "ServiceMessage.GameScoreSimple", .zero) + self._ServiceMessage_GameScoreSimple_one = getValueWithForm(dict, "ServiceMessage.GameScoreSimple", .one) + self._ServiceMessage_GameScoreSimple_two = getValueWithForm(dict, "ServiceMessage.GameScoreSimple", .two) + self._ServiceMessage_GameScoreSimple_few = getValueWithForm(dict, "ServiceMessage.GameScoreSimple", .few) + self._ServiceMessage_GameScoreSimple_many = getValueWithForm(dict, "ServiceMessage.GameScoreSimple", .many) + self._ServiceMessage_GameScoreSimple_other = getValueWithForm(dict, "ServiceMessage.GameScoreSimple", .other) + self._MessageTimer_Weeks_zero = getValueWithForm(dict, "MessageTimer.Weeks", .zero) + self._MessageTimer_Weeks_one = getValueWithForm(dict, "MessageTimer.Weeks", .one) + self._MessageTimer_Weeks_two = getValueWithForm(dict, "MessageTimer.Weeks", .two) + self._MessageTimer_Weeks_few = getValueWithForm(dict, "MessageTimer.Weeks", .few) + self._MessageTimer_Weeks_many = getValueWithForm(dict, "MessageTimer.Weeks", .many) + self._MessageTimer_Weeks_other = getValueWithForm(dict, "MessageTimer.Weeks", .other) + self._SharedMedia_Generic_zero = getValueWithForm(dict, "SharedMedia.Generic", .zero) + self._SharedMedia_Generic_one = getValueWithForm(dict, "SharedMedia.Generic", .one) + self._SharedMedia_Generic_two = getValueWithForm(dict, "SharedMedia.Generic", .two) + self._SharedMedia_Generic_few = getValueWithForm(dict, "SharedMedia.Generic", .few) + self._SharedMedia_Generic_many = getValueWithForm(dict, "SharedMedia.Generic", .many) + self._SharedMedia_Generic_other = getValueWithForm(dict, "SharedMedia.Generic", .other) + self._DialogList_LiveLocationChatsCount_zero = getValueWithForm(dict, "DialogList.LiveLocationChatsCount", .zero) + self._DialogList_LiveLocationChatsCount_one = getValueWithForm(dict, "DialogList.LiveLocationChatsCount", .one) + self._DialogList_LiveLocationChatsCount_two = getValueWithForm(dict, "DialogList.LiveLocationChatsCount", .two) + self._DialogList_LiveLocationChatsCount_few = getValueWithForm(dict, "DialogList.LiveLocationChatsCount", .few) + self._DialogList_LiveLocationChatsCount_many = getValueWithForm(dict, "DialogList.LiveLocationChatsCount", .many) + self._DialogList_LiveLocationChatsCount_other = getValueWithForm(dict, "DialogList.LiveLocationChatsCount", .other) + self._AttachmentMenu_SendVideo_zero = getValueWithForm(dict, "AttachmentMenu.SendVideo", .zero) + self._AttachmentMenu_SendVideo_one = getValueWithForm(dict, "AttachmentMenu.SendVideo", .one) + self._AttachmentMenu_SendVideo_two = getValueWithForm(dict, "AttachmentMenu.SendVideo", .two) + self._AttachmentMenu_SendVideo_few = getValueWithForm(dict, "AttachmentMenu.SendVideo", .few) + self._AttachmentMenu_SendVideo_many = getValueWithForm(dict, "AttachmentMenu.SendVideo", .many) + self._AttachmentMenu_SendVideo_other = getValueWithForm(dict, "AttachmentMenu.SendVideo", .other) + self._LiveLocationUpdated_MinutesAgo_zero = getValueWithForm(dict, "LiveLocationUpdated.MinutesAgo", .zero) + self._LiveLocationUpdated_MinutesAgo_one = getValueWithForm(dict, "LiveLocationUpdated.MinutesAgo", .one) + self._LiveLocationUpdated_MinutesAgo_two = getValueWithForm(dict, "LiveLocationUpdated.MinutesAgo", .two) + self._LiveLocationUpdated_MinutesAgo_few = getValueWithForm(dict, "LiveLocationUpdated.MinutesAgo", .few) + self._LiveLocationUpdated_MinutesAgo_many = getValueWithForm(dict, "LiveLocationUpdated.MinutesAgo", .many) + self._LiveLocationUpdated_MinutesAgo_other = getValueWithForm(dict, "LiveLocationUpdated.MinutesAgo", .other) + self._SharedMedia_Photo_zero = getValueWithForm(dict, "SharedMedia.Photo", .zero) + self._SharedMedia_Photo_one = getValueWithForm(dict, "SharedMedia.Photo", .one) + self._SharedMedia_Photo_two = getValueWithForm(dict, "SharedMedia.Photo", .two) + self._SharedMedia_Photo_few = getValueWithForm(dict, "SharedMedia.Photo", .few) + self._SharedMedia_Photo_many = getValueWithForm(dict, "SharedMedia.Photo", .many) + self._SharedMedia_Photo_other = getValueWithForm(dict, "SharedMedia.Photo", .other) + self._StickerPack_RemoveStickerCount_zero = getValueWithForm(dict, "StickerPack.RemoveStickerCount", .zero) + self._StickerPack_RemoveStickerCount_one = getValueWithForm(dict, "StickerPack.RemoveStickerCount", .one) + self._StickerPack_RemoveStickerCount_two = getValueWithForm(dict, "StickerPack.RemoveStickerCount", .two) + self._StickerPack_RemoveStickerCount_few = getValueWithForm(dict, "StickerPack.RemoveStickerCount", .few) + self._StickerPack_RemoveStickerCount_many = getValueWithForm(dict, "StickerPack.RemoveStickerCount", .many) + self._StickerPack_RemoveStickerCount_other = getValueWithForm(dict, "StickerPack.RemoveStickerCount", .other) + self._ServiceMessage_GameScoreSelfSimple_zero = getValueWithForm(dict, "ServiceMessage.GameScoreSelfSimple", .zero) + self._ServiceMessage_GameScoreSelfSimple_one = getValueWithForm(dict, "ServiceMessage.GameScoreSelfSimple", .one) + self._ServiceMessage_GameScoreSelfSimple_two = getValueWithForm(dict, "ServiceMessage.GameScoreSelfSimple", .two) + self._ServiceMessage_GameScoreSelfSimple_few = getValueWithForm(dict, "ServiceMessage.GameScoreSelfSimple", .few) + self._ServiceMessage_GameScoreSelfSimple_many = getValueWithForm(dict, "ServiceMessage.GameScoreSelfSimple", .many) + self._ServiceMessage_GameScoreSelfSimple_other = getValueWithForm(dict, "ServiceMessage.GameScoreSelfSimple", .other) + self._Notifications_ExceptionMuteExpires_Hours_zero = getValueWithForm(dict, "Notifications.ExceptionMuteExpires.Hours", .zero) + self._Notifications_ExceptionMuteExpires_Hours_one = getValueWithForm(dict, "Notifications.ExceptionMuteExpires.Hours", .one) + self._Notifications_ExceptionMuteExpires_Hours_two = getValueWithForm(dict, "Notifications.ExceptionMuteExpires.Hours", .two) + self._Notifications_ExceptionMuteExpires_Hours_few = getValueWithForm(dict, "Notifications.ExceptionMuteExpires.Hours", .few) + self._Notifications_ExceptionMuteExpires_Hours_many = getValueWithForm(dict, "Notifications.ExceptionMuteExpires.Hours", .many) + self._Notifications_ExceptionMuteExpires_Hours_other = getValueWithForm(dict, "Notifications.ExceptionMuteExpires.Hours", .other) + self._ForwardedMessages_zero = getValueWithForm(dict, "ForwardedMessages", .zero) + self._ForwardedMessages_one = getValueWithForm(dict, "ForwardedMessages", .one) + self._ForwardedMessages_two = getValueWithForm(dict, "ForwardedMessages", .two) + self._ForwardedMessages_few = getValueWithForm(dict, "ForwardedMessages", .few) + self._ForwardedMessages_many = getValueWithForm(dict, "ForwardedMessages", .many) + self._ForwardedMessages_other = getValueWithForm(dict, "ForwardedMessages", .other) + self._ServiceMessage_GameScoreExtended_zero = getValueWithForm(dict, "ServiceMessage.GameScoreExtended", .zero) + self._ServiceMessage_GameScoreExtended_one = getValueWithForm(dict, "ServiceMessage.GameScoreExtended", .one) + self._ServiceMessage_GameScoreExtended_two = getValueWithForm(dict, "ServiceMessage.GameScoreExtended", .two) + self._ServiceMessage_GameScoreExtended_few = getValueWithForm(dict, "ServiceMessage.GameScoreExtended", .few) + self._ServiceMessage_GameScoreExtended_many = getValueWithForm(dict, "ServiceMessage.GameScoreExtended", .many) + self._ServiceMessage_GameScoreExtended_other = getValueWithForm(dict, "ServiceMessage.GameScoreExtended", .other) + self._Notifications_Exceptions_zero = getValueWithForm(dict, "Notifications.Exceptions", .zero) + self._Notifications_Exceptions_one = getValueWithForm(dict, "Notifications.Exceptions", .one) + self._Notifications_Exceptions_two = getValueWithForm(dict, "Notifications.Exceptions", .two) + self._Notifications_Exceptions_few = getValueWithForm(dict, "Notifications.Exceptions", .few) + self._Notifications_Exceptions_many = getValueWithForm(dict, "Notifications.Exceptions", .many) + self._Notifications_Exceptions_other = getValueWithForm(dict, "Notifications.Exceptions", .other) + self._Passport_Scans_zero = getValueWithForm(dict, "Passport.Scans", .zero) + self._Passport_Scans_one = getValueWithForm(dict, "Passport.Scans", .one) + self._Passport_Scans_two = getValueWithForm(dict, "Passport.Scans", .two) + self._Passport_Scans_few = getValueWithForm(dict, "Passport.Scans", .few) + self._Passport_Scans_many = getValueWithForm(dict, "Passport.Scans", .many) + self._Passport_Scans_other = getValueWithForm(dict, "Passport.Scans", .other) } } diff --git a/TelegramUI/RadialCloudProgressContentNode.swift b/TelegramUI/RadialCloudProgressContentNode.swift index 3cc0109aa6..1b3a251047 100644 --- a/TelegramUI/RadialCloudProgressContentNode.swift +++ b/TelegramUI/RadialCloudProgressContentNode.swift @@ -271,7 +271,7 @@ final class RadialCloudProgressContentNode: RadialStatusContentNode { } override func enqueueReadyForTransition(_ f: @escaping () -> Void) { - if self.spinnerNode.isAnimatingProgress { + if self.spinnerNode.isAnimatingProgress && self.progress == 1.0 { self.enqueuedReadyForTransition = f } else { f() diff --git a/TelegramUI/RenderedTotalUnreadCount.swift b/TelegramUI/RenderedTotalUnreadCount.swift index 452577afe0..8480bb6882 100644 --- a/TelegramUI/RenderedTotalUnreadCount.swift +++ b/TelegramUI/RenderedTotalUnreadCount.swift @@ -10,59 +10,46 @@ public enum RenderedTotalUnreadCountType { public func renderedTotalUnreadCount(transaction: Transaction) -> (Int32, RenderedTotalUnreadCountType) { let totalUnreadState = transaction.getTotalUnreadState() let inAppSettings: InAppNotificationSettings = (transaction.getPreferencesEntry(key: ApplicationSpecificPreferencesKeys.inAppNotificationSettings) as? InAppNotificationSettings) ?? .defaultSettings - switch inAppSettings.totalUnreadCountDisplayCategory { - case .chats: - switch inAppSettings.totalUnreadCountDisplayStyle { - case .raw: - return (totalUnreadState.absoluteCounters.chatCount, .raw) - case .filtered: - return (totalUnreadState.filteredCounters.chatCount, .filtered) - } - case .messages: - switch inAppSettings.totalUnreadCountDisplayStyle { - case .raw: - return (totalUnreadState.absoluteCounters.messageCount, .raw) - case .filtered: - return (totalUnreadState.filteredCounters.messageCount, .filtered) - } + return renderedTotalUnreadCount(inAppSettings: inAppSettings, totalUnreadState: totalUnreadState) +} + +func renderedTotalUnreadCount(inAppSettings: InAppNotificationSettings, totalUnreadState: ChatListTotalUnreadState) -> (Int32, RenderedTotalUnreadCountType) { + let type: RenderedTotalUnreadCountType + switch inAppSettings.totalUnreadCountDisplayStyle { + case .raw: + type = .raw + case .filtered: + type = .filtered } + return (totalUnreadState.count(for: inAppSettings.totalUnreadCountDisplayStyle.category, in: inAppSettings.totalUnreadCountDisplayCategory.statsType, with: inAppSettings.totalUnreadCountIncludeTags), type) } public func renderedTotalUnreadCount(postbox: Postbox) -> Signal<(Int32, RenderedTotalUnreadCountType), NoError> { - let unreadCountsKey = PostboxViewKey.unreadCounts(items: [UnreadMessageCountsItem.total(.raw, .chats)]) + let unreadCountsKey = PostboxViewKey.unreadCounts(items: [.total(nil)]) let inAppSettingsKey = PostboxViewKey.preferences(keys: Set([ApplicationSpecificPreferencesKeys.inAppNotificationSettings])) return postbox.combinedView(keys: [unreadCountsKey, inAppSettingsKey]) |> map { view -> (Int32, RenderedTotalUnreadCountType) in - var value: Int32 = 0 - var style: TotalUnreadCountDisplayStyle = .raw - var categoryType: TotalUnreadCountDisplayCategory = .messages - if let preferences = view.views[inAppSettingsKey] as? PreferencesView, let inAppSettings = preferences.values[ApplicationSpecificPreferencesKeys.inAppNotificationSettings] as? InAppNotificationSettings { - style = inAppSettings.totalUnreadCountDisplayStyle - categoryType = inAppSettings.totalUnreadCountDisplayCategory + let totalUnreadState: ChatListTotalUnreadState + if let value = view.views[unreadCountsKey] as? UnreadMessageCountsView, let (_, total) = value.total() { + totalUnreadState = total + } else { + totalUnreadState = ChatListTotalUnreadState(absoluteCounters: [:], filteredCounters: [:]) + } + + let inAppSettings: InAppNotificationSettings + if let preferences = view.views[inAppSettingsKey] as? PreferencesView, let value = preferences.values[ApplicationSpecificPreferencesKeys.inAppNotificationSettings] as? InAppNotificationSettings { + inAppSettings = value + } else { + inAppSettings = .defaultSettings } let type: RenderedTotalUnreadCountType - let category: UnreadMessageCountsTotalCategory - switch style { + switch inAppSettings.totalUnreadCountDisplayStyle { case .raw: type = .raw case .filtered: type = .filtered } - switch categoryType { - case .chats: - category = .chats - case .messages: - category = .messages - } - if let unreadCounts = view.views[unreadCountsKey] as? UnreadMessageCountsView { - switch style { - case .raw: - value = unreadCounts.count(for: .total(.raw, category)) ?? 0 - case .filtered: - value = unreadCounts.count(for: .total(.filtered, category)) ?? 0 - } - } - return (value, type) + return (totalUnreadState.count(for: inAppSettings.totalUnreadCountDisplayStyle.category, in: inAppSettings.totalUnreadCountDisplayCategory.statsType, with: inAppSettings.totalUnreadCountIncludeTags), type) } |> distinctUntilChanged(isEqual: { lhs, rhs in return lhs == rhs diff --git a/TelegramUI/SelectablePeerNode.swift b/TelegramUI/SelectablePeerNode.swift index 97c176a35e..b387b1dff9 100644 --- a/TelegramUI/SelectablePeerNode.swift +++ b/TelegramUI/SelectablePeerNode.swift @@ -65,14 +65,13 @@ final class SelectablePeerNode: ASDisplayNode { private var currentSelected = false - private var peer: Peer? - private var chatPeer: Peer? + private var peer: RenderedPeer? var theme: SelectablePeerNodeTheme = SelectablePeerNodeTheme(textColor: .black, secretTextColor: .green, selectedTextColor: .blue, checkBackgroundColor: .white, checkFillColor: .blue, checkColor: .white) { didSet { if !self.theme.isEqual(to: oldValue) { - if let peer = self.peer { - self.textNode.attributedText = NSAttributedString(string: peer.displayTitle, font: textFont, textColor: self.currentSelected ? self.theme.selectedTextColor : self.theme.textColor, paragraphAlignment: .center) + if let peer = self.peer, let mainPeer = peer.chatMainPeer { + self.textNode.attributedText = NSAttributedString(string: mainPeer.displayTitle, font: textFont, textColor: self.currentSelected ? self.theme.selectedTextColor : (peer.peerId.namespace == Namespaces.Peer.SecretChat ? self.theme.secretTextColor : self.theme.textColor), paragraphAlignment: .center) } } } @@ -107,30 +106,26 @@ final class SelectablePeerNode: ASDisplayNode { } - func setup(account: Account, strings: PresentationStrings, peer: Peer, chatPeer: Peer?, numberOfLines: Int = 2) { + func setup(account: Account, strings: PresentationStrings, peer: RenderedPeer, numberOfLines: Int = 2) { self.peer = peer - self.chatPeer = chatPeer - - var defaultColor: UIColor = self.theme.textColor - if let chatPeer = chatPeer, chatPeer.id.namespace == Namespaces.Peer.SecretChat { - defaultColor = self.theme.secretTextColor + guard let mainPeer = peer.chatMainPeer else { + return } + let defaultColor: UIColor = peer.peerId.namespace == Namespaces.Peer.SecretChat ? self.theme.secretTextColor : self.theme.textColor + let text: String var overrideImage: AvatarNodeImageOverride? - if peer.id == account.peerId { + if peer.peerId == account.peerId { text = strings.DialogList_SavedMessages overrideImage = .savedMessagesIcon } else { - text = peer.compactDisplayTitle + text = mainPeer.compactDisplayTitle } self.textNode.maximumNumberOfLines = UInt(numberOfLines) self.textNode.attributedText = NSAttributedString(string: text, font: textFont, textColor: self.currentSelected ? self.theme.selectedTextColor : defaultColor, paragraphAlignment: .center) - self.avatarNode.setPeer(account: account, peer: peer, overrideImage: overrideImage) + self.avatarNode.setPeer(account: account, peer: mainPeer, overrideImage: overrideImage) self.setNeedsLayout() - - - } func updateSelection(selected: Bool, animated: Bool) { diff --git a/TelegramUI/ShareController.swift b/TelegramUI/ShareController.swift index 09d441bb87..478ddd4ab3 100644 --- a/TelegramUI/ShareController.swift +++ b/TelegramUI/ShareController.swift @@ -170,7 +170,7 @@ public final class ShareController: ViewController { private let immediateExternalShare: Bool private let subject: ShareControllerSubject - private let peers = Promise<([Peer], Peer)>() + private let peers = Promise<([RenderedPeer], Peer)>() private let peersDisposable = MetaDisposable() private var defaultAction: ShareControllerAction? @@ -265,13 +265,16 @@ public final class ShareController: ViewController { }) } - self.peers.set(combineLatest(account.postbox.loadedPeerWithId(account.peerId) |> take(1), account.viewTracker.tailChatListView(groupId: nil, count: 150) |> take(1)) |> map { accountPeer, view -> ([Peer], Peer) in - var peers: [Peer] = [] + self.peers.set(combineLatest(account.postbox.loadedPeerWithId(account.peerId) + |> take(1), account.viewTracker.tailChatListView(groupId: nil, count: 150) + |> take(1)) + |> map { accountPeer, view -> ([RenderedPeer], Peer) in + var peers: [RenderedPeer] = [] for entry in view.0.entries.reversed() { switch entry { case let .MessageEntry(_, _, _, _, _, renderedPeer, _): - if let peer = renderedPeer.chatMainPeer, peer.id != accountPeer.id, canSendMessagesToPeer(peer) { - peers.append(peer) + if let peer = renderedPeer.peers[renderedPeer.peerId], peer.id != accountPeer.id, canSendMessagesToPeer(peer) { + peers.append(renderedPeer) } default: break diff --git a/TelegramUI/ShareControllerNode.swift b/TelegramUI/ShareControllerNode.swift index d8a75032f1..fdce9e0ee0 100644 --- a/TelegramUI/ShareControllerNode.swift +++ b/TelegramUI/ShareControllerNode.swift @@ -531,7 +531,7 @@ final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate } } - func updatePeers(peers: [Peer], accountPeer: Peer, defaultAction: ShareControllerAction?) { + func updatePeers(peers: [RenderedPeer], accountPeer: Peer, defaultAction: ShareControllerAction?) { let peersContentNode = SharePeersContainerNode(account: self.account, theme: self.presentationData.theme, strings: self.presentationData.strings, peers: peers, accountPeer: accountPeer, controllerInteraction: self.controllerInteraction!, externalShare: self.externalShare) self.peersContentNode = peersContentNode peersContentNode.openSearch = { [weak self] in diff --git a/TelegramUI/ShareControllerPeerGridItem.swift b/TelegramUI/ShareControllerPeerGridItem.swift index fdbe69db58..29ba374601 100644 --- a/TelegramUI/ShareControllerPeerGridItem.swift +++ b/TelegramUI/ShareControllerPeerGridItem.swift @@ -83,19 +83,17 @@ final class ShareControllerPeerGridItem: GridItem { let account: Account let theme: PresentationTheme let strings: PresentationStrings - let peer: Peer - let chatPeer: Peer? + let peer: RenderedPeer let controllerInteraction: ShareControllerInteraction let search: Bool let section: GridSection? - init(account: Account, theme: PresentationTheme, strings: PresentationStrings, peer: Peer, chatPeer: Peer?, controllerInteraction: ShareControllerInteraction, sectionTitle: String? = nil, search: Bool = false) { + init(account: Account, theme: PresentationTheme, strings: PresentationStrings, peer: RenderedPeer, controllerInteraction: ShareControllerInteraction, sectionTitle: String? = nil, search: Bool = false) { self.account = account self.theme = theme self.strings = strings self.peer = peer - self.chatPeer = chatPeer self.controllerInteraction = controllerInteraction self.search = search @@ -109,7 +107,7 @@ final class ShareControllerPeerGridItem: GridItem { func node(layout: GridNodeLayout) -> GridItemNode { let node = ShareControllerPeerGridItemNode() node.controllerInteraction = self.controllerInteraction - node.setup(account: self.account, theme: self.theme, strings: self.strings, peer: self.peer, chatPeer: self.chatPeer, search: self.search) + node.setup(account: self.account, theme: self.theme, strings: self.strings, peer: self.peer, search: self.search) return node } @@ -119,12 +117,12 @@ final class ShareControllerPeerGridItem: GridItem { return } node.controllerInteraction = self.controllerInteraction - node.setup(account: self.account, theme: self.theme, strings: self.strings, peer: self.peer, chatPeer: self.chatPeer, search: self.search) + node.setup(account: self.account, theme: self.theme, strings: self.strings, peer: self.peer, search: self.search) } } final class ShareControllerPeerGridItemNode: GridItemNode { - private var currentState: (Account, Peer, Peer?, Bool)? + private var currentState: (Account, RenderedPeer, Bool)? private let peerNode: SelectablePeerNode var controllerInteraction: ShareControllerInteraction? @@ -136,21 +134,22 @@ final class ShareControllerPeerGridItemNode: GridItemNode { self.peerNode.toggleSelection = { [weak self] in if let strongSelf = self { - if let (_, peer, chatPeer, search) = strongSelf.currentState { - let mainPeer = chatPeer ?? peer - strongSelf.controllerInteraction?.togglePeer(mainPeer, search) + if let (_, peer, search) = strongSelf.currentState { + if let actualPeer = peer.peers[peer.peerId] { + strongSelf.controllerInteraction?.togglePeer(actualPeer, search) + } } } } self.addSubnode(self.peerNode) } - func setup(account: Account, theme: PresentationTheme, strings: PresentationStrings, peer: Peer, chatPeer: Peer?, search: Bool) { - if self.currentState == nil || self.currentState!.0 !== account || !arePeersEqual(self.currentState!.1, peer) { + func setup(account: Account, theme: PresentationTheme, strings: PresentationStrings, peer: RenderedPeer, search: Bool) { + if self.currentState == nil || self.currentState!.0 !== account || self.currentState!.1 != peer { let itemTheme = SelectablePeerNodeTheme(textColor: theme.actionSheet.primaryTextColor, secretTextColor: theme.chatList.secretTitleColor, selectedTextColor: theme.actionSheet.controlAccentColor, checkBackgroundColor: theme.actionSheet.opaqueItemBackgroundColor, checkFillColor: theme.actionSheet.controlAccentColor, checkColor: theme.actionSheet.checkContentColor) self.peerNode.theme = itemTheme - self.peerNode.setup(account: account, strings: strings, peer: peer, chatPeer: chatPeer) - self.currentState = (account, peer, chatPeer, search) + self.peerNode.setup(account: account, strings: strings, peer: peer) + self.currentState = (account, peer, search) self.setNeedsLayout() } self.updateSelection(animated: false) @@ -158,9 +157,8 @@ final class ShareControllerPeerGridItemNode: GridItemNode { func updateSelection(animated: Bool) { var selected = false - if let controllerInteraction = self.controllerInteraction, let (_, peer, chatPeer, _) = self.currentState { - let mainPeer = chatPeer ?? peer - selected = controllerInteraction.selectedPeerIds.contains(mainPeer.id) + if let controllerInteraction = self.controllerInteraction, let (_, peer, _) = self.currentState { + selected = controllerInteraction.selectedPeerIds.contains(peer.peerId) } self.peerNode.updateSelection(selected: selected, animated: animated) diff --git a/TelegramUI/SharePeersContainerNode.swift b/TelegramUI/SharePeersContainerNode.swift index 46259f20b8..a85f65661b 100644 --- a/TelegramUI/SharePeersContainerNode.swift +++ b/TelegramUI/SharePeersContainerNode.swift @@ -9,19 +9,19 @@ private let subtitleFont = Font.regular(12.0) private struct SharePeerEntry: Comparable, Identifiable { let index: Int32 - let peer: Peer + let peer: RenderedPeer let theme: PresentationTheme let strings: PresentationStrings var stableId: Int64 { - return self.peer.id.toInt64() + return self.peer.peerId.toInt64() } static func ==(lhs: SharePeerEntry, rhs: SharePeerEntry) -> Bool { if lhs.index != rhs.index { return false } - if !arePeersEqual(lhs.peer, rhs.peer) { + if lhs.peer != rhs.peer { return false } return true @@ -32,7 +32,7 @@ private struct SharePeerEntry: Comparable, Identifiable { } func item(account: Account, interfaceInteraction: ShareControllerInteraction) -> GridItem { - return ShareControllerPeerGridItem(account: account, theme: self.theme, strings: self.strings, peer: self.peer, chatPeer: nil, controllerInteraction: interfaceInteraction, search: false) + return ShareControllerPeerGridItem(account: account, theme: self.theme, strings: self.strings, peer: self.peer, controllerInteraction: interfaceInteraction, search: false) } } @@ -82,7 +82,7 @@ final class SharePeersContainerNode: ASDisplayNode, ShareContentContainerNode { private var validLayout: (CGSize, CGFloat)? private var overrideGridOffsetTransition: ContainedViewLayoutTransition? - init(account: Account, theme: PresentationTheme, strings: PresentationStrings, peers: [Peer], accountPeer: Peer, controllerInteraction: ShareControllerInteraction, externalShare: Bool) { + init(account: Account, theme: PresentationTheme, strings: PresentationStrings, peers: [RenderedPeer], accountPeer: Peer, controllerInteraction: ShareControllerInteraction, externalShare: Bool) { self.account = account self.theme = theme self.strings = strings @@ -90,32 +90,31 @@ final class SharePeersContainerNode: ASDisplayNode, ShareContentContainerNode { self.accountPeer = accountPeer let items: Signal<[SharePeerEntry], NoError> = combineLatest(.single(peers), foundPeers.get()) - |> map { initialPeers, foundPeers -> [SharePeerEntry] in - var entries: [SharePeerEntry] = [] - var index: Int32 = 0 - - var existingPeerIds: Set = Set() - - entries.append(SharePeerEntry(index: index, peer: accountPeer, theme: theme, strings: strings)) + |> map { initialPeers, foundPeers -> [SharePeerEntry] in + var entries: [SharePeerEntry] = [] + var index: Int32 = 0 + + var existingPeerIds: Set = Set() + + entries.append(SharePeerEntry(index: index, peer: RenderedPeer(peer: accountPeer), theme: theme, strings: strings)) + index += 1 + + for peer in foundPeers.reversed() { + entries.append(SharePeerEntry(index: index, peer: RenderedPeer(peer: peer), theme: theme, strings: strings)) + existingPeerIds.insert(peer.id) index += 1 - - for peer in foundPeers.reversed() { + } + + for peer in initialPeers { + if !existingPeerIds.contains(peer.peerId) { entries.append(SharePeerEntry(index: index, peer: peer, theme: theme, strings: strings)) - existingPeerIds.insert(peer.id) + existingPeerIds.insert(peer.peerId) index += 1 } - - for peer in initialPeers { - if !existingPeerIds.contains(peer.id) { - entries.append(SharePeerEntry(index: index, peer: peer, theme: theme, strings: strings)) - existingPeerIds.insert(peer.id) - index += 1 - } - } - return entries + } + return entries } - self.contentGridNode = GridNode() self.contentTitleNode = ASTextNode() @@ -249,7 +248,7 @@ final class SharePeersContainerNode: ASDisplayNode, ShareContentContainerNode { var scrollToItem: GridNodeScrollToItem? if let ensurePeerVisibleOnLayout = self.ensurePeerVisibleOnLayout { self.ensurePeerVisibleOnLayout = nil - if let index = self.entries.index(where: { $0.peer.id == ensurePeerVisibleOnLayout }) { + if let index = self.entries.index(where: { $0.peer.peerId == ensurePeerVisibleOnLayout }) { scrollToItem = GridNodeScrollToItem(index: index, position: .visible, transition: transition, directionHint: .up, adjustForSection: false) } } diff --git a/TelegramUI/ShareSearchContainerNode.swift b/TelegramUI/ShareSearchContainerNode.swift index e10c3b371c..226a6510d4 100644 --- a/TelegramUI/ShareSearchContainerNode.swift +++ b/TelegramUI/ShareSearchContainerNode.swift @@ -94,16 +94,12 @@ private enum ShareSearchRecentEntry: Comparable, Identifiable { case let .topPeers(theme, strings): return ShareControllerRecentPeersGridItem(account: account, theme: theme, strings: strings, controllerInteraction: interfaceInteraction) case let .peer(_, theme, peer, associatedPeer, strings): - let primaryPeer: Peer - var chatPeer: Peer? + var peers: [PeerId: Peer] = [peer.id: peer] if let associatedPeer = associatedPeer { - primaryPeer = associatedPeer - chatPeer = peer - } else { - primaryPeer = peer - chatPeer = associatedPeer + peers[associatedPeer.id] = associatedPeer } - return ShareControllerPeerGridItem(account: account, theme: theme, strings: strings, peer: primaryPeer, chatPeer: chatPeer, controllerInteraction: interfaceInteraction, sectionTitle: strings.DialogList_SearchSectionRecent, search: true) + let peer = RenderedPeer(peerId: peer.id, peers: SimpleDictionary(peers)) + return ShareControllerPeerGridItem(account: account, theme: theme, strings: strings, peer: peer, controllerInteraction: interfaceInteraction, sectionTitle: strings.DialogList_SearchSectionRecent, search: true) } } } @@ -133,7 +129,7 @@ private struct ShareSearchPeerEntry: Comparable, Identifiable { } func item(account: Account, interfaceInteraction: ShareControllerInteraction) -> GridItem { - return ShareControllerPeerGridItem(account: account, theme: self.theme, strings: self.strings, peer: self.peer, chatPeer: nil, controllerInteraction: interfaceInteraction, search: true) + return ShareControllerPeerGridItem(account: account, theme: self.theme, strings: self.strings, peer: RenderedPeer(peer: self.peer), controllerInteraction: interfaceInteraction, search: true) } } diff --git a/TelegramUI/SharedMediaPlayer.swift b/TelegramUI/SharedMediaPlayer.swift index 4b4cea280c..f8e5754627 100644 --- a/TelegramUI/SharedMediaPlayer.swift +++ b/TelegramUI/SharedMediaPlayer.swift @@ -129,13 +129,17 @@ final class SharedMediaPlaylistState: Equatable { let loading: Bool let playedToEnd: Bool let item: SharedMediaPlaylistItem? + let nextItem: SharedMediaPlaylistItem? + let previousItem: SharedMediaPlaylistItem? let order: MusicPlaybackSettingsOrder let looping: MusicPlaybackSettingsLooping - init(loading: Bool, playedToEnd: Bool, item: SharedMediaPlaylistItem?, order: MusicPlaybackSettingsOrder, looping: MusicPlaybackSettingsLooping) { + init(loading: Bool, playedToEnd: Bool, item: SharedMediaPlaylistItem?, nextItem: SharedMediaPlaylistItem?, previousItem: SharedMediaPlaylistItem?, order: MusicPlaybackSettingsOrder, looping: MusicPlaybackSettingsLooping) { self.loading = loading self.playedToEnd = playedToEnd self.item = item + self.nextItem = nextItem + self.previousItem = previousItem self.order = order self.looping = looping } @@ -147,6 +151,12 @@ final class SharedMediaPlaylistState: Equatable { if !arePlaylistItemsEqual(lhs.item, rhs.item) { return false } + if !arePlaylistItemsEqual(lhs.nextItem, rhs.nextItem) { + return false + } + if !arePlaylistItemsEqual(lhs.previousItem, rhs.previousItem) { + return false + } if lhs.order != rhs.order { return false } @@ -381,6 +391,8 @@ final class SharedMediaPlayer { } private let state = Promise(nil) + private var playbackStateValueDisposable: Disposable? + private var _playbackStateValue: SharedMediaPlayerState? private let playbackStateValue = Promise(nil) var playbackState: Signal { return self.playbackStateValue.get() @@ -412,7 +424,8 @@ final class SharedMediaPlayer { self.forceAudioToSpeaker = !DeviceProximityManager.shared().currentValue() } - self.stateDisposable = (playlist.state |> deliverOnMainQueue).start(next: { [weak self] state in + self.stateDisposable = (playlist.state + |> deliverOnMainQueue).start(next: { [weak self] state in if let strongSelf = self { let previousPlaybackItem = strongSelf.playbackItem if state.item?.playbackData != strongSelf.stateValue?.item?.playbackData { @@ -525,7 +538,8 @@ final class SharedMediaPlayer { let playlistLocation = strongSelf.playlist.location let playerIndex = strongSelf.playerIndex if let playbackItem = strongSelf.playbackItem, let item = state.item { - strongSelf.playbackStateValue.set(playbackItem.playbackStatus |> map { itemStatus in + strongSelf.playbackStateValue.set(playbackItem.playbackStatus + |> map { itemStatus in return .item(SharedMediaPlayerItemPlaybackState(playlistId: playlistId, playlistLocation: playlistLocation, item: item, status: itemStatus, order: state.order, looping: state.looping, playerIndex: playerIndex)) }) strongSelf.markItemAsPlayedDisposable.set((playbackItem.playbackStatus @@ -558,6 +572,11 @@ final class SharedMediaPlayer { } }) + self.playbackStateValueDisposable = (self.playbackState + |> deliverOnMainQueue).start(next: { [weak self] value in + self?._playbackStateValue = value + }) + if controlPlaybackWithProximity { self.proximityManagerIndex = DeviceProximityManager.shared().add { [weak self] value in let forceAudioToSpeaker = !value @@ -578,6 +597,7 @@ final class SharedMediaPlayer { self.stateDisposable?.dispose() self.markItemAsPlayedDisposable.dispose() self.inForegroundDisposable?.dispose() + self.playbackStateValueDisposable?.dispose() if let proximityManagerIndex = self.proximityManagerIndex { DeviceProximityManager.shared().remove(proximityManagerIndex) @@ -600,8 +620,13 @@ final class SharedMediaPlayer { self.scheduledPlaybackAction = .play self.playlist.control(.next) case .previous: - self.scheduledPlaybackAction = .play - self.playlist.control(.previous) + let threshold: Double = 5.0 + if let playbackStateValue = self._playbackStateValue, case let .item(item) = playbackStateValue, item.status.duration > threshold, item.status.timestamp > threshold { + self.control(.seek(0.0)) + } else { + self.scheduledPlaybackAction = .play + self.playlist.control(.previous) + } case let .playback(action): if let playbackItem = self.playbackItem { switch action {