diff --git a/Images.xcassets/Call/Star.imageset/CallStar@2x.png b/Images.xcassets/Call/Star.imageset/CallStar@2x.png new file mode 100644 index 0000000000..c5805fdae1 Binary files /dev/null and b/Images.xcassets/Call/Star.imageset/CallStar@2x.png differ diff --git a/Images.xcassets/Call/Star.imageset/CallStar@3x.png b/Images.xcassets/Call/Star.imageset/CallStar@3x.png new file mode 100644 index 0000000000..7d073fdfaa Binary files /dev/null and b/Images.xcassets/Call/Star.imageset/CallStar@3x.png differ diff --git a/Images.xcassets/Call/Star.imageset/Contents.json b/Images.xcassets/Call/Star.imageset/Contents.json new file mode 100644 index 0000000000..c14c666eeb --- /dev/null +++ b/Images.xcassets/Call/Star.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "CallStar@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "CallStar@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Images.xcassets/Call/StarHighlighted.imageset/CallStar_Highlighted@2x.png b/Images.xcassets/Call/StarHighlighted.imageset/CallStar_Highlighted@2x.png new file mode 100644 index 0000000000..556454a85a Binary files /dev/null and b/Images.xcassets/Call/StarHighlighted.imageset/CallStar_Highlighted@2x.png differ diff --git a/Images.xcassets/Call/StarHighlighted.imageset/CallStar_Highlighted@3x.png b/Images.xcassets/Call/StarHighlighted.imageset/CallStar_Highlighted@3x.png new file mode 100644 index 0000000000..fc738df153 Binary files /dev/null and b/Images.xcassets/Call/StarHighlighted.imageset/CallStar_Highlighted@3x.png differ diff --git a/Images.xcassets/Call/StarHighlighted.imageset/Contents.json b/Images.xcassets/Call/StarHighlighted.imageset/Contents.json new file mode 100644 index 0000000000..03030f91e4 --- /dev/null +++ b/Images.xcassets/Call/StarHighlighted.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "CallStar_Highlighted@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "CallStar_Highlighted@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Images.xcassets/Secure ID/ViewPassportIcon.imageset/PassportSettingsLogo@2x.png b/Images.xcassets/Secure ID/ViewPassportIcon.imageset/PassportSettingsLogo@2x.png index bc8c076bdd..79227b4d77 100644 Binary files a/Images.xcassets/Secure ID/ViewPassportIcon.imageset/PassportSettingsLogo@2x.png and b/Images.xcassets/Secure ID/ViewPassportIcon.imageset/PassportSettingsLogo@2x.png differ diff --git a/Images.xcassets/Settings/MenuIcons/Passport.imageset/SettingsPassportIcon@3x.png b/Images.xcassets/Settings/MenuIcons/Passport.imageset/SettingsPassportIcon@3x.png index 25db6e87ec..b52419e129 100644 Binary files a/Images.xcassets/Settings/MenuIcons/Passport.imageset/SettingsPassportIcon@3x.png and b/Images.xcassets/Settings/MenuIcons/Passport.imageset/SettingsPassportIcon@3x.png differ diff --git a/TelegramUI.xcodeproj/project.pbxproj b/TelegramUI.xcodeproj/project.pbxproj index 584f90aff9..821fbe3281 100644 --- a/TelegramUI.xcodeproj/project.pbxproj +++ b/TelegramUI.xcodeproj/project.pbxproj @@ -76,7 +76,6 @@ 09B4EE5E21AC626B00847FA6 /* PermissionContentNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09B4EE5D21AC626B00847FA6 /* PermissionContentNode.swift */; }; 09B4EE6021AD4A0E00847FA6 /* InstantPageContentNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09B4EE5F21AD4A0E00847FA6 /* InstantPageContentNode.swift */; }; 09B4EE6221AD791600847FA6 /* InstantPageStoredState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09B4EE6121AD791600847FA6 /* InstantPageStoredState.swift */; }; - 09B4EE6421AD7B3E00847FA6 /* ItemCacheKeys.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09B4EE6321AD7B3E00847FA6 /* ItemCacheKeys.swift */; }; 09C3466D2167D63A00B76780 /* Accessibility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09C3466C2167D63A00B76780 /* Accessibility.swift */; }; 09C500242142BA6400EF253E /* ItemListWebsiteItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09C500232142BA6400EF253E /* ItemListWebsiteItem.swift */; }; 09C9EA33219F79F600E90146 /* ID3Artwork.m in Sources */ = {isa = PBXBuildFile; fileRef = 09C9EA31219F79F500E90146 /* ID3Artwork.m */; }; @@ -84,8 +83,13 @@ 09C9EA3821A044B500E90146 /* StringForDuration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09C9EA3721A044B500E90146 /* StringForDuration.swift */; }; 09D304152173C0E900C00567 /* WatchManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09D304142173C0E900C00567 /* WatchManager.swift */; }; 09D304182173C15700C00567 /* WatchSettingsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09D304172173C15700C00567 /* WatchSettingsController.swift */; }; - 09DD88E721BAE11B000766BC /* WebSearchRecentQueriesNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09DD88E621BAE11B000766BC /* WebSearchRecentQueriesNode.swift */; }; 09DD88E921BAF65E000766BC /* ItemListAddressItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09DD88E821BAF65E000766BC /* ItemListAddressItem.swift */; }; + 09DD88ED21BDC8B7000766BC /* FormEditableBlockItemNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09DD88EC21BDC8B7000766BC /* FormEditableBlockItemNode.swift */; }; + 09DD88EF21BDDE2B000766BC /* Geocoding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09DD88EE21BDDE2B000766BC /* Geocoding.swift */; }; + 09DD88F121BE1090000766BC /* CallRatingController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09DD88F021BE1090000766BC /* CallRatingController.swift */; }; + 09DD88F321BF907C000766BC /* WebSearchRecentQueryItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09DD88F221BF907C000766BC /* WebSearchRecentQueryItem.swift */; }; + 09DD88F521BF9730000766BC /* WebSearchRecentQueries.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09DD88F421BF9730000766BC /* WebSearchRecentQueries.swift */; }; + 09DD88FA21BFD70B000766BC /* ThemedTextAlertController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09DD88F921BFD70B000766BC /* ThemedTextAlertController.swift */; }; 09FE756D2153F5F900A3120F /* CallRouteActionSheetItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09FE756C2153F5F900A3120F /* CallRouteActionSheetItem.swift */; }; 9F06830921A404AB001D8EDB /* NotificationExceptionControllerNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F06830821A404AB001D8EDB /* NotificationExceptionControllerNode.swift */; }; 9F06830B21A404C4001D8EDB /* NotificationExcetionSettingsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F06830A21A404C4001D8EDB /* NotificationExcetionSettingsController.swift */; }; @@ -632,7 +636,7 @@ D0EC6CD31EB9F58800EBF1C3 /* GenerateTextEntities.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0F917B41E0DA396003687E6 /* GenerateTextEntities.swift */; }; D0EC6CD41EB9F58800EBF1C3 /* StringWithAppliedEntities.swift in Sources */ = {isa = PBXBuildFile; fileRef = D017494D1E1059570057C89A /* StringWithAppliedEntities.swift */; }; D0EC6CD51EB9F58800EBF1C3 /* StoredMessageFromSearchPeer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01749541E1082770057C89A /* StoredMessageFromSearchPeer.swift */; }; - D0EC6CD61EB9F58800EBF1C3 /* PreferencesKeys.swift in Sources */ = {isa = PBXBuildFile; fileRef = D087750B1E3E7B7600A97350 /* PreferencesKeys.swift */; }; + D0EC6CD61EB9F58800EBF1C3 /* PostboxKeys.swift in Sources */ = {isa = PBXBuildFile; fileRef = D087750B1E3E7B7600A97350 /* PostboxKeys.swift */; }; D0EC6CD71EB9F58800EBF1C3 /* EmojiUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01D6BFB1E42AB3C006151C6 /* EmojiUtils.swift */; }; D0EC6CD81EB9F58800EBF1C3 /* ShakeAnimation.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0DA44551E4E7F43005FDCA7 /* ShakeAnimation.swift */; }; D0EC6CD91EB9F58800EBF1C3 /* ValidateAddressNameInteractive.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E305A41E5B2BFB00D7A3A2 /* ValidateAddressNameInteractive.swift */; }; @@ -1014,7 +1018,7 @@ D0EC6E6B1EB9F58900EBF1C3 /* InstalledStickerPacksController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0FA0AC41E77431A005BB9B7 /* InstalledStickerPacksController.swift */; }; D0EC6E6C1EB9F58900EBF1C3 /* FeaturedStickerPacksController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E23DD71E805E2600B9B6D2 /* FeaturedStickerPacksController.swift */; }; D0EC6E6D1EB9F58900EBF1C3 /* ItemListStickerPackItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D04791661E79A22000F18979 /* ItemListStickerPackItem.swift */; }; - D0EC6E6E1EB9F58900EBF1C3 /* ArhivedStickerPacksController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E23DDC1E8081A200B9B6D2 /* ArhivedStickerPacksController.swift */; }; + D0EC6E6E1EB9F58900EBF1C3 /* ArchivedStickerPacksController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E23DDC1E8081A200B9B6D2 /* ArchivedStickerPacksController.swift */; }; D0EC6E711EB9F58900EBF1C3 /* ThemeGalleryController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D05174A41EAA456600A1BF36 /* ThemeGalleryController.swift */; }; D0EC6E721EB9F58900EBF1C3 /* ThemeGalleryItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D05174A81EAA46E000A1BF36 /* ThemeGalleryItem.swift */; }; D0EC6E731EB9F58900EBF1C3 /* ThemeGalleryToolbarNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D05174AA1EAA5B4700A1BF36 /* ThemeGalleryToolbarNode.swift */; }; @@ -1166,7 +1170,6 @@ 09B4EE5D21AC626B00847FA6 /* PermissionContentNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PermissionContentNode.swift; sourceTree = ""; }; 09B4EE5F21AD4A0E00847FA6 /* InstantPageContentNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstantPageContentNode.swift; sourceTree = ""; }; 09B4EE6121AD791600847FA6 /* InstantPageStoredState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstantPageStoredState.swift; sourceTree = ""; }; - 09B4EE6321AD7B3E00847FA6 /* ItemCacheKeys.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemCacheKeys.swift; sourceTree = ""; }; 09C3466C2167D63A00B76780 /* Accessibility.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Accessibility.swift; sourceTree = ""; }; 09C500232142BA6400EF253E /* ItemListWebsiteItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemListWebsiteItem.swift; sourceTree = ""; }; 09C9EA31219F79F500E90146 /* ID3Artwork.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ID3Artwork.m; sourceTree = ""; }; @@ -1174,8 +1177,13 @@ 09C9EA3721A044B500E90146 /* StringForDuration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StringForDuration.swift; sourceTree = ""; }; 09D304142173C0E900C00567 /* WatchManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WatchManager.swift; sourceTree = ""; }; 09D304172173C15700C00567 /* WatchSettingsController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WatchSettingsController.swift; sourceTree = ""; }; - 09DD88E621BAE11B000766BC /* WebSearchRecentQueriesNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebSearchRecentQueriesNode.swift; sourceTree = ""; }; 09DD88E821BAF65E000766BC /* ItemListAddressItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemListAddressItem.swift; sourceTree = ""; }; + 09DD88EC21BDC8B7000766BC /* FormEditableBlockItemNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FormEditableBlockItemNode.swift; sourceTree = ""; }; + 09DD88EE21BDDE2B000766BC /* Geocoding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Geocoding.swift; sourceTree = ""; }; + 09DD88F021BE1090000766BC /* CallRatingController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallRatingController.swift; sourceTree = ""; }; + 09DD88F221BF907C000766BC /* WebSearchRecentQueryItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebSearchRecentQueryItem.swift; sourceTree = ""; }; + 09DD88F421BF9730000766BC /* WebSearchRecentQueries.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebSearchRecentQueries.swift; sourceTree = ""; }; + 09DD88F921BFD70B000766BC /* ThemedTextAlertController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThemedTextAlertController.swift; sourceTree = ""; }; 09FE756C2153F5F900A3120F /* CallRouteActionSheetItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CallRouteActionSheetItem.swift; sourceTree = ""; }; 9F06830821A404AB001D8EDB /* NotificationExceptionControllerNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NotificationExceptionControllerNode.swift; sourceTree = ""; }; 9F06830A21A404C4001D8EDB /* NotificationExcetionSettingsController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NotificationExcetionSettingsController.swift; sourceTree = ""; }; @@ -1664,7 +1672,7 @@ D08774F71E3DE7BF00A97350 /* ItemListEditableDeleteControlNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ItemListEditableDeleteControlNode.swift; sourceTree = ""; }; D08774F91E3E2A5600A97350 /* ItemListCheckboxItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ItemListCheckboxItem.swift; sourceTree = ""; }; D08775081E3E59DE00A97350 /* PeerNotificationSoundStrings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PeerNotificationSoundStrings.swift; sourceTree = ""; }; - D087750B1E3E7B7600A97350 /* PreferencesKeys.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PreferencesKeys.swift; sourceTree = ""; }; + D087750B1E3E7B7600A97350 /* PostboxKeys.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PostboxKeys.swift; sourceTree = ""; }; D087750F1E3F46A400A97350 /* ComposeController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ComposeController.swift; sourceTree = ""; }; D08775111E3F46AB00A97350 /* ComposeControllerNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ComposeControllerNode.swift; sourceTree = ""; }; D08775131E3F4A7700A97350 /* ContactListNameIndexHeader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContactListNameIndexHeader.swift; sourceTree = ""; }; @@ -1931,7 +1939,7 @@ D0DF0CA31D82BCD0008AEB01 /* MentionChatInputContextPanelNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MentionChatInputContextPanelNode.swift; sourceTree = ""; }; D0DFD5E11FCE2BA50039B3B1 /* CalculatingCacheSizeItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalculatingCacheSizeItem.swift; sourceTree = ""; }; D0E23DD71E805E2600B9B6D2 /* FeaturedStickerPacksController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FeaturedStickerPacksController.swift; sourceTree = ""; }; - D0E23DDC1E8081A200B9B6D2 /* ArhivedStickerPacksController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ArhivedStickerPacksController.swift; sourceTree = ""; }; + D0E23DDC1E8081A200B9B6D2 /* ArchivedStickerPacksController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ArchivedStickerPacksController.swift; sourceTree = ""; }; D0E266FC1F66706500BFC79F /* ChatBubbleVideoDecoration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatBubbleVideoDecoration.swift; sourceTree = ""; }; D0E305A41E5B2BFB00D7A3A2 /* ValidateAddressNameInteractive.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ValidateAddressNameInteractive.swift; sourceTree = ""; }; D0E305AC1E5BA3E700D7A3A2 /* ItemListControllerEmptyStateItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ItemListControllerEmptyStateItem.swift; sourceTree = ""; }; @@ -2360,7 +2368,8 @@ 0962E66421B3631100245FD9 /* WebSearchNavigationContentNode.swift */, 0962E67A21BA00C900245FD9 /* WebSearchInterfaceState.swift */, 0962E67E21BA786A00245FD9 /* WebSearchItem.swift */, - 09DD88E621BAE11B000766BC /* WebSearchRecentQueriesNode.swift */, + 09DD88F221BF907C000766BC /* WebSearchRecentQueryItem.swift */, + 09DD88F421BF9730000766BC /* WebSearchRecentQueries.swift */, ); name = "Web Search"; sourceTree = ""; @@ -2441,6 +2450,14 @@ name = Watch; sourceTree = ""; }; + 09DD88F821BFD6FA000766BC /* Alert */ = { + isa = PBXGroup; + children = ( + 09DD88F921BFD70B000766BC /* ThemedTextAlertController.swift */, + ); + name = Alert; + sourceTree = ""; + }; D00C7CDA1E3776CA0080C3D5 /* Secret Preview */ = { isa = PBXGroup; children = ( @@ -2749,20 +2766,21 @@ D046142C2004DB1D00EC0EF2 /* Live Location Manager */ = { isa = PBXGroup; children = ( - D046142D2004DB3700EC0EF2 /* LiveLocationManager.swift */, - D0461438200514F000EC0EF2 /* LiveLocationSummaryManager.swift */, ); name = "Live Location Manager"; sourceTree = ""; }; - D04614352005093B00EC0EF2 /* Device Location */ = { + D04614352005093B00EC0EF2 /* Location */ = { isa = PBXGroup; children = ( D06F31E02135829A001A0F12 /* EDSunriseSet.h */, D06F31DF2135829A001A0F12 /* EDSunriseSet.m */, D04614362005094E00EC0EF2 /* DeviceLocationManager.swift */, + D046142D2004DB3700EC0EF2 /* LiveLocationManager.swift */, + D0461438200514F000EC0EF2 /* LiveLocationSummaryManager.swift */, + 09DD88EE21BDDE2B000766BC /* Geocoding.swift */, ); - name = "Device Location"; + name = Location; sourceTree = ""; }; D0471B521EFD8EBC0074D609 /* Resources */ = { @@ -3373,6 +3391,7 @@ D0E412D2206A7DC100BEE4A2 /* DateSelectionActionSheetController.swift */, D0E412CF206A75B200BEE4A2 /* FormControllerDetailActionItem.swift */, D0E412CB206A6B2300BEE4A2 /* FormControllerActionItem.swift */, + 09DD88EC21BDC8B7000766BC /* FormEditableBlockItemNode.swift */, ); name = Form; sourceTree = ""; @@ -4019,6 +4038,7 @@ D0ACCB191EC5E0C20079D8BF /* CallControllerKeyPreviewNode.swift */, 09FE756C2153F5F900A3120F /* CallRouteActionSheetItem.swift */, 0962E65C21B1486D00245FD9 /* CallDebugNode.swift */, + 09DD88F021BE1090000766BC /* CallRatingController.swift */, ); name = Call; sourceTree = ""; @@ -4219,6 +4239,7 @@ D0F69DD31D6B8A160046BCD6 /* Controllers */ = { isa = PBXGroup; children = ( + 09DD88F821BFD6FA000766BC /* Alert */, D01B27931E38F3920022A4C0 /* Item List */, D0736F261DF4D2F300F2C02A /* Telegram Controller */, D093D81E20699A6000BC3599 /* Form */, @@ -4565,7 +4586,7 @@ isa = PBXGroup; children = ( D0E817462010E62E00B82BBB /* MergeLists.swift */, - D04614352005093B00EC0EF2 /* Device Location */, + D04614352005093B00EC0EF2 /* Location */, D025A4241F79428300563950 /* Fetch Manager */, D046142C2004DB1D00EC0EF2 /* Live Location Manager */, D0383ED5207D19BC00C45548 /* Emoji */, @@ -4583,7 +4604,7 @@ D023836F1DDF0462004018B6 /* UrlHandling.swift */, D0F917B41E0DA396003687E6 /* GenerateTextEntities.swift */, D01749541E1082770057C89A /* StoredMessageFromSearchPeer.swift */, - D087750B1E3E7B7600A97350 /* PreferencesKeys.swift */, + D087750B1E3E7B7600A97350 /* PostboxKeys.swift */, D0DA44551E4E7F43005FDCA7 /* ShakeAnimation.swift */, D0E305A41E5B2BFB00D7A3A2 /* ValidateAddressNameInteractive.swift */, D0F3A8AA1E82D83E00B4C64C /* TelegramAccountAuxiliaryMethods.swift */, @@ -4617,7 +4638,6 @@ 0902838C2194AEB90067EFBD /* ImageTransparency.swift */, 09C9EA32219F79F600E90146 /* ID3Artwork.h */, 09C9EA31219F79F500E90146 /* ID3Artwork.m */, - 09B4EE6321AD7B3E00847FA6 /* ItemCacheKeys.swift */, ); name = Utils; sourceTree = ""; @@ -4683,7 +4703,7 @@ D0FA0AC41E77431A005BB9B7 /* InstalledStickerPacksController.swift */, D0E23DD71E805E2600B9B6D2 /* FeaturedStickerPacksController.swift */, D04791661E79A22000F18979 /* ItemListStickerPackItem.swift */, - D0E23DDC1E8081A200B9B6D2 /* ArhivedStickerPacksController.swift */, + D0E23DDC1E8081A200B9B6D2 /* ArchivedStickerPacksController.swift */, ); name = Stickers; sourceTree = ""; @@ -5064,7 +5084,7 @@ D0192D46210F4F950005FA10 /* FixSearchableListNodeScrolling.swift in Sources */, D0EC6CD51EB9F58800EBF1C3 /* StoredMessageFromSearchPeer.swift in Sources */, D0471B5E1EFEB5860074D609 /* BotPaymentHeaderItemNode.swift in Sources */, - D0EC6CD61EB9F58800EBF1C3 /* PreferencesKeys.swift in Sources */, + D0EC6CD61EB9F58800EBF1C3 /* PostboxKeys.swift in Sources */, D0EC6CD71EB9F58800EBF1C3 /* EmojiUtils.swift in Sources */, D0EC6CD81EB9F58800EBF1C3 /* ShakeAnimation.swift in Sources */, D0EC6CD91EB9F58800EBF1C3 /* ValidateAddressNameInteractive.swift in Sources */, @@ -5090,6 +5110,7 @@ D0EC6CDF1EB9F58800EBF1C3 /* PresentationResourceKey.swift in Sources */, D0EC6CE01EB9F58800EBF1C3 /* PresentationResourcesRootController.swift in Sources */, D0EC6CE11EB9F58800EBF1C3 /* PresentationResourcesItemList.swift in Sources */, + 09DD88F121BE1090000766BC /* CallRatingController.swift in Sources */, D05D8B3F2192FC6E0064586F /* LocalizationListControllerNode.swift in Sources */, D0EC6CE21EB9F58800EBF1C3 /* PresentationResourcesChatList.swift in Sources */, D0EC6CE31EB9F58800EBF1C3 /* PresentationResourcesChat.swift in Sources */, @@ -5142,7 +5163,6 @@ 096C98BF21787C6700C211FF /* TGBridgeAudioEncoder.m in Sources */, D01776B81F1D6FB30044446D /* RadialProgressContentNode.swift in Sources */, D05D8B762195CD930064586F /* SetupTwoStepVerificationControllerNode.swift in Sources */, - 09B4EE6421AD7B3E00847FA6 /* ItemCacheKeys.swift in Sources */, D0EC6CFA1EB9F58800EBF1C3 /* ManagedAudioSession.swift in Sources */, D0EB5ADF1F798033004E89B6 /* PeerMediaCollectionEmptyNode.swift in Sources */, D0EC6CFB1EB9F58800EBF1C3 /* ManagedAudioRecorder.swift in Sources */, @@ -5191,6 +5211,7 @@ D0EC6D181EB9F58800EBF1C3 /* FFMpegMediaFrameSource.swift in Sources */, D0EC6D191EB9F58800EBF1C3 /* FFMpegMediaFrameSourceContext.swift in Sources */, D02D60AE206BD47300FEFE1E /* SecureIdDocumentTypeSelectionController.swift in Sources */, + 09DD88ED21BDC8B7000766BC /* FormEditableBlockItemNode.swift in Sources */, D079FCE11F05C9380038FADE /* BotReceiptControllerNode.swift in Sources */, 09C9EA33219F79F600E90146 /* ID3Artwork.m in Sources */, D0FA08CA2049BEAC00DD23FC /* ChatEmptyNode.swift in Sources */, @@ -5247,6 +5268,7 @@ D0E9BAE01F0574D800F079A4 /* STPDispatchFunctions.m in Sources */, D0EC6D2B1EB9F58800EBF1C3 /* FileMediaResourceStatus.swift in Sources */, D0EC6D2C1EB9F58800EBF1C3 /* TouchDownGestureRecognizer.swift in Sources */, + 09DD88FA21BFD70B000766BC /* ThemedTextAlertController.swift in Sources */, D0EC6D2D1EB9F58800EBF1C3 /* TapLongTapOrDoubleTapGestureRecognizer.swift in Sources */, D0AF7C461ED84BC500CD8E0F /* LanguageSelectionController.swift in Sources */, D0B69C3C20EBD8C8003632C7 /* CheckDeviceAccess.swift in Sources */, @@ -5277,7 +5299,6 @@ D0EC6D3A1EB9F58800EBF1C3 /* AudioWaveformNode.swift in Sources */, D0105D682182680E007C04A7 /* IsMediaStreamable.swift in Sources */, D0EB41F71F30D4A800838FE6 /* LegacyMediaLocations.swift in Sources */, - 09DD88E721BAE11B000766BC /* WebSearchRecentQueriesNode.swift in Sources */, D0EC6D3B1EB9F58800EBF1C3 /* EditableTokenListNode.swift in Sources */, D0EC6D3C1EB9F58800EBF1C3 /* PhoneInputNode.swift in Sources */, D0147BAB206EA6C100E40378 /* SecureIdDocumentImageGalleryItem.swift in Sources */, @@ -5608,6 +5629,7 @@ D09D88711F86D36700BEB4C9 /* CountryList.swift in Sources */, D0EC6DE11EB9F58900EBF1C3 /* ChatMessageSelectionInputPanelNode.swift in Sources */, D04281FA200E5CDC009DDE36 /* ChatRecentActionsControllerState.swift in Sources */, + 09DD88EF21BDDE2B000766BC /* Geocoding.swift in Sources */, D0EC6DE21EB9F58900EBF1C3 /* ChatChannelSubscriberInputPanelNode.swift in Sources */, D0EC6DE31EB9F58900EBF1C3 /* ChatBotStartInputPanelNode.swift in Sources */, D0EC6DE41EB9F58900EBF1C3 /* ChatUnblockInputPanelNode.swift in Sources */, @@ -5670,6 +5692,7 @@ D0EC6E071EB9F58900EBF1C3 /* ChatExternalFileGalleryItem.swift in Sources */, D0EC6E081EB9F58900EBF1C3 /* ChatImageGalleryItem.swift in Sources */, D048EA891F4F297500188713 /* InstantPageSettingsFontFamilyItemNode.swift in Sources */, + 09DD88F321BF907C000766BC /* WebSearchRecentQueryItem.swift in Sources */, D04554A421B42982007A6DD9 /* ConfirmPhoneNumberController.swift in Sources */, D0EC6E0A1EB9F58900EBF1C3 /* ChatVideoGalleryItemScrubberView.swift in Sources */, D0EC6E0B1EB9F58900EBF1C3 /* ZoomableContentGalleryItemNode.swift in Sources */, @@ -5793,6 +5816,7 @@ D0383ED4207CFBB900C45548 /* GalleryThumbnailContainerNode.swift in Sources */, 0962E67B21BA00C900245FD9 /* WebSearchInterfaceState.swift in Sources */, D0EC6E4B1EB9F58900EBF1C3 /* ItemListControllerSegmentedTitleView.swift in Sources */, + 09DD88F521BF9730000766BC /* WebSearchRecentQueries.swift in Sources */, D0EC6E4D1EB9F58900EBF1C3 /* PeerInfoController.swift in Sources */, D0EC6E4E1EB9F58900EBF1C3 /* GroupInfoController.swift in Sources */, D0380DAD204ED434000414AB /* LegacyLiveUploadInterface.swift in Sources */, @@ -5852,7 +5876,7 @@ D0EC6E6C1EB9F58900EBF1C3 /* FeaturedStickerPacksController.swift in Sources */, D0B85C231FF70BF400E795B4 /* AuthorizationSequenceAwaitingAccountResetController.swift in Sources */, D0EC6E6D1EB9F58900EBF1C3 /* ItemListStickerPackItem.swift in Sources */, - D0EC6E6E1EB9F58900EBF1C3 /* ArhivedStickerPacksController.swift in Sources */, + D0EC6E6E1EB9F58900EBF1C3 /* ArchivedStickerPacksController.swift in Sources */, D0DE5805205B202500C356A8 /* ScreenCaptureDetection.swift in Sources */, D0EC6E711EB9F58900EBF1C3 /* ThemeGalleryController.swift in Sources */, D0C0B5B11EE1C421000F4D2C /* ChatDateSelectionSheet.swift in Sources */, diff --git a/TelegramUI/ActivityIndicator.swift b/TelegramUI/ActivityIndicator.swift index ab524d659f..c4ada62229 100644 --- a/TelegramUI/ActivityIndicator.swift +++ b/TelegramUI/ActivityIndicator.swift @@ -46,14 +46,14 @@ final class ActivityIndicator: ASDisplayNode { switch self.type { case let .navigationAccent(theme): self.indicatorNode.image = PresentationResourcesRootController.navigationIndefiniteActivityImage(theme) - case let .custom(color, diameter, lineWidth, forceCustom): + case let .custom(color, diameter, lineWidth, _): self.indicatorNode.image = generateIndefiniteActivityIndicatorImage(color: color, diameter: diameter, lineWidth: lineWidth) } switch self.type { case let .navigationAccent(theme): self.indicatorView?.color = theme.rootController.navigationBar.controlColor - case let .custom(color, diameter, lineWidth, forceCustom): + case let .custom(color, diameter, lineWidth, _): self.indicatorView?.color = convertIndicatorColor(color) } } @@ -101,7 +101,7 @@ final class ActivityIndicator: ASDisplayNode { switch self.type { case let .navigationAccent(theme): indicatorView.color = theme.rootController.navigationBar.controlColor - case let .custom(color, diameter, lineWidth, forceCustom): + case let .custom(color, _, _, forceCustom): indicatorView.color = convertIndicatorColor(color) if !forceCustom { self.view.addSubview(indicatorView) diff --git a/TelegramUI/ArhivedStickerPacksController.swift b/TelegramUI/ArchivedStickerPacksController.swift similarity index 96% rename from TelegramUI/ArhivedStickerPacksController.swift rename to TelegramUI/ArchivedStickerPacksController.swift index 30c1c20a92..d6349886ac 100644 --- a/TelegramUI/ArhivedStickerPacksController.swift +++ b/TelegramUI/ArchivedStickerPacksController.swift @@ -4,6 +4,11 @@ import SwiftSignalKit import Postbox import TelegramCore +public enum ArchivedStickerPacksControllerMode { + case stickers + case masks +} + private final class ArchivedStickerPacksControllerArguments { let account: Account @@ -221,7 +226,7 @@ private func archivedStickerPacksControllerEntries(presentationData: Presentatio return entries } -public func archivedStickerPacksController(account: Account, archived: [ArchivedStickerPackItem]?, updatedPacks: @escaping([ArchivedStickerPackItem]?)->Void) -> ViewController { +public func archivedStickerPacksController(account: Account, mode: ArchivedStickerPacksControllerMode, archived: [ArchivedStickerPackItem]?, updatedPacks: @escaping ([ArchivedStickerPackItem]?) -> Void) -> ViewController { let statePromise = ValuePromise(ArchivedStickerPacksControllerState(), ignoreRepeated: true) let stateValue = Atomic(value: ArchivedStickerPacksControllerState()) let updateState: ((ArchivedStickerPacksControllerState) -> ArchivedStickerPacksControllerState) -> Void = { f in @@ -238,9 +243,15 @@ public func archivedStickerPacksController(account: Account, archived: [Archived let removePackDisposables = DisposableDict() actionsDisposable.add(removePackDisposables) + let namespace: ArchivedStickerPacksNamespace + switch mode { + case .stickers: + namespace = .stickers + case .masks: + namespace = .masks + } let stickerPacks = Promise<[ArchivedStickerPackItem]?>() - stickerPacks.set(.single(archived) |> then(archivedStickerPacks(account: account) |> map(Optional.init))) - + stickerPacks.set(.single(archived) |> then(archivedStickerPacks(account: account, namespace: namespace) |> map(Optional.init))) actionsDisposable.add(stickerPacks.get().start(next: { packs in updatedPacks(packs) diff --git a/TelegramUI/BotCheckoutControllerNode.swift b/TelegramUI/BotCheckoutControllerNode.swift index 7e108c8624..71d892d86d 100644 --- a/TelegramUI/BotCheckoutControllerNode.swift +++ b/TelegramUI/BotCheckoutControllerNode.swift @@ -582,7 +582,7 @@ final class BotCheckoutControllerNode: ItemListControllerNode, if methods.isEmpty { openNewCard() } else { - strongSelf.present(BotCheckoutPaymentMethodSheetController(theme: strongSelf.presentationData.theme, strings: strongSelf.presentationData.strings, currentMethod: strongSelf.currentPaymentMethod, methods: methods, applyValue: { method in + strongSelf.present(BotCheckoutPaymentMethodSheetController(account: strongSelf.account, currentMethod: strongSelf.currentPaymentMethod, methods: methods, applyValue: { method in applyPaymentMethod(method) }, newCard: { openNewCard() @@ -593,7 +593,7 @@ final class BotCheckoutControllerNode: ItemListControllerNode, openShippingMethodImpl = { [weak self] in if let strongSelf = self, let paymentFormValue = strongSelf.paymentFormValue, let shippingOptions = strongSelf.currentValidatedFormInfo?.shippingOptions, !shippingOptions.isEmpty { - strongSelf.present(BotCheckoutPaymentShippingOptionSheetController(theme: strongSelf.presentationData.theme, strings: strongSelf.presentationData.strings, currency: paymentFormValue.invoice.currency, options: shippingOptions, currentId: strongSelf.currentShippingOptionId, applyValue: { id in + strongSelf.present(BotCheckoutPaymentShippingOptionSheetController(account: strongSelf.account, currency: paymentFormValue.invoice.currency, options: shippingOptions, currentId: strongSelf.currentShippingOptionId, applyValue: { id in if let strongSelf = self, let paymentFormValue = strongSelf.paymentFormValue, let currentFormInfo = strongSelf.currentFormInfo { strongSelf.currentShippingOptionId = id strongSelf.paymentFormAndInfo.set(.single((paymentFormValue, currentFormInfo, strongSelf.currentValidatedFormInfo, strongSelf.currentShippingOptionId, strongSelf.currentPaymentMethod))) diff --git a/TelegramUI/BotCheckoutPaymentMethodSheet.swift b/TelegramUI/BotCheckoutPaymentMethodSheet.swift index a41e17873b..3bb3e02a1b 100644 --- a/TelegramUI/BotCheckoutPaymentMethodSheet.swift +++ b/TelegramUI/BotCheckoutPaymentMethodSheet.swift @@ -31,15 +31,21 @@ enum BotCheckoutPaymentMethod: Equatable { } final class BotCheckoutPaymentMethodSheetController: ActionSheetController { - private let theme: PresentationTheme - private let strings: PresentationStrings + private var presentationDisposable: Disposable? - init(theme: PresentationTheme, strings: PresentationStrings, currentMethod: BotCheckoutPaymentMethod?, methods: [BotCheckoutPaymentMethod], applyValue: @escaping (BotCheckoutPaymentMethod) -> Void, newCard: @escaping () -> Void) { - self.theme = theme - self.strings = strings + init(account: Account, currentMethod: BotCheckoutPaymentMethod?, methods: [BotCheckoutPaymentMethod], applyValue: @escaping (BotCheckoutPaymentMethod) -> Void, newCard: @escaping () -> Void) { + let presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 } + let theme = presentationData.theme + let strings = presentationData.strings super.init(theme: ActionSheetControllerTheme(presentationTheme: theme)) + self.presentationDisposable = account.telegramApplicationContext.presentationData.start(next: { [weak self] presentationData in + if let strongSelf = self { + strongSelf.theme = ActionSheetControllerTheme(presentationTheme: presentationData.theme) + } + }) + var items: [ActionSheetItem] = [] items.append(ActionSheetTextItem(title: strings.Checkout_PaymentMethod)) @@ -91,6 +97,10 @@ final class BotCheckoutPaymentMethodSheetController: ActionSheetController { required init(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } + + deinit { + self.presentationDisposable?.dispose() + } } public class BotCheckoutPaymentMethodItem: ActionSheetItem { diff --git a/TelegramUI/BotCheckoutPaymentShippingOptionSheetController.swift b/TelegramUI/BotCheckoutPaymentShippingOptionSheetController.swift index da0cb863a6..0e70f336b5 100644 --- a/TelegramUI/BotCheckoutPaymentShippingOptionSheetController.swift +++ b/TelegramUI/BotCheckoutPaymentShippingOptionSheetController.swift @@ -5,15 +5,21 @@ import SwiftSignalKit import TelegramCore final class BotCheckoutPaymentShippingOptionSheetController: ActionSheetController { - private let theme: PresentationTheme - private let strings: PresentationStrings + private var presentationDisposable: Disposable? - init(theme: PresentationTheme, strings: PresentationStrings, currency: String, options: [BotPaymentShippingOption], currentId: String?, applyValue: @escaping (String) -> Void) { - self.theme = theme - self.strings = strings + init(account: Account, currency: String, options: [BotPaymentShippingOption], currentId: String?, applyValue: @escaping (String) -> Void) { + let presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 } + let theme = presentationData.theme + let strings = presentationData.strings super.init(theme: ActionSheetControllerTheme(presentationTheme: theme)) + self.presentationDisposable = account.telegramApplicationContext.presentationData.start(next: { [weak self] presentationData in + if let strongSelf = self { + strongSelf.theme = ActionSheetControllerTheme(presentationTheme: presentationData.theme) + } + }) + var items: [ActionSheetItem] = [] items.append(ActionSheetTextItem(title: strings.Checkout_ShippingMethod)) @@ -67,6 +73,10 @@ final class BotCheckoutPaymentShippingOptionSheetController: ActionSheetControll required init(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } + + deinit { + self.presentationDisposable?.dispose() + } } public class BotCheckoutPaymentShippingOptionItem: ActionSheetItem { diff --git a/TelegramUI/CallRatingController.swift b/TelegramUI/CallRatingController.swift new file mode 100644 index 0000000000..60a75d7d59 --- /dev/null +++ b/TelegramUI/CallRatingController.swift @@ -0,0 +1,366 @@ +import Foundation +import SwiftSignalKit +import AsyncDisplayKit +import Display +import TelegramCore + +private final class CallRatingContentActionNode: HighlightableButtonNode { + private let backgroundNode: ASDisplayNode + + let action: TextAlertAction + + init(theme: AlertControllerTheme, action: TextAlertAction) { + self.backgroundNode = ASDisplayNode() + self.backgroundNode.isLayerBacked = true + self.backgroundNode.alpha = 0.0 + + self.action = action + + super.init() + + self.titleNode.maximumNumberOfLines = 2 + + self.highligthedChanged = { [weak self] value in + if let strongSelf = self { + if value { + if strongSelf.backgroundNode.supernode == nil { + strongSelf.insertSubnode(strongSelf.backgroundNode, at: 0) + } + strongSelf.backgroundNode.layer.removeAnimation(forKey: "opacity") + strongSelf.backgroundNode.alpha = 1.0 + } else if !strongSelf.backgroundNode.alpha.isZero { + strongSelf.backgroundNode.alpha = 0.0 + strongSelf.backgroundNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25) + } + } + } + + self.updateTheme(theme) + } + + func updateTheme(_ theme: AlertControllerTheme) { + self.backgroundNode.backgroundColor = theme.highlightedItemColor + + var font = Font.regular(17.0) + var color = theme.accentColor + switch self.action.type { + case .defaultAction, .genericAction: + break + case .destructiveAction: + color = theme.destructiveColor + } + switch self.action.type { + case .defaultAction: + font = Font.semibold(17.0) + case .destructiveAction, .genericAction: + break + } + self.setAttributedTitle(NSAttributedString(string: self.action.title, font: font, textColor: color, paragraphAlignment: .center), for: []) + } + + override func didLoad() { + super.didLoad() + + self.addTarget(self, action: #selector(self.pressed), forControlEvents: .touchUpInside) + } + + @objc func pressed() { + self.action.action() + } + + override func layout() { + super.layout() + + self.backgroundNode.frame = self.bounds + } +} + +private final class CallRatingAlertContentNode: AlertContentNode { + private var validLayout: CGSize? + private let strings: PresentationStrings + + var rating: Int? + + private let titleNode: ASTextNode + private let starNodes: [ASButtonNode] + private let inputFieldNode: ShareInputFieldNode + + private let actionNodesSeparator: ASDisplayNode + private let actionNodes: [CallRatingContentActionNode] + private let actionVerticalSeparators: [ASDisplayNode] + + private let disposable = MetaDisposable() + + override var dismissOnOutsideTap: Bool { + return self.isUserInteractionEnabled + } + + init(theme: AlertControllerTheme, ptheme: PresentationTheme, strings: PresentationStrings, actions: [TextAlertAction], dismiss: @escaping () -> Void) { + self.strings = strings + + self.titleNode = ASTextNode() + self.titleNode.maximumNumberOfLines = 2 + + var starNodes: [ASButtonNode] = [] + for _ in 0 ..< 5 { + starNodes.append(ASButtonNode()) + } + self.starNodes = starNodes + + self.inputFieldNode = ShareInputFieldNode(theme: ShareInputFieldNodeTheme(presentationTheme: ptheme), placeholder: strings.Calls_RatingFeedback) + self.inputFieldNode.alpha = 0.0 + + self.actionNodesSeparator = ASDisplayNode() + self.actionNodesSeparator.isLayerBacked = true + + self.actionNodes = actions.map { action -> CallRatingContentActionNode in + return CallRatingContentActionNode(theme: theme, action: action) + } + + var actionVerticalSeparators: [ASDisplayNode] = [] + if actions.count > 1 { + for _ in 0 ..< actions.count - 1 { + let separatorNode = ASDisplayNode() + separatorNode.isLayerBacked = true + actionVerticalSeparators.append(separatorNode) + } + } + self.actionVerticalSeparators = actionVerticalSeparators + + super.init() + + self.addSubnode(self.titleNode) + + for node in self.starNodes { + node.addTarget(self, action: #selector(self.starPressed(_:)), forControlEvents: .touchUpInside) + self.addSubnode(node) + } + + self.addSubnode(self.inputFieldNode) + + self.addSubnode(self.actionNodesSeparator) + + for actionNode in self.actionNodes { + self.addSubnode(actionNode) + } + + for separatorNode in self.actionVerticalSeparators { + self.addSubnode(separatorNode) + } + + self.inputFieldNode.updateHeight = { [weak self] in + if let strongSelf = self { + if let _ = strongSelf.validLayout { + strongSelf.requestLayout?(.animated(duration: 0.15, curve: .spring)) + } + } + } + + self.updateTheme(theme) + } + + deinit { + self.disposable.dispose() + } + + var comment: String { + return self.inputFieldNode.text + } + + @objc func starPressed(_ sender: ASButtonNode) { + if let index = self.starNodes.firstIndex(of: sender) { + self.rating = index + 1 + for i in 0 ..< self.starNodes.count { + let node = self.starNodes[i] + node.isSelected = i <= index + } + if index < 3 { + self.inputFieldNode.placeholder = self.strings.Call_ReportPlaceholder + } else { + self.inputFieldNode.placeholder = self.strings.Calls_RatingFeedback + } + self.requestLayout?(.animated(duration: 0.3, curve: .spring)) + } + } + + override func updateTheme(_ theme: AlertControllerTheme) { + self.titleNode.attributedText = NSAttributedString(string: strings.Calls_RatingTitle, font: Font.bold(17.0), textColor: theme.primaryColor, paragraphAlignment: .center) + + for node in self.starNodes { + node.setImage(generateTintedImage(image: UIImage(bundleImageName: "Call/Star"), color: theme.accentColor), for: []) + let highlighted = generateTintedImage(image: UIImage(bundleImageName: "Call/StarHighlighted"), color: theme.accentColor) + node.setImage(highlighted, for: [.selected]) + node.setImage(highlighted, for: [.selected, .highlighted]) + } + + self.actionNodesSeparator.backgroundColor = theme.separatorColor + for actionNode in self.actionNodes { + actionNode.updateTheme(theme) + } + for separatorNode in self.actionVerticalSeparators { + separatorNode.backgroundColor = theme.separatorColor + } + + if let size = self.validLayout { + _ = self.updateLayout(size: size, transition: .immediate) + } + } + + override func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize { + var size = size + size.width = min(size.width , 270.0) + + self.validLayout = size + + var origin: CGPoint = CGPoint(x: 0.0, y: 20.0) + + let titleSize = self.titleNode.measure(size) + transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - titleSize.width) / 2.0), y: origin.y), size: titleSize)) + origin.y += titleSize.height + 13.0 + + let actionButtonHeight: CGFloat = 44.0 + var minActionsWidth: CGFloat = 0.0 + let maxActionWidth: CGFloat = floor(size.width / CGFloat(self.actionNodes.count)) + let actionTitleInsets: CGFloat = 8.0 + + var effectiveActionLayout = TextAlertContentActionLayout.horizontal + for actionNode in self.actionNodes { + let actionTitleSize = actionNode.titleNode.measure(CGSize(width: maxActionWidth, height: actionButtonHeight)) + if case .horizontal = effectiveActionLayout, actionTitleSize.height > actionButtonHeight * 0.6667 { + effectiveActionLayout = .vertical + } + switch effectiveActionLayout { + case .horizontal: + minActionsWidth += actionTitleSize.width + actionTitleInsets + case .vertical: + minActionsWidth = max(minActionsWidth, actionTitleSize.width + actionTitleInsets) + } + } + + let insets = UIEdgeInsets(top: 18.0, left: 18.0, bottom: 18.0, right: 18.0) + + var contentWidth = max(titleSize.width, minActionsWidth) + contentWidth = max(contentWidth, 234.0) + + var actionsHeight: CGFloat = 0.0 + switch effectiveActionLayout { + case .horizontal: + actionsHeight = actionButtonHeight + case .vertical: + actionsHeight = actionButtonHeight * CGFloat(self.actionNodes.count) + } + + let resultWidth = contentWidth + insets.left + insets.right + + let starSize = CGSize(width: 42.0, height: 38.0) + let starsOrigin = floorToScreenPixels((resultWidth - starSize.width * 5.0) / 2.0) + for i in 0 ..< self.starNodes.count { + let node = self.starNodes[i] + transition.updateFrame(node: node, frame: CGRect(x: starsOrigin + 42.0 * CGFloat(i), y: origin.y, width: starSize.width, height: starSize.height)) + } + origin.y += titleSize.height + + let inputFieldWidth = resultWidth + let inputFieldHeight = self.inputFieldNode.updateLayout(width: inputFieldWidth, transition: transition) + var inputHeight: CGFloat = 0.0 + if let rating = rating, rating < 5 { + inputHeight += inputFieldHeight + } + transition.updateFrame(node: self.inputFieldNode, frame: CGRect(x: 0.0, y: origin.y, width: resultWidth, height: inputFieldHeight)) + transition.updateAlpha(node: self.inputFieldNode, alpha: inputHeight > 0.0 ? 1.0 : 0.0) + + let resultSize = CGSize(width: resultWidth, height: titleSize.height + actionsHeight + 56.0 + inputHeight + insets.top + insets.bottom) + + transition.updateFrame(node: self.actionNodesSeparator, frame: CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel))) + + var actionOffset: CGFloat = 0.0 + let actionWidth: CGFloat = floor(resultSize.width / CGFloat(self.actionNodes.count)) + var separatorIndex = -1 + var nodeIndex = 0 + for actionNode in self.actionNodes { + if separatorIndex >= 0 { + let separatorNode = self.actionVerticalSeparators[separatorIndex] + switch effectiveActionLayout { + case .horizontal: + transition.updateFrame(node: separatorNode, frame: CGRect(origin: CGPoint(x: actionOffset - UIScreenPixel, y: resultSize.height - actionsHeight), size: CGSize(width: UIScreenPixel, height: actionsHeight - UIScreenPixel))) + case .vertical: + transition.updateFrame(node: separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight + actionOffset - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel))) + } + } + separatorIndex += 1 + + let currentActionWidth: CGFloat + switch effectiveActionLayout { + case .horizontal: + if nodeIndex == self.actionNodes.count - 1 { + currentActionWidth = resultSize.width - actionOffset + } else { + currentActionWidth = actionWidth + } + case .vertical: + currentActionWidth = resultSize.width + } + + let actionNodeFrame: CGRect + switch effectiveActionLayout { + case .horizontal: + actionNodeFrame = CGRect(origin: CGPoint(x: actionOffset, y: resultSize.height - actionsHeight), size: CGSize(width: currentActionWidth, height: actionButtonHeight)) + actionOffset += currentActionWidth + case .vertical: + actionNodeFrame = CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight + actionOffset), size: CGSize(width: currentActionWidth, height: actionButtonHeight)) + actionOffset += actionButtonHeight + } + + transition.updateFrame(node: actionNode, frame: actionNodeFrame) + + nodeIndex += 1 + } + + return resultSize + } +} + +private func rateCallAndSendLogs(account: Account, report: ReportCallRating, starsCount: Int, comment: String, includeLogs: Bool) { + let _ = rateCall(account: account, report: report, starsCount: Int32(starsCount), comment: comment).start() +} + +func callRatingController(account: Account, report: ReportCallRating, present: @escaping (ViewController) -> Void) -> AlertController { + let presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 } + let theme = presentationData.theme + let strings = presentationData.strings + + var dismissImpl: ((Bool) -> Void)? + var contentNode: CallRatingAlertContentNode? + let actions: [TextAlertAction] = [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_NotNow, action: { + dismissImpl?(true) + }), TextAlertAction(type: .defaultAction, title: presentationData.strings.Calls_SubmitRating, action: { + dismissImpl?(true) + if let contentNode = contentNode, let rating = contentNode.rating { + if rating < 4 { + let controller = textAlertController(account: account, title: strings.Call_ReportIncludeLog, text: strings.Call_ReportIncludeLogDescription, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Call_ReportSkip, action: { + rateCallAndSendLogs(account: account, report: report, starsCount: rating, comment: contentNode.comment, includeLogs: false) + }), TextAlertAction(type: .defaultAction, title: presentationData.strings.Call_ReportSend, action: { + rateCallAndSendLogs(account: account, report: report, starsCount: rating, comment: contentNode.comment, includeLogs: true) + })]) + present(controller) + } else { + rateCallAndSendLogs(account: account, report: report, starsCount: rating, comment: contentNode.comment, includeLogs: false) + } + } + })] + + contentNode = CallRatingAlertContentNode(theme: AlertControllerTheme(presentationTheme: theme), ptheme: theme, strings: strings, actions: actions, dismiss: { + dismissImpl?(true) + }) + + let controller = AlertController(theme: AlertControllerTheme(presentationTheme: theme), contentNode: contentNode!) + dismissImpl = { [weak controller] animated in + if animated { + controller?.dismissAnimated() + } else { + controller?.dismiss() + } + } + return controller +} + diff --git a/TelegramUI/ChannelBannedMemberController.swift b/TelegramUI/ChannelBannedMemberController.swift index dd7603dc9f..8c3d0baf02 100644 --- a/TelegramUI/ChannelBannedMemberController.swift +++ b/TelegramUI/ChannelBannedMemberController.swift @@ -412,7 +412,7 @@ public func channelBannedMemberController(account: Account, peerId: PeerId, memb } items.append(ActionSheetButtonItem(title: presentationData.strings.MessageTimer_Custom, color: .accent, action: { [weak actionSheet] in actionSheet?.dismissAnimated() - presentControllerImpl?(PeerBanTimeoutController(theme: presentationData.theme, strings: presentationData.strings, currentValue: Int32(Date().timeIntervalSince1970), applyValue: { value in + presentControllerImpl?(PeerBanTimeoutController(account: account, currentValue: Int32(Date().timeIntervalSince1970), applyValue: { value in applyValue(value) }), nil) })) diff --git a/TelegramUI/ChannelInfoController.swift b/TelegramUI/ChannelInfoController.swift index 94a56f80a8..f46e717437 100644 --- a/TelegramUI/ChannelInfoController.swift +++ b/TelegramUI/ChannelInfoController.swift @@ -659,7 +659,7 @@ public func channelInfoController(account: Account, peerId: PeerId) -> ViewContr account.postbox.mediaBox.storeResourceData(resource.id, data: data) let representation = TelegramMediaImageRepresentation(dimensions: CGSize(width: 640.0, height: 640.0), resource: resource) updateState { - $0.withUpdatedUpdatingAvatar(.image(representation)) + $0.withUpdatedUpdatingAvatar(.image(representation, true)) } updateAvatarDisposable.set((updatePeerPhoto(postbox: account.postbox, network: account.network, stateManager: account.stateManager, accountPeerId: account.peerId, peerId: peerId, photo: uploadedPeerPhoto(postbox: account.postbox, network: account.network, resource: resource)) |> deliverOnMainQueue).start(next: { result in switch result { @@ -678,7 +678,7 @@ public func channelInfoController(account: Account, peerId: PeerId) -> ViewContr let _ = currentAvatarMixin.swap(nil) updateState { if let profileImage = peer?.smallProfileImage { - return $0.withUpdatedUpdatingAvatar(.image(profileImage)) + return $0.withUpdatedUpdatingAvatar(.image(profileImage, false)) } else { return $0.withUpdatedUpdatingAvatar(.none) } diff --git a/TelegramUI/ChatButtonKeyboardInputNode.swift b/TelegramUI/ChatButtonKeyboardInputNode.swift index f35e83c748..34f4a425b4 100644 --- a/TelegramUI/ChatButtonKeyboardInputNode.swift +++ b/TelegramUI/ChatButtonKeyboardInputNode.swift @@ -72,7 +72,7 @@ final class ChatButtonKeyboardInputNode: ChatInputNode { if self.theme !== interfaceState.theme { self.theme = interfaceState.theme - self.separatorNode.backgroundColor = interfaceState.theme.chat.inputButtonPanel.panelSerapatorColor + self.separatorNode.backgroundColor = interfaceState.theme.chat.inputButtonPanel.panelSeparatorColor self.backgroundColor = interfaceState.theme.chat.inputButtonPanel.panelBackgroundColor } diff --git a/TelegramUI/ChatContextResultPeekContentNode.swift b/TelegramUI/ChatContextResultPeekContentNode.swift index 1b1f5f0236..0b15f6374d 100644 --- a/TelegramUI/ChatContextResultPeekContentNode.swift +++ b/TelegramUI/ChatContextResultPeekContentNode.swift @@ -33,6 +33,15 @@ final class ChatContextResultPeekContent: PeekControllerContent { return ChatContextResultPeekNode(account: self.account, contextResult: self.contextResult) } + func topAccessoryNode() -> ASDisplayNode? { + let arrowNode = ASImageNode() + if let image = UIImage(bundleImageName: "Peek/Arrow") { + arrowNode.image = image + arrowNode.frame = CGRect(origin: CGPoint(), size: image.size) + } + return arrowNode + } + func isEqual(to: PeekControllerContent) -> Bool { if let to = to as? ChatContextResultPeekContent { return self.contextResult == to.contextResult @@ -265,7 +274,7 @@ private final class ChatContextResultPeekNode: ASDisplayNode, PeekControllerCont if !self.ticking { self.ticking = true } - + return croppedImageDimensions } } diff --git a/TelegramUI/ChatController.swift b/TelegramUI/ChatController.swift index b189b0fa45..edb3db3370 100644 --- a/TelegramUI/ChatController.swift +++ b/TelegramUI/ChatController.swift @@ -451,7 +451,10 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal var hasActions = false for media in updatedMessages[0].media { if media is TelegramMediaAction || media is TelegramMediaExpiredContent { - hasActions = true + if let action = media as? TelegramMediaAction, case .phoneCall = action.action { + } else { + hasActions = true + } break } } @@ -1079,6 +1082,15 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal self?.present(controller, in: .window(.root), with: arguments) }) } + }, rateCall: { [weak self] message in + if let strongSelf = self { + let controller = callRatingController(account: strongSelf.account, report: ReportCallRating(id: 0, accessHash: 0), present: { [weak self] controller in + if let strongSelf = self { + strongSelf.present(controller, in: .window(.root)) + } + }) + strongSelf.present(controller, in: .window(.root)) + } }, requestMessageUpdate: { [weak self] id in if let strongSelf = self { strongSelf.chatDisplayNode.historyNode.requestMessageUpdate(id) @@ -2565,7 +2577,7 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal strongSelf.chatDisplayNode.dismissInput() if let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer as? TelegramSecretChat { - let controller = ChatSecretAutoremoveTimerActionSheetController(theme: strongSelf.presentationData.theme, strings: strongSelf.presentationData.strings, currentValue: peer.messageAutoremoveTimeout == nil ? 0 : peer.messageAutoremoveTimeout!, applyValue: { value in + let controller = ChatSecretAutoremoveTimerActionSheetController(account: strongSelf.account, currentValue: peer.messageAutoremoveTimeout == nil ? 0 : peer.messageAutoremoveTimeout!, applyValue: { value in if let strongSelf = self { let _ = setSecretChatMessageAutoremoveTimeoutInteractively(account: strongSelf.account, peerId: peer.id, timeout: value == 0 ? nil : value).start() } @@ -3655,6 +3667,13 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal strongSelf.present(legacyController, in: .window(.root)) controller.present(in: emptyController, sourceView: nil, animated: true) + + let presentationDisposable = strongSelf.account.telegramApplicationContext.presentationData.start(next: { [weak controller] presentationData in + if let controller = controller { + controller.pallete = legacyMenuPaletteFromTheme(presentationData.theme) + } + }) + legacyController.disposables.add(presentationDisposable) }) } @@ -5104,7 +5123,7 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal private func openUrlIn(_ url: String) { if let applicationContext = self.account.applicationContext as? TelegramApplicationContext { - let actionSheet = OpenInActionSheetController(postbox: self.account.postbox, applicationContext: applicationContext, theme: self.presentationData.theme, strings: self.presentationData.strings, item: .url(url: url), openUrl: { [weak self] url in + let actionSheet = OpenInActionSheetController(account: self.account, item: .url(url: url), openUrl: { [weak self] url in if let strongSelf = self, let applicationContext = strongSelf.account.applicationContext as? TelegramApplicationContext, let navigationController = strongSelf.navigationController as? NavigationController { openExternalUrl(account: strongSelf.account, url: url, forceExternal: true, presentationData: strongSelf.presentationData, applicationContext: applicationContext, navigationController: navigationController, dismissInput: { self?.chatDisplayNode.dismissInput() diff --git a/TelegramUI/ChatControllerInteraction.swift b/TelegramUI/ChatControllerInteraction.swift index 8a7e7c4197..04310bbd21 100644 --- a/TelegramUI/ChatControllerInteraction.swift +++ b/TelegramUI/ChatControllerInteraction.swift @@ -77,6 +77,7 @@ public final class ChatControllerInteraction { let navigateToFirstDateMessage: (Int32) -> Void let requestRedeliveryOfFailedMessages: (MessageId) -> Void let addContact: (String) -> Void + let rateCall: (Message) -> Void let requestMessageUpdate: (MessageId) -> Void let cancelInteractiveKeyboardGestures: () -> Void @@ -87,7 +88,7 @@ public final class ChatControllerInteraction { var contextHighlightedState: ChatInterfaceHighlightedState? var automaticMediaDownloadSettings: AutomaticMediaDownloadSettings - init(openMessage: @escaping (Message, ChatControllerInteractionOpenMessageMode) -> Bool, openPeer: @escaping (PeerId?, ChatControllerInteractionNavigateToPeer, Message?) -> Void, openPeerMention: @escaping (String) -> Void, openMessageContextMenu: @escaping (Message, Bool, 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, requestRedeliveryOfFailedMessages: @escaping (MessageId) -> Void, addContact: @escaping (String) -> 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, Bool, 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, requestRedeliveryOfFailedMessages: @escaping (MessageId) -> Void, addContact: @escaping (String) -> Void, rateCall: @escaping (Message) -> Void, requestMessageUpdate: @escaping (MessageId) -> Void, cancelInteractiveKeyboardGestures: @escaping () -> Void, automaticMediaDownloadSettings: AutomaticMediaDownloadSettings) { self.openMessage = openMessage self.openPeer = openPeer self.openPeerMention = openPeerMention @@ -121,6 +122,7 @@ public final class ChatControllerInteraction { self.navigateToFirstDateMessage = navigateToFirstDateMessage self.requestRedeliveryOfFailedMessages = requestRedeliveryOfFailedMessages self.addContact = addContact + self.rateCall = rateCall self.requestMessageUpdate = requestMessageUpdate self.cancelInteractiveKeyboardGestures = cancelInteractiveKeyboardGestures diff --git a/TelegramUI/ChatHistorySearchContainerNode.swift b/TelegramUI/ChatHistorySearchContainerNode.swift index d3fc25ff25..cecf2f13bc 100644 --- a/TelegramUI/ChatHistorySearchContainerNode.swift +++ b/TelegramUI/ChatHistorySearchContainerNode.swift @@ -87,17 +87,18 @@ private struct ChatHistorySearchContainerTransition { let deletions: [ListViewDeleteItem] let insertions: [ListViewInsertItem] let updates: [ListViewUpdateItem] + let query: String let displayingResults: Bool } -private func chatHistorySearchContainerPreparedTransition(from fromEntries: [ChatHistorySearchEntry], to toEntries: [ChatHistorySearchEntry], displayingResults: Bool, account: Account, peerId: PeerId, interaction: ChatControllerInteraction) -> ChatHistorySearchContainerTransition { +private func chatHistorySearchContainerPreparedTransition(from fromEntries: [ChatHistorySearchEntry], to toEntries: [ChatHistorySearchEntry], query: String, displayingResults: Bool, account: Account, peerId: PeerId, interaction: ChatControllerInteraction) -> ChatHistorySearchContainerTransition { let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries) let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) } let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, peerId: peerId, interaction: interaction), directionHint: nil) } let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, peerId: peerId, interaction: interaction), directionHint: nil) } - return ChatHistorySearchContainerTransition(deletions: deletions, insertions: insertions, updates: updates, displayingResults: displayingResults) + return ChatHistorySearchContainerTransition(deletions: deletions, insertions: insertions, updates: updates, query: query, displayingResults: displayingResults) } final class ChatHistorySearchContainerNode: SearchDisplayControllerContentNode { @@ -106,10 +107,14 @@ final class ChatHistorySearchContainerNode: SearchDisplayControllerContentNode { private let dimNode: ASDisplayNode private let listNode: ListView + private let emptyResultsTitleNode: ImmediateTextNode + private let emptyResultsTextNode: ImmediateTextNode + private var containerLayout: (ContainerViewLayout, CGFloat)? private var currentEntries: [ChatHistorySearchEntry]? + private var currentQuery: String? private let searchQuery = Promise() private let searchQueryDisposable = MetaDisposable() private let searchDisposable = MetaDisposable() @@ -120,6 +125,8 @@ final class ChatHistorySearchContainerNode: SearchDisplayControllerContentNode { } private var presentationData: PresentationData + private var presentationDataDisposable: Disposable? + private let themeAndStringsPromise: Promise<(PresentationTheme, PresentationStrings, PresentationDateTimeFormat)> private var enqueuedTransitions: [(ChatHistorySearchContainerTransition, Bool)] = [] @@ -135,6 +142,14 @@ final class ChatHistorySearchContainerNode: SearchDisplayControllerContentNode { self.dimNode.backgroundColor = UIColor.black.withAlphaComponent(0.5) self.listNode = ListView() + self.emptyResultsTitleNode = ImmediateTextNode() + self.emptyResultsTitleNode.attributedText = NSAttributedString(string: self.presentationData.strings.SharedMedia_SearchNoResults, font: Font.semibold(17.0), textColor: self.presentationData.theme.list.freeTextColor) + self.emptyResultsTitleNode.textAlignment = .center + + self.emptyResultsTextNode = ImmediateTextNode() + self.emptyResultsTextNode.maximumNumberOfLines = 0 + self.emptyResultsTextNode.textAlignment = .center + super.init() self.backgroundColor = nil @@ -143,13 +158,16 @@ final class ChatHistorySearchContainerNode: SearchDisplayControllerContentNode { self.addSubnode(self.dimNode) self.addSubnode(self.listNode) + self.addSubnode(self.emptyResultsTitleNode) + self.addSubnode(self.emptyResultsTextNode) + self.listNode.isHidden = true let themeAndStringsPromise = self.themeAndStringsPromise let previousEntriesValue = Atomic<[ChatHistorySearchEntry]?>(value: nil) - self.searchQueryDisposable.set((searchQuery.get() + self.searchQueryDisposable.set((self.searchQuery.get() |> deliverOnMainQueue).start(next: { [weak self] query in if let strongSelf = self { let signal: Signal<[ChatHistorySearchEntry]?, NoError> @@ -160,7 +178,7 @@ final class ChatHistorySearchContainerNode: SearchDisplayControllerContentNode { signal = combineLatest(foundRemoteMessages, themeAndStringsPromise.get()) |> map { messages, themeAndStrings -> [ChatHistorySearchEntry]? in if messages.isEmpty { - return nil + return [] } else { return messages.map { message -> ChatHistorySearchEntry in return .message(message, themeAndStrings.0, themeAndStrings.1, themeAndStrings.2) @@ -180,7 +198,7 @@ final class ChatHistorySearchContainerNode: SearchDisplayControllerContentNode { let previousEntries = previousEntriesValue.swap(entries) let firstTime = previousEntries == nil - let transition = chatHistorySearchContainerPreparedTransition(from: previousEntries ?? [], to: entries ?? [], displayingResults: entries != nil, account: account, peerId: peerId, interaction: interfaceInteraction) + let transition = chatHistorySearchContainerPreparedTransition(from: previousEntries ?? [], to: entries ?? [], query: query ?? "", displayingResults: entries != nil, account: account, peerId: peerId, interaction: interfaceInteraction) strongSelf.currentEntries = entries strongSelf.enqueueTransition(transition, firstTime: firstTime) strongSelf._isSearching.set(false) @@ -192,9 +210,22 @@ final class ChatHistorySearchContainerNode: SearchDisplayControllerContentNode { self.listNode.beganInteractiveDragging = { [weak self] in self?.dismissInput?() } + + self.presentationDataDisposable = account.telegramApplicationContext.presentationData.start(next: { [weak self] presentationData in + if let strongSelf = self { + strongSelf.themeAndStringsPromise.set(.single((presentationData.theme, presentationData.strings, presentationData.dateTimeFormat))) + + strongSelf.emptyResultsTitleNode.attributedText = NSAttributedString(string: presentationData.strings.SharedMedia_SearchNoResults, font: Font.semibold(17.0), textColor: presentationData.theme.list.freeTextColor, paragraphAlignment: .center) + + if let (layout, navigationBarHeight) = strongSelf.containerLayout { + strongSelf.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .immediate) + } + } + }) } deinit { + self.presentationDataDisposable?.dispose() self.searchQueryDisposable.dispose() self.searchDisposable.dispose() } @@ -222,6 +253,19 @@ final class ChatHistorySearchContainerNode: SearchDisplayControllerContentNode { let topInset = navigationBarHeight 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))) + let padding: CGFloat = 16.0 + let emptyTitleSize = self.emptyResultsTitleNode.updateLayout(CGSize(width: layout.size.width - layout.safeInsets.left - layout.safeInsets.right - padding * 2.0, height: CGFloat.greatestFiniteMagnitude)) + let emptyTextSize = self.emptyResultsTextNode.updateLayout(CGSize(width: layout.size.width - layout.safeInsets.left - layout.safeInsets.right - padding * 2.0, height: CGFloat.greatestFiniteMagnitude)) + + let insets = layout.insets(options: [.input]) + let emptyTextSpacing: CGFloat = 8.0 + let emptyTotalHeight = emptyTitleSize.height + emptyTextSize.height + emptyTextSpacing + let emptyTitleY = navigationBarHeight + floorToScreenPixels((layout.size.height - navigationBarHeight - max(insets.bottom, layout.intrinsicInsets.bottom) - emptyTotalHeight) / 2.0) + + transition.updateFrame(node: self.emptyResultsTitleNode, frame: CGRect(origin: CGPoint(x: layout.safeInsets.left + padding + (layout.size.width - layout.safeInsets.left - layout.safeInsets.right - padding * 2.0 - emptyTitleSize.width) / 2.0, y: emptyTitleY), size: emptyTitleSize)) + + transition.updateFrame(node: self.emptyResultsTextNode, frame: CGRect(origin: CGPoint(x: layout.safeInsets.left + padding + (layout.size.width - layout.safeInsets.left - layout.safeInsets.right - padding * 2.0 - emptyTextSize.width) / 2.0, y: emptyTitleY + emptyTitleSize.height + emptyTextSpacing), size: emptyTextSize)) + 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(duration: nil)), stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in }) @@ -234,7 +278,7 @@ final class ChatHistorySearchContainerNode: SearchDisplayControllerContentNode { } private func enqueueTransition(_ transition: ChatHistorySearchContainerTransition, firstTime: Bool) { - enqueuedTransitions.append((transition, firstTime)) + self.enqueuedTransitions.append((transition, firstTime)) if self.containerLayout != nil { while !self.enqueuedTransitions.isEmpty { @@ -256,10 +300,22 @@ final class ChatHistorySearchContainerNode: SearchDisplayControllerContentNode { let displayingResults = transition.displayingResults self.listNode.transaction(deleteIndices: transition.deletions, insertIndicesAndItems: transition.insertions, updateIndicesAndItems: transition.updates, options: options, updateSizeAndInsets: nil, updateOpaqueState: nil, completion: { [weak self] _ in if let strongSelf = self { - if displayingResults != !strongSelf.listNode.isHidden { + if displayingResults != !strongSelf.listNode.isHidden || strongSelf.currentQuery != transition.query { + strongSelf.currentQuery = transition.query + strongSelf.listNode.isHidden = !displayingResults strongSelf.dimNode.isHidden = displayingResults strongSelf.backgroundColor = displayingResults ? strongSelf.presentationData.theme.list.plainBackgroundColor : nil + + strongSelf.emptyResultsTextNode.attributedText = NSAttributedString(string: strongSelf.presentationData.strings.SharedMedia_SearchNoResultsDescription(transition.query).0, font: Font.regular(15.0), textColor: strongSelf.presentationData.theme.list.freeTextColor) + + let emptyResults = displayingResults && strongSelf.currentEntries?.isEmpty ?? false + strongSelf.emptyResultsTitleNode.isHidden = !emptyResults + strongSelf.emptyResultsTextNode.isHidden = !emptyResults + + if let (layout, navigationBarHeight) = strongSelf.containerLayout { + strongSelf.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .immediate) + } } } }) diff --git a/TelegramUI/ChatInterfaceStateContextMenus.swift b/TelegramUI/ChatInterfaceStateContextMenus.swift index 0b99e14f28..8abcbbf3ab 100644 --- a/TelegramUI/ChatInterfaceStateContextMenus.swift +++ b/TelegramUI/ChatInterfaceStateContextMenus.swift @@ -457,6 +457,12 @@ func contextMenuForChatPresentationIntefaceState(chatPresentationInterfaceState: }))) } + if data.messageActions.options.contains(.rateCall) { + actions.append(.sheet(ChatMessageContextMenuSheetAction(color: .accent, title: chatPresentationInterfaceState.strings.Call_RateCall, action: { + let _ = controllerInteraction.rateCall(message) + }))) + } + if data.messageActions.options.contains(.forward) { actions.append(.sheet(ChatMessageContextMenuSheetAction(color: .accent, title: chatPresentationInterfaceState.strings.Conversation_ContextMenuForward, action: { interfaceInteraction.forwardMessages(selectAll ? messages : [message]) @@ -469,7 +475,7 @@ func contextMenuForChatPresentationIntefaceState(chatPresentationInterfaceState: }))) } - if !data.messageActions.options.intersection([.deleteLocally, .deleteGlobally]).isEmpty { + if !data.messageActions.options.intersection([.deleteLocally, .deleteGlobally]).isEmpty && !isAction { actions.append(.sheet(ChatMessageContextMenuSheetAction(color: .destructive, title: chatPresentationInterfaceState.strings.Conversation_ContextMenuDelete, action: { interfaceInteraction.deleteMessages(selectAll ? messages : [message]) }))) @@ -495,6 +501,7 @@ struct ChatAvailableMessageActionOptions: OptionSet { static let forward = ChatAvailableMessageActionOptions(rawValue: 1 << 2) static let report = ChatAvailableMessageActionOptions(rawValue: 1 << 3) static let viewStickerPack = ChatAvailableMessageActionOptions(rawValue: 1 << 4) + static let rateCall = ChatAvailableMessageActionOptions(rawValue: 1 << 5) } struct ChatAvailableMessageActions { @@ -534,6 +541,8 @@ func chatAvailableMessageActions(postbox: Postbox, accountPeerId: PeerId, messag } break } + } else if let action = media as? TelegramMediaAction, case .phoneCall = action.action { + optionsMap[id]!.insert(.rateCall) } } if id.peerId == accountPeerId { diff --git a/TelegramUI/ChatItemGalleryFooterContentNode.swift b/TelegramUI/ChatItemGalleryFooterContentNode.swift index 989de9b2ec..6437054648 100644 --- a/TelegramUI/ChatItemGalleryFooterContentNode.swift +++ b/TelegramUI/ChatItemGalleryFooterContentNode.swift @@ -712,7 +712,7 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode { if availableOpenInOptions(applicationContext: strongSelf.account.telegramApplicationContext, item: item).count > 1 { preferredAction = .custom(action: ShareControllerAction(title: presentationData.strings.Conversation_FileOpenIn, action: { [weak self] in if let strongSelf = self { - let openInController = OpenInActionSheetController(postbox: strongSelf.account.postbox, applicationContext: strongSelf.account.telegramApplicationContext, theme: presentationData.theme, strings: presentationData.strings, item: item, additionalAction: nil, openUrl: { [weak self] url in + let openInController = OpenInActionSheetController(account: strongSelf.account, item: item, additionalAction: nil, openUrl: { [weak self] url in if let strongSelf = self, let applicationContext = strongSelf.account.applicationContext as? TelegramApplicationContext { openExternalUrl(account: strongSelf.account, url: url, forceExternal: true, presentationData: presentationData, applicationContext: applicationContext, navigationController: nil, dismissInput: {}) } @@ -823,7 +823,7 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode { if availableOpenInOptions(applicationContext: self.account.telegramApplicationContext, item: item).count > 1 { preferredAction = .custom(action: ShareControllerAction(title: presentationData.strings.Conversation_FileOpenIn, action: { [weak self] in if let strongSelf = self { - let openInController = OpenInActionSheetController(postbox: strongSelf.account.postbox, applicationContext: strongSelf.account.telegramApplicationContext, theme: presentationData.theme, strings: presentationData.strings, item: item, additionalAction: nil, openUrl: { [weak self] url in + let openInController = OpenInActionSheetController(account: strongSelf.account, item: item, additionalAction: nil, openUrl: { [weak self] url in if let strongSelf = self, let applicationContext = strongSelf.account.applicationContext as? TelegramApplicationContext { openExternalUrl(account: strongSelf.account, url: url, forceExternal: true, presentationData: presentationData, applicationContext: applicationContext, navigationController: nil, dismissInput: {}) } diff --git a/TelegramUI/ChatListSearchContainerNode.swift b/TelegramUI/ChatListSearchContainerNode.swift index 68930afec4..4816408f0d 100644 --- a/TelegramUI/ChatListSearchContainerNode.swift +++ b/TelegramUI/ChatListSearchContainerNode.swift @@ -906,47 +906,47 @@ final class ChatListSearchContainerNode: SearchDisplayControllerContentNode { } var recentItemsTransition = combineLatest(hasRecentPeers, fixedRecentlySearchedPeers, presentationDataPromise.get(), self.statePromise.get()) - |> mapToSignal { [weak self] hasRecentPeers, peers, presentationData, state -> Signal<(ChatListSearchContainerRecentTransition, Bool), NoError> in - var entries: [ChatListRecentEntry] = [] - if !filter.contains(.onlyGroups) { - if groupId == nil, hasRecentPeers { - entries.append(.topPeers([], presentationData.theme, presentationData.strings)) - } + |> mapToSignal { [weak self] hasRecentPeers, peers, presentationData, state -> Signal<(ChatListSearchContainerRecentTransition, Bool), NoError> in + var entries: [ChatListRecentEntry] = [] + if !filter.contains(.onlyGroups) { + if groupId == nil, hasRecentPeers { + entries.append(.topPeers([], presentationData.theme, presentationData.strings)) } - var peerIds = Set() - var index = 0 - loop: for searchedPeer in peers { - if let peer = searchedPeer.peer.peers[searchedPeer.peer.peerId] { - if peerIds.contains(peer.id) { - continue loop - } - if !doesPeerMatchFilter(peer: peer, filter: filter) { - continue - } - peerIds.insert(peer.id) - - entries.append(.peer(index: index, peer: searchedPeer, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameSortOrder, presentationData.nameDisplayOrder, state.peerIdWithRevealedOptions == peer.id)) - index += 1 + } + var peerIds = Set() + var index = 0 + loop: for searchedPeer in peers { + if let peer = searchedPeer.peer.peers[searchedPeer.peer.peerId] { + if peerIds.contains(peer.id) { + continue loop } + if !doesPeerMatchFilter(peer: peer, filter: filter) { + continue + } + peerIds.insert(peer.id) + + entries.append(.peer(index: index, peer: searchedPeer, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameSortOrder, presentationData.nameDisplayOrder, state.peerIdWithRevealedOptions == peer.id)) + index += 1 } - let previousEntries = previousRecentItems.swap(entries) - - let transition = chatListSearchContainerPreparedRecentTransition(from: previousEntries ?? [], to: entries, account: account, filter: filter, peerSelected: { peer in - openPeer(peer, true) - let _ = addRecentlySearchedPeer(postbox: account.postbox, peerId: peer.id).start() - self?.recentListNode.clearHighlightAnimated(true) - }, peerLongTapped: { peer in - openRecentPeerOptions(peer) - }, clearRecentlySearchedPeers: { - self?.clearRecentSearch() - }, setPeerIdWithRevealedOptions: { peerId, fromPeerId in - interaction.setPeerIdWithRevealedOptions(peerId, fromPeerId) - }, deletePeer: { peerId in - if let strongSelf = self { - let _ = removeRecentlySearchedPeer(postbox: strongSelf.account.postbox, peerId: peerId).start() - } - }) - return .single((transition, previousEntries == nil)) + } + let previousEntries = previousRecentItems.swap(entries) + + let transition = chatListSearchContainerPreparedRecentTransition(from: previousEntries ?? [], to: entries, account: account, filter: filter, peerSelected: { peer in + openPeer(peer, true) + let _ = addRecentlySearchedPeer(postbox: account.postbox, peerId: peer.id).start() + self?.recentListNode.clearHighlightAnimated(true) + }, peerLongTapped: { peer in + openRecentPeerOptions(peer) + }, clearRecentlySearchedPeers: { + self?.clearRecentSearch() + }, setPeerIdWithRevealedOptions: { peerId, fromPeerId in + interaction.setPeerIdWithRevealedOptions(peerId, fromPeerId) + }, deletePeer: { peerId in + if let strongSelf = self { + let _ = removeRecentlySearchedPeer(postbox: strongSelf.account.postbox, peerId: peerId).start() + } + }) + return .single((transition, previousEntries == nil)) } if filter.contains(.excludeRecent) { @@ -962,31 +962,32 @@ final class ChatListSearchContainerNode: SearchDisplayControllerContentNode { })) self.searchDisposable.set((foundItems - |> deliverOnMainQueue).start(next: { [weak self] entriesAndFlags in - if let strongSelf = self { - strongSelf._isSearching.set(entriesAndFlags?.1 ?? false) - - let previousEntries = previousSearchItems.swap(entriesAndFlags?.0) - - let firstTime = previousEntries == nil - let transition = chatListSearchContainerPreparedTransition(from: previousEntries ?? [], to: entriesAndFlags?.0 ?? [], displayingResults: entriesAndFlags?.0 != nil, account: account, enableHeaders: true, filter: filter, interaction: interaction) - strongSelf.enqueueTransition(transition, firstTime: firstTime) - } - })) + |> deliverOnMainQueue).start(next: { [weak self] entriesAndFlags in + if let strongSelf = self { + strongSelf._isSearching.set(entriesAndFlags?.1 ?? false) + + let previousEntries = previousSearchItems.swap(entriesAndFlags?.0) + + let firstTime = previousEntries == nil + let transition = chatListSearchContainerPreparedTransition(from: previousEntries ?? [], to: entriesAndFlags?.0 ?? [], displayingResults: entriesAndFlags?.0 != nil, account: account, enableHeaders: true, filter: filter, interaction: interaction) + strongSelf.enqueueTransition(transition, firstTime: firstTime) + } + })) self.presentationDataDisposable = (account.telegramApplicationContext.presentationData - |> deliverOnMainQueue).start(next: { [weak self] presentationData in - if let strongSelf = self { - let previousTheme = strongSelf.presentationData.theme - //let previousStrings = strongSelf.presentationData.strings - - strongSelf.presentationData = presentationData - - if previousTheme !== presentationData.theme { - strongSelf.updateTheme(theme: presentationData.theme) - } + |> deliverOnMainQueue).start(next: { [weak self] presentationData in + if let strongSelf = self { + let previousTheme = strongSelf.presentationData.theme + //let previousStrings = strongSelf.presentationData.strings + + strongSelf.presentationData = presentationData + strongSelf.presentationDataPromise.set(.single(ChatListPresentationData(theme: presentationData.theme, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, nameSortOrder: presentationData.nameSortOrder, nameDisplayOrder: presentationData.nameDisplayOrder, disableAnimations: presentationData.disableAnimations))) + + if previousTheme !== presentationData.theme { + strongSelf.updateTheme(theme: presentationData.theme) } - }) + } + }) self.recentListNode.beganInteractiveDragging = { [weak self] in self?.dismissInput?() @@ -1116,14 +1117,13 @@ final class ChatListSearchContainerNode: SearchDisplayControllerContentNode { case let .animated(animationDuration, animationCurve): duration = animationDuration switch animationCurve { - case .easeInOut: - break - case .spring: - curve = 7 - } + case .easeInOut: + break + case .spring: + curve = 7 + } } - let listViewCurve: ListViewAnimationCurve if curve == 7 { listViewCurve = .Spring(duration: duration) diff --git a/TelegramUI/ChatListSearchRecentPeersNode.swift b/TelegramUI/ChatListSearchRecentPeersNode.swift index 5208186ece..e69855dc07 100644 --- a/TelegramUI/ChatListSearchRecentPeersNode.swift +++ b/TelegramUI/ChatListSearchRecentPeersNode.swift @@ -236,7 +236,7 @@ final class ChatListSearchRecentPeersNode: ASDisplayNode { self.theme = theme self.strings = strings - self.sectionHeaderNode.title = strings.DialogList_RecentTitlePeople + self.sectionHeaderNode.title = strings.DialogList_RecentTitlePeople.uppercased() self.sectionHeaderNode.updateTheme(theme: theme) } } diff --git a/TelegramUI/ChatLoadingNode.swift b/TelegramUI/ChatLoadingNode.swift index 6391b1c9c9..d664df098c 100644 --- a/TelegramUI/ChatLoadingNode.swift +++ b/TelegramUI/ChatLoadingNode.swift @@ -6,6 +6,7 @@ import TelegramCore final class ChatLoadingNode: ASDisplayNode { private let backgroundNode: ASImageNode private let activityIndicator: ActivityIndicator + private let offset: CGPoint init(theme: PresentationTheme, chatWallpaper: TelegramWallpaper) { self.backgroundNode = ASImageNode() @@ -18,6 +19,11 @@ final class ChatLoadingNode: ASDisplayNode { let serviceColor = serviceMessageColorComponents(theme: theme, wallpaper: chatWallpaper) self.activityIndicator = ActivityIndicator(type: .custom(serviceColor.primaryText, 22.0, 2.0, false), speed: .regular) + if serviceColor.primaryText != .white { + self.offset = CGPoint(x: 0.5, y: 0.5) + } else { + self.offset = CGPoint() + } super.init() @@ -33,7 +39,7 @@ final class ChatLoadingNode: ASDisplayNode { } let activitySize = self.activityIndicator.measure(size) - transition.updateFrame(node: self.activityIndicator, frame: CGRect(origin: CGPoint(x: displayRect.minX + floor((displayRect.width - activitySize.width) / 2.0), y: displayRect.minY + floor((displayRect.height - activitySize.height) / 2.0)), size: activitySize)) + transition.updateFrame(node: self.activityIndicator, frame: CGRect(origin: CGPoint(x: displayRect.minX + floor((displayRect.width - activitySize.width) / 2.0) + self.offset.x, y: displayRect.minY + floor((displayRect.height - activitySize.height) / 2.0) + self.offset.y), size: activitySize)) } } diff --git a/TelegramUI/ChatMediaInputGifPane.swift b/TelegramUI/ChatMediaInputGifPane.swift index 4228ab9e7b..0d485620f7 100644 --- a/TelegramUI/ChatMediaInputGifPane.swift +++ b/TelegramUI/ChatMediaInputGifPane.swift @@ -16,7 +16,7 @@ final class ChatMediaInputGifPane: ChatMediaInputPane, UIScrollViewDelegate { private let disposable = MetaDisposable() - private var validLayout: CGSize? + private var validLayout: (CGSize, CGFloat, CGFloat, Bool, Bool)? private var didScrollPreviousOffset: CGFloat? private var didScrollPreviousState: ChatMediaInputPaneScrollState? @@ -35,17 +35,26 @@ final class ChatMediaInputGifPane: ChatMediaInputPane, UIScrollViewDelegate { super.init() - self.backgroundColor = theme.chat.inputMediaPanel.gifsBackgroundColor - self.addSubnode(self.emptyNode) + + self.updateThemeAndStrings(theme: theme, strings: strings) } deinit { self.disposable.dispose() } + override func updateThemeAndStrings(theme: PresentationTheme, strings: PresentationStrings) { + self.backgroundColor = theme.chat.inputMediaPanel.gifsBackgroundColor + self.emptyNode.attributedText = NSAttributedString(string: strings.Conversation_EmptyGifPanelPlaceholder, font: Font.regular(15.0), textColor: theme.chat.inputMediaPanel.stickersSectionTextColor) + + if let layout = self.validLayout { + self.updateLayout(size: layout.0, topInset: layout.1, bottomInset: layout.2, isExpanded: layout.3, isVisible: layout.4, transition: .immediate) + } + } + override func updateLayout(size: CGSize, topInset: CGFloat, bottomInset: CGFloat, isExpanded: Bool, isVisible: Bool, transition: ContainedViewLayoutTransition) { - self.validLayout = size + self.validLayout = (size, topInset, bottomInset, isExpanded, isVisible) let emptySize = self.emptyNode.updateLayout(size) transition.updateFrame(node: self.emptyNode, frame: CGRect(origin: CGPoint(x: floor(size.width - emptySize.width) / 2.0, y: topInset + floor(size.height - topInset - emptySize.height) / 2.0), size: emptySize)) @@ -72,8 +81,8 @@ final class ChatMediaInputGifPane: ChatMediaInputPane, UIScrollViewDelegate { if self.multiplexedNode == nil { let multiplexedNode = MultiplexedVideoNode(account: account) self.multiplexedNode = multiplexedNode - if let validLayout = self.validLayout { - multiplexedNode.frame = CGRect(origin: CGPoint(), size: validLayout) + if let layout = self.validLayout { + multiplexedNode.frame = CGRect(origin: CGPoint(), size: layout.0) } self.view.addSubview(multiplexedNode) diff --git a/TelegramUI/ChatMediaInputNode.swift b/TelegramUI/ChatMediaInputNode.swift index fa1a417670..ce06de5fd8 100644 --- a/TelegramUI/ChatMediaInputNode.swift +++ b/TelegramUI/ChatMediaInputNode.swift @@ -440,7 +440,7 @@ final class ChatMediaInputNode: ChatInputNode { self.collectionListSeparator = ASDisplayNode() self.collectionListSeparator.isLayerBacked = true - self.collectionListSeparator.backgroundColor = theme.chat.inputMediaPanel.panelSerapatorColor + self.collectionListSeparator.backgroundColor = theme.chat.inputMediaPanel.panelSeparatorColor self.collectionListContainer = CollectionListContainerNode() self.collectionListContainer.clipsToBounds = true @@ -766,8 +766,14 @@ final class ChatMediaInputNode: ChatInputNode { self.strings = strings self.collectionListPanel.backgroundColor = theme.chat.inputPanel.panelBackgroundColor - self.collectionListSeparator.backgroundColor = theme.chat.inputMediaPanel.panelSerapatorColor - self.backgroundColor = theme.chat.inputMediaPanel.gifsBackgroundColor + self.collectionListSeparator.backgroundColor = theme.chat.inputMediaPanel.panelSeparatorColor + self.backgroundColor = theme.chat.inputMediaPanel.stickersBackgroundColor + + self.stickerSearchContainerNode?.updateThemeAndStrings(theme: theme, strings: strings) + + self.stickerPane.updateThemeAndStrings(theme: theme, strings: strings) + self.gifPane.updateThemeAndStrings(theme: theme, strings: strings) + self.trendingPane.updateThemeAndStrings(theme: theme, strings: strings) self.themeAndStringsPromise.set(.single((theme, strings))) } diff --git a/TelegramUI/ChatMediaInputPane.swift b/TelegramUI/ChatMediaInputPane.swift index e45b0e7ce3..d93a7c78c0 100644 --- a/TelegramUI/ChatMediaInputPane.swift +++ b/TelegramUI/ChatMediaInputPane.swift @@ -12,4 +12,7 @@ class ChatMediaInputPane: ASDisplayNode { func updateLayout(size: CGSize, topInset: CGFloat, bottomInset: CGFloat, isExpanded: Bool, isVisible: Bool, transition: ContainedViewLayoutTransition) { } + + func updateThemeAndStrings(theme: PresentationTheme, strings: PresentationStrings) { + } } diff --git a/TelegramUI/ChatMediaInputTrendingPane.swift b/TelegramUI/ChatMediaInputTrendingPane.swift index fe2c043d21..f647df4e28 100644 --- a/TelegramUI/ChatMediaInputTrendingPane.swift +++ b/TelegramUI/ChatMediaInputTrendingPane.swift @@ -20,13 +20,17 @@ final class TrendingPaneInteraction { private final class TrendingPaneEntry: Identifiable, Comparable { let index: Int let info: StickerPackCollectionInfo + let theme: PresentationTheme + let strings: PresentationStrings let topItems: [StickerPackItem] let installed: Bool let unread: Bool - init(index: Int, info: StickerPackCollectionInfo, topItems: [StickerPackItem], installed: Bool, unread: Bool) { + init(index: Int, info: StickerPackCollectionInfo, theme: PresentationTheme, strings: PresentationStrings, topItems: [StickerPackItem], installed: Bool, unread: Bool) { self.index = index self.info = info + self.theme = theme + self.strings = strings self.topItems = topItems self.installed = installed self.unread = unread @@ -43,6 +47,12 @@ private final class TrendingPaneEntry: Identifiable, Comparable { if lhs.info != rhs.info { return false } + if lhs.theme !== rhs.theme { + return false + } + if lhs.strings !== rhs.strings { + return false + } if lhs.topItems != rhs.topItems { return false } @@ -56,8 +66,8 @@ private final class TrendingPaneEntry: Identifiable, Comparable { return lhs.index < rhs.index } - func item(account: Account, theme: PresentationTheme, strings: PresentationStrings, interaction: TrendingPaneInteraction) -> ListViewItem { - return MediaInputPaneTrendingItem(account: account, theme: theme, strings: strings, interaction: interaction, info: self.info, topItems: self.topItems, installed: self.installed, unread: self.unread) + func item(account: Account, interaction: TrendingPaneInteraction) -> ListViewItem { + return MediaInputPaneTrendingItem(account: account, theme: self.theme, strings: self.strings, interaction: interaction, info: self.info, topItems: self.topItems, installed: self.installed, unread: self.unread) } } @@ -68,22 +78,22 @@ private struct TrendingPaneTransition { let initial: Bool } -private func preparedTransition(from fromEntries: [TrendingPaneEntry], to toEntries: [TrendingPaneEntry], account: Account, theme: PresentationTheme, strings: PresentationStrings, interaction: TrendingPaneInteraction, initial: Bool) -> TrendingPaneTransition { +private func preparedTransition(from fromEntries: [TrendingPaneEntry], to toEntries: [TrendingPaneEntry], account: Account, interaction: TrendingPaneInteraction, initial: Bool) -> TrendingPaneTransition { let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries) let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) } - let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, theme: theme, strings: strings, interaction: interaction), directionHint: nil) } - let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, theme: theme, strings: strings, interaction: interaction), directionHint: nil) } + let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, interaction: interaction), directionHint: nil) } + let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, interaction: interaction), directionHint: nil) } return TrendingPaneTransition(deletions: deletions, insertions: insertions, updates: updates, initial: initial) } -private func trendingPaneEntries(trendingEntries: [FeaturedStickerPackItem], installedPacks: Set) -> [TrendingPaneEntry] { +private func trendingPaneEntries(trendingEntries: [FeaturedStickerPackItem], installedPacks: Set, theme: PresentationTheme, strings: PresentationStrings) -> [TrendingPaneEntry] { var result: [TrendingPaneEntry] = [] var index = 0 for item in trendingEntries { if !installedPacks.contains(item.info.id) { - result.append(TrendingPaneEntry(index: index, info: item.info, topItems: item.topItems, installed: installedPacks.contains(item.info.id), unread: item.unread)) + result.append(TrendingPaneEntry(index: index, info: item.info, theme: theme, strings: strings, topItems: item.topItems, installed: installedPacks.contains(item.info.id), unread: item.unread)) index += 1 } } @@ -137,10 +147,6 @@ final class ChatMediaInputTrendingPane: ChatMediaInputPane { } self.isActivated = true - let presentationData = self.account.telegramApplicationContext.currentPresentationData.with { $0 } - let theme = presentationData.theme - let strings = presentationData.strings - let interaction = TrendingPaneInteraction(installPack: { [weak self] info in if let strongSelf = self, let info = info as? StickerPackCollectionInfo { let _ = (loadedStickerPack(postbox: strongSelf.account.postbox, network: strongSelf.account.network, reference: .id(id: info.id.id, accessHash: info.accessHash), forceActualized: false) @@ -160,7 +166,8 @@ final class ChatMediaInputTrendingPane: ChatMediaInputPane { return .complete() } |> deliverOnMainQueue).start(completed: { if let strongSelf = self { - strongSelf.controllerInteraction.presentController(OverlayStatusController(theme: theme, strings: strings, type: .success), nil) + let presentationData = strongSelf.account.telegramApplicationContext.currentPresentationData.with { $0 } + strongSelf.controllerInteraction.presentController(OverlayStatusController(theme: presentationData.theme, strings: presentationData.strings, type: .success), nil) } }) } @@ -179,8 +186,8 @@ final class ChatMediaInputTrendingPane: ChatMediaInputPane { let previousEntries = Atomic<[TrendingPaneEntry]?>(value: nil) let account = self.account - self.disposable = (combineLatest(account.viewTracker.featuredStickerPacks(), account.postbox.combinedView(keys: [.itemCollectionInfos(namespaces: [Namespaces.ItemCollection.CloudStickerPacks])])) - |> map { trendingEntries, view -> TrendingPaneTransition in + self.disposable = (combineLatest(account.viewTracker.featuredStickerPacks(), account.postbox.combinedView(keys: [.itemCollectionInfos(namespaces: [Namespaces.ItemCollection.CloudStickerPacks])]), account.telegramApplicationContext.presentationData) + |> map { trendingEntries, view, presentationData -> TrendingPaneTransition in var installedPacks = Set() if let stickerPacksView = view.views[.itemCollectionInfos(namespaces: [Namespaces.ItemCollection.CloudStickerPacks])] as? ItemCollectionInfosView { if let packsEntries = stickerPacksView.entriesByNamespace[Namespaces.ItemCollection.CloudStickerPacks] { @@ -189,10 +196,10 @@ final class ChatMediaInputTrendingPane: ChatMediaInputPane { } } } - let entries = trendingPaneEntries(trendingEntries: trendingEntries, installedPacks: installedPacks) + let entries = trendingPaneEntries(trendingEntries: trendingEntries, installedPacks: installedPacks, theme: presentationData.theme, strings: presentationData.strings) let previous = previousEntries.swap(entries) - return preparedTransition(from: previous ?? [], to: entries, account: account, theme: presentationData.theme, strings: presentationData.strings, interaction: interaction, initial: previous == nil) + return preparedTransition(from: previous ?? [], to: entries, account: account, interaction: interaction, initial: previous == nil) } |> deliverOnMainQueue).start(next: { [weak self] transition in guard let strongSelf = self else { diff --git a/TelegramUI/ChatMessageAnimatedStickerItemNode.swift b/TelegramUI/ChatMessageAnimatedStickerItemNode.swift index c19eb03d22..9bddbb8f19 100644 --- a/TelegramUI/ChatMessageAnimatedStickerItemNode.swift +++ b/TelegramUI/ChatMessageAnimatedStickerItemNode.swift @@ -387,6 +387,8 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { strongSelf.replyBackgroundNode = updatedReplyBackgroundNode strongSelf.addSubnode(updatedReplyBackgroundNode) updatedReplyBackgroundNode.image = replyBackgroundImage + } else { + strongSelf.replyBackgroundNode?.image = replyBackgroundImage } } else if let replyBackgroundNode = strongSelf.replyBackgroundNode { replyBackgroundNode.removeFromSupernode() diff --git a/TelegramUI/ChatMessageInstantVideoItemNode.swift b/TelegramUI/ChatMessageInstantVideoItemNode.swift index 8d5b78536d..d9693d8a2c 100644 --- a/TelegramUI/ChatMessageInstantVideoItemNode.swift +++ b/TelegramUI/ChatMessageInstantVideoItemNode.swift @@ -5,6 +5,11 @@ import SwiftSignalKit import Postbox import TelegramCore +private let nameFont = Font.medium(14.0) + +private let inlineBotPrefixFont = Font.regular(14.0) +private let inlineBotNameFont = nameFont + class ChatMessageInstantVideoItemNode: ChatMessageItemView { private let interactiveVideoNode: ChatMessageInteractiveInstantVideoNode @@ -17,6 +22,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView { private var forwardInfoNode: ChatMessageForwardInfoNode? private var forwardBackgroundNode: ASImageNode? + private var viaBotNode: TextNode? private var replyInfoNode: ChatMessageReplyInfoNode? private var replyBackgroundNode: ASImageNode? @@ -71,6 +77,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView { let makeVideoLayout = self.interactiveVideoNode.asyncLayout() + let viaBotLayout = TextNode.asyncLayout(self.viaBotNode) let makeReplyInfoLayout = ChatMessageReplyInfoNode.asyncLayout(self.replyInfoNode) let currentReplyBackgroundNode = self.replyBackgroundNode @@ -132,25 +139,36 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView { let videoFrame = CGRect(origin: CGPoint(x: (incoming ? (params.leftInset + layoutConstants.bubble.edgeInset + avatarInset + layoutConstants.bubble.contentInsets.left) : (params.width - params.rightInset - videoLayout.contentSize.width - layoutConstants.bubble.edgeInset - layoutConstants.bubble.contentInsets.left)), y: 0.0), size: videoLayout.contentSize) + var viaBotApply: (TextNodeLayout, () -> TextNode)? var replyInfoApply: (CGSize, () -> ChatMessageReplyInfoNode)? var updatedReplyBackgroundNode: ASImageNode? var replyBackgroundImage: UIImage? var replyMarkup: ReplyMarkupMessageAttribute? var inlineBotNameString: String? + let availableWidth = max(60.0, params.width - params.leftInset - params.rightInset - videoLayout.contentSize.width - 20.0 - layoutConstants.bubble.edgeInset * 2.0 - avatarInset - layoutConstants.bubble.contentInsets.left) + for attribute in item.message.attributes { - if let replyAttribute = attribute as? ReplyMessageAttribute, let replyMessage = item.message.associatedMessages[replyAttribute.messageId] { - let availableWidth = max(60.0, params.width - params.leftInset - params.rightInset - videoLayout.contentSize.width - 20.0 - layoutConstants.bubble.edgeInset * 2.0 - avatarInset - layoutConstants.bubble.contentInsets.left) - replyInfoApply = makeReplyInfoLayout(item.presentationData.theme, item.presentationData.strings, item.account, .standalone, replyMessage, CGSize(width: availableWidth, height: CGFloat.greatestFiniteMagnitude)) - - if let currentReplyBackgroundNode = currentReplyBackgroundNode { - updatedReplyBackgroundNode = currentReplyBackgroundNode + if let attribute = attribute as? InlineBotMessageAttribute { + var inlineBotNameString: String? + if let peerId = attribute.peerId, let bot = item.message.peers[peerId] as? TelegramUser { + inlineBotNameString = bot.username } else { - updatedReplyBackgroundNode = ASImageNode() + inlineBotNameString = attribute.title } - let graphics = PresentationResourcesChat.additionalGraphics(item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper) - replyBackgroundImage = graphics.chatServiceBubbleFillImage + if let inlineBotNameString = inlineBotNameString { + let inlineBotNameColor = serviceMessageColorComponents(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper).primaryText + + let bodyAttributes = MarkdownAttributeSet(font: nameFont, textColor: inlineBotNameColor) + let boldAttributes = MarkdownAttributeSet(font: inlineBotPrefixFont, textColor: inlineBotNameColor) + let botString = addAttributesToStringWithRanges(item.presentationData.strings.Conversation_MessageViaUser("@\(inlineBotNameString)"), body: bodyAttributes, argumentAttributes: [0: boldAttributes]) + + viaBotApply = viaBotLayout(TextNodeLayoutArguments(attributedString: botString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: max(0, availableWidth), height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) + } + } + if let replyAttribute = attribute as? ReplyMessageAttribute, let replyMessage = item.message.associatedMessages[replyAttribute.messageId] { + replyInfoApply = makeReplyInfoLayout(item.presentationData.theme, item.presentationData.strings, item.account, .standalone, replyMessage, CGSize(width: availableWidth, height: CGFloat.greatestFiniteMagnitude)) } else if let attribute = attribute as? InlineBotMessageAttribute { if let peerId = attribute.peerId, let bot = item.message.peers[peerId] as? TelegramUser { inlineBotNameString = bot.username @@ -162,6 +180,17 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView { } } + if replyInfoApply != nil || viaBotApply != nil { + if let currentReplyBackgroundNode = currentReplyBackgroundNode { + updatedReplyBackgroundNode = currentReplyBackgroundNode + } else { + updatedReplyBackgroundNode = ASImageNode() + } + + let graphics = PresentationResourcesChat.additionalGraphics(item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper) + replyBackgroundImage = graphics.chatFreeformContentAdditionalInfoBackgroundImage + } + let availableContentWidth = params.width - params.leftInset - params.rightInset - layoutConstants.bubble.edgeInset * 2.0 - avatarInset - layoutConstants.bubble.contentInsets.left var forwardInfoSizeApply: (CGSize, () -> ChatMessageForwardInfoNode)? @@ -239,21 +268,46 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView { strongSelf.replyBackgroundNode = updatedReplyBackgroundNode strongSelf.addSubnode(updatedReplyBackgroundNode) updatedReplyBackgroundNode.image = replyBackgroundImage + } else { + strongSelf.replyBackgroundNode?.image = replyBackgroundImage } } else if let replyBackgroundNode = strongSelf.replyBackgroundNode { replyBackgroundNode.removeFromSupernode() strongSelf.replyBackgroundNode = nil } + if let (viaBotLayout, viaBotApply) = viaBotApply { + let viaBotNode = viaBotApply() + if strongSelf.viaBotNode == nil { + strongSelf.viaBotNode = viaBotNode + strongSelf.addSubnode(viaBotNode) + } + let viaBotFrame = CGRect(origin: CGPoint(x: (!incoming ? (params.leftInset + layoutConstants.bubble.edgeInset + 10.0) : (params.width - params.rightInset - viaBotLayout.size.width - layoutConstants.bubble.edgeInset - 10.0)), y: 8.0), size: viaBotLayout.size) + viaBotNode.frame = viaBotFrame + strongSelf.replyBackgroundNode?.frame = CGRect(origin: CGPoint(x: viaBotFrame.minX - 4.0, y: viaBotFrame.minY - 2.0), size: CGSize(width: viaBotFrame.size.width + 8.0, height: viaBotFrame.size.height + 5.0)) + } else if let viaBotNode = strongSelf.viaBotNode { + viaBotNode.removeFromSupernode() + strongSelf.viaBotNode = nil + } + if let (replyInfoSize, replyInfoApply) = replyInfoApply { let replyInfoNode = replyInfoApply() if strongSelf.replyInfoNode == nil { strongSelf.replyInfoNode = replyInfoNode strongSelf.addSubnode(replyInfoNode) } - let replyInfoFrame = CGRect(origin: CGPoint(x: (!incoming ? (params.leftInset + layoutConstants.bubble.edgeInset + 10.0) : (params.width - params.rightInset - replyInfoSize.width - layoutConstants.bubble.edgeInset - 10.0)), y: videoLayout.contentSize.height - replyInfoSize.height - 8.0), size: replyInfoSize) + var viaBotSize = CGSize() + if let viaBotNode = strongSelf.viaBotNode { + viaBotSize = viaBotNode.frame.size + } + let replyInfoFrame = CGRect(origin: CGPoint(x: (!incoming ? (params.leftInset + layoutConstants.bubble.edgeInset + 10.0) : (params.width - params.rightInset - max(replyInfoSize.width, viaBotSize.width) - layoutConstants.bubble.edgeInset - 10.0)), y: 8.0 + viaBotSize.height), size: replyInfoSize) + if let viaBotNode = strongSelf.viaBotNode { + if replyInfoFrame.minX < viaBotNode.frame.minX { + viaBotNode.frame = viaBotNode.frame.offsetBy(dx: replyInfoFrame.minX - viaBotNode.frame.minX, dy: 0.0) + } + } replyInfoNode.frame = replyInfoFrame - strongSelf.replyBackgroundNode?.frame = CGRect(origin: CGPoint(x: replyInfoFrame.minX - 4.0, y: replyInfoFrame.minY - 2.0), size: CGSize(width: replyInfoFrame.size.width + 8.0, height: replyInfoFrame.size.height + 5.0)) + strongSelf.replyBackgroundNode?.frame = CGRect(origin: CGPoint(x: replyInfoFrame.minX - 4.0, y: replyInfoFrame.minY - viaBotSize.height - 2.0), size: CGSize(width: max(replyInfoFrame.size.width, viaBotSize.width) + 8.0, height: replyInfoFrame.size.height + viaBotSize.height + 5.0)) } else if let replyInfoNode = strongSelf.replyInfoNode { replyInfoNode.removeFromSupernode() strongSelf.replyInfoNode = nil diff --git a/TelegramUI/ChatMessageInteractiveFileNode.swift b/TelegramUI/ChatMessageInteractiveFileNode.swift index 3ea2132437..4e07012007 100644 --- a/TelegramUI/ChatMessageInteractiveFileNode.swift +++ b/TelegramUI/ChatMessageInteractiveFileNode.swift @@ -695,19 +695,21 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode { } } + let backgroundNodeColor: UIColor + if self.iconNode != nil { + backgroundNodeColor = bubbleTheme.mediaOverlayControlBackgroundColor + } else if incoming { + backgroundNodeColor = bubbleTheme.incomingMediaActiveControlColor + } else { + backgroundNodeColor = bubbleTheme.outgoingMediaActiveControlColor + } if state != .none && self.statusNode == nil { - let backgroundNodeColor: UIColor - if self.iconNode != nil { - backgroundNodeColor = bubbleTheme.mediaOverlayControlBackgroundColor - } else if incoming { - backgroundNodeColor = bubbleTheme.incomingMediaActiveControlColor - } else { - backgroundNodeColor = bubbleTheme.outgoingMediaActiveControlColor - } let statusNode = RadialStatusNode(backgroundNodeColor: backgroundNodeColor) self.statusNode = statusNode statusNode.frame = progressFrame self.addSubnode(statusNode) + } else if let statusNode = self.statusNode { + statusNode.backgroundNodeColor = backgroundNodeColor } if streamingState != .none && self.streamingStatusNode == nil { diff --git a/TelegramUI/ChatMessageMediaBubbleContentNode.swift b/TelegramUI/ChatMessageMediaBubbleContentNode.swift index ecf76193dc..032e2bac2d 100644 --- a/TelegramUI/ChatMessageMediaBubbleContentNode.swift +++ b/TelegramUI/ChatMessageMediaBubbleContentNode.swift @@ -147,7 +147,7 @@ class ChatMessageMediaBubbleContentNode: ChatMessageBubbleContentNode { var statusApply: ((Bool) -> Void)? if let statusType = statusType { - let (size, apply) = statusLayout(item.presentationData.theme, item.presentationData.strings, edited && !sentViaBot, viewCount, dateText, statusType, CGSize(width: 200.0, height: CGFloat.greatestFiniteMagnitude)) + let (size, apply) = statusLayout(item.presentationData.theme, item.presentationData.strings, edited && !sentViaBot, viewCount, dateText, statusType, CGSize(width: imageSize.width - 30.0, height: CGFloat.greatestFiniteMagnitude)) statusSize = size statusApply = apply } diff --git a/TelegramUI/ChatMessageSelectionNode.swift b/TelegramUI/ChatMessageSelectionNode.swift index 11af9c2669..733d39eddb 100644 --- a/TelegramUI/ChatMessageSelectionNode.swift +++ b/TelegramUI/ChatMessageSelectionNode.swift @@ -16,7 +16,7 @@ final class ChatMessageSelectionNode: ASDisplayNode { self.addSubnode(self.checkNode) - self.hitTestSlop = UIEdgeInsetsMake(0.0, 42.0, 0.0, 0.0) + //self.hitTestSlop = UIEdgeInsetsMake(0.0, 42.0, 0.0, 0.0) } override func didLoad() { diff --git a/TelegramUI/ChatMessageStickerItemNode.swift b/TelegramUI/ChatMessageStickerItemNode.swift index 3ef0d713ee..7601994df5 100644 --- a/TelegramUI/ChatMessageStickerItemNode.swift +++ b/TelegramUI/ChatMessageStickerItemNode.swift @@ -5,6 +5,11 @@ import SwiftSignalKit import Postbox import TelegramCore +private let nameFont = Font.medium(14.0) + +private let inlineBotPrefixFont = Font.regular(14.0) +private let inlineBotNameFont = nameFont + class ChatMessageStickerItemNode: ChatMessageItemView { let imageNode: TransformImageNode var progressNode: RadialProgressNode? @@ -19,6 +24,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView { private let fetchDisposable = MetaDisposable() + private var viaBotNode: TextNode? private let dateAndStatusNode: ChatMessageDateAndStatusNode private var replyInfoNode: ChatMessageReplyInfoNode? private var replyBackgroundNode: ASImageNode? @@ -101,6 +107,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView { let makeDateAndStatusLayout = self.dateAndStatusNode.asyncLayout() let actionButtonsLayout = ChatMessageActionButtonsNode.asyncLayout(self.actionButtonsNode) + let viaBotLayout = TextNode.asyncLayout(self.viaBotNode) let makeReplyInfoLayout = ChatMessageReplyInfoNode.asyncLayout(self.replyInfoNode) let currentReplyBackgroundNode = self.replyBackgroundNode let currentShareButtonNode = self.shareButtonNode @@ -231,29 +238,52 @@ class ChatMessageStickerItemNode: ChatMessageItemView { let (dateAndStatusSize, dateAndStatusApply) = makeDateAndStatusLayout(item.presentationData.theme, item.presentationData.strings, edited && !sentViaBot, viewCount, dateText, statusType, CGSize(width: params.width, height: CGFloat.greatestFiniteMagnitude)) + var viaBotApply: (TextNodeLayout, () -> TextNode)? var replyInfoApply: (CGSize, () -> ChatMessageReplyInfoNode)? var updatedReplyBackgroundNode: ASImageNode? var replyBackgroundImage: UIImage? var replyMarkup: ReplyMarkupMessageAttribute? + let availableWidth = max(60.0, params.width - params.leftInset - params.rightInset - imageSize.width - 20.0 - layoutConstants.bubble.edgeInset * 2.0 - avatarInset - layoutConstants.bubble.contentInsets.left) + for attribute in item.message.attributes { - if let replyAttribute = attribute as? ReplyMessageAttribute, let replyMessage = item.message.associatedMessages[replyAttribute.messageId] { - let availableWidth = max(60.0, params.width - params.leftInset - params.rightInset - imageSize.width - 20.0 - layoutConstants.bubble.edgeInset * 2.0 - avatarInset - layoutConstants.bubble.contentInsets.left) - replyInfoApply = makeReplyInfoLayout(item.presentationData.theme, item.presentationData.strings, item.account, .standalone, replyMessage, CGSize(width: availableWidth, height: CGFloat.greatestFiniteMagnitude)) - - if let currentReplyBackgroundNode = currentReplyBackgroundNode { - updatedReplyBackgroundNode = currentReplyBackgroundNode + if let attribute = attribute as? InlineBotMessageAttribute { + var inlineBotNameString: String? + if let peerId = attribute.peerId, let bot = item.message.peers[peerId] as? TelegramUser { + inlineBotNameString = bot.username } else { - updatedReplyBackgroundNode = ASImageNode() + inlineBotNameString = attribute.title } - let graphics = PresentationResourcesChat.additionalGraphics(item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper) - replyBackgroundImage = graphics.chatFreeformContentAdditionalInfoBackgroundImage + if let inlineBotNameString = inlineBotNameString { + let inlineBotNameColor = serviceMessageColorComponents(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper).primaryText + + let bodyAttributes = MarkdownAttributeSet(font: nameFont, textColor: inlineBotNameColor) + let boldAttributes = MarkdownAttributeSet(font: inlineBotPrefixFont, textColor: inlineBotNameColor) + let botString = addAttributesToStringWithRanges(item.presentationData.strings.Conversation_MessageViaUser("@\(inlineBotNameString)"), body: bodyAttributes, argumentAttributes: [0: boldAttributes]) + + viaBotApply = viaBotLayout(TextNodeLayoutArguments(attributedString: botString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: max(0, availableWidth), height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) + } + } + if let replyAttribute = attribute as? ReplyMessageAttribute, let replyMessage = item.message.associatedMessages[replyAttribute.messageId] { + + replyInfoApply = makeReplyInfoLayout(item.presentationData.theme, item.presentationData.strings, item.account, .standalone, replyMessage, CGSize(width: availableWidth, height: CGFloat.greatestFiniteMagnitude)) } else if let attribute = attribute as? ReplyMarkupMessageAttribute, attribute.flags.contains(.inline), !attribute.rows.isEmpty { replyMarkup = attribute } } + if replyInfoApply != nil || viaBotApply != nil { + if let currentReplyBackgroundNode = currentReplyBackgroundNode { + updatedReplyBackgroundNode = currentReplyBackgroundNode + } else { + updatedReplyBackgroundNode = ASImageNode() + } + + let graphics = PresentationResourcesChat.additionalGraphics(item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper) + replyBackgroundImage = graphics.chatFreeformContentAdditionalInfoBackgroundImage + } + var updatedShareButtonBackground: UIImage? var updatedShareButtonNode: HighlightableButtonNode? @@ -338,21 +368,46 @@ class ChatMessageStickerItemNode: ChatMessageItemView { strongSelf.replyBackgroundNode = updatedReplyBackgroundNode strongSelf.addSubnode(updatedReplyBackgroundNode) updatedReplyBackgroundNode.image = replyBackgroundImage + } else { + strongSelf.replyBackgroundNode?.image = replyBackgroundImage } } else if let replyBackgroundNode = strongSelf.replyBackgroundNode { replyBackgroundNode.removeFromSupernode() strongSelf.replyBackgroundNode = nil } + if let (viaBotLayout, viaBotApply) = viaBotApply { + let viaBotNode = viaBotApply() + if strongSelf.viaBotNode == nil { + strongSelf.viaBotNode = viaBotNode + strongSelf.addSubnode(viaBotNode) + } + let viaBotFrame = CGRect(origin: CGPoint(x: (!incoming ? (params.leftInset + layoutConstants.bubble.edgeInset + 10.0) : (params.width - params.rightInset - viaBotLayout.size.width - layoutConstants.bubble.edgeInset - 10.0)), y: 8.0), size: viaBotLayout.size) + viaBotNode.frame = viaBotFrame + strongSelf.replyBackgroundNode?.frame = CGRect(origin: CGPoint(x: viaBotFrame.minX - 4.0, y: viaBotFrame.minY - 2.0), size: CGSize(width: viaBotFrame.size.width + 8.0, height: viaBotFrame.size.height + 5.0)) + } else if let viaBotNode = strongSelf.viaBotNode { + viaBotNode.removeFromSupernode() + strongSelf.viaBotNode = nil + } + if let (replyInfoSize, replyInfoApply) = replyInfoApply { let replyInfoNode = replyInfoApply() if strongSelf.replyInfoNode == nil { strongSelf.replyInfoNode = replyInfoNode strongSelf.addSubnode(replyInfoNode) } - let replyInfoFrame = CGRect(origin: CGPoint(x: (!incoming ? (params.leftInset + layoutConstants.bubble.edgeInset + 10.0) : (params.width - params.rightInset - replyInfoSize.width - layoutConstants.bubble.edgeInset - 10.0)), y: 8.0), size: replyInfoSize) + var viaBotSize = CGSize() + if let viaBotNode = strongSelf.viaBotNode { + viaBotSize = viaBotNode.frame.size + } + let replyInfoFrame = CGRect(origin: CGPoint(x: (!incoming ? (params.leftInset + layoutConstants.bubble.edgeInset + 10.0) : (params.width - params.rightInset - max(replyInfoSize.width, viaBotSize.width) - layoutConstants.bubble.edgeInset - 10.0)), y: 8.0 + viaBotSize.height), size: replyInfoSize) + if let viaBotNode = strongSelf.viaBotNode { + if replyInfoFrame.minX < viaBotNode.frame.minX { + viaBotNode.frame = viaBotNode.frame.offsetBy(dx: replyInfoFrame.minX - viaBotNode.frame.minX, dy: 0.0) + } + } replyInfoNode.frame = replyInfoFrame - strongSelf.replyBackgroundNode?.frame = CGRect(origin: CGPoint(x: replyInfoFrame.minX - 4.0, y: replyInfoFrame.minY - 2.0), size: CGSize(width: replyInfoFrame.size.width + 8.0, height: replyInfoFrame.size.height + 5.0)) + strongSelf.replyBackgroundNode?.frame = CGRect(origin: CGPoint(x: replyInfoFrame.minX - 4.0, y: replyInfoFrame.minY - viaBotSize.height - 2.0), size: CGSize(width: max(replyInfoFrame.size.width, viaBotSize.width) + 8.0, height: replyInfoFrame.size.height + viaBotSize.height + 5.0)) } else if let replyInfoNode = strongSelf.replyInfoNode { replyInfoNode.removeFromSupernode() strongSelf.replyInfoNode = nil @@ -410,6 +465,31 @@ class ChatMessageStickerItemNode: ChatMessageItemView { return } + if let viaBotNode = self.viaBotNode, viaBotNode.frame.contains(location) { + if let item = self.item { + for attribute in item.message.attributes { + if let attribute = attribute as? InlineBotMessageAttribute { + var botAddressName: String? + if let peerId = attribute.peerId, let botPeer = item.message.peers[peerId], let addressName = botPeer.addressName { + botAddressName = addressName + } else { + botAddressName = attribute.title + } + + if let botAddressName = botAddressName { + item.controllerInteraction.updateInputState { textInputState in + return ChatTextInputState(inputText: NSAttributedString(string: "@" + botAddressName + " ")) + } + item.controllerInteraction.updateInputMode { _ in + return .text + } + } + return + } + } + } + } + if let replyInfoNode = self.replyInfoNode, replyInfoNode.frame.contains(location) { if let item = self.item { for attribute in item.message.attributes { diff --git a/TelegramUI/ChatRecentActionsController.swift b/TelegramUI/ChatRecentActionsController.swift index 82dfea5df8..8dc47877a3 100644 --- a/TelegramUI/ChatRecentActionsController.swift +++ b/TelegramUI/ChatRecentActionsController.swift @@ -12,6 +12,7 @@ final class ChatRecentActionsController: TelegramController { private let account: Account private let peer: Peer private var presentationData: PresentationData + private var presentationDataDisposable: Disposable? private var interaction: ChatRecentActionsInteraction! private var panelInteraction: ChatPanelInterfaceInteraction! @@ -100,12 +101,37 @@ final class ChatRecentActionsController: TelegramController { self.titleView.pressed = { [weak self] in self?.openFilterSetup() } + + self.presentationDataDisposable = (account.telegramApplicationContext.presentationData + |> deliverOnMainQueue).start(next: { [weak self] presentationData in + if let strongSelf = self { + let previousTheme = strongSelf.presentationData.theme + let previousStrings = strongSelf.presentationData.strings + + strongSelf.presentationData = presentationData + + if previousTheme !== presentationData.theme || previousStrings !== presentationData.strings { + strongSelf.updateThemeAndStrings() + } + } + }) } required init(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } + private func updateThemeAndStrings() { + self.titleView.color = self.presentationData.theme.rootController.navigationBar.primaryTextColor + self.updateTitle() + + let rightButton = ChatNavigationButton(action: .search, buttonItem: UIBarButtonItem(image: PresentationResourcesRootController.navigationCompactSearchIcon(self.presentationData.theme), style: .plain, target: self, action: #selector(self.activateSearch))) + self.navigationItem.setRightBarButton(rightButton.buttonItem, animated: false) + + self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBar.style.style + self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData)) + } + override func loadDisplayNode() { self.displayNode = ChatRecentActionsControllerNode(account: self.account, peer: self.peer, presentationData: self.presentationData, interaction: self.interaction, pushController: { [weak self] c in (self?.navigationController as? NavigationController)?.pushViewController(c) diff --git a/TelegramUI/ChatRecentActionsControllerNode.swift b/TelegramUI/ChatRecentActionsControllerNode.swift index d74218a9c1..904aca463c 100644 --- a/TelegramUI/ChatRecentActionsControllerNode.swift +++ b/TelegramUI/ChatRecentActionsControllerNode.swift @@ -350,6 +350,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode { }, requestRedeliveryOfFailedMessages: { _ in }, addContact: { _ in + }, rateCall: { _ in }, requestMessageUpdate: { _ in }, cancelInteractiveKeyboardGestures: { }, automaticMediaDownloadSettings: self.automaticMediaDownloadSettings) @@ -423,9 +424,22 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode { } })) } + + self.presentationDataDisposable = (account.telegramApplicationContext.presentationData + |> deliverOnMainQueue).start(next: { [weak self] presentationData in + if let strongSelf = self { + let previousTheme = strongSelf.presentationData.theme + + strongSelf.presentationData = presentationData + strongSelf.chatPresentationDataPromise.set(.single(ChatPresentationData(theme: ChatPresentationThemeData(theme: presentationData.theme, wallpaper: presentationData.chatWallpaper), fontSize: presentationData.fontSize, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, disableAnimations: presentationData.disableAnimations))) + + strongSelf.updateThemeAndStrings(theme: presentationData.theme, strings: presentationData.strings) + } + }) } deinit { + self.presentationDataDisposable?.dispose() self.historyDisposable?.dispose() self.navigationActionDisposable.dispose() self.galleryHiddenMesageAndMediaDisposable.dispose() @@ -434,6 +448,12 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode { self.banDisposables.dispose() } + func updateThemeAndStrings(theme: PresentationTheme, strings: PresentationStrings) { + self.panelBackgroundNode.backgroundColor = theme.chat.inputPanel.panelBackgroundColor + self.panelSeparatorNode.backgroundColor = theme.chat.inputPanel.panelStrokeColor + self.panelButtonNode.setTitle(presentationData.strings.Channel_AdminLog_InfoPanelTitle, with: Font.regular(17.0), with: theme.chat.inputPanel.panelControlAccentColor, for: []) + } + func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) { let isFirstLayout = self.containerLayout == nil diff --git a/TelegramUI/ChatRecentActionsHistoryTransition.swift b/TelegramUI/ChatRecentActionsHistoryTransition.swift index b3886385a1..e67e8e94d0 100644 --- a/TelegramUI/ChatRecentActionsHistoryTransition.swift +++ b/TelegramUI/ChatRecentActionsHistoryTransition.swift @@ -80,6 +80,9 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { if lhs.id != rhs.id { return false } + if lhs.presentationData !== rhs.presentationData { + return false + } if lhs.entry != rhs.entry { return false } diff --git a/TelegramUI/ChatRecentActionsTitleView.swift b/TelegramUI/ChatRecentActionsTitleView.swift index da277a424f..83f1d7f1ba 100644 --- a/TelegramUI/ChatRecentActionsTitleView.swift +++ b/TelegramUI/ChatRecentActionsTitleView.swift @@ -21,7 +21,13 @@ final class ChatRecentActionsTitleView: UIView { private let titleNode: TextNode private let arrowNode: ASImageNode - private var color: UIColor + var color: UIColor { + didSet { + if self.color != oldValue { + self.setNeedsLayout() + } + } + } var pressed: (() -> Void)? diff --git a/TelegramUI/ChatSearchNavigationContentNode.swift b/TelegramUI/ChatSearchNavigationContentNode.swift index 2c2745614f..c268616fc4 100644 --- a/TelegramUI/ChatSearchNavigationContentNode.swift +++ b/TelegramUI/ChatSearchNavigationContentNode.swift @@ -68,6 +68,8 @@ final class ChatSearchNavigationContentNode: NavigationBarContentNode { func update(presentationInterfaceState: ChatPresentationInterfaceState) { if let search = presentationInterfaceState.search { + self.searchBar.updateThemeAndStrings(theme: SearchBarNodeTheme(theme: presentationInterfaceState.theme), strings: presentationInterfaceState.strings) + switch search.domain { case .everything: self.searchBar.prefixString = nil diff --git a/TelegramUI/ChatSecretAutoremoveTimerActionSheet.swift b/TelegramUI/ChatSecretAutoremoveTimerActionSheet.swift index 8b42bd7c7e..64ff4fc060 100644 --- a/TelegramUI/ChatSecretAutoremoveTimerActionSheet.swift +++ b/TelegramUI/ChatSecretAutoremoveTimerActionSheet.swift @@ -2,24 +2,31 @@ import Foundation import Display import AsyncDisplayKit import UIKit +import TelegramCore import SwiftSignalKit import Photos final class ChatSecretAutoremoveTimerActionSheetController: ActionSheetController { - private let theme: PresentationTheme - private let strings: PresentationStrings + private var presentationDisposable: Disposable? private let _ready = Promise() override var ready: Promise { return self._ready } - init(theme: PresentationTheme, strings: PresentationStrings, currentValue: Int32, applyValue: @escaping (Int32) -> Void) { - self.theme = theme - self.strings = strings + init(account: Account, currentValue: Int32, applyValue: @escaping (Int32) -> Void) { + let presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 } + let theme = presentationData.theme + let strings = presentationData.strings super.init(theme: ActionSheetControllerTheme(presentationTheme: theme)) + self.presentationDisposable = account.telegramApplicationContext.presentationData.start(next: { [weak self] presentationData in + if let strongSelf = self { + strongSelf.theme = ActionSheetControllerTheme(presentationTheme: presentationData.theme) + } + }) + self._ready.set(.single(true)) var updatedValue = currentValue > 0 ? currentValue : 7 @@ -39,6 +46,10 @@ final class ChatSecretAutoremoveTimerActionSheetController: ActionSheetControlle required init(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } + + deinit { + self.presentationDisposable?.dispose() + } } private final class AutoremoveTimeoutSelectorItem: ActionSheetItem { diff --git a/TelegramUI/ChatTextInputMediaRecordingButton.swift b/TelegramUI/ChatTextInputMediaRecordingButton.swift index 592e341367..e4979528b0 100644 --- a/TelegramUI/ChatTextInputMediaRecordingButton.swift +++ b/TelegramUI/ChatTextInputMediaRecordingButton.swift @@ -242,7 +242,7 @@ final class ChatTextInputMediaRecordingButton: TGModernConversationInputMicButto let inputPanelTheme = theme.chat.inputPanel - self.pallete = TGModernConversationInputMicPallete(dark: theme.overallDarkAppearance, buttonColor: inputPanelTheme.actionControlFillColor, iconColor: inputPanelTheme.actionControlForegroundColor, backgroundColor: inputPanelTheme.panelBackgroundColor, borderColor: inputPanelTheme.panelStrokeColor, lock: inputPanelTheme.panelControlAccentColor, textColor: inputPanelTheme.primaryTextColor, secondaryTextColor: inputPanelTheme.secondaryTextColor, recording: inputPanelTheme.mediaRecordingDotColor) + self.pallete = legacyInputMicPalette(from: theme) self.insertSubview(self.innerIconView, at: 0) @@ -311,9 +311,7 @@ final class ChatTextInputMediaRecordingButton: TGModernConversationInputMicButto self.innerIconView.image = PresentationResourcesChat.chatInputPanelVideoButtonImage(self.theme) } - let inputPanelTheme = theme.chat.inputPanel - - self.pallete = TGModernConversationInputMicPallete(dark: theme.overallDarkAppearance, buttonColor: inputPanelTheme.actionControlFillColor, iconColor: inputPanelTheme.actionControlForegroundColor, backgroundColor: inputPanelTheme.panelBackgroundColor, borderColor: inputPanelTheme.panelStrokeColor, lock: inputPanelTheme.panelControlAccentColor, textColor: inputPanelTheme.primaryTextColor, secondaryTextColor: inputPanelTheme.secondaryTextColor, recording: inputPanelTheme.mediaRecordingDotColor) + self.pallete = legacyInputMicPalette(from: theme) } deinit { diff --git a/TelegramUI/ChatTextInputPanelNode.swift b/TelegramUI/ChatTextInputPanelNode.swift index c5ddcc9b58..85fa95bb94 100644 --- a/TelegramUI/ChatTextInputPanelNode.swift +++ b/TelegramUI/ChatTextInputPanelNode.swift @@ -173,7 +173,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate { var textPlaceholderNode: TextNode var contextPlaceholderNode: TextNode? let textInputContainer: ASDisplayNode - var textInputNode: ASEditableTextNode? + var textInputNode: EditableTextNode? let textInputBackgroundView: UIImageView let actionButtons: ChatTextInputActionButtonsNode @@ -388,7 +388,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate { } private func loadTextInputNode() { - let textInputNode = ASEditableTextNode() + let textInputNode = EditableTextNode() var textColor: UIColor = .black var tintColor: UIColor = .blue var baseFontSize: CGFloat = 17.0 diff --git a/TelegramUI/CheckDeviceAccess.swift b/TelegramUI/CheckDeviceAccess.swift index 963cd8bcb2..0f14b0c81f 100644 --- a/TelegramUI/CheckDeviceAccess.swift +++ b/TelegramUI/CheckDeviceAccess.swift @@ -69,11 +69,11 @@ public final class DeviceAccess { UNUserNotificationCenter.current().getNotificationSettings(completionHandler: { settings in switch settings.authorizationStatus { case .authorized: - if settings.alertSetting == .disabled || settings.soundSetting == .disabled || settings.badgeSetting == .disabled || settings.notificationCenterSetting == .disabled || settings.lockScreenSetting == .disabled { - subscriber.putNext(.unreachable) - } else { +// if settings.alertSetting == .disabled || settings.soundSetting == .disabled || settings.badgeSetting == .disabled || settings.notificationCenterSetting == .disabled || settings.lockScreenSetting == .disabled { +// subscriber.putNext(.unreachable) +// } else { subscriber.putNext(.allowed) - } +// } case .denied: subscriber.putNext(.denied) case .notDetermined: diff --git a/TelegramUI/ContactListNode.swift b/TelegramUI/ContactListNode.swift index 5a9415556e..9d3b3b921f 100644 --- a/TelegramUI/ContactListNode.swift +++ b/TelegramUI/ContactListNode.swift @@ -160,7 +160,7 @@ private enum ContactListNodeEntry: Comparable, Identifiable { interaction.activateSearch() }) case let .permissionInfo(theme, strings): - return PermissionInfoItem(theme: theme, strings: strings, subject: .contacts, type: .denied) + return PermissionInfoItem(theme: theme, strings: strings, subject: .contacts, type: .denied, style: .plain) case let .permissionEnable(theme, text): return ContactListActionItem(theme: theme, title: text, icon: nil, header: nil, action: { interaction.authorize() diff --git a/TelegramUI/ContactsPeerItem.swift b/TelegramUI/ContactsPeerItem.swift index f1e4acff77..c2a820b9c3 100644 --- a/TelegramUI/ContactsPeerItem.swift +++ b/TelegramUI/ContactsPeerItem.swift @@ -712,8 +712,8 @@ class ContactsPeerItemNode: ItemListRevealOptionsItemNode { if item.editing.editable { - strongSelf.setRevealOptions((left: [], right: [ItemListRevealOption(key: 0, title: item.strings.Common_Delete, icon: .none, color: item.theme.list.itemDisclosureActions.destructive.fillColor, textColor: item.theme.list.itemDisclosureActions.destructive.foregroundColor)])) - strongSelf.setRevealOptionsOpened(item.editing.revealed, animated: animated) + strongSelf.setRevealOptions((left: [], right: [ItemListRevealOption(key: 0, title: item.strings.Common_Delete, icon: .none, color: item.theme.list.itemDisclosureActions.destructive.fillColor, textColor: item.theme.list.itemDisclosureActions.destructive.foregroundColor)])) + strongSelf.setRevealOptionsOpened(item.editing.revealed, animated: animated) } else { strongSelf.setRevealOptions((left: [], right: [])) } diff --git a/TelegramUI/CreateChannelController.swift b/TelegramUI/CreateChannelController.swift index 7026163360..2a58690df4 100644 --- a/TelegramUI/CreateChannelController.swift +++ b/TelegramUI/CreateChannelController.swift @@ -283,7 +283,7 @@ public func createChannelController(account: Account) -> ViewController { uploadedAvatar.set(uploadedPeerPhoto(postbox: account.postbox, network: account.network, resource: resource)) updateState { current in var current = current - current.avatar = .image(representation) + current.avatar = .image(representation, false) return current } } diff --git a/TelegramUI/CreateGroupController.swift b/TelegramUI/CreateGroupController.swift index 90468943cc..04749ec964 100644 --- a/TelegramUI/CreateGroupController.swift +++ b/TelegramUI/CreateGroupController.swift @@ -299,7 +299,7 @@ public func createGroupController(account: Account, peerIds: [PeerId]) -> ViewCo uploadedAvatar.set(uploadedPeerPhoto(postbox: account.postbox, network: account.network, resource: resource)) updateState { current in var current = current - current.avatar = .image(representation) + current.avatar = .image(representation, false) return current } } diff --git a/TelegramUI/DateSelectionActionSheetController.swift b/TelegramUI/DateSelectionActionSheetController.swift index f4c5245755..2fc588dc45 100644 --- a/TelegramUI/DateSelectionActionSheetController.swift +++ b/TelegramUI/DateSelectionActionSheetController.swift @@ -3,23 +3,30 @@ import Display import AsyncDisplayKit import UIKit import SwiftSignalKit +import TelegramCore import Photos final class DateSelectionActionSheetController: ActionSheetController { - private let theme: PresentationTheme - private let strings: PresentationStrings + private var presentationDisposable: Disposable? private let _ready = Promise() override var ready: Promise { return self._ready } - init(theme: PresentationTheme, strings: PresentationStrings, title: String?, currentValue: Int32, minimumDate: Date? = nil, maximumDate: Date? = nil, emptyTitle: String? = nil, applyValue: @escaping (Int32?) -> Void) { - self.theme = theme - self.strings = strings + init(account: Account, title: String?, currentValue: Int32, minimumDate: Date? = nil, maximumDate: Date? = nil, emptyTitle: String? = nil, applyValue: @escaping (Int32?) -> Void) { + let presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 } + let theme = presentationData.theme + let strings = presentationData.strings super.init(theme: ActionSheetControllerTheme(presentationTheme: theme)) + self.presentationDisposable = account.telegramApplicationContext.presentationData.start(next: { [weak self] presentationData in + if let strongSelf = self { + strongSelf.theme = ActionSheetControllerTheme(presentationTheme: presentationData.theme) + } + }) + self._ready.set(.single(true)) var updatedValue = currentValue @@ -53,6 +60,10 @@ final class DateSelectionActionSheetController: ActionSheetController { required init(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } + + deinit { + self.presentationDisposable?.dispose() + } } private final class DateSelectionActionSheetItem: ActionSheetItem { diff --git a/TelegramUI/DeclareEncodables.swift b/TelegramUI/DeclareEncodables.swift index 7f67a1b63e..097f18315f 100644 --- a/TelegramUI/DeclareEncodables.swift +++ b/TelegramUI/DeclareEncodables.swift @@ -30,6 +30,7 @@ private var telegramUIDeclaredEncodables: Void = { declareEncodable(InstantPageStoredDetailsState.self, f: { InstantPageStoredDetailsState(decoder: $0) }) declareEncodable(WatchPresetSettings.self, f: { WatchPresetSettings(decoder: $0) }) declareEncodable(WebSearchSettings.self, f: { WebSearchSettings(decoder: $0) }) + declareEncodable(RecentWebSearchQueryItem.self, f: { RecentWebSearchQueryItem(decoder: $0) }) return }() diff --git a/TelegramUI/DefaultDarkAccentPresentationTheme.swift b/TelegramUI/DefaultDarkAccentPresentationTheme.swift index 956642ac46..45ce8c0257 100644 --- a/TelegramUI/DefaultDarkAccentPresentationTheme.swift +++ b/TelegramUI/DefaultDarkAccentPresentationTheme.swift @@ -192,7 +192,7 @@ private let bubble = PresentationThemeChatBubble( ) private let serviceMessage = PresentationThemeServiceMessage( - components: PresentationThemeServiceMessageColor(withDefaultWallpaper: PresentationThemeServiceMessageColorComponents(fill: UIColor(rgb: 0x18222D, alpha: 1.0), primaryText: UIColor(rgb: 0xffffff), linkHighlight: UIColor(rgb: 0xffffff, alpha: 0.12), dateFillStatic: UIColor(rgb: 0x18222D, alpha: 1.0), dateFillFloating: UIColor(rgb: 0x18222D, alpha: 0.2)), withCustomWallpaper: PresentationThemeServiceMessageColorComponents(fill: UIColor(rgb: 0x18222D, alpha: 1.0), primaryText: UIColor(rgb: 0xffffff), linkHighlight: UIColor(rgb: 0xffffff, alpha: 0.12), dateFillStatic: UIColor(rgb: 0x18222D, alpha: 1.0), dateFillFloating: UIColor(rgb: 0x18222D, alpha: 0.2))), + components: PresentationThemeServiceMessageColor(withDefaultWallpaper: PresentationThemeServiceMessageColorComponents(fill: UIColor(rgb: 0x18222D, alpha: 1.0), primaryText: .white, linkHighlight: UIColor(rgb: 0xffffff, alpha: 0.12), dateFillStatic: UIColor(rgb: 0x18222D, alpha: 1.0), dateFillFloating: UIColor(rgb: 0x18222D, alpha: 0.2)), withCustomWallpaper: PresentationThemeServiceMessageColorComponents(fill: UIColor(rgb: 0x18222D, alpha: 1.0), primaryText: .white, linkHighlight: UIColor(rgb: 0xffffff, alpha: 0.12), dateFillStatic: UIColor(rgb: 0x18222D, alpha: 1.0), dateFillFloating: UIColor(rgb: 0x18222D, alpha: 0.2))), unreadBarFillColor: UIColor(rgb: 0x213040), unreadBarStrokeColor: UIColor(rgb: 0x213040), unreadBarTextColor: UIColor(rgb: 0xffffff), @@ -231,7 +231,7 @@ private let inputPanel = PresentationThemeChatInputPanel( ) private let inputMediaPanel = PresentationThemeInputMediaPanel( - panelSerapatorColor: UIColor(rgb: 0x213040), + panelSeparatorColor: UIColor(rgb: 0x213040), panelIconColor: UIColor(rgb: 0xDBF5FF, alpha: 0.5), panelHighlightedIconBackgroundColor: UIColor(rgb: 0x131C26), //!!! stickersBackgroundColor: UIColor(rgb: 0x18222d), @@ -244,7 +244,7 @@ private let inputMediaPanel = PresentationThemeInputMediaPanel( ) private let inputButtonPanel = PresentationThemeInputButtonPanel( - panelSerapatorColor: UIColor(rgb: 0x213040), + panelSeparatorColor: UIColor(rgb: 0x213040), panelBackgroundColor: UIColor(rgb: 0x161A20), buttonFillColor: UIColor(rgb: 0x5B5F62), buttonStrokeColor: UIColor(rgb: 0x0D1013), diff --git a/TelegramUI/DefaultDarkPresentationTheme.swift b/TelegramUI/DefaultDarkPresentationTheme.swift index c7abcd6a0e..227c00415e 100644 --- a/TelegramUI/DefaultDarkPresentationTheme.swift +++ b/TelegramUI/DefaultDarkPresentationTheme.swift @@ -192,7 +192,7 @@ private let bubble = PresentationThemeChatBubble( ) private let serviceMessage = PresentationThemeServiceMessage( - components: PresentationThemeServiceMessageColor(withDefaultWallpaper: PresentationThemeServiceMessageColorComponents(fill: UIColor(rgb: 0x1f1f1f, alpha: 1.0), primaryText: UIColor(rgb: 0xffffff), linkHighlight: UIColor(rgb: 0xffffff, alpha: 0.12), dateFillStatic: UIColor(rgb: 0x1f1f1f, alpha: 1.0), dateFillFloating: UIColor(rgb: 0xffffff, alpha: 0.2)), withCustomWallpaper: PresentationThemeServiceMessageColorComponents(fill: UIColor(rgb: 0x1f1f1f, alpha: 1.0), primaryText: UIColor(rgb: 0xffffff), linkHighlight: UIColor(rgb: 0xffffff, alpha: 0.12), dateFillStatic: UIColor(rgb: 0x1f1f1f, alpha: 1.0), dateFillFloating: UIColor(rgb: 0xffffff, alpha: 0.2))), + components: PresentationThemeServiceMessageColor(withDefaultWallpaper: PresentationThemeServiceMessageColorComponents(fill: UIColor(rgb: 0x1f1f1f, alpha: 1.0), primaryText: UIColor(rgb: 0xffffff), linkHighlight: UIColor(rgb: 0xffffff, alpha: 0.12), dateFillStatic: UIColor(rgb: 0x1f1f1f, alpha: 1.0), dateFillFloating: UIColor(rgb: 0xffffff, alpha: 0.2)), withCustomWallpaper: PresentationThemeServiceMessageColorComponents(fill: UIColor(rgb: 0x1f1f1f, alpha: 1.0), primaryText: .white, linkHighlight: UIColor(rgb: 0xffffff, alpha: 0.12), dateFillStatic: UIColor(rgb: 0x1f1f1f, alpha: 1.0), dateFillFloating: UIColor(rgb: 0xffffff, alpha: 0.2))), unreadBarFillColor: UIColor(rgb: 0x1b1b1b), //!!! unreadBarStrokeColor: UIColor(rgb: 0x000000), unreadBarTextColor: UIColor(rgb: 0xb2b2b2), //!!! @@ -231,7 +231,7 @@ private let inputPanel = PresentationThemeChatInputPanel( ) private let inputMediaPanel = PresentationThemeInputMediaPanel( - panelSerapatorColor: UIColor(rgb: 0x000000), + panelSeparatorColor: UIColor(rgb: 0x000000), panelIconColor: UIColor(rgb: 0x808080), panelHighlightedIconBackgroundColor: UIColor(rgb: 0x000000), //!!! stickersBackgroundColor: UIColor(rgb: 0x000000), @@ -244,7 +244,7 @@ private let inputMediaPanel = PresentationThemeInputMediaPanel( ) private let inputButtonPanel = PresentationThemeInputButtonPanel( - panelSerapatorColor: UIColor(rgb: 0x000000), + panelSeparatorColor: UIColor(rgb: 0x000000), panelBackgroundColor: UIColor(rgb: 0x141414), buttonFillColor: UIColor(rgb: 0x5A5A5A), buttonStrokeColor: UIColor(rgb: 0x0C0C0C), diff --git a/TelegramUI/DefaultPresentationTheme.swift b/TelegramUI/DefaultPresentationTheme.swift index 3ba7b0faa7..cef75c74ba 100644 --- a/TelegramUI/DefaultPresentationTheme.swift +++ b/TelegramUI/DefaultPresentationTheme.swift @@ -260,10 +260,10 @@ private func makeDefaultPresentationTheme(accentColor: UIColor, day: Bool) -> Pr shareButtonForegroundColor: accentColor, mediaOverlayControlBackgroundColor: UIColor(rgb: 0x000000, alpha: 0.6), mediaOverlayControlForegroundColor: UIColor(rgb: 0xffffff, alpha: 1.0), - actionButtonsIncomingFillColor: PresentationThemeVariableColor(withWallpaper: UIColor(rgb: 0xffffff, alpha: 0.5), withoutWallpaper: UIColor(rgb: 0xffffff, alpha: 0.5)), + actionButtonsIncomingFillColor: PresentationThemeVariableColor(withWallpaper: UIColor(rgb: 0xffffff, alpha: 0.8), withoutWallpaper: UIColor(rgb: 0xffffff, alpha: 0.8)), actionButtonsIncomingStrokeColor: UIColor(rgb: 0x3996ee), actionButtonsIncomingTextColor: UIColor(rgb: 0x3996ee), - actionButtonsOutgoingFillColor: PresentationThemeVariableColor(withWallpaper: UIColor(rgb: 0xffffff, alpha: 0.5), withoutWallpaper: UIColor(rgb: 0xffffff, alpha: 0.5)), + actionButtonsOutgoingFillColor: PresentationThemeVariableColor(withWallpaper: UIColor(rgb: 0xffffff, alpha: 0.8), withoutWallpaper: UIColor(rgb: 0xffffff, alpha: 0.8)), actionButtonsOutgoingStrokeColor: UIColor(rgb: 0x3996ee), actionButtonsOutgoingTextColor: UIColor(rgb: 0x3996ee), selectionControlBorderColor: UIColor(rgb: 0xC7C7CC), @@ -324,7 +324,7 @@ private func makeDefaultPresentationTheme(accentColor: UIColor, day: Bool) -> Pr ) let inputMediaPanel = PresentationThemeInputMediaPanel( - panelSerapatorColor: UIColor(rgb: 0xBEC2C6), + panelSeparatorColor: UIColor(rgb: 0xBEC2C6), panelIconColor: UIColor(rgb: 0x858e99), panelHighlightedIconBackgroundColor: UIColor(rgb: 0x858e99, alpha: 0.2), stickersBackgroundColor: UIColor(rgb: 0xe8ebf0), @@ -337,7 +337,7 @@ private func makeDefaultPresentationTheme(accentColor: UIColor, day: Bool) -> Pr ) let inputButtonPanel = PresentationThemeInputButtonPanel( - panelSerapatorColor: UIColor(rgb: 0xBEC2C6), + panelSeparatorColor: UIColor(rgb: 0xBEC2C6), panelBackgroundColor: UIColor(rgb: 0xdee2e6), buttonFillColor: .white, buttonStrokeColor: UIColor(rgb: 0xc3c7c9), diff --git a/TelegramUI/DeviceContactData.swift b/TelegramUI/DeviceContactData.swift index 2f4fd0286b..8e5349eb4e 100644 --- a/TelegramUI/DeviceContactData.swift +++ b/TelegramUI/DeviceContactData.swift @@ -434,3 +434,25 @@ extension DeviceContactExtendedData { self.init(basicData: DeviceContactBasicData(firstName: user.firstName ?? "", lastName: user.lastName ?? "", phoneNumbers: [DeviceContactPhoneNumberData(label: "_$!!$_", value: phone)]), middleName: "", prefix: "", suffix: "", organization: "", jobTitle: "", department: "", emailAddresses: [], urls: [], addresses: [], birthdayDate: nil, socialProfiles: [], instantMessagingProfiles: []) } } + +extension DeviceContactAddressData { + public var dictionary: [String: String] { + var dictionary: [String: String] = [:] + if !self.street1.isEmpty { + dictionary["Street"] = self.street1 + } + if !self.city.isEmpty { + dictionary["City"] = self.city + } + if !self.state.isEmpty { + dictionary["State"] = self.state + } + if !self.country.isEmpty { + dictionary["Country"] = self.country + } + if !self.postcode.isEmpty { + dictionary["ZIP"] = self.postcode + } + return dictionary + } +} diff --git a/TelegramUI/DeviceContactInfoController.swift b/TelegramUI/DeviceContactInfoController.swift index 518b6b1bf8..d1fedfb796 100644 --- a/TelegramUI/DeviceContactInfoController.swift +++ b/TelegramUI/DeviceContactInfoController.swift @@ -597,7 +597,17 @@ private func deviceContactInfoEntries(account: Account, presentationData: Presen var addressIndex = 0 for address in contactData.addresses { - entries.append(.address(entries.count, addressIndex, presentationData.theme, localizedGenericContactFieldLabel(label: address.label, strings: presentationData.strings), address, nil, selecting ? !state.excludedComponents.contains(.address(address)) : nil)) + let signal = geocodeLocation(dictionary: address.dictionary) + |> mapToSignal { coordinates -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> in + if let (latitude, longitude) = coordinates { + let resource = MapSnapshotMediaResource(latitude: latitude, longitude: longitude, width: 90, height: 90) + return chatMapSnapshotImage(account: account, resource: resource) + } else { + return .single({ _ in return nil }) + } + } + + entries.append(.address(entries.count, addressIndex, presentationData.theme, localizedGenericContactFieldLabel(label: address.label, strings: presentationData.strings), address, signal, selecting ? !state.excludedComponents.contains(.address(address)) : nil)) addressIndex += 1 } diff --git a/TelegramUI/EditSettingsController.swift b/TelegramUI/EditSettingsController.swift index cbb9249a4b..96c8c7c1b4 100644 --- a/TelegramUI/EditSettingsController.swift +++ b/TelegramUI/EditSettingsController.swift @@ -453,7 +453,7 @@ func editSettingsController(account: Account, currentName: ItemListAvatarAndName account.postbox.mediaBox.storeResourceData(resource.id, data: data) let representation = TelegramMediaImageRepresentation(dimensions: CGSize(width: 640.0, height: 640.0), resource: resource) updateState { - $0.withUpdatedUpdatingAvatar(.image(representation)) + $0.withUpdatedUpdatingAvatar(.image(representation, true)) } updateAvatarDisposable.set((updateAccountPhoto(account: account, resource: resource) |> deliverOnMainQueue).start(next: { result in switch result { @@ -472,7 +472,7 @@ func editSettingsController(account: Account, currentName: ItemListAvatarAndName let _ = currentAvatarMixin.swap(nil) updateState { if let profileImage = peer?.smallProfileImage { - return $0.withUpdatedUpdatingAvatar(.image(profileImage)) + return $0.withUpdatedUpdatingAvatar(.image(profileImage, false)) } else { return $0.withUpdatedUpdatingAvatar(.none) } diff --git a/TelegramUI/FormBlockItemNode.swift b/TelegramUI/FormBlockItemNode.swift index 053621d035..0213ba24e0 100644 --- a/TelegramUI/FormBlockItemNode.swift +++ b/TelegramUI/FormBlockItemNode.swift @@ -109,5 +109,12 @@ class FormBlockItemNode: ASDisplayNode, FormController func selected() { } + + var preventsTouchesToOtherItems: Bool { + return false + } + + func touchesToOtherItemsPrevented() { + } } diff --git a/TelegramUI/FormControllerHeaderItem.swift b/TelegramUI/FormControllerHeaderItem.swift index c14125d5f5..e92ab5a5a5 100644 --- a/TelegramUI/FormControllerHeaderItem.swift +++ b/TelegramUI/FormControllerHeaderItem.swift @@ -58,4 +58,11 @@ final class FormControllerHeaderItemNode: ASDisplayNode, FormControllerItemNode return height } + + var preventsTouchesToOtherItems: Bool { + return false + } + + func touchesToOtherItemsPrevented() { + } } diff --git a/TelegramUI/FormControllerItem.swift b/TelegramUI/FormControllerItem.swift index 9c658319bf..9bc59e4218 100644 --- a/TelegramUI/FormControllerItem.swift +++ b/TelegramUI/FormControllerItem.swift @@ -29,4 +29,6 @@ protocol FormControllerItem { } protocol FormControllerItemNode { + var preventsTouchesToOtherItems: Bool { get } + func touchesToOtherItemsPrevented() } diff --git a/TelegramUI/FormControllerNode.swift b/TelegramUI/FormControllerNode.swift index 7b4967b32b..9ca9767504 100644 --- a/TelegramUI/FormControllerNode.swift +++ b/TelegramUI/FormControllerNode.swift @@ -141,8 +141,35 @@ class FormControllerNode: View self.scrollNode.backgroundColor = nil self.scrollNode.isOpaque = false self.scrollNode.delegate = self - self.addSubnode(self.scrollNode) + + self.scrollNode.view.delaysContentTouches = true + self.scrollNode.touchesPrevented = { [weak self] position in + guard let strongSelf = self else { + return false + } + + for i in 0 ..< strongSelf.itemNodes.count { + if strongSelf.itemNodes[i].preventsTouchesToOtherItems { + if let node = strongSelf.itemNodeAtPoint(position), node === strongSelf.itemNodes[i] { + return false + } + strongSelf.itemNodes[i].touchesToOtherItemsPrevented() + return true + } + } + + return false + } + } + + func itemNodeAtPoint(_ point: CGPoint) -> (ASDisplayNode & FormControllerItemNode)? { + for node in self.itemNodes { + if node.frame.contains(point) { + return node + } + } + return nil } func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationHeight: CGFloat, transition: ContainedViewLayoutTransition) { @@ -376,4 +403,8 @@ class FormControllerNode: View self.stateUpdated(state: FormControllerState(layoutState: layoutState, presentationState: self.internalState.presentationState, innerState: innerState), transition: transition) } } + + func scrollToItemNode(_ itemNode: ASDisplayNode & FormControllerItemNode) { + self.scrollNode.view.contentOffset = CGPoint(x: 0.0, y: max(0.0, min(itemNode.frame.minY, self.scrollNode.view.contentSize.height - self.scrollNode.view.frame.height))) + } } diff --git a/TelegramUI/FormControllerScrollerNode.swift b/TelegramUI/FormControllerScrollerNode.swift index bcb09efa89..22f616dfc4 100644 --- a/TelegramUI/FormControllerScrollerNode.swift +++ b/TelegramUI/FormControllerScrollerNode.swift @@ -2,6 +2,7 @@ import Foundation import AsyncDisplayKit final class FormControllerScrollerNodeView: UIScrollView { + weak var target: FormControllerScrollerNode? var ignoreUpdateBounds = false override init(frame: CGRect) { @@ -32,6 +33,16 @@ final class FormControllerScrollerNodeView: UIScrollView { override func scrollRectToVisible(_ rect: CGRect, animated: Bool) { } + + override func touchesShouldBegin(_ touches: Set, with event: UIEvent?, in view: UIView) -> Bool { + return self.target?.touchesShouldBegin(touches, with: event) ?? super.touchesShouldBegin(touches, with: event, in: view) + } + + override func touchesBegan(_ touches: Set, with event: UIEvent?) { + if self.target?.touchesShouldBegin(touches, with: event) ?? true { + super.touchesBegan(touches, with: event) + } + } } final class FormControllerScrollerNode: ASDisplayNode, UIScrollViewDelegate { @@ -41,12 +52,15 @@ final class FormControllerScrollerNode: ASDisplayNode, UIScrollViewDelegate { weak var delegate: UIScrollViewDelegate? + var touchesPrevented: ((CGPoint) -> Bool)? + override init() { super.init() self.setViewBlock({ return FormControllerScrollerNodeView(frame: CGRect()) }) + self.view.target = self } override func didLoad() { @@ -57,4 +71,16 @@ final class FormControllerScrollerNode: ASDisplayNode, UIScrollViewDelegate { func scrollViewDidScroll(_ scrollView: UIScrollView) { self.delegate?.scrollViewDidScroll?(scrollView) } + + func touchesShouldBegin(_ touches: Set, with event: UIEvent?) -> Bool { + let touchesPosition = touches.first!.location(in: self.view) + + if let touchesPrevented = self.touchesPrevented { + if touchesPrevented(touchesPosition) { + return false + } + } + + return true + } } diff --git a/TelegramUI/FormControllerTextItem.swift b/TelegramUI/FormControllerTextItem.swift index ce21fc4546..7160a8e6f5 100644 --- a/TelegramUI/FormControllerTextItem.swift +++ b/TelegramUI/FormControllerTextItem.swift @@ -62,4 +62,11 @@ final class FormControllerTextItemNode: ASDisplayNode, FormControllerItemNode { return textSize.height + 14.0 } + + var preventsTouchesToOtherItems: Bool { + return false + } + + func touchesToOtherItemsPrevented() { + } } diff --git a/TelegramUI/FormEditableBlockItemNode.swift b/TelegramUI/FormEditableBlockItemNode.swift new file mode 100644 index 0000000000..82695e9521 --- /dev/null +++ b/TelegramUI/FormEditableBlockItemNode.swift @@ -0,0 +1,472 @@ +import Foundation +import AsyncDisplayKit +import Display + +class FormEditableBlockItemNode: ASDisplayNode, FormControllerItemNode, FormBlockItemNodeProto, UIGestureRecognizerDelegate { + private let topSeparatorInset: FormBlockItemInset + + private let highlightedBackgroundNode: ASDisplayNode + let backgroundNode: ASDisplayNode + private let topSeparatorNode: ASDisplayNode + private let bottomSeparatorNode: ASDisplayNode + private let selectionButtonNode: HighlightTrackingButtonNode + + private var leftRevealNode: ItemListRevealOptionsNode? + private var rightRevealNode: ItemListRevealOptionsNode? + private var revealOptions: (left: [ItemListRevealOption], right: [ItemListRevealOption]) = ([], []) + + private var initialRevealOffset: CGFloat = 0.0 + private(set) var revealOffset: CGFloat = 0.0 + + private var recognizer: ItemListRevealOptionsGestureRecognizer? + private var hapticFeedback: HapticFeedback? + + private var allowAnyDirection = false + + private var validLayout: (CGSize, CGFloat, CGFloat)? + + var isDisplayingRevealedOptions: Bool { + return !self.revealOffset.isZero + } + + var canBeSelected: Bool { + return !self.isDisplayingRevealedOptions + } + + init(selectable: Bool, topSeparatorInset: FormBlockItemInset) { + self.topSeparatorInset = topSeparatorInset + + self.backgroundNode = ASDisplayNode() + self.backgroundNode.isLayerBacked = true + self.topSeparatorNode = ASDisplayNode() + self.topSeparatorNode.isLayerBacked = true + self.bottomSeparatorNode = ASDisplayNode() + self.bottomSeparatorNode.isLayerBacked = true + self.highlightedBackgroundNode = ASDisplayNode() + self.highlightedBackgroundNode.isLayerBacked = true + self.highlightedBackgroundNode.alpha = 0.0 + + self.selectionButtonNode = HighlightTrackingButtonNode() + + super.init() + + self.addSubnode(self.backgroundNode) + self.addSubnode(self.topSeparatorNode) + self.addSubnode(self.bottomSeparatorNode) + self.addSubnode(self.highlightedBackgroundNode) + + if selectable { + self.selectionButtonNode.highligthedChanged = { [weak self] highlighted in + if let strongSelf = self, strongSelf.canBeSelected { + if highlighted { + strongSelf.highlightedBackgroundNode.layer.removeAnimation(forKey: "opacity") + strongSelf.highlightedBackgroundNode.alpha = 1.0 + } else { + strongSelf.highlightedBackgroundNode.alpha = 0.0 + strongSelf.highlightedBackgroundNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3) + } + } + } + self.addSubnode(self.selectionButtonNode) + self.selectionButtonNode.addTarget(self, action: #selector(self.selectionButtonPressed), forControlEvents: .touchUpInside) + } + + let recognizer = ItemListRevealOptionsGestureRecognizer(target: self, action: #selector(self.revealGesture(_:))) + self.recognizer = recognizer + recognizer.allowAnyDirection = self.allowAnyDirection + self.view.addGestureRecognizer(recognizer) + } + + func setRevealOptions(_ options: (left: [ItemListRevealOption], right: [ItemListRevealOption])) { + if self.revealOptions == options { + return + } + let previousOptions = self.revealOptions + let wasEmpty = self.revealOptions.left.isEmpty && self.revealOptions.right.isEmpty + self.revealOptions = options + let isEmpty = options.left.isEmpty && options.right.isEmpty + if options.left.isEmpty { + if let _ = self.leftRevealNode { + self.recognizer?.becomeCancelled() + self.updateRevealOffsetInternal(offset: 0.0, transition: .animated(duration: 0.3, curve: .spring)) + } + } else if previousOptions.left != options.left { + /*if let _ = self.leftRevealNode { + self.revealOptionsInteractivelyClosed() + self.recognizer?.becomeCancelled() + self.updateRevealOffsetInternal(offset: 0.0, transition: .animated(duration: 0.3, curve: .spring)) + }*/ + } + if options.right.isEmpty { + if let _ = self.rightRevealNode { + self.recognizer?.becomeCancelled() + self.updateRevealOffsetInternal(offset: 0.0, transition: .animated(duration: 0.3, curve: .spring)) + } + } else if previousOptions.right != options.right { + if let _ = self.rightRevealNode { + /*self.revealOptionsInteractivelyClosed() + self.recognizer?.becomeCancelled() + self.updateRevealOffsetInternal(offset: 0.0, transition: .animated(duration: 0.3, curve: .spring))*/ + } + } + if wasEmpty != isEmpty { + self.recognizer?.isEnabled = !isEmpty + } + let allowAnyDirection = !options.left.isEmpty || !self.revealOffset.isZero + if allowAnyDirection != self.allowAnyDirection { + self.allowAnyDirection = allowAnyDirection + self.recognizer?.allowAnyDirection = allowAnyDirection + if self.isNodeLoaded { + self.view.disablesInteractiveTransitionGestureRecognizer = allowAnyDirection + } + } + } + + override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { + return true + } + + func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { + if let recognizer = self.recognizer, otherGestureRecognizer == recognizer { + return true + } else { + return false + } + } + + @objc func revealGesture(_ recognizer: ItemListRevealOptionsGestureRecognizer) { + guard let (size, _, _) = self.validLayout else { + return + } + switch recognizer.state { + case .began: + if let leftRevealNode = self.leftRevealNode { + let revealSize = leftRevealNode.bounds.size + let location = recognizer.location(in: self.view) + if location.x < revealSize.width { + recognizer.becomeCancelled() + } else { + self.initialRevealOffset = self.revealOffset + } + } else if let rightRevealNode = self.rightRevealNode { + let revealSize = rightRevealNode.bounds.size + let location = recognizer.location(in: self.view) + if location.x > size.width - revealSize.width { + recognizer.becomeCancelled() + } else { + self.initialRevealOffset = self.revealOffset + } + } else { + if self.revealOptions.left.isEmpty && self.revealOptions.right.isEmpty { + recognizer.becomeCancelled() + } + self.initialRevealOffset = self.revealOffset + } + case .changed: + var translation = recognizer.translation(in: self.view) + translation.x += self.initialRevealOffset + if self.revealOptions.left.isEmpty { + translation.x = min(0.0, translation.x) + } + if self.leftRevealNode == nil && CGFloat(0.0).isLess(than: translation.x) { + self.setupAndAddLeftRevealNode() + self.revealOptionsInteractivelyOpened() + } else if self.rightRevealNode == nil && translation.x.isLess(than: 0.0) { + self.setupAndAddRightRevealNode() + self.revealOptionsInteractivelyOpened() + } + self.updateRevealOffsetInternal(offset: translation.x, transition: .immediate) + if self.leftRevealNode == nil && self.rightRevealNode == nil { + self.revealOptionsInteractivelyClosed() + } + case .ended, .cancelled: + guard let recognizer = self.recognizer else { + break + } + + if let leftRevealNode = self.leftRevealNode { + let velocity = recognizer.velocity(in: self.view) + let revealSize = leftRevealNode.bounds.size + var reveal = false + if abs(velocity.x) < 100.0 { + if self.initialRevealOffset.isZero && self.revealOffset > 0.0 { + reveal = true + } else if self.revealOffset > revealSize.width { + reveal = true + } else { + reveal = false + } + } else { + if velocity.x > 0.0 { + reveal = true + } else { + reveal = false + } + } + + var selectedOption: ItemListRevealOption? + if reveal && leftRevealNode.isDisplayingExtendedAction() { + reveal = false + selectedOption = self.revealOptions.left.first + } else { + self.updateRevealOffsetInternal(offset: reveal ?revealSize.width : 0.0, transition: .animated(duration: 0.3, curve: .spring)) + } + + if let selectedOption = selectedOption { + self.revealOptionSelected(selectedOption, animated: true) + } else { + if !reveal { + self.revealOptionsInteractivelyClosed() + } + } + } else if let rightRevealNode = self.rightRevealNode { + let velocity = recognizer.velocity(in: self.view) + let revealSize = rightRevealNode.bounds.size + var reveal = false + if abs(velocity.x) < 100.0 { + if self.initialRevealOffset.isZero && self.revealOffset < 0.0 { + reveal = true + } else if self.revealOffset < -revealSize.width { + reveal = true + } else { + reveal = false + } + } else { + if velocity.x < 0.0 { + reveal = true + } else { + reveal = false + } + } + self.updateRevealOffsetInternal(offset: reveal ? -revealSize.width : 0.0, transition: .animated(duration: 0.3, curve: .spring)) + if !reveal { + self.revealOptionsInteractivelyClosed() + } + } + default: + break + } + } + + private func setupAndAddLeftRevealNode() { + if !self.revealOptions.left.isEmpty { + let revealNode = ItemListRevealOptionsNode(optionSelected: { [weak self] option in + self?.revealOptionSelected(option, animated: false) + }, tapticAction: { [weak self] in + self?.hapticImpact() + }) + revealNode.setOptions(self.revealOptions.left) + self.leftRevealNode = revealNode + + if let (size, leftInset, _) = self.validLayout { + var revealSize = revealNode.measure(CGSize(width: CGFloat.greatestFiniteMagnitude, height: size.height)) + revealSize.width += leftInset + + revealNode.frame = CGRect(origin: CGPoint(x: min(self.revealOffset - revealSize.width, 0.0), y: 0.0), size: revealSize) + revealNode.updateRevealOffset(offset: 0.0, sideInset: leftInset, transition: .immediate) + } + + self.addSubnode(revealNode) + } + } + + private func setupAndAddRightRevealNode() { + if !self.revealOptions.right.isEmpty { + let revealNode = ItemListRevealOptionsNode(optionSelected: { [weak self] option in + self?.revealOptionSelected(option, animated: false) + }, tapticAction: { [weak self] in + self?.hapticImpact() + }) + revealNode.setOptions(self.revealOptions.right) + self.rightRevealNode = revealNode + + if let (size, _, rightInset) = self.validLayout { + var revealSize = revealNode.measure(CGSize(width: CGFloat.greatestFiniteMagnitude, height: size.height)) + revealSize.width += rightInset + + revealNode.frame = CGRect(origin: CGPoint(x: size.width + max(self.revealOffset, -revealSize.width), y: 0.0), size: revealSize) + revealNode.updateRevealOffset(offset: 0.0, sideInset: -rightInset, transition: .immediate) + } + + self.addSubnode(revealNode) + } + } + + final func updateInternal(item: Item, theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, width: CGFloat, previousNeighbor: FormControllerItemNeighbor, nextNeighbor: FormControllerItemNeighbor, transition: ContainedViewLayoutTransition) -> (FormControllerItemPreLayout, (FormControllerItemLayoutParams) -> CGFloat) { + let (preLayout, apply) = self.update(item: item, theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, width: width, previousNeighbor: previousNeighbor, nextNeighbor: nextNeighbor, transition: transition) + return (preLayout, { params in + self.backgroundNode.backgroundColor = theme.list.itemBlocksBackgroundColor + self.topSeparatorNode.backgroundColor = theme.list.itemBlocksSeparatorColor + self.bottomSeparatorNode.backgroundColor = theme.list.itemBlocksSeparatorColor + self.highlightedBackgroundNode.backgroundColor = theme.list.itemHighlightedBackgroundColor + + let height = apply(params) + + let topSeparatorInset: CGFloat + switch previousNeighbor { + case let .item(item) where item is FormBlockItemNodeProto: + switch self.topSeparatorInset { + case .regular: + topSeparatorInset = 16.0 + case let .custom(value): + topSeparatorInset = value + } + default: + topSeparatorInset = 0.0 + } + + switch nextNeighbor { + case let .item(item) where item is FormBlockItemNodeProto: + self.bottomSeparatorNode.isHidden = true + default: + self.bottomSeparatorNode.isHidden = false + } + + if let leftRevealNode = self.leftRevealNode { + let revealSize = leftRevealNode.measure(CGSize(width: CGFloat.greatestFiniteMagnitude, height: height)) + //revealSize.width += leftInset + leftRevealNode.frame = CGRect(origin: CGPoint(x: min(self.revealOffset - revealSize.width, 0.0), y: 0.0), size: revealSize) + } + + if let rightRevealNode = self.rightRevealNode { + let revealSize = rightRevealNode.measure(CGSize(width: CGFloat.greatestFiniteMagnitude, height: height)) + //revealSize.width += rightInset + rightRevealNode.frame = CGRect(origin: CGPoint(x: width + max(self.revealOffset, -revealSize.width), y: 0.0), size: revealSize) + } + + self.validLayout = (CGSize(width: width, height: height), 0.0, 0.0) + + transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: width, height: height))) + transition.updateFrame(node: self.highlightedBackgroundNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: width, height: height))) + transition.updateFrame(node: self.selectionButtonNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: width, height: height))) + transition.updateFrame(node: self.topSeparatorNode, frame: CGRect(origin: CGPoint(x: topSeparatorInset, y: 0.0), size: CGSize(width: width - topSeparatorInset, height: UIScreenPixel))) + transition.updateFrame(node: self.bottomSeparatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: height - UIScreenPixel), size: CGSize(width: width, height: UIScreenPixel))) + + return height + }) + } + + func updateRevealOffsetInternal(offset: CGFloat, transition: ContainedViewLayoutTransition) { + self.revealOffset = offset + guard let (size, leftInset, rightInset) = self.validLayout else { + return + } + + if let leftRevealNode = self.leftRevealNode { + let revealSize = leftRevealNode.bounds.size + + let revealFrame = CGRect(origin: CGPoint(x: min(self.revealOffset - revealSize.width, 0.0), y: 0.0), size: revealSize) + //let revealNodeOffset = max(-self.revealOffset, revealSize.width) + let revealNodeOffset = -self.revealOffset + leftRevealNode.updateRevealOffset(offset: revealNodeOffset, sideInset: leftInset, transition: transition) + + if CGFloat(offset).isLessThanOrEqualTo(0.0) { + self.leftRevealNode = nil + transition.updateFrame(node: leftRevealNode, frame: revealFrame, completion: { [weak leftRevealNode] _ in + leftRevealNode?.removeFromSupernode() + }) + } else { + transition.updateFrame(node: leftRevealNode, frame: revealFrame) + } + } + if let rightRevealNode = self.rightRevealNode { + let revealSize = rightRevealNode.bounds.size + + let revealFrame = CGRect(origin: CGPoint(x: size.width + max(self.revealOffset, -revealSize.width), y: 0.0), size: revealSize) + let revealNodeOffset = -max(self.revealOffset, -revealSize.width) + rightRevealNode.updateRevealOffset(offset: revealNodeOffset, sideInset: -rightInset, transition: transition) + + if CGFloat(0.0).isLessThanOrEqualTo(offset) { + self.rightRevealNode = nil + transition.updateFrame(node: rightRevealNode, frame: revealFrame, completion: { [weak rightRevealNode] _ in + rightRevealNode?.removeFromSupernode() + }) + } else { + transition.updateFrame(node: rightRevealNode, frame: revealFrame) + } + } + let allowAnyDirection = !self.revealOptions.left.isEmpty || !offset.isZero + if allowAnyDirection != self.allowAnyDirection { + self.allowAnyDirection = allowAnyDirection + self.recognizer?.allowAnyDirection = allowAnyDirection + self.view.disablesInteractiveTransitionGestureRecognizer = allowAnyDirection + } + + self.updateRevealOffset(offset: offset, transition: transition) + } + + func updateRevealOffset(offset: CGFloat, transition: ContainedViewLayoutTransition) { + + } + + func revealOptionsInteractivelyOpened() { + + } + + func revealOptionsInteractivelyClosed() { + + } + + func setRevealOptionsOpened(_ value: Bool, animated: Bool) { + if value != !self.revealOffset.isZero { + if !self.revealOffset.isZero { + self.recognizer?.becomeCancelled() + } + let transition: ContainedViewLayoutTransition + if animated { + transition = .animated(duration: 0.3, curve: .spring) + } else { + transition = .immediate + } + if value { + if self.rightRevealNode == nil { + self.setupAndAddRightRevealNode() + if let rightRevealNode = self.rightRevealNode, rightRevealNode.isNodeLoaded, let _ = self.validLayout { + rightRevealNode.layout() + let revealSize = rightRevealNode.bounds.size + self.updateRevealOffsetInternal(offset: -revealSize.width, transition: transition) + } + } + } else if !self.revealOffset.isZero { + self.updateRevealOffsetInternal(offset: 0.0, transition: transition) + } + } + } + + func revealOptionSelected(_ option: ItemListRevealOption, animated: Bool) { + } + + private func hapticImpact() { + if self.hapticFeedback == nil { + self.hapticFeedback = HapticFeedback() + } + self.hapticFeedback?.impact(.medium) + } + + func update(item: Item, theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, width: CGFloat, previousNeighbor: FormControllerItemNeighbor, nextNeighbor: FormControllerItemNeighbor, transition: ContainedViewLayoutTransition) -> (FormControllerItemPreLayout, (FormControllerItemLayoutParams) -> CGFloat) { + preconditionFailure() + } + + @objc private func selectionButtonPressed() { + if self.canBeSelected { + self.selected() + } else { + self.updateRevealOffsetInternal(offset: 0.0, transition: .animated(duration: 0.3, curve: .spring)) + self.revealOptionsInteractivelyClosed() + } + } + + func selected() { + } + + var preventsTouchesToOtherItems: Bool { + return self.isDisplayingRevealedOptions + } + + func touchesToOtherItemsPrevented() { + if self.isDisplayingRevealedOptions { + self.setRevealOptionsOpened(false, animated: true) + } + } +} + diff --git a/TelegramUI/Geocoding.swift b/TelegramUI/Geocoding.swift new file mode 100644 index 0000000000..828b7ba6b4 --- /dev/null +++ b/TelegramUI/Geocoding.swift @@ -0,0 +1,35 @@ +import Foundation +import CoreLocation +import SwiftSignalKit + +func geocodeLocation(dictionary: [String: String]) -> Signal<(Double, Double)?, NoError> { + return Signal { subscriber in + let geocoder = CLGeocoder() + geocoder.geocodeAddressDictionary(dictionary, completionHandler: { placemarks, _ in + if let location = placemarks?.first?.location { + subscriber.putNext((location.coordinate.latitude, location.coordinate.longitude)) + } else { + subscriber.putNext(nil) + } + subscriber.putCompletion() + }) + return ActionDisposable { + geocoder.cancelGeocode() + } + } +} + +func reverseGeocodeLocation(latitude: Double, longitude: Double) -> Signal { + return Signal { subscriber in + let geocoder = CLGeocoder() + geocoder.reverseGeocodeLocation(CLLocation(latitude: latitude, longitude: longitude), completionHandler: { placemarks, _ in + if let placemarks = placemarks, let locality = placemarks.first?.locality { + subscriber.putNext(locality) + subscriber.putCompletion() + } + }) + return ActionDisposable { + geocoder.cancelGeocode() + } + } +} diff --git a/TelegramUI/GridMessageItem.swift b/TelegramUI/GridMessageItem.swift index f78e000c5d..372302bf52 100644 --- a/TelegramUI/GridMessageItem.swift +++ b/TelegramUI/GridMessageItem.swift @@ -200,7 +200,7 @@ final class GridMessageItemNode: GridItemNode { super.init() self.addSubnode(self.imageNode) - self.imageNode.addSubnode(videoAccessoryNode) + self.imageNode.addSubnode(self.videoAccessoryNode) } deinit { diff --git a/TelegramUI/GroupInfoController.swift b/TelegramUI/GroupInfoController.swift index 96201232dc..8f03707982 100644 --- a/TelegramUI/GroupInfoController.swift +++ b/TelegramUI/GroupInfoController.swift @@ -1231,7 +1231,7 @@ public func groupInfoController(account: Account, peerId: PeerId) -> ViewControl account.postbox.mediaBox.storeResourceData(resource.id, data: data) let representation = TelegramMediaImageRepresentation(dimensions: CGSize(width: 640.0, height: 640.0), resource: resource) updateState { - $0.withUpdatedUpdatingAvatar(.image(representation)) + $0.withUpdatedUpdatingAvatar(.image(representation, true)) } updateAvatarDisposable.set((updatePeerPhoto(postbox: account.postbox, network: account.network, stateManager: account.stateManager, accountPeerId: account.peerId, peerId: peerId, photo: uploadedPeerPhoto(postbox: account.postbox, network: account.network, resource: resource)) |> deliverOnMainQueue).start(next: { result in switch result { @@ -1250,7 +1250,7 @@ public func groupInfoController(account: Account, peerId: PeerId) -> ViewControl let _ = currentAvatarMixin.swap(nil) updateState { if let profileImage = peer?.smallProfileImage { - return $0.withUpdatedUpdatingAvatar(.image(profileImage)) + return $0.withUpdatedUpdatingAvatar(.image(profileImage, false)) } else { return $0.withUpdatedUpdatingAvatar(.none) } diff --git a/TelegramUI/HorizontalListContextResultsChatInputContextPanelNode.swift b/TelegramUI/HorizontalListContextResultsChatInputContextPanelNode.swift index 5b72ad787d..dc4fb29e82 100644 --- a/TelegramUI/HorizontalListContextResultsChatInputContextPanelNode.swift +++ b/TelegramUI/HorizontalListContextResultsChatInputContextPanelNode.swift @@ -158,7 +158,12 @@ final class HorizontalListContextResultsChatInputContextPanelNode: ChatInputCont selectedItemNodeAndContent = (itemNode, StickerPreviewPeekContent(account: item.account, item: .found(FoundStickerItem(file: file, stringRepresentations: [])), menu: menuItems)) } else { var menuItems: [PeekControllerMenuItem] = [] - menuItems.append(PeekControllerMenuItem(title: strongSelf.strings.ShareMenu_Send, color: .accent, action: { + if case let .internalReference(internalReference) = item.result, let file = internalReference.file, file.isAnimated { + menuItems.append(PeekControllerMenuItem(title: strongSelf.strings.Preview_SaveGif, color: .accent, action: { + let _ = addSavedGif(postbox: strongSelf.account.postbox, fileReference: .standalone(media: file)).start() + })) + } + menuItems.append(PeekControllerMenuItem(title: strongSelf.strings.ShareMenu_Send, color: .accent, font: .bold, action: { item.resultSelected(item.result) })) selectedItemNodeAndContent = (itemNode, ChatContextResultPeekContent(account: item.account, contextResult: item.result, menu: menuItems)) diff --git a/TelegramUI/HorizontalPeerItem.swift b/TelegramUI/HorizontalPeerItem.swift index 92fc5c223a..ba7695ff1b 100644 --- a/TelegramUI/HorizontalPeerItem.swift +++ b/TelegramUI/HorizontalPeerItem.swift @@ -9,8 +9,8 @@ enum HorizontalPeerItemMode { case list case actionSheet } -private let badgeFont = Font.regular(14.0) +private let badgeFont = Font.regular(14.0) final class HorizontalPeerItem: ListViewItem { let theme: PresentationTheme @@ -178,7 +178,7 @@ final class HorizontalPeerItemNode: ListViewItemNode { strongSelf.badgeBackgroundNode.isHidden = false badgeBackgroundWidth = max(badgeLayout.size.width + 10.0, currentBadgeBackgroundImage.size.width) - let badgeBackgroundFrame = CGRect(x: itemLayout.size.width - badgeBackgroundWidth * 2, y: 0, width: badgeBackgroundWidth, height: currentBadgeBackgroundImage.size.height) + let badgeBackgroundFrame = CGRect(x: itemLayout.size.width - floorToScreenPixels(badgeBackgroundWidth * 1.8), y: 2.0, width: badgeBackgroundWidth, height: currentBadgeBackgroundImage.size.height) let badgeTextFrame = CGRect(origin: CGPoint(x: badgeBackgroundFrame.midX - badgeLayout.size.width / 2.0, y: badgeBackgroundFrame.minY + 2.0), size: badgeLayout.size) strongSelf.badgeTextNode.frame = badgeTextFrame @@ -190,7 +190,6 @@ final class HorizontalPeerItemNode: ListViewItemNode { } let _ = badgeApply() - } }) } diff --git a/TelegramUI/InstalledStickerPacksController.swift b/TelegramUI/InstalledStickerPacksController.swift index 273dfc8e8e..76f2ca8016 100644 --- a/TelegramUI/InstalledStickerPacksController.swift +++ b/TelegramUI/InstalledStickerPacksController.swift @@ -340,7 +340,9 @@ private func installedStickerPacksControllerEntries(presentationData: Presentati entries.append(.masks(presentationData.theme, presentationData.strings.MaskStickerSettings_Title)) entries.append(.packsTitle(presentationData.theme, presentationData.strings.StickerPacksSettings_StickerPacksSection)) case .masks: - break + if let archived = archived, !archived.isEmpty { + entries.append(.archived(presentationData.theme, presentationData.strings.StickerPacksSettings_ArchivedMasks, Int32(archived.count), archived)) + } } if let stickerPacksView = view.views[.itemCollectionInfos(namespaces: [namespaceForMode(mode)])] as? ItemCollectionInfosView { @@ -378,7 +380,7 @@ public enum InstalledStickerPacksControllerMode { case masks } -public func installedStickerPacksController(account: Account, mode: InstalledStickerPacksControllerMode, archivedPacks:[ArchivedStickerPackItem]? = nil, updatedPacks: @escaping([ArchivedStickerPackItem]?) -> Void = { _ in}) -> ViewController { +public func installedStickerPacksController(account: Account, mode: InstalledStickerPacksControllerMode, archivedPacks: [ArchivedStickerPackItem]? = nil, updatedPacks: @escaping ([ArchivedStickerPackItem]?) -> Void = { _ in }) -> ViewController { let initialState = InstalledStickerPacksControllerState().withUpdatedEditing(mode == .modal) let statePromise = ValuePromise(initialState, ignoreRepeated: true) let stateValue = Atomic(value: initialState) @@ -398,7 +400,6 @@ public func installedStickerPacksController(account: Account, mode: InstalledSti let archivedPromise = Promise<[ArchivedStickerPackItem]?>() - var presentStickerPackController: ((StickerPackCollectionInfo) -> Void)? let arguments = InstalledStickerPacksControllerArguments(account: account, openStickerPack: { info in @@ -430,10 +431,8 @@ public func installedStickerPacksController(account: Account, mode: InstalledSti archivedPromise.set(.single(packs)) updatedPacks(packs) }) -// + let _ = removeStickerPackInteractively(postbox: account.postbox, id: archivedItem.info.id, option: .archive).start() - - }), ActionSheetButtonItem(title: presentationData.strings.Common_Delete, color: .destructive, action: { dismissAction() @@ -454,7 +453,14 @@ public func installedStickerPacksController(account: Account, mode: InstalledSti }, openFeatured: { pushControllerImpl?(featuredStickerPacksController(account: account)) }, openArchived: { archived in - pushControllerImpl?(archivedStickerPacksController(account: account, archived: archived, updatedPacks: { packs in + let archivedMode: ArchivedStickerPacksControllerMode + switch mode { + case .masks: + archivedMode = .masks + default: + archivedMode = .stickers + } + pushControllerImpl?(archivedStickerPacksController(account: account, mode: archivedMode, archived: archived, updatedPacks: { packs in archivedPromise.set(.single(packs)) updatedPacks(packs) })) @@ -508,7 +514,7 @@ public func installedStickerPacksController(account: Account, mode: InstalledSti archivedPromise.set(.single(archivedPacks) |> then(archivedStickerPacks(account: account) |> map(Optional.init))) case .masks: featured.set(.single([])) - archivedPromise.set(.single(nil)) + archivedPromise.set(.single(nil) |> then(archivedStickerPacks(account: account, namespace: .masks) |> map(Optional.init))) } @@ -518,8 +524,6 @@ public func installedStickerPacksController(account: Account, mode: InstalledSti let preferencesKey: PostboxViewKey = .preferences(keys: Set([stickerSettingsKey])) let preferencesView = account.postbox.combinedView(keys: [preferencesKey]) - - let signal = combineLatest((account.applicationContext as! TelegramApplicationContext).presentationData, statePromise.get() |> deliverOnMainQueue, stickerPacks.get() |> deliverOnMainQueue, combineLatest(featured.get() |> deliverOnMainQueue, archivedPromise.get() |> deliverOnMainQueue), preferencesView |> deliverOnMainQueue) |> deliverOnMainQueue |> map { presentationData, state, view, featuredAndArchived, preferencesView -> (ItemListControllerState, (ItemListNodeState, InstalledStickerPacksEntry.ItemGenerationArguments)) in diff --git a/TelegramUI/InstantPageControllerNode.swift b/TelegramUI/InstantPageControllerNode.swift index 8c22953b26..93d86891d6 100644 --- a/TelegramUI/InstantPageControllerNode.swift +++ b/TelegramUI/InstantPageControllerNode.swift @@ -1119,7 +1119,7 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate { if let strongSelf = self { switch result { case let .result(webpage): - if let webpage = webpage { + if let webpage = webpage, case .Loaded = webpage.content { strongSelf.loadProgress.set(1.0) strongSelf.pushController(InstantPageController(account: strongSelf.account, webPage: webpage, anchor: anchor)) } @@ -1172,7 +1172,7 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate { private func openUrlIn(_ url: InstantPageUrlItem) { if let applicationContext = self.account.applicationContext as? TelegramApplicationContext { let presentationData = applicationContext.currentPresentationData.with { $0 } - let actionSheet = OpenInActionSheetController(postbox: self.account.postbox, applicationContext: applicationContext, theme: presentationData.theme, strings: presentationData.strings, item: .url(url: url.url), openUrl: { [weak self] url in + let actionSheet = OpenInActionSheetController(account: self.account, item: .url(url: url.url), openUrl: { [weak self] url in if let strongSelf = self, let navigationController = strongSelf.getNavigationController() { openExternalUrl(account: strongSelf.account, url: url, forceExternal: true, presentationData: presentationData, applicationContext: applicationContext, navigationController: navigationController, dismissInput: {}) } diff --git a/TelegramUI/InstantPagePlayableVideoNode.swift b/TelegramUI/InstantPagePlayableVideoNode.swift index d51b8d1807..476950becb 100644 --- a/TelegramUI/InstantPagePlayableVideoNode.swift +++ b/TelegramUI/InstantPagePlayableVideoNode.swift @@ -25,7 +25,13 @@ final class InstantPagePlayableVideoNode: ASDisplayNode, InstantPageNode { self.interactive = interactive self.openMedia = openMedia - self.videoNode = UniversalVideoNode(postbox: account.postbox, audioSession: account.telegramApplicationContext.mediaManager!.audioSession, manager: account.telegramApplicationContext.mediaManager!.universalVideoManager, decoration: GalleryVideoDecoration(), content: NativeVideoContent(id: .instantPage(webPage.webpageId, media.media.id!), fileReference: .webPage(webPage: WebpageReference(webPage), media: media.media as! TelegramMediaFile), loopVideo: true, enableSound: false, fetchAutomatically: true, placeholderColor: theme.pageBackgroundColor), priority: .embedded, autoplay: true) + var imageReference: ImageMediaReference? + if let file = media.media as? TelegramMediaFile, let presentation = smallestImageRepresentation(file.previewRepresentations) { + let image = TelegramMediaImage(imageId: MediaId(namespace: 0, id: 0), representations: [presentation], reference: nil, partialReference: nil) + imageReference = ImageMediaReference.webPage(webPage: WebpageReference(webPage), media: image) + } + + self.videoNode = UniversalVideoNode(postbox: account.postbox, audioSession: account.telegramApplicationContext.mediaManager!.audioSession, manager: account.telegramApplicationContext.mediaManager!.universalVideoManager, decoration: GalleryVideoDecoration(), content: NativeVideoContent(id: .instantPage(webPage.webpageId, media.media.id!), fileReference: .webPage(webPage: WebpageReference(webPage), media: media.media as! TelegramMediaFile), imageReference: imageReference, loopVideo: true, enableSound: false, fetchAutomatically: true, placeholderColor: theme.pageBackgroundColor), priority: .embedded, autoplay: true) self.videoNode.isUserInteractionEnabled = false super.init() diff --git a/TelegramUI/InviteContactsControllerNode.swift b/TelegramUI/InviteContactsControllerNode.swift index dc915ccc72..07baf86f53 100644 --- a/TelegramUI/InviteContactsControllerNode.swift +++ b/TelegramUI/InviteContactsControllerNode.swift @@ -346,6 +346,7 @@ final class InviteContactsControllerNode: ASDisplayNode { let previousStrings = strongSelf.presentationData.strings strongSelf.presentationData = presentationData + strongSelf.themeAndStringsPromise.set(.single((presentationData.theme, presentationData.strings, presentationData.nameSortOrder, presentationData.nameDisplayOrder))) if previousTheme !== presentationData.theme || previousStrings !== presentationData.strings { strongSelf.updateThemeAndStrings() diff --git a/TelegramUI/ItemCacheKeys.swift b/TelegramUI/ItemCacheKeys.swift deleted file mode 100644 index ac266efee6..0000000000 --- a/TelegramUI/ItemCacheKeys.swift +++ /dev/null @@ -1,11 +0,0 @@ -import Foundation -import TelegramCore -import Postbox - -private enum ApplicationSpecificItemCacheCollectionIdValues: Int8 { - case instantPageStoredState = 0 -} - -public struct ApplicationSpecificItemCacheCollectionId { - public static let instantPageStoredState = applicationSpecificItemCacheCollectionId(ApplicationSpecificItemCacheCollectionIdValues.instantPageStoredState.rawValue) -} diff --git a/TelegramUI/ItemListAddressItem.swift b/TelegramUI/ItemListAddressItem.swift index c8a0235ff8..38cca9ac95 100644 --- a/TelegramUI/ItemListAddressItem.swift +++ b/TelegramUI/ItemListAddressItem.swift @@ -119,6 +119,7 @@ class ItemListAddressItemNode: ListViewItemNode { self.textNode.contentsScale = UIScreen.main.scale self.imageNode = TransformImageNode() + self.imageNode.contentAnimations = [.firstUpdate, .subsequentUpdates] super.init(layerBacked: false, dynamicBounce: false) @@ -130,6 +131,7 @@ class ItemListAddressItemNode: ListViewItemNode { func asyncLayout() -> (_ item: ItemListAddressItem, _ params: ListViewItemLayoutParams, _ insets: ItemListNeighbors) -> (ListViewItemNodeLayout, (ListViewItemUpdateAnimation) -> Void) { let makeLabelLayout = TextNode.asyncLayout(self.labelNode) let makeTextLayout = TextNode.asyncLayout(self.textNode) + let makeImageLayout = self.imageNode.asyncLayout() let currentItem = self.item @@ -160,8 +162,13 @@ class ItemListAddressItemNode: ListViewItemNode { let baseColor = item.theme.list.itemPrimaryTextColor let string = stringWithAppliedEntities(item.text, entities: [], baseColor: baseColor, linkColor: item.theme.list.itemAccentColor, baseFont: textFont, linkFont: textFont, boldFont: textBoldFont, italicFont: textItalicFont, fixedFont: textFixedFont) - let (textLayout, textApply) = makeTextLayout(TextNodeLayoutArguments(attributedString: string, backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: params.width - leftOffset - leftInset - rightInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) + let (textLayout, textApply) = makeTextLayout(TextNodeLayoutArguments(attributedString: string, backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: params.width - leftOffset - leftInset - rightInset - 98.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) let contentSize = CGSize(width: params.width, height: textLayout.size.height + 39.0) + + let imageSide = min(90.0, contentSize.height - 18.0) + let imageSize = CGSize(width: imageSide, height: imageSide) + let imageApply = makeImageLayout(TransformImageArguments(corners: ImageCorners(radius: 4.0), imageSize: imageSize, boundingSize: imageSize, intrinsicInsets: UIEdgeInsets())) + let nodeLayout = ListViewItemNodeLayout(contentSize: contentSize, insets: insets) return (nodeLayout, { [weak self] animation in if let strongSelf = self { @@ -173,6 +180,11 @@ class ItemListAddressItemNode: ListViewItemNode { } strongSelf.item = item + if let signal = item.imageSignal { + strongSelf.imageNode.setSignal(signal) + } else { + strongSelf.imageNode.clearContents() + } if let _ = updatedTheme { strongSelf.topStripeNode.backgroundColor = item.theme.list.itemPlainSeparatorColor @@ -183,6 +195,7 @@ class ItemListAddressItemNode: ListViewItemNode { let _ = labelApply() let _ = textApply() + let _ = imageApply() if let (selectionWidth, selectionApply) = selectionNodeWidthAndApply { let selectionFrame = CGRect(origin: CGPoint(x: params.leftInset, y: 0.0), size: CGSize(width: selectionWidth, height: nodeLayout.contentSize.height)) @@ -206,6 +219,7 @@ class ItemListAddressItemNode: ListViewItemNode { strongSelf.labelNode.frame = CGRect(origin: CGPoint(x: leftOffset + leftInset, y: 11.0), size: labelLayout.size) strongSelf.textNode.frame = CGRect(origin: CGPoint(x: leftOffset + leftInset, y: 31.0), size: textLayout.size) + strongSelf.imageNode.frame = CGRect(origin: CGPoint(x: params.width - imageSize.width - rightInset, y: floorToScreenPixels((contentSize.height - imageSize.height) / 2.0)), size: imageSize) let leftInset: CGFloat let style = ItemListStyle.plain diff --git a/TelegramUI/ItemListAvatarAndNameItem.swift b/TelegramUI/ItemListAvatarAndNameItem.swift index 8baee9f1da..729029d198 100644 --- a/TelegramUI/ItemListAvatarAndNameItem.swift +++ b/TelegramUI/ItemListAvatarAndNameItem.swift @@ -120,13 +120,13 @@ enum ItemListAvatarAndNameInfoItemStyle { } enum ItemListAvatarAndNameInfoItemUpdatingAvatar: Equatable { - case image(TelegramMediaImageRepresentation) + case image(TelegramMediaImageRepresentation, Bool) case none static func ==(lhs: ItemListAvatarAndNameInfoItemUpdatingAvatar, rhs: ItemListAvatarAndNameInfoItemUpdatingAvatar) -> Bool { switch lhs { - case let .image(representation): - if case .image(representation) = rhs { + case let .image(representation, activity): + if case .image(representation, activity) = rhs { return true } else { return false @@ -545,7 +545,9 @@ class ItemListAvatarAndNameInfoItemNode: ListViewItemNode, ItemListItemNode, Ite if strongSelf.updatingAvatarOverlay.supernode == nil { strongSelf.insertSubnode(strongSelf.updatingAvatarOverlay, aboveSubnode: strongSelf.avatarNode) } - strongSelf.activityIndicator.isHidden = false + if let updatingImage = item.updatingImage, case .image(_, true) = updatingImage { + strongSelf.activityIndicator.isHidden = false + } } else if strongSelf.updatingAvatarOverlay.supernode != nil { if animated { strongSelf.updatingAvatarOverlay.alpha = 0.0 @@ -624,7 +626,7 @@ class ItemListAvatarAndNameInfoItemNode: ListViewItemNode, ItemListItemNode, Ite switch updatingImage { case .none: overrideImage = AvatarNodeImageOverride.none - case let .image(representation): + case let .image(representation, _): overrideImage = .image(representation) } } else if case .editSettings = item.mode { @@ -677,20 +679,17 @@ class ItemListAvatarAndNameInfoItemNode: ListViewItemNode, ItemListItemNode, Ite case let .personName(firstName, lastName): if strongSelf.inputSeparator == nil { let inputSeparator = ASDisplayNode() - inputSeparator.backgroundColor = itemSeparatorColor inputSeparator.isLayerBacked = true strongSelf.addSubnode(inputSeparator) strongSelf.inputSeparator = inputSeparator } + strongSelf.inputSeparator?.backgroundColor = itemSeparatorColor if strongSelf.inputFirstField == nil { let inputFirstField = TextFieldNodeView() inputFirstField.font = Font.regular(17.0) - inputFirstField.textColor = item.theme.list.itemPrimaryTextColor - inputFirstField.keyboardAppearance = item.theme.chatList.searchBarKeyboardColor.keyboardAppearance inputFirstField.autocorrectionType = .no inputFirstField.returnKeyType = .next - inputFirstField.attributedPlaceholder = NSAttributedString(string: item.strings.UserInfo_FirstNamePlaceholder, font: Font.regular(17.0), textColor: item.theme.list.itemPlaceholderTextColor) inputFirstField.attributedText = NSAttributedString(string: firstName, font: Font.regular(17.0), textColor: item.theme.list.itemPrimaryTextColor) strongSelf.inputFirstField = inputFirstField strongSelf.view.addSubview(inputFirstField) @@ -699,14 +698,15 @@ class ItemListAvatarAndNameInfoItemNode: ListViewItemNode, ItemListItemNode, Ite strongSelf.inputFirstField?.text = firstName } + strongSelf.inputFirstField?.textColor = item.theme.list.itemPrimaryTextColor + strongSelf.inputFirstField?.keyboardAppearance = item.theme.chatList.searchBarKeyboardColor.keyboardAppearance + strongSelf.inputFirstField?.attributedPlaceholder = NSAttributedString(string: item.strings.UserInfo_FirstNamePlaceholder, font: Font.regular(17.0), textColor: item.theme.list.itemPlaceholderTextColor) + if strongSelf.inputSecondField == nil { let inputSecondField = TextFieldNodeView() inputSecondField.font = Font.regular(17.0) - inputSecondField.textColor = item.theme.list.itemPrimaryTextColor - inputSecondField.keyboardAppearance = item.theme.chatList.searchBarKeyboardColor.keyboardAppearance inputSecondField.autocorrectionType = .no inputSecondField.returnKeyType = .done - inputSecondField.attributedPlaceholder = NSAttributedString(string: item.strings.UserInfo_LastNamePlaceholder, font: Font.regular(17.0), textColor: item.theme.list.itemPlaceholderTextColor) inputSecondField.attributedText = NSAttributedString(string: lastName, font: Font.regular(17.0), textColor: item.theme.list.itemPrimaryTextColor) strongSelf.inputSecondField = inputSecondField strongSelf.view.addSubview(inputSecondField) @@ -715,6 +715,10 @@ class ItemListAvatarAndNameInfoItemNode: ListViewItemNode, ItemListItemNode, Ite strongSelf.inputSecondField?.text = lastName } + strongSelf.inputSecondField?.textColor = item.theme.list.itemPrimaryTextColor + strongSelf.inputSecondField?.keyboardAppearance = item.theme.chatList.searchBarKeyboardColor.keyboardAppearance + strongSelf.inputSecondField?.attributedPlaceholder = NSAttributedString(string: item.strings.UserInfo_LastNamePlaceholder, font: Font.regular(17.0), textColor: item.theme.list.itemPlaceholderTextColor) + strongSelf.inputSeparator?.frame = CGRect(origin: CGPoint(x: params.leftInset + 100.0, y: 46.0), size: CGSize(width: params.width - params.leftInset - params.rightInset - 100.0, height: separatorHeight)) strongSelf.inputFirstField?.frame = CGRect(origin: CGPoint(x: params.leftInset + 111.0, y: 12.0), size: CGSize(width: params.width - params.leftInset - params.rightInset - 111.0 - 8.0, height: 30.0)) strongSelf.inputSecondField?.frame = CGRect(origin: CGPoint(x: params.leftInset + 111.0, y: 52.0), size: CGSize(width: params.width - params.leftInset - params.rightInset - 111.0 - 8.0, height: 30.0)) @@ -727,27 +731,16 @@ class ItemListAvatarAndNameInfoItemNode: ListViewItemNode, ItemListItemNode, Ite case let .title(title, type): if strongSelf.inputSeparator == nil { let inputSeparator = ASDisplayNode() - inputSeparator.backgroundColor = itemSeparatorColor inputSeparator.isLayerBacked = true strongSelf.addSubnode(inputSeparator) strongSelf.inputSeparator = inputSeparator } - + strongSelf.inputSeparator?.backgroundColor = itemSeparatorColor if strongSelf.inputFirstField == nil { let inputFirstField = TextFieldNodeView() inputFirstField.font = Font.regular(17.0) - inputFirstField.textColor = item.theme.list.itemPrimaryTextColor - inputFirstField.keyboardAppearance = item.theme.chatList.searchBarKeyboardColor.keyboardAppearance inputFirstField.autocorrectionType = .no - let placeholder: String - switch type { - case .group: - placeholder = item.strings.GroupInfo_GroupNamePlaceholder - case .channel: - placeholder = item.strings.GroupInfo_ChannelListNamePlaceholder - } - inputFirstField.attributedPlaceholder = NSAttributedString(string: placeholder, font: Font.regular(19.0), textColor: item.theme.list.itemPlaceholderTextColor) inputFirstField.attributedText = NSAttributedString(string: title, font: Font.regular(19.0), textColor: item.theme.list.itemPrimaryTextColor) strongSelf.inputFirstField = inputFirstField strongSelf.view.addSubview(inputFirstField) @@ -755,6 +748,16 @@ class ItemListAvatarAndNameInfoItemNode: ListViewItemNode, ItemListItemNode, Ite } else if strongSelf.inputFirstField?.text != title { strongSelf.inputFirstField?.text = title } + strongSelf.inputFirstField?.textColor = item.theme.list.itemPrimaryTextColor + strongSelf.inputFirstField?.keyboardAppearance = item.theme.chatList.searchBarKeyboardColor.keyboardAppearance + let placeholder: String + switch type { + case .group: + placeholder = item.strings.GroupInfo_GroupNamePlaceholder + case .channel: + placeholder = item.strings.GroupInfo_ChannelListNamePlaceholder + } + strongSelf.inputFirstField?.attributedPlaceholder = NSAttributedString(string: placeholder, font: Font.regular(19.0), textColor: item.theme.list.itemPlaceholderTextColor) strongSelf.inputSeparator?.frame = CGRect(origin: CGPoint(x: params.leftInset + 100.0, y: 62.0), size: CGSize(width: params.width - params.leftInset - params.rightInset - 100.0, height: separatorHeight)) strongSelf.inputFirstField?.frame = CGRect(origin: CGPoint(x: params.leftInset + 102.0, y: 26.0), size: CGSize(width: params.width - params.leftInset - params.rightInset - 102.0 - 8.0, height: 35.0)) diff --git a/TelegramUI/ItemListDisclosureItem.swift b/TelegramUI/ItemListDisclosureItem.swift index 999cd3b25e..d1475b8d46 100644 --- a/TelegramUI/ItemListDisclosureItem.swift +++ b/TelegramUI/ItemListDisclosureItem.swift @@ -243,7 +243,7 @@ class ItemListDisclosureItemNode: ListViewItemNode { let labelFont: UIFont let labelBadgeColor: UIColor if case .badge = item.labelStyle { - labelBadgeColor = item.theme.list.plainBackgroundColor + labelBadgeColor = item.theme.rootController.tabBar.badgeTextColor labelFont = badgeFont } else { labelBadgeColor = item.theme.list.itemSecondaryTextColor @@ -252,10 +252,10 @@ class ItemListDisclosureItemNode: ListViewItemNode { let titleColor: UIColor switch item.kind { - case .generic: - titleColor = item.titleColor == .accent ? item.theme.list.itemAccentColor : item.theme.list.itemPrimaryTextColor - case .disabled: - titleColor = item.theme.list.itemDisabledTextColor + case .generic: + titleColor = item.titleColor == .accent ? item.theme.list.itemAccentColor : item.theme.list.itemPrimaryTextColor + case .disabled: + titleColor = item.theme.list.itemDisabledTextColor } let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.title, font: titleFont, textColor: titleColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - params.rightInset - 20.0 - leftInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) diff --git a/TelegramUI/ItemListEditableItem.swift b/TelegramUI/ItemListEditableItem.swift index d31e27e34e..87820eae98 100644 --- a/TelegramUI/ItemListEditableItem.swift +++ b/TelegramUI/ItemListEditableItem.swift @@ -18,7 +18,7 @@ final class ItemListRevealOptionsGestureRecognizer: UIPanGestureRecognizer { override func reset() { super.reset() - validatedGesture = false + self.validatedGesture = false } func becomeCancelled() { @@ -34,19 +34,19 @@ final class ItemListRevealOptionsGestureRecognizer: UIPanGestureRecognizer { override func touchesMoved(_ touches: Set, with event: UIEvent) { let location = touches.first!.location(in: self.view) - let translation = CGPoint(x: location.x - firstLocation.x, y: location.y - firstLocation.y) + let translation = CGPoint(x: location.x - self.firstLocation.x, y: location.y - self.firstLocation.y) - if !validatedGesture { + if !self.validatedGesture { if !self.allowAnyDirection && translation.x > 0.0 { self.state = .failed } else if abs(translation.y) > 4.0 && abs(translation.y) > abs(translation.x) * 2.5 { self.state = .failed } else if abs(translation.x) > 4.0 && abs(translation.y) * 2.5 < abs(translation.x) { - validatedGesture = true + self.validatedGesture = true } } - if validatedGesture { + if self.validatedGesture { self.lastVelocity = self.velocity(in: self.view) super.touchesMoved(touches, with: event) } @@ -172,7 +172,7 @@ class ItemListRevealOptionsItemNode: ListViewItemNode, UIGestureRecognizerDelega switch recognizer.state { case .began: if let leftRevealNode = self.leftRevealNode { - let revealSize = leftRevealNode.calculatedSize + let revealSize = leftRevealNode.bounds.size let location = recognizer.location(in: self.view) if location.x < revealSize.width { recognizer.becomeCancelled() @@ -180,7 +180,7 @@ class ItemListRevealOptionsItemNode: ListViewItemNode, UIGestureRecognizerDelega self.initialRevealOffset = self.revealOffset } } else if let rightRevealNode = self.rightRevealNode { - let revealSize = rightRevealNode.calculatedSize + let revealSize = rightRevealNode.bounds.size let location = recognizer.location(in: self.view) if location.x > size.width - revealSize.width { recognizer.becomeCancelled() @@ -217,7 +217,7 @@ class ItemListRevealOptionsItemNode: ListViewItemNode, UIGestureRecognizerDelega if let leftRevealNode = self.leftRevealNode { let velocity = recognizer.velocity(in: self.view) - let revealSize = leftRevealNode.calculatedSize + let revealSize = leftRevealNode.bounds.size var reveal = false if abs(velocity.x) < 100.0 { if self.initialRevealOffset.isZero && self.revealOffset > 0.0 { @@ -252,7 +252,7 @@ class ItemListRevealOptionsItemNode: ListViewItemNode, UIGestureRecognizerDelega } } else if let rightRevealNode = self.rightRevealNode { let velocity = recognizer.velocity(in: self.view) - let revealSize = rightRevealNode.calculatedSize + let revealSize = rightRevealNode.bounds.size var reveal = false if abs(velocity.x) < 100.0 { if self.initialRevealOffset.isZero && self.revealOffset < 0.0 { @@ -289,11 +289,12 @@ class ItemListRevealOptionsItemNode: ListViewItemNode, UIGestureRecognizerDelega revealNode.setOptions(self.revealOptions.left) self.leftRevealNode = revealNode - if let (size, _, rightInset) = self.validLayout { - let revealSize = revealNode.measure(CGSize(width: CGFloat.greatestFiniteMagnitude, height: size.height)) + if let (size, leftInset, _) = self.validLayout { + var revealSize = revealNode.measure(CGSize(width: CGFloat.greatestFiniteMagnitude, height: size.height)) + revealSize.width += leftInset revealNode.frame = CGRect(origin: CGPoint(x: min(self.revealOffset - revealSize.width, 0.0), y: 0.0), size: revealSize) - revealNode.updateRevealOffset(offset: 0.0, rightInset: rightInset, transition: .immediate) + revealNode.updateRevealOffset(offset: 0.0, sideInset: leftInset, transition: .immediate) } self.addSubnode(revealNode) @@ -311,10 +312,11 @@ class ItemListRevealOptionsItemNode: ListViewItemNode, UIGestureRecognizerDelega self.rightRevealNode = revealNode if let (size, _, rightInset) = self.validLayout { - let revealSize = revealNode.measure(CGSize(width: CGFloat.greatestFiniteMagnitude, height: size.height)) + var revealSize = revealNode.measure(CGSize(width: CGFloat.greatestFiniteMagnitude, height: size.height)) + revealSize.width += rightInset - revealNode.frame = CGRect(origin: CGPoint(x: size.width + max(self.revealOffset, -revealSize.width - rightInset), y: 0.0), size: revealSize) - revealNode.updateRevealOffset(offset: 0.0, rightInset: rightInset, transition: .immediate) + revealNode.frame = CGRect(origin: CGPoint(x: size.width + max(self.revealOffset, -revealSize.width), y: 0.0), size: revealSize) + revealNode.updateRevealOffset(offset: 0.0, sideInset: -rightInset, transition: .immediate) } self.addSubnode(revealNode) @@ -325,29 +327,31 @@ class ItemListRevealOptionsItemNode: ListViewItemNode, UIGestureRecognizerDelega self.validLayout = (size, leftInset, rightInset) if let leftRevealNode = self.leftRevealNode { - let revealSize = leftRevealNode.measure(CGSize(width: CGFloat.greatestFiniteMagnitude, height: size.height)) - leftRevealNode.frame = CGRect(origin: CGPoint(x: leftInset + min(self.revealOffset - revealSize.width, 0.0), y: 0.0), size: revealSize) + var revealSize = leftRevealNode.measure(CGSize(width: CGFloat.greatestFiniteMagnitude, height: size.height)) + revealSize.width += leftInset + leftRevealNode.frame = CGRect(origin: CGPoint(x: min(self.revealOffset - revealSize.width, 0.0), y: 0.0), size: revealSize) } if let rightRevealNode = self.rightRevealNode { - let revealSize = rightRevealNode.measure(CGSize(width: CGFloat.greatestFiniteMagnitude, height: size.height)) - rightRevealNode.frame = CGRect(origin: CGPoint(x: size.width - rightInset + max(self.revealOffset, -revealSize.width - rightInset), y: 0.0), size: revealSize) + var revealSize = rightRevealNode.measure(CGSize(width: CGFloat.greatestFiniteMagnitude, height: size.height)) + revealSize.width += rightInset + rightRevealNode.frame = CGRect(origin: CGPoint(x: size.width + max(self.revealOffset, -revealSize.width), y: 0.0), size: revealSize) } } func updateRevealOffsetInternal(offset: CGFloat, transition: ContainedViewLayoutTransition) { self.revealOffset = offset - guard let (size, _, rightInset) = self.validLayout else { + guard let (size, leftInset, rightInset) = self.validLayout else { return } if let leftRevealNode = self.leftRevealNode { - let revealSize = leftRevealNode.calculatedSize + let revealSize = leftRevealNode.bounds.size let revealFrame = CGRect(origin: CGPoint(x: min(self.revealOffset - revealSize.width, 0.0), y: 0.0), size: revealSize) //let revealNodeOffset = max(-self.revealOffset, revealSize.width) let revealNodeOffset = -self.revealOffset - leftRevealNode.updateRevealOffset(offset: revealNodeOffset, rightInset: rightInset, transition: transition) + leftRevealNode.updateRevealOffset(offset: revealNodeOffset, sideInset: leftInset, transition: transition) if CGFloat(offset).isLessThanOrEqualTo(0.0) { self.leftRevealNode = nil @@ -359,11 +363,11 @@ class ItemListRevealOptionsItemNode: ListViewItemNode, UIGestureRecognizerDelega } } if let rightRevealNode = self.rightRevealNode { - let revealSize = rightRevealNode.calculatedSize + let revealSize = rightRevealNode.bounds.size let revealFrame = CGRect(origin: CGPoint(x: size.width + max(self.revealOffset, -revealSize.width), y: 0.0), size: revealSize) - let revealNodeOffset = -max(self.revealOffset, -revealSize.width - rightInset) - rightRevealNode.updateRevealOffset(offset: revealNodeOffset, rightInset: rightInset, transition: transition) + let revealNodeOffset = -max(self.revealOffset, -revealSize.width) + rightRevealNode.updateRevealOffset(offset: revealNodeOffset, sideInset: -rightInset, transition: transition) if CGFloat(0.0).isLessThanOrEqualTo(offset) { self.rightRevealNode = nil @@ -410,10 +414,10 @@ class ItemListRevealOptionsItemNode: ListViewItemNode, UIGestureRecognizerDelega if value { if self.rightRevealNode == nil { self.setupAndAddRightRevealNode() - if let rightRevealNode = self.rightRevealNode, rightRevealNode.isNodeLoaded, let (_, _, rightInset) = self.validLayout { + if let rightRevealNode = self.rightRevealNode, rightRevealNode.isNodeLoaded, let _ = self.validLayout { rightRevealNode.layout() - let revealSize = rightRevealNode.calculatedSize - self.updateRevealOffsetInternal(offset: -revealSize.width - rightInset, transition: transition) + let revealSize = rightRevealNode.bounds.size + self.updateRevealOffsetInternal(offset: -revealSize.width, transition: transition) } } } else if !self.revealOffset.isZero { diff --git a/TelegramUI/ItemListMultilineInputItem.swift b/TelegramUI/ItemListMultilineInputItem.swift index c3eabb2292..c4cf80582d 100644 --- a/TelegramUI/ItemListMultilineInputItem.swift +++ b/TelegramUI/ItemListMultilineInputItem.swift @@ -68,7 +68,7 @@ class ItemListMultilineInputItemNode: ListViewItemNode, ASEditableTextNodeDelega private let bottomStripeNode: ASDisplayNode private let textClippingNode: ASDisplayNode - private let textNode: ASEditableTextNode + private let textNode: EditableTextNode private let measureTextNode: TextNode private let limitTextNode: TextNode @@ -93,7 +93,7 @@ class ItemListMultilineInputItemNode: ListViewItemNode, ASEditableTextNodeDelega self.textClippingNode = ASDisplayNode() self.textClippingNode.clipsToBounds = true - self.textNode = ASEditableTextNode() + self.textNode = EditableTextNode() self.measureTextNode = TextNode() self.limitTextNode = TextNode() diff --git a/TelegramUI/ItemListRevealOptionsNode.swift b/TelegramUI/ItemListRevealOptionsNode.swift index 7eb14e2b39..c2d79a1939 100644 --- a/TelegramUI/ItemListRevealOptionsNode.swift +++ b/TelegramUI/ItemListRevealOptionsNode.swift @@ -156,7 +156,7 @@ private final class ItemListRevealOptionNode: ASDisplayNode { self.backgroundColor = color } - func updateLayout(baseSize: CGSize, alignment: ItemListRevealOptionAlignment, extendedWidth: CGFloat, transition: ContainedViewLayoutTransition, revealFactor: CGFloat) { + func updateLayout(baseSize: CGSize, alignment: ItemListRevealOptionAlignment, extendedWidth: CGFloat, sideInset: CGFloat, transition: ContainedViewLayoutTransition, revealFactor: CGFloat) { var animateAdditive = false if transition.isAnimated, self.alignment != alignment { animateAdditive = true @@ -174,13 +174,13 @@ private final class ItemListRevealOptionNode: ASDisplayNode { if let animationNode = self.animationNode, let imageSize = animationNode.preferredSize() { let iconOffset: CGFloat = -2.0 let titleIconSpacing: CGFloat = 11.0 - let iconFrame = CGRect(origin: CGPoint(x: contentRect.minX + floor((baseSize.width - imageSize.width) / 2.0), y: contentRect.midY - imageSize.height / 2.0 + iconOffset), size: imageSize) + let iconFrame = CGRect(origin: CGPoint(x: contentRect.minX + floor((baseSize.width - imageSize.width + sideInset) / 2.0), y: contentRect.midY - imageSize.height / 2.0 + iconOffset), size: imageSize) if animateAdditive { transition.animatePositionAdditive(node: animationNode, offset: CGPoint(x: animationNode.frame.minX - iconFrame.minX, y: 0.0)) } animationNode.frame = iconFrame - let titleFrame = CGRect(origin: CGPoint(x: contentRect.minX + floor((baseSize.width - titleSize.width) / 2.0), y: contentRect.midY + titleIconSpacing), size: titleSize) + let titleFrame = CGRect(origin: CGPoint(x: contentRect.minX + floor((baseSize.width - titleSize.width + sideInset) / 2.0), y: contentRect.midY + titleIconSpacing), size: titleSize) if animateAdditive { transition.animatePositionAdditive(node: self.titleNode, offset: CGPoint(x: self.titleNode.frame.minX - titleFrame.minX, y: 0.0)) } @@ -193,7 +193,7 @@ private final class ItemListRevealOptionNode: ASDisplayNode { } } else { - self.titleNode.frame = CGRect(origin: CGPoint(x: contentRect.minX + floor((baseSize.width - titleSize.width) / 2.0), y: contentRect.minY + floor((baseSize.height - titleSize.height) / 2.0)), size: titleSize) + self.titleNode.frame = CGRect(origin: CGPoint(x: contentRect.minX + floor((baseSize.width - titleSize.width + sideInset) / 2.0), y: contentRect.minY + floor((baseSize.height - titleSize.height) / 2.0)), size: titleSize) } } @@ -215,7 +215,7 @@ final class ItemListRevealOptionsNode: ASDisplayNode { private var optionNodes: [ItemListRevealOptionNode] = [] private var revealOffset: CGFloat = 0.0 - private var rightInset: CGFloat = 0.0 + private var sideInset: CGFloat = 0.0 init(optionSelected: @escaping (ItemListRevealOption) -> Void, tapticAction: @escaping () -> Void) { self.optionSelected = optionSelected @@ -255,9 +255,9 @@ final class ItemListRevealOptionsNode: ASDisplayNode { return CGSize(width: maxWidth * CGFloat(self.optionNodes.count), height: constrainedSize.height) } - func updateRevealOffset(offset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) { + func updateRevealOffset(offset: CGFloat, sideInset: CGFloat, transition: ContainedViewLayoutTransition) { self.revealOffset = offset - self.rightInset = rightInset + self.sideInset = sideInset self.updateNodesLayout(transition: transition) } @@ -266,9 +266,9 @@ final class ItemListRevealOptionsNode: ASDisplayNode { if size.width.isLessThanOrEqualTo(0.0) || self.optionNodes.isEmpty { return } - let basicNodeWidth = floorToScreenPixels(size.width / CGFloat(self.optionNodes.count)) + let basicNodeWidth = floorToScreenPixels((size.width - abs(self.sideInset)) / CGFloat(self.optionNodes.count)) let lastNodeWidth = size.width - basicNodeWidth * CGFloat(self.optionNodes.count - 1) - let revealFactor = min(1.0, self.revealOffset / (size.width + self.rightInset)) + let revealFactor = min(1.0, self.revealOffset / (size.width)) var leftOffset: CGFloat = 0.0 for i in 0 ..< self.optionNodes.count { let node = self.optionNodes[i] @@ -288,8 +288,14 @@ final class ItemListRevealOptionsNode: ASDisplayNode { self.tapticAction() } } + + var sideInset: CGFloat = 0.0 + if i == self.optionNodes.count - 1 { + sideInset = self.sideInset + } + transition.updateFrame(node: node, frame: CGRect(origin: CGPoint(x: floorToScreenPixels(leftOffset * revealFactor), y: 0.0), size: CGSize(width: extendedWidth, height: size.height))) - node.updateLayout(baseSize: CGSize(width: nodeWidth, height: size.height), alignment: alignment, extendedWidth: extendedWidth, transition: nodeTransition, revealFactor: revealFactor) + node.updateLayout(baseSize: CGSize(width: nodeWidth, height: size.height), alignment: alignment, extendedWidth: extendedWidth, sideInset: sideInset, transition: nodeTransition, revealFactor: revealFactor) leftOffset += nodeWidth } } diff --git a/TelegramUI/LegacyAttachmentMenu.swift b/TelegramUI/LegacyAttachmentMenu.swift index eb767e9d13..0a07723710 100644 --- a/TelegramUI/LegacyAttachmentMenu.swift +++ b/TelegramUI/LegacyAttachmentMenu.swift @@ -138,6 +138,11 @@ func legacyAttachmentMenu(account: Account, peer: Peer, editMediaOptions: Messag return controller } +func legacyMenuPaletteFromTheme(_ theme: PresentationTheme) -> TGMenuSheetPallete { + let sheetTheme = theme.actionSheet + return TGMenuSheetPallete(dark: theme.overallDarkAppearance, backgroundColor: sheetTheme.opaqueItemBackgroundColor, selectionColor: sheetTheme.opaqueItemHighlightedBackgroundColor, separatorColor: sheetTheme.opaqueItemSeparatorColor, accentColor: sheetTheme.controlAccentColor, destructiveColor: sheetTheme.destructiveActionTextColor, textColor: sheetTheme.primaryTextColor, secondaryTextColor: sheetTheme.secondaryTextColor, spinnerColor: sheetTheme.secondaryTextColor, badgeTextColor: sheetTheme.controlAccentColor, badgeImage: nil, cornersImage: generateStretchableFilledCircleImage(diameter: 11.0, color: nil, strokeColor: nil, strokeWidth: nil, backgroundColor: sheetTheme.opaqueItemBackgroundColor)) +} + func legacyPasteMenu(account: Account, peer: Peer, saveEditedPhotos: Bool, allowGrouping: Bool, theme: PresentationTheme, strings: PresentationStrings, images: [UIImage], sendMessagesWithSignals: @escaping ([Any]?) -> Void) -> ViewController { let legacyController = LegacyController(presentation: .custom, theme: theme) @@ -169,6 +174,13 @@ func legacyPasteMenu(account: Account, peer: Peer, saveEditedPhotos: Bool, allow } } } - + + let presentationDisposable = account.telegramApplicationContext.presentationData.start(next: { [weak legacyController] presentationData in + if let legacyController = legacyController, let controller = legacyController.legacyController as? TGMenuSheetController { + controller.pallete = legacyMenuPaletteFromTheme(presentationData.theme) + } + }) + legacyController.disposables.add(presentationDisposable) + return legacyController } diff --git a/TelegramUI/LegacyController.swift b/TelegramUI/LegacyController.swift index cd99c1235a..25066b1e10 100644 --- a/TelegramUI/LegacyController.swift +++ b/TelegramUI/LegacyController.swift @@ -1,5 +1,6 @@ import Foundation import Display +import SwiftSignalKit import LegacyComponents public enum LegacyControllerPresentation { @@ -318,6 +319,8 @@ public class LegacyController: ViewController { } private var enableContainerLayoutUpdates = false + var disposables = DisposableSet() + public init(presentation: LegacyControllerPresentation, theme: PresentationTheme? = nil, strings: PresentationStrings? = nil, initialLayout: ContainerViewLayout? = nil) { self.sizeClass.set(SSignal.single(UIUserInterfaceSizeClass.compact.rawValue as NSNumber)) self.presentation = presentation @@ -340,6 +343,7 @@ public class LegacyController: ViewController { } deinit { + self.disposables.dispose() } required public init(coder aDecoder: NSCoder) { diff --git a/TelegramUI/LegacyInstantVideoController.swift b/TelegramUI/LegacyInstantVideoController.swift index 5cfb67d7ea..4d09c834fb 100644 --- a/TelegramUI/LegacyInstantVideoController.swift +++ b/TelegramUI/LegacyInstantVideoController.swift @@ -84,6 +84,11 @@ final class InstantVideoController: LegacyController { } } +func legacyInputMicPalette(from theme: PresentationTheme) -> TGModernConversationInputMicPallete { + let inputPanelTheme = theme.chat.inputPanel + return TGModernConversationInputMicPallete(dark: theme.overallDarkAppearance, buttonColor: inputPanelTheme.actionControlFillColor, iconColor: inputPanelTheme.actionControlForegroundColor, backgroundColor: inputPanelTheme.panelBackgroundColor, borderColor: inputPanelTheme.panelStrokeColor, lock: inputPanelTheme.panelControlAccentColor, textColor: inputPanelTheme.primaryTextColor, secondaryTextColor: inputPanelTheme.secondaryTextColor, recording: inputPanelTheme.mediaRecordingDotColor) +} + func legacyInstantVideoController(theme: PresentationTheme, panelFrame: CGRect, account: Account, peerId: PeerId, send: @escaping (EnqueueMessage) -> Void) -> InstantVideoController { let legacyController = InstantVideoController(presentation: .custom, theme: theme) legacyController.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .all) @@ -94,7 +99,6 @@ func legacyInstantVideoController(theme: PresentationTheme, panelFrame: CGRect, legacyController.presentationCompleted = { [weak legacyController, weak baseController] in if let legacyController = legacyController, let baseController = baseController { legacyController.view.disablesInteractiveTransitionGestureRecognizer = true - let inputPanelTheme = theme.chat.inputPanel var uploadInterface: LegacyLiveUploadInterface? if peerId.namespace != Namespaces.Peer.SecretChat { uploadInterface = LegacyLiveUploadInterface(account: account) @@ -103,7 +107,7 @@ func legacyInstantVideoController(theme: PresentationTheme, panelFrame: CGRect, return nil }, parentController: baseController, controlsFrame: panelFrame, isAlreadyLocked: { return false - }, liveUploadInterface: uploadInterface, pallete: TGModernConversationInputMicPallete(dark: theme.overallDarkAppearance, buttonColor: inputPanelTheme.actionControlFillColor, iconColor: inputPanelTheme.actionControlForegroundColor, backgroundColor: inputPanelTheme.panelBackgroundColor, borderColor: inputPanelTheme.panelStrokeColor, lock: inputPanelTheme.panelControlAccentColor, textColor: inputPanelTheme.primaryTextColor, secondaryTextColor: inputPanelTheme.secondaryTextColor, recording: inputPanelTheme.mediaRecordingDotColor))! + }, liveUploadInterface: uploadInterface, pallete: legacyInputMicPalette(from: theme))! controller.finishedWithVideo = { videoUrl, previewImage, _, duration, dimensions, liveUploadData, adjustments in guard let videoUrl = videoUrl else { return @@ -164,6 +168,13 @@ func legacyInstantVideoController(theme: PresentationTheme, panelFrame: CGRect, } } legacyController.bindCaptureController(controller) + + let presentationDisposable = account.telegramApplicationContext.presentationData.start(next: { [weak controller] presentationData in + if let controller = controller { + controller.pallete = legacyInputMicPalette(from: presentationData.theme) + } + }) + legacyController.disposables.add(presentationDisposable) } } return legacyController diff --git a/TelegramUI/LegacyLocationController.swift b/TelegramUI/LegacyLocationController.swift index 4ee25cac35..5aa5e47b47 100644 --- a/TelegramUI/LegacyLocationController.swift +++ b/TelegramUI/LegacyLocationController.swift @@ -114,6 +114,12 @@ private func telegramMap(for location: TGLocationMediaAttachment) -> TelegramMed return TelegramMediaMap(latitude: location.latitude, longitude: location.longitude, geoPlace: nil, venue: mapVenue, liveBroadcastingTimeout: nil) } +func legacyLocationPalette(from theme: PresentationTheme) -> TGLocationPallete { + let listTheme = theme.list + let searchTheme = theme.rootController.activeNavigationSearchBar + return TGLocationPallete(backgroundColor: listTheme.plainBackgroundColor, selectionColor: listTheme.itemHighlightedBackgroundColor, separatorColor: listTheme.itemPlainSeparatorColor, textColor: listTheme.itemPrimaryTextColor, secondaryTextColor: listTheme.itemSecondaryTextColor, accentColor: listTheme.itemAccentColor, destructiveColor: listTheme.itemDestructiveColor, locationColor: UIColor(rgb: 0x008df2), liveLocationColor: UIColor(rgb: 0xff6464), iconColor: searchTheme.backgroundColor, sectionHeaderBackgroundColor: theme.chatList.sectionHeaderFillColor, sectionHeaderTextColor: theme.chatList.sectionHeaderTextColor, searchBarPallete: TGSearchBarPallete(dark: theme.overallDarkAppearance, backgroundColor: searchTheme.backgroundColor, highContrastBackgroundColor: searchTheme.backgroundColor, textColor: searchTheme.inputTextColor, placeholderColor: searchTheme.inputPlaceholderTextColor, clearIcon: generateClearIcon(color: theme.rootController.activeNavigationSearchBar.inputClearButtonColor), barBackgroundColor: searchTheme.backgroundColor, barSeparatorColor: searchTheme.separatorColor, plainBackgroundColor: searchTheme.backgroundColor, accentColor: searchTheme.accentColor, accentContrastColor: searchTheme.backgroundColor, menuBackgroundColor: searchTheme.backgroundColor, segmentedControlBackgroundImage: nil, segmentedControlSelectedImage: nil, segmentedControlHighlightedImage: nil, segmentedControlDividerImage: nil), avatarPlaceholder: nil) +} + func legacyLocationController(message: Message?, mapMedia: TelegramMediaMap, account: Account, modal: Bool, openPeer: @escaping (Peer) -> Void, sendLiveLocation: @escaping (CLLocationCoordinate2D, Int32) -> Void, stopLiveLocation: @escaping () -> Void, openUrl: @escaping (String) -> Void) -> ViewController { let legacyLocation = TGLocationMediaAttachment() legacyLocation.latitude = mapMedia.latitude @@ -219,10 +225,7 @@ func legacyLocationController(message: Message?, mapMedia: TelegramMediaMap, acc } let theme = (account.telegramApplicationContext.currentPresentationData.with { $0 }).theme - - let listTheme = theme.list - let searchTheme = theme.rootController.activeNavigationSearchBar - controller.pallete = TGLocationPallete(backgroundColor: listTheme.plainBackgroundColor, selectionColor: listTheme.itemHighlightedBackgroundColor, separatorColor: listTheme.itemPlainSeparatorColor, textColor: listTheme.itemPrimaryTextColor, secondaryTextColor: listTheme.itemSecondaryTextColor, accentColor: listTheme.itemAccentColor, destructiveColor: listTheme.itemDestructiveColor, locationColor: UIColor(rgb: 0x008df2), liveLocationColor: UIColor(rgb: 0xff6464), iconColor: searchTheme.backgroundColor, sectionHeaderBackgroundColor: theme.chatList.sectionHeaderFillColor, sectionHeaderTextColor: theme.chatList.sectionHeaderTextColor, searchBarPallete: TGSearchBarPallete(dark: theme.overallDarkAppearance, backgroundColor: searchTheme.backgroundColor, highContrastBackgroundColor: searchTheme.backgroundColor, textColor: searchTheme.inputTextColor, placeholderColor: searchTheme.inputPlaceholderTextColor, clearIcon: generateClearIcon(color: theme.rootController.activeNavigationSearchBar.inputClearButtonColor), barBackgroundColor: searchTheme.backgroundColor, barSeparatorColor: searchTheme.separatorColor, plainBackgroundColor: searchTheme.backgroundColor, accentColor: searchTheme.accentColor, accentContrastColor: searchTheme.backgroundColor, menuBackgroundColor: searchTheme.backgroundColor, segmentedControlBackgroundImage: nil, segmentedControlSelectedImage: nil, segmentedControlHighlightedImage: nil, segmentedControlDividerImage: nil), avatarPlaceholder: nil) + controller.pallete = legacyLocationPalette(from: theme) controller.modalMode = modal controller.presentActionsMenu = { [weak legacyController] legacyLocation, directions in @@ -234,7 +237,7 @@ func legacyLocationController(message: Message?, mapMedia: TelegramMediaMap, acc strongLegacyController.present(ShareController(account: account, subject: .mapMedia(map), externalShare: true), in: .window(.root), with: nil) }) - strongLegacyController.present(OpenInActionSheetController(postbox: account.postbox, applicationContext: account.telegramApplicationContext, theme: presentationData.theme, strings: presentationData.strings, item: .location(location: map, withDirections: directions), additionalAction: !directions ? shareAction : nil, openUrl: openUrl), in: .window(.root), with: nil) + strongLegacyController.present(OpenInActionSheetController(account: account, item: .location(location: map, withDirections: directions), additionalAction: !directions ? shareAction : nil, openUrl: openUrl), in: .window(.root), with: nil) } } @@ -254,10 +257,22 @@ func legacyLocationController(message: Message?, mapMedia: TelegramMediaMap, acc }, rootController: nil) } else { legacyController.navigationItem.title = controller.navigationItem.title - - legacyController.navigationItem.rightBarButtonItem = UIBarButtonItem(image: PresentationResourcesRootController.navigationShareIcon(theme), style: .plain, target: controller, action: #selector(controller.actionsButtonPressed)) + controller.updateRightBarItem = { item, action, animated in + if action { + legacyController.navigationItem.rightBarButtonItem = UIBarButtonItem(image: PresentationResourcesRootController.navigationShareIcon(theme), style: .plain, target: controller, action: #selector(controller.actionsButtonPressed)) + } else { + legacyController.navigationItem.setRightBarButton(item, animated: animated) + } + } legacyController.bind(controller: controller) } + + let presentationDisposable = account.telegramApplicationContext.presentationData.start(next: { [weak controller] presentationData in + if let controller = controller { + controller.pallete = legacyLocationPalette(from: presentationData.theme) + } + }) + legacyController.disposables.add(presentationDisposable) return legacyController } diff --git a/TelegramUI/LegacyLocationPicker.swift b/TelegramUI/LegacyLocationPicker.swift index 49e2a12776..8550effd94 100644 --- a/TelegramUI/LegacyLocationPicker.swift +++ b/TelegramUI/LegacyLocationPicker.swift @@ -14,9 +14,7 @@ func legacyLocationPickerController(account: Account, selfPeer: Peer, peer: Peer let controller = TGLocationPickerController(context: legacyController.context, intent: TGLocationPickerControllerDefaultIntent)! controller.peer = makeLegacyPeer(selfPeer) controller.receivingPeer = makeLegacyPeer(peer) - let listTheme = theme.list - let searchTheme = theme.rootController.activeNavigationSearchBar - controller.pallete = TGLocationPallete(backgroundColor: listTheme.plainBackgroundColor, selectionColor: listTheme.itemHighlightedBackgroundColor, separatorColor: listTheme.itemPlainSeparatorColor, textColor: listTheme.itemPrimaryTextColor, secondaryTextColor: listTheme.itemSecondaryTextColor, accentColor: listTheme.itemAccentColor, destructiveColor: listTheme.itemDestructiveColor, locationColor: UIColor(rgb: 0x008df2), liveLocationColor: UIColor(rgb: 0xff6464), iconColor: searchTheme.backgroundColor, sectionHeaderBackgroundColor: theme.chatList.sectionHeaderFillColor, sectionHeaderTextColor: theme.chatList.sectionHeaderTextColor, searchBarPallete: TGSearchBarPallete(dark: theme.overallDarkAppearance, backgroundColor: searchTheme.inputFillColor, highContrastBackgroundColor: searchTheme.inputFillColor, textColor: searchTheme.inputTextColor, placeholderColor: searchTheme.inputPlaceholderTextColor, clearIcon: generateClearIcon(color: theme.rootController.activeNavigationSearchBar.inputClearButtonColor), barBackgroundColor: searchTheme.backgroundColor, barSeparatorColor: searchTheme.separatorColor, plainBackgroundColor: searchTheme.backgroundColor, accentColor: searchTheme.accentColor, accentContrastColor: searchTheme.backgroundColor, menuBackgroundColor: searchTheme.backgroundColor, segmentedControlBackgroundImage: nil, segmentedControlSelectedImage: nil, segmentedControlHighlightedImage: nil, segmentedControlDividerImage: nil), avatarPlaceholder: nil) + controller.pallete = legacyLocationPalette(from: theme) let namespacesWithEnabledLiveLocation: Set = Set([ Namespaces.Peer.CloudChannel, Namespaces.Peer.CloudGroup, @@ -86,5 +84,11 @@ func legacyLocationPickerController(account: Account, selfPeer: Peer, peer: Peer }) }) } + let presentationDisposable = account.telegramApplicationContext.presentationData.start(next: { [weak controller] presentationData in + if let controller = controller { + controller.pallete = legacyLocationPalette(from: presentationData.theme) + } + }) + legacyController.disposables.add(presentationDisposable) return legacyController } diff --git a/TelegramUI/NotificationsAndSounds.swift b/TelegramUI/NotificationsAndSounds.swift index 3004b1dca1..c03b7a5415 100644 --- a/TelegramUI/NotificationsAndSounds.swift +++ b/TelegramUI/NotificationsAndSounds.swift @@ -432,7 +432,7 @@ private enum NotificationsAndSoundsEntry: ItemListNodeEntry { func item(_ arguments: NotificationsAndSoundsArguments) -> ListViewItem { switch self { case let .permissionInfo(theme, strings, type): - return PermissionInfoItemListItem(theme: theme, strings: strings, subject: .notifications, type: type, sectionId: self.section) + return PermissionInfoItemListItem(theme: theme, strings: strings, subject: .notifications, type: type, style: .blocks, sectionId: self.section) case let .permissionEnable(theme, text): return ItemListActionItem(theme: theme, title: text, kind: .generic, alignment: .natural, sectionId: self.section, style: .blocks, action: { arguments.authorizeNotifications() diff --git a/TelegramUI/OngoingCallContext.swift b/TelegramUI/OngoingCallContext.swift index c376a1f5d7..23f68f7782 100644 --- a/TelegramUI/OngoingCallContext.swift +++ b/TelegramUI/OngoingCallContext.swift @@ -12,7 +12,11 @@ private func callConnectionDescription(_ connection: CallSessionConnection) -> O private let callLogsLimit = 20 private func callLogsPath(account: Account) -> String { - let path = account.basePath + "/calls" + return account.basePath + "/calls" +} + +private func cleanupCallLogs(account: Account) { + let path = callLogsPath(account: account) let fileManager = FileManager.default if !fileManager.fileExists(atPath: path, isDirectory: nil) { try? fileManager.createDirectory(atPath: path, withIntermediateDirectories: true, attributes: nil) @@ -39,7 +43,6 @@ private func callLogsPath(account: Account) -> String { if count > callLogsLimit, let oldest = oldest { try? fileManager.removeItem(atPath: oldest.0.path) } - return path } private let setupLogs: Bool = { @@ -145,7 +148,7 @@ final class OngoingCallContext { return OngoingCallThreadLocalContext.maxLayer() } - init(account: Account, callSessionManager: CallSessionManager, internalId: CallSessionInternalId, proxyServer: ProxyServerSettings?, initialNetworkType: NetworkType, updatedNetworkType: Signal, serializedData: String?, dataSaving: VoiceCallDataSaving) { + init(account: Account, callSessionManager: CallSessionManager, internalId: CallSessionInternalId, proxyServer: ProxyServerSettings?, initialNetworkType: NetworkType, updatedNetworkType: Signal, serializedData: String?, dataSaving: VoiceCallDataSaving, logPath: String) { let _ = setupLogs OngoingCallThreadLocalContext.applyServerConfig(serializedData) @@ -163,7 +166,7 @@ final class OngoingCallContext { break } } - let context = OngoingCallThreadLocalContext(queue: OngoingCallThreadLocalContextQueueImpl(queue: queue), proxy: voipProxyServer, networkType: ongoingNetworkTypeForType(initialNetworkType), dataSaving: ongoingDataSavingForType(dataSaving)) + let context = OngoingCallThreadLocalContext(queue: OngoingCallThreadLocalContextQueueImpl(queue: queue), proxy: voipProxyServer, networkType: ongoingNetworkTypeForType(initialNetworkType), dataSaving: ongoingDataSavingForType(dataSaving), logPath: logPath) self.contextRef = Unmanaged.passRetained(context) context.stateChanged = { [weak self] state in self?.contextState.set(.single(state)) diff --git a/TelegramUI/OngoingCallThreadLocalContext.h b/TelegramUI/OngoingCallThreadLocalContext.h index 901e828b91..70abe60f6f 100644 --- a/TelegramUI/OngoingCallThreadLocalContext.h +++ b/TelegramUI/OngoingCallThreadLocalContext.h @@ -63,7 +63,7 @@ typedef NS_ENUM(int32_t, OngoingCallDataSaving) { @property (nonatomic, copy) void (^ _Nullable signalBarsChanged)(int32_t); @property (nonatomic, copy) void (^ _Nullable callEnded)(NSString * _Nullable debugLog, int64_t bytesSentWifi, int64_t bytesReceivedWifi, int64_t bytesSentMobile, int64_t bytesReceivedMobile); -- (instancetype _Nonnull)initWithQueue:(id _Nonnull)queue proxy:(VoipProxyServer * _Nullable)proxy networkType:(OngoingCallNetworkType)networkType dataSaving:(OngoingCallDataSaving)dataSaving; +- (instancetype _Nonnull)initWithQueue:(id _Nonnull)queue proxy:(VoipProxyServer * _Nullable)proxy networkType:(OngoingCallNetworkType)networkType dataSaving:(OngoingCallDataSaving)dataSaving logPath:(NSString * _Nonnull)logPath; - (void)startWithKey:(NSData * _Nonnull)key isOutgoing:(bool)isOutgoing primaryConnection:(OngoingCallConnectionDescription * _Nonnull)primaryConnection alternativeConnections:(NSArray * _Nonnull)alternativeConnections maxLayer:(int32_t)maxLayer allowP2P:(BOOL)allowP2P; - (void)stop; diff --git a/TelegramUI/OngoingCallThreadLocalContext.mm b/TelegramUI/OngoingCallThreadLocalContext.mm index 2e1f4258d2..a318747fd1 100644 --- a/TelegramUI/OngoingCallThreadLocalContext.mm +++ b/TelegramUI/OngoingCallThreadLocalContext.mm @@ -128,6 +128,7 @@ static void withContext(int32_t contextId, void (^f)(OngoingCallThreadLocalConte NSTimeInterval _callConnectTimeout; NSTimeInterval _callPacketTimeout; int32_t _dataSavingMode; + NSString *_logPath; tgvoip::VoIPController *_controller; @@ -213,7 +214,7 @@ static int callControllerDataSavingForType(OngoingCallDataSaving type) { return tgvoip::VoIPController::GetConnectionMaxLayer(); } -- (instancetype _Nonnull)initWithQueue:(id _Nonnull)queue proxy:(VoipProxyServer * _Nullable)proxy networkType:(OngoingCallNetworkType)networkType dataSaving:(OngoingCallDataSaving)dataSaving { +- (instancetype _Nonnull)initWithQueue:(id _Nonnull)queue proxy:(VoipProxyServer * _Nullable)proxy networkType:(OngoingCallNetworkType)networkType dataSaving:(OngoingCallDataSaving)dataSaving logPath:(NSString * _Nonnull)logPath { self = [super init]; if (self != nil) { _queue = queue; @@ -226,6 +227,7 @@ static int callControllerDataSavingForType(OngoingCallDataSaving type) { _callPacketTimeout = 10.0; _dataSavingMode = callControllerDataSavingForType(dataSaving); _networkType = networkType; + _logPath = logPath; _controller = new tgvoip::VoIPController(); _controller->implData = (void *)((intptr_t)_contextId); @@ -286,7 +288,7 @@ static int callControllerDataSavingForType(OngoingCallDataSaving type) { } tgvoip::VoIPController::Config config(_callConnectTimeout, _callPacketTimeout, _dataSavingMode, false, true, true); - config.logFilePath = ""; + config.logFilePath = _logPath.length > 0 ? std::string(_logPath.UTF8String) : ""; config.statsDumpFilePath = ""; if (_controller != nil) { diff --git a/TelegramUI/OpenInActionSheetController.swift b/TelegramUI/OpenInActionSheetController.swift index 742f9dbb2e..98c8cccc53 100644 --- a/TelegramUI/OpenInActionSheetController.swift +++ b/TelegramUI/OpenInActionSheetController.swift @@ -12,20 +12,27 @@ public struct OpenInControllerAction { } final class OpenInActionSheetController: ActionSheetController { - private let theme: PresentationTheme - private let strings: PresentationStrings + private var presentationDisposable: Disposable? private let _ready = Promise() override var ready: Promise { return self._ready } - init(postbox: Postbox, applicationContext: TelegramApplicationContext, theme: PresentationTheme, strings: PresentationStrings, item: OpenInItem, additionalAction: OpenInControllerAction? = nil, openUrl: @escaping (String) -> Void) { - self.theme = theme - self.strings = strings + init(account: Account, item: OpenInItem, additionalAction: OpenInControllerAction? = nil, openUrl: @escaping (String) -> Void) { + let applicationContext = account.telegramApplicationContext + let presentationData = applicationContext.currentPresentationData.with { $0 } + let theme = presentationData.theme + let strings = presentationData.strings super.init(theme: ActionSheetControllerTheme(presentationTheme: theme)) + self.presentationDisposable = account.telegramApplicationContext.presentationData.start(next: { [weak self] presentationData in + if let strongSelf = self { + strongSelf.theme = ActionSheetControllerTheme(presentationTheme: presentationData.theme) + } + }) + self._ready.set(.single(true)) let invokeActionImpl: (OpenInAction) -> Void = { action in @@ -48,7 +55,7 @@ final class OpenInActionSheetController: ActionSheetController { } var items: [ActionSheetItem] = [] - items.append(OpenInActionSheetItem(postbox: postbox, applicationContext: applicationContext, strings: strings, options: availableOpenInOptions(applicationContext: applicationContext, item: item), invokeAction: invokeActionImpl)) + items.append(OpenInActionSheetItem(postbox: account.postbox, applicationContext: applicationContext, strings: strings, options: availableOpenInOptions(applicationContext: applicationContext, item: item), invokeAction: invokeActionImpl)) if let action = additionalAction { items.append(ActionSheetButtonItem(title: action.title, action: { [weak self] in @@ -70,6 +77,10 @@ final class OpenInActionSheetController: ActionSheetController { required init(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } + + deinit { + self.presentationDisposable?.dispose() + } } private final class OpenInActionSheetItem: ActionSheetItem { diff --git a/TelegramUI/OpenResolvedUrl.swift b/TelegramUI/OpenResolvedUrl.swift index 5430fd1192..fbaf8ed8c2 100644 --- a/TelegramUI/OpenResolvedUrl.swift +++ b/TelegramUI/OpenResolvedUrl.swift @@ -89,7 +89,7 @@ func openResolvedUrl(_ resolvedUrl: ResolvedUrl, account: Account, context: Open } dismissInput() - present(ProxyServerActionSheetController(account: account, theme: presentationData.theme, strings: presentationData.strings, server: server), nil) + present(ProxyServerActionSheetController(account: account, server: server), nil) case let .confirmationCode(code): if let topController = navigationController?.topViewController as? ChangePhoneNumberCodeController { topController.applyCode(code) diff --git a/TelegramUI/OverlayPlayerControllerNode.swift b/TelegramUI/OverlayPlayerControllerNode.swift index 002f39eea5..ac98e976e1 100644 --- a/TelegramUI/OverlayPlayerControllerNode.swift +++ b/TelegramUI/OverlayPlayerControllerNode.swift @@ -68,6 +68,7 @@ final class OverlayPlayerControllerNode: ViewControllerTracingNode, UIGestureRec }, navigateToFirstDateMessage: { _ in }, requestRedeliveryOfFailedMessages: { _ in }, addContact: { _ in + }, rateCall: { _ in }, requestMessageUpdate: { _ in }, cancelInteractiveKeyboardGestures: { }, automaticMediaDownloadSettings: AutomaticMediaDownloadSettings.defaultSettings) diff --git a/TelegramUI/PeerBanTimeoutController.swift b/TelegramUI/PeerBanTimeoutController.swift index 7535dc143f..f0fe99fa3b 100644 --- a/TelegramUI/PeerBanTimeoutController.swift +++ b/TelegramUI/PeerBanTimeoutController.swift @@ -3,25 +3,31 @@ import Display import AsyncDisplayKit import UIKit import SwiftSignalKit -import Photos +import TelegramCore final class PeerBanTimeoutController: ActionSheetController { - private let theme: PresentationTheme - private let strings: PresentationStrings + private var presentationDisposable: Disposable? private let _ready = Promise() override var ready: Promise { return self._ready } - init(theme: PresentationTheme, strings: PresentationStrings, currentValue: Int32, applyValue: @escaping (Int32?) -> Void) { - self.theme = theme - self.strings = strings + init(account: Account, currentValue: Int32, applyValue: @escaping (Int32?) -> Void) { + let presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 } + let theme = presentationData.theme + let strings = presentationData.strings super.init(theme: ActionSheetControllerTheme(presentationTheme: theme)) self._ready.set(.single(true)) + self.presentationDisposable = account.telegramApplicationContext.presentationData.start(next: { [weak self] presentationData in + if let strongSelf = self { + strongSelf.theme = ActionSheetControllerTheme(presentationTheme: presentationData.theme) + } + }) + var updatedValue = currentValue var items: [ActionSheetItem] = [] items.append(PeerBanTimeoutActionSheetItem(strings: strings, currentValue: currentValue, valueChanged: { value in @@ -44,6 +50,10 @@ final class PeerBanTimeoutController: ActionSheetController { required init(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } + + deinit { + self.presentationDisposable?.dispose() + } } private final class PeerBanTimeoutActionSheetItem: ActionSheetItem { diff --git a/TelegramUI/PeerMediaCollectionController.swift b/TelegramUI/PeerMediaCollectionController.swift index 7d4cb151dc..f5b4d7e64d 100644 --- a/TelegramUI/PeerMediaCollectionController.swift +++ b/TelegramUI/PeerMediaCollectionController.swift @@ -233,6 +233,7 @@ public class PeerMediaCollectionController: TelegramController { }, navigateToFirstDateMessage: { _ in }, requestRedeliveryOfFailedMessages: { _ in }, addContact: { _ in + }, rateCall: { _ in }, requestMessageUpdate: { _ in }, cancelInteractiveKeyboardGestures: { }, automaticMediaDownloadSettings: AutomaticMediaDownloadSettings.defaultSettings) diff --git a/TelegramUI/PermissionInfoItem.swift b/TelegramUI/PermissionInfoItem.swift index 7101082922..fd05e1b191 100644 --- a/TelegramUI/PermissionInfoItem.swift +++ b/TelegramUI/PermissionInfoItem.swift @@ -10,12 +10,14 @@ class PermissionInfoItem: ListViewItem { let strings: PresentationStrings let subject: DeviceAccessSubject let type: AccessType + let style: ItemListStyle - init(theme: PresentationTheme, strings: PresentationStrings, subject: DeviceAccessSubject, type: AccessType) { + init(theme: PresentationTheme, strings: PresentationStrings, subject: DeviceAccessSubject, type: AccessType, style: ItemListStyle) { self.theme = theme self.strings = strings self.subject = subject self.type = type + self.style = style } func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal?, (ListViewItemApply) -> Void)) -> Void) { @@ -55,9 +57,9 @@ class PermissionInfoItem: ListViewItem { class PermissionInfoItemListItem: PermissionInfoItem, ItemListItem { let sectionId: ItemListSectionId - init(theme: PresentationTheme, strings: PresentationStrings, subject: DeviceAccessSubject, type: AccessType, sectionId: ItemListSectionId) { + init(theme: PresentationTheme, strings: PresentationStrings, subject: DeviceAccessSubject, type: AccessType, style: ItemListStyle, sectionId: ItemListSectionId) { self.sectionId = sectionId - super.init(theme: theme, strings: strings, subject: subject, type: type) + super.init(theme: theme, strings: strings, subject: subject, type: type, style: style) } override func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal?, (ListViewItemApply) -> Void)) -> Void) { @@ -174,8 +176,18 @@ class PermissionInfoItemNode: ListViewItemNode { insets = UIEdgeInsets() } let separatorHeight = UIScreenPixel - let itemBackgroundColor = item.theme.list.itemBlocksBackgroundColor - let itemSeparatorColor = item.theme.list.itemBlocksSeparatorColor + + let itemBackgroundColor: UIColor + let itemSeparatorColor: UIColor + + switch item.style { + case .plain: + itemBackgroundColor = item.theme.list.plainBackgroundColor + itemSeparatorColor = item.theme.list.itemPlainSeparatorColor + case .blocks: + itemBackgroundColor = item.theme.list.itemBlocksBackgroundColor + itemSeparatorColor = item.theme.list.itemBlocksSeparatorColor + } let title: String let text: String diff --git a/TelegramUI/PreferencesKeys.swift b/TelegramUI/PostboxKeys.swift similarity index 82% rename from TelegramUI/PreferencesKeys.swift rename to TelegramUI/PostboxKeys.swift index 7848ab2b62..0ff762770f 100644 --- a/TelegramUI/PreferencesKeys.swift +++ b/TelegramUI/PostboxKeys.swift @@ -39,3 +39,19 @@ public struct ApplicationSpecificPreferencesKeys { public static let watchPresetSettings = applicationSpecificPreferencesKey(ApplicationSpecificPreferencesKeyValues.watchPresetSettings.rawValue) public static let webSearchSettings = applicationSpecificPreferencesKey(ApplicationSpecificPreferencesKeyValues.webSearchSettings.rawValue) } + +private enum ApplicationSpecificItemCacheCollectionIdValues: Int8 { + case instantPageStoredState = 0 +} + +public struct ApplicationSpecificItemCacheCollectionId { + public static let instantPageStoredState = applicationSpecificItemCacheCollectionId(ApplicationSpecificItemCacheCollectionIdValues.instantPageStoredState.rawValue) +} + +private enum ApplicationSpecificOrderedItemListCollectionIdValues: Int32 { + case webSearchRecentQueries = 0 +} + +public struct ApplicationSpecificOrderedItemListCollectionId { + public static let webSearchRecentQueries = applicationSpecificOrderedItemListCollectionId(ApplicationSpecificOrderedItemListCollectionIdValues.webSearchRecentQueries.rawValue) +} diff --git a/TelegramUI/PresentationCall.swift b/TelegramUI/PresentationCall.swift index 4ef8a08f6d..01281de417 100644 --- a/TelegramUI/PresentationCall.swift +++ b/TelegramUI/PresentationCall.swift @@ -232,7 +232,7 @@ public final class PresentationCall { self.isOutgoing = isOutgoing self.peer = peer - self.ongoingContext = OngoingCallContext(account: account, callSessionManager: self.callSessionManager, internalId: self.internalId, proxyServer: proxyServer, initialNetworkType: currentNetworkType, updatedNetworkType: updatedNetworkType, serializedData: serializedData, dataSaving: dataSaving) + self.ongoingContext = OngoingCallContext(account: account, callSessionManager: self.callSessionManager, internalId: self.internalId, proxyServer: proxyServer, initialNetworkType: currentNetworkType, updatedNetworkType: updatedNetworkType, serializedData: serializedData, dataSaving: dataSaving, logPath: "") var didReceiveAudioOutputs = false self.sessionStateDisposable = (callSessionManager.callState(internalId: internalId) diff --git a/TelegramUI/PresentationData.swift b/TelegramUI/PresentationData.swift index e48f742712..1135dba32a 100644 --- a/TelegramUI/PresentationData.swift +++ b/TelegramUI/PresentationData.swift @@ -366,7 +366,7 @@ private func automaticThemeShouldSwitch(_ settings: AutomaticThemeSwitchSetting, } } -public func updatedPresentationData(postbox: Postbox) -> Signal { +public func updatedPresentationData(postbox: Postbox, applicationBindings: TelegramApplicationBindings) -> Signal { let preferencesKey = PostboxViewKey.preferences(keys: Set([ApplicationSpecificPreferencesKeys.presentationThemeSettings, PreferencesKeys.localizationSettings])) return postbox.combinedView(keys: [preferencesKey]) |> mapToSignal { view -> Signal in @@ -377,64 +377,71 @@ public func updatedPresentationData(postbox: Postbox) -> Signal distinctUntilChanged - |> map { shouldSwitch in - let themeValue: PresentationTheme - let effectiveTheme: PresentationThemeReference - var effectiveChatWallpaper: TelegramWallpaper = themeSettings.chatWallpaper - if shouldSwitch { - effectiveTheme = .builtin(themeSettings.automaticThemeSwitchSetting.theme) - switch effectiveChatWallpaper { - case .builtin, .color: - switch themeSettings.automaticThemeSwitchSetting.theme { - case .nightAccent: - effectiveChatWallpaper = .color(0x18222d) - case .nightGrayscale: - effectiveChatWallpaper = .color(0x000000) + return applicationBindings.applicationInForeground + |> mapToSignal({ inForeground -> Signal in + if inForeground { + return automaticThemeShouldSwitch(themeSettings.automaticThemeSwitchSetting, currentTheme: themeSettings.theme) + |> distinctUntilChanged + |> map { shouldSwitch in + let themeValue: PresentationTheme + let effectiveTheme: PresentationThemeReference + var effectiveChatWallpaper: TelegramWallpaper = themeSettings.chatWallpaper + if shouldSwitch { + effectiveTheme = .builtin(themeSettings.automaticThemeSwitchSetting.theme) + switch effectiveChatWallpaper { + case .builtin, .color: + switch themeSettings.automaticThemeSwitchSetting.theme { + case .nightAccent: + effectiveChatWallpaper = .color(0x18222d) + case .nightGrayscale: + effectiveChatWallpaper = .color(0x000000) + default: + break + } default: break } - default: - break + } else { + effectiveTheme = themeSettings.theme + } + switch effectiveTheme { + case let .builtin(reference): + switch reference { + case .dayClassic: + themeValue = defaultPresentationTheme + case .nightGrayscale: + themeValue = defaultDarkPresentationTheme + case .nightAccent: + themeValue = defaultDarkAccentPresentationTheme + case .day: + themeValue = makeDefaultDayPresentationTheme(accentColor: themeSettings.themeAccentColor ?? defaultDayAccentColor) + } + } + + let localizationSettings: LocalizationSettings? + if let current = (view.views[preferencesKey] as! PreferencesView).values[PreferencesKeys.localizationSettings] as? LocalizationSettings { + localizationSettings = current + } else { + localizationSettings = nil + } + + let stringsValue: PresentationStrings + if let localizationSettings = localizationSettings { + stringsValue = PresentationStrings(primaryComponent: PresentationStringsComponent(languageCode: localizationSettings.primaryComponent.languageCode, localizedName: localizationSettings.primaryComponent.localizedName, pluralizationRulesCode: localizationSettings.primaryComponent.customPluralizationCode, dict: dictFromLocalization(localizationSettings.primaryComponent.localization)), secondaryComponent: localizationSettings.secondaryComponent.flatMap({ PresentationStringsComponent(languageCode: $0.languageCode, localizedName: $0.localizedName, pluralizationRulesCode: $0.customPluralizationCode, dict: dictFromLocalization($0.localization)) })) + } else { + stringsValue = defaultPresentationStrings + } + + let dateTimeFormat = currentDateTimeFormat() + let nameDisplayOrder = currentPersonNameDisplayOrder() + let nameSortOrder = currentPersonNameSortOrder() + + return PresentationData(strings: stringsValue, theme: themeValue, chatWallpaper: effectiveChatWallpaper, volumeControlStatusBarIcons: volumeControlStatusBarIcons(), fontSize: themeSettings.fontSize, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, nameSortOrder: nameSortOrder, disableAnimations: themeSettings.disableAnimations) } } else { - effectiveTheme = themeSettings.theme + return .complete() } - switch effectiveTheme { - case let .builtin(reference): - switch reference { - case .dayClassic: - themeValue = defaultPresentationTheme - case .nightGrayscale: - themeValue = defaultDarkPresentationTheme - case .nightAccent: - themeValue = defaultDarkAccentPresentationTheme - case .day: - themeValue = makeDefaultDayPresentationTheme(accentColor: themeSettings.themeAccentColor ?? defaultDayAccentColor) - } - } - - let localizationSettings: LocalizationSettings? - if let current = (view.views[preferencesKey] as! PreferencesView).values[PreferencesKeys.localizationSettings] as? LocalizationSettings { - localizationSettings = current - } else { - localizationSettings = nil - } - - let stringsValue: PresentationStrings - if let localizationSettings = localizationSettings { - stringsValue = PresentationStrings(primaryComponent: PresentationStringsComponent(languageCode: localizationSettings.primaryComponent.languageCode, localizedName: localizationSettings.primaryComponent.localizedName, pluralizationRulesCode: localizationSettings.primaryComponent.customPluralizationCode, dict: dictFromLocalization(localizationSettings.primaryComponent.localization)), secondaryComponent: localizationSettings.secondaryComponent.flatMap({ PresentationStringsComponent(languageCode: $0.languageCode, localizedName: $0.localizedName, pluralizationRulesCode: $0.customPluralizationCode, dict: dictFromLocalization($0.localization)) })) - } else { - stringsValue = defaultPresentationStrings - } - - let dateTimeFormat = currentDateTimeFormat() - let nameDisplayOrder = currentPersonNameDisplayOrder() - let nameSortOrder = currentPersonNameSortOrder() - - return PresentationData(strings: stringsValue, theme: themeValue, chatWallpaper: effectiveChatWallpaper, volumeControlStatusBarIcons: volumeControlStatusBarIcons(), fontSize: themeSettings.fontSize, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, nameSortOrder: nameSortOrder, disableAnimations: themeSettings.disableAnimations) - } + }) } } diff --git a/TelegramUI/PresentationStrings.swift b/TelegramUI/PresentationStrings.swift index d2926e0a82..a92c939b1b 100644 --- a/TelegramUI/PresentationStrings.swift +++ b/TelegramUI/PresentationStrings.swift @@ -1720,1797 +1720,1801 @@ public final class PresentationStrings { } public var Conversation_PinMessageAlert_OnlyPin: String { return self._s[1437]! } public var Group_Setup_HistoryVisibleHelp: String { return self._s[1438]! } - public func TwoStepAuth_RecoveryEmailUnavailable(_ _0: String) -> (String, [(Int, NSRange)]) { + public func SharedMedia_SearchNoResultsDescription(_ _0: String) -> (String, [(Int, NSRange)]) { return formatWithArgumentRanges(self._s[1440]!, self._r[1440]!, [_0]) } - public var Privacy_PaymentsClearInfoHelp: String { return self._s[1441]! } - public var Presence_online: String { return self._s[1443]! } - public var PasscodeSettings_Title: String { return self._s[1444]! } - public var Passport_Identity_ExpiryDatePlaceholder: String { return self._s[1445]! } - public var Web_OpenExternal: String { return self._s[1446]! } - public func AutoNightTheme_AutomaticHelp(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1447]!, self._r[1447]!, [_0]) + public func TwoStepAuth_RecoveryEmailUnavailable(_ _0: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[1441]!, self._r[1441]!, [_0]) } - public var FastTwoStepSetup_PasswordConfirmationPlaceholder: String { return self._s[1448]! } - public var Map_YouAreHere: String { return self._s[1449]! } + public var Privacy_PaymentsClearInfoHelp: String { return self._s[1442]! } + public var Presence_online: String { return self._s[1444]! } + public var PasscodeSettings_Title: String { return self._s[1445]! } + public var Passport_Identity_ExpiryDatePlaceholder: String { return self._s[1446]! } + public var Web_OpenExternal: String { return self._s[1447]! } + public func AutoNightTheme_AutomaticHelp(_ _0: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[1448]!, self._r[1448]!, [_0]) + } + public var FastTwoStepSetup_PasswordConfirmationPlaceholder: String { return self._s[1449]! } + public var Map_YouAreHere: String { return self._s[1450]! } public func MESSAGE_GIF(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1450]!, self._r[1450]!, [_1]) + return formatWithArgumentRanges(self._s[1451]!, self._r[1451]!, [_1]) } public func AuthSessions_Message(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1451]!, self._r[1451]!, [_0]) + return formatWithArgumentRanges(self._s[1452]!, self._r[1452]!, [_0]) } - public var PrivacyLastSeenSettings_AlwaysShareWith: String { return self._s[1452]! } - public var Target_InviteToGroupErrorAlreadyInvited: String { return self._s[1453]! } + public var PrivacyLastSeenSettings_AlwaysShareWith: String { return self._s[1453]! } + public var Target_InviteToGroupErrorAlreadyInvited: String { return self._s[1454]! } public func AuthSessions_AppUnofficial(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1454]!, self._r[1454]!, [_0]) - } - public func DialogList_LiveLocationSharingTo(_ _0: String) -> (String, [(Int, NSRange)]) { return formatWithArgumentRanges(self._s[1455]!, self._r[1455]!, [_0]) } - public var SocksProxySetup_Username: String { return self._s[1456]! } - public var Bot_Start: String { return self._s[1457]! } - public func Channel_AdminLog_EmptyFilterQueryText(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1458]!, self._r[1458]!, [_0]) + public func DialogList_LiveLocationSharingTo(_ _0: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[1456]!, self._r[1456]!, [_0]) } - public func Channel_AdminLog_MessagePinned(_ _0: String) -> (String, [(Int, NSRange)]) { + public var SocksProxySetup_Username: String { return self._s[1457]! } + public var Bot_Start: String { return self._s[1458]! } + public func Channel_AdminLog_EmptyFilterQueryText(_ _0: String) -> (String, [(Int, NSRange)]) { return formatWithArgumentRanges(self._s[1459]!, self._r[1459]!, [_0]) } + public func Channel_AdminLog_MessagePinned(_ _0: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[1460]!, self._r[1460]!, [_0]) + } public func PINNED_GEOLIVE(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1461]!, self._r[1461]!, [_1]) + return formatWithArgumentRanges(self._s[1462]!, self._r[1462]!, [_1]) } - public var Conversation_DiscardVoiceMessageTitle: String { return self._s[1462]! } + public var Conversation_DiscardVoiceMessageTitle: String { return self._s[1463]! } public func PrivacySettings_LastSeenContactsMinus(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1463]!, self._r[1463]!, [_0]) + return formatWithArgumentRanges(self._s[1464]!, self._r[1464]!, [_0]) } - public var Passport_Email_EnterOtherEmail: String { return self._s[1464]! } - public var Login_InfoAvatarPhoto: String { return self._s[1465]! } - public var Privacy_PaymentsClear_ShippingInfo: String { return self._s[1466]! } - public var Tour_Title4: String { return self._s[1467]! } - public var Passport_Identity_Translation: String { return self._s[1468]! } - public var Login_TermsOfServiceLabel: String { return self._s[1470]! } - public var Passport_Language_it: String { return self._s[1471]! } - public var KeyCommand_JumpToNextUnreadChat: String { return self._s[1472]! } - public var Passport_Identity_SelfieHelp: String { return self._s[1473]! } - public var Conversation_ClearAll: String { return self._s[1475]! } + public var Passport_Email_EnterOtherEmail: String { return self._s[1465]! } + public var Login_InfoAvatarPhoto: String { return self._s[1466]! } + public var Privacy_PaymentsClear_ShippingInfo: String { return self._s[1467]! } + public var Tour_Title4: String { return self._s[1468]! } + public var Passport_Identity_Translation: String { return self._s[1469]! } + public var Login_TermsOfServiceLabel: String { return self._s[1471]! } + public var Passport_Language_it: String { return self._s[1472]! } + public var KeyCommand_JumpToNextUnreadChat: String { return self._s[1473]! } + public var Passport_Identity_SelfieHelp: String { return self._s[1474]! } + public var Conversation_ClearAll: String { return self._s[1476]! } public func MESSAGE_PHOTOS(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1477]!, self._r[1477]!, [_1, _2]) + return formatWithArgumentRanges(self._s[1478]!, self._r[1478]!, [_1, _2]) } - public var TwoStepAuth_FloodError: String { return self._s[1478]! } - public var Paint_Delete: String { return self._s[1479]! } + public var TwoStepAuth_FloodError: String { return self._s[1479]! } + public var Paint_Delete: String { return self._s[1480]! } public func Passport_AcceptHelp(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1480]!, self._r[1480]!, [_1, _2]) + return formatWithArgumentRanges(self._s[1481]!, self._r[1481]!, [_1, _2]) } - public var Message_PinnedAudioMessage: String { return self._s[1481]! } + public var Message_PinnedAudioMessage: String { return self._s[1482]! } public func Watch_Time_ShortTodayAt(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1482]!, self._r[1482]!, [_0]) + return formatWithArgumentRanges(self._s[1483]!, self._r[1483]!, [_0]) } - public var Notification_Mute1hMin: String { return self._s[1483]! } - public var Notifications_GroupNotificationsSound: String { return self._s[1484]! } - public var SocksProxySetup_ShareProxyList: String { return self._s[1485]! } - public var Conversation_MessageEditedLabel: String { return self._s[1486]! } - public var Notification_Exceptions_AlwaysOff: String { return self._s[1487]! } + public var Notification_Mute1hMin: String { return self._s[1484]! } + public var Notifications_GroupNotificationsSound: String { return self._s[1485]! } + public var SocksProxySetup_ShareProxyList: String { return self._s[1486]! } + public var Conversation_MessageEditedLabel: String { return self._s[1487]! } + public var Notification_Exceptions_AlwaysOff: String { return self._s[1488]! } public func Channel_AdminLog_MessageAdmin(_ _0: String, _ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1488]!, self._r[1488]!, [_0, _1, _2]) + return formatWithArgumentRanges(self._s[1489]!, self._r[1489]!, [_0, _1, _2]) } - public var NetworkUsageSettings_ResetStats: String { return self._s[1489]! } - public var AccessDenied_LocationTracking: String { return self._s[1490]! } - public var Month_GenOctober: String { return self._s[1491]! } - public var GroupInfo_InviteLink_RevokeAlert_Revoke: String { return self._s[1492]! } - public var EnterPasscode_EnterPasscode: String { return self._s[1493]! } - public var MediaPicker_TimerTooltip: String { return self._s[1495]! } - public var SharedMedia_TitleAll: String { return self._s[1496]! } - public var Conversation_RestrictedMedia: String { return self._s[1498]! } - public var AccessDenied_PhotosRestricted: String { return self._s[1499]! } - public var ChangePhoneNumberCode_Called: String { return self._s[1501]! } + public var NetworkUsageSettings_ResetStats: String { return self._s[1490]! } + public var AccessDenied_LocationTracking: String { return self._s[1491]! } + public var Month_GenOctober: String { return self._s[1492]! } + public var GroupInfo_InviteLink_RevokeAlert_Revoke: String { return self._s[1493]! } + public var EnterPasscode_EnterPasscode: String { return self._s[1494]! } + public var MediaPicker_TimerTooltip: String { return self._s[1496]! } + public var SharedMedia_TitleAll: String { return self._s[1497]! } + public var Conversation_RestrictedMedia: String { return self._s[1499]! } + public var AccessDenied_PhotosRestricted: String { return self._s[1500]! } + public var ChangePhoneNumberCode_Called: String { return self._s[1502]! } public func Notification_PinnedDocumentMessage(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1502]!, self._r[1502]!, [_0]) + return formatWithArgumentRanges(self._s[1503]!, self._r[1503]!, [_0]) } - public var Conversation_SavedMessages: String { return self._s[1505]! } - public var Your_cards_expiration_month_is_invalid: String { return self._s[1507]! } - public var FastTwoStepSetup_PasswordPlaceholder: String { return self._s[1508]! } + public var Conversation_SavedMessages: String { return self._s[1506]! } + public var Your_cards_expiration_month_is_invalid: String { return self._s[1508]! } + public var FastTwoStepSetup_PasswordPlaceholder: String { return self._s[1509]! } public func Target_ShareGameConfirmationGroup(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1510]!, self._r[1510]!, [_0]) + return formatWithArgumentRanges(self._s[1511]!, self._r[1511]!, [_0]) } - public var ReportPeer_AlertSuccess: String { return self._s[1511]! } + public var ReportPeer_AlertSuccess: String { return self._s[1512]! } public func InstantPage_RelatedArticleAuthorAndDateTitle(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1512]!, self._r[1512]!, [_1, _2]) + return formatWithArgumentRanges(self._s[1513]!, self._r[1513]!, [_1, _2]) } - public var PhotoEditor_CropAspectRatioOriginal: String { return self._s[1513]! } - public var Checkout_PasswordEntry_Title: String { return self._s[1514]! } - public var PhotoEditor_FadeTool: String { return self._s[1515]! } - public var Privacy_ContactsReset: String { return self._s[1516]! } + public var PhotoEditor_CropAspectRatioOriginal: String { return self._s[1514]! } + public var Checkout_PasswordEntry_Title: String { return self._s[1515]! } + public var PhotoEditor_FadeTool: String { return self._s[1516]! } + public var Privacy_ContactsReset: String { return self._s[1517]! } public func Channel_AdminLog_MessageRestrictedUntil(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1518]!, self._r[1518]!, [_0]) + return formatWithArgumentRanges(self._s[1519]!, self._r[1519]!, [_0]) } - public var Message_PinnedVideoMessage: String { return self._s[1519]! } - public var Permissions_CellularDataText_v0: String { return self._s[1520]! } - public var ShareMenu_SelectChats: String { return self._s[1522]! } - public var MusicPlayer_VoiceNote: String { return self._s[1523]! } - public var Conversation_RestrictedText: String { return self._s[1524]! } - public var TwoStepAuth_DisableSuccess: String { return self._s[1525]! } - public var Cache_Videos: String { return self._s[1526]! } - public var FeatureDisabled_Oops: String { return self._s[1528]! } - public var Passport_Address_PostcodePlaceholder: String { return self._s[1529]! } + public var Message_PinnedVideoMessage: String { return self._s[1520]! } + public var Permissions_CellularDataText_v0: String { return self._s[1521]! } + public var ShareMenu_SelectChats: String { return self._s[1523]! } + public var MusicPlayer_VoiceNote: String { return self._s[1524]! } + public var Conversation_RestrictedText: String { return self._s[1525]! } + public var TwoStepAuth_DisableSuccess: String { return self._s[1526]! } + public var Cache_Videos: String { return self._s[1527]! } + public var FeatureDisabled_Oops: String { return self._s[1529]! } + public var Passport_Address_PostcodePlaceholder: String { return self._s[1530]! } public func CHAT_MESSAGE_VIDEO(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1530]!, self._r[1530]!, [_1, _2]) + return formatWithArgumentRanges(self._s[1531]!, self._r[1531]!, [_1, _2]) } - public var Stickers_GroupStickersHelp: String { return self._s[1531]! } - public var Message_VideoExpired: String { return self._s[1533]! } - public var Notifications_Badge: String { return self._s[1534]! } - public var GroupInfo_GroupHistoryVisible: String { return self._s[1535]! } - public var Username_InvalidTooShort: String { return self._s[1536]! } - public var EnterPasscode_EnterNewPasscodeChange: String { return self._s[1537]! } + public var Stickers_GroupStickersHelp: String { return self._s[1532]! } + public var Message_VideoExpired: String { return self._s[1534]! } + public var Notifications_Badge: String { return self._s[1535]! } + public var GroupInfo_GroupHistoryVisible: String { return self._s[1536]! } + public var Username_InvalidTooShort: String { return self._s[1537]! } + public var EnterPasscode_EnterNewPasscodeChange: String { return self._s[1538]! } public func Notification_MessageLifetimeRemoved(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1538]!, self._r[1538]!, [_1]) + return formatWithArgumentRanges(self._s[1539]!, self._r[1539]!, [_1]) } - public var Permissions_SiriAllowInSettings_v0: String { return self._s[1539]! } - public var SharedMedia_CategoryDocs: String { return self._s[1542]! } + public var Permissions_SiriAllowInSettings_v0: String { return self._s[1540]! } + public var SharedMedia_CategoryDocs: String { return self._s[1543]! } public func Notification_MessageLifetimeChangedOutgoing(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1544]!, self._r[1544]!, [_1]) + return formatWithArgumentRanges(self._s[1545]!, self._r[1545]!, [_1]) } - public var CheckoutInfo_ErrorShippingNotAvailable: String { return self._s[1545]! } + public var CheckoutInfo_ErrorShippingNotAvailable: String { return self._s[1546]! } public func Time_MonthOfYear_m12(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1546]!, self._r[1546]!, [_0]) + return formatWithArgumentRanges(self._s[1547]!, self._r[1547]!, [_0]) } - public var ChatSettings_PrivateChats: String { return self._s[1547]! } - public var Channel_UpdatePhotoItem: String { return self._s[1548]! } - public var GroupInfo_LeftStatus: String { return self._s[1549]! } - public var Watch_MessageView_Forward: String { return self._s[1551]! } - public var ReportPeer_ReasonChildAbuse: String { return self._s[1552]! } - public var Cache_ClearEmpty: String { return self._s[1554]! } - public var Localization_LanguageName: String { return self._s[1555]! } - public var WebSearch_GIFs: String { return self._s[1556]! } - public var Notifications_DisplayNamesOnLockScreenInfoWithLink: String { return self._s[1557]! } - public var Username_InvalidStartsWithNumber: String { return self._s[1558]! } - public var Common_Back: String { return self._s[1559]! } - public var Passport_Identity_DateOfBirthPlaceholder: String { return self._s[1560]! } + public var ChatSettings_PrivateChats: String { return self._s[1548]! } + public var Channel_UpdatePhotoItem: String { return self._s[1549]! } + public var GroupInfo_LeftStatus: String { return self._s[1550]! } + public var Watch_MessageView_Forward: String { return self._s[1552]! } + public var ReportPeer_ReasonChildAbuse: String { return self._s[1553]! } + public var Cache_ClearEmpty: String { return self._s[1555]! } + public var Localization_LanguageName: String { return self._s[1556]! } + public var WebSearch_GIFs: String { return self._s[1557]! } + public var Notifications_DisplayNamesOnLockScreenInfoWithLink: String { return self._s[1558]! } + public var Username_InvalidStartsWithNumber: String { return self._s[1559]! } + public var Common_Back: String { return self._s[1560]! } + public var Passport_Identity_DateOfBirthPlaceholder: String { return self._s[1561]! } public func CHANNEL_MESSAGE_AUDIO(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1561]!, self._r[1561]!, [_1]) - } - public func CHANNEL_MESSAGE_ROUND(_ _1: String) -> (String, [(Int, NSRange)]) { return formatWithArgumentRanges(self._s[1562]!, self._r[1562]!, [_1]) } - public var Passport_Email_Help: String { return self._s[1563]! } - public var Watch_Conversation_Reply: String { return self._s[1564]! } - public var Conversation_EditingMessageMediaChange: String { return self._s[1566]! } - public var Passport_Identity_IssueDatePlaceholder: String { return self._s[1567]! } - public var Channel_BanUser_Unban: String { return self._s[1569]! } - public var Channel_EditAdmin_PermissionPostMessages: String { return self._s[1570]! } - public var Group_Username_CreatePublicLinkHelp: String { return self._s[1571]! } - public var TwoStepAuth_ConfirmEmailCodePlaceholder: String { return self._s[1572]! } - public var Passport_Identity_Name: String { return self._s[1574]! } - public var Conversation_BlockUser: String { return self._s[1575]! } - public var Month_GenJanuary: String { return self._s[1576]! } - public var ChatSettings_TextSize: String { return self._s[1577]! } - public var Notification_PassportValuePhone: String { return self._s[1578]! } - public var Passport_Language_ne: String { return self._s[1579]! } - public var Notification_CallBack: String { return self._s[1580]! } - public var TwoStepAuth_EmailHelp: String { return self._s[1581]! } + public func CHANNEL_MESSAGE_ROUND(_ _1: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[1563]!, self._r[1563]!, [_1]) + } + public var Passport_Email_Help: String { return self._s[1564]! } + public var Watch_Conversation_Reply: String { return self._s[1565]! } + public var Conversation_EditingMessageMediaChange: String { return self._s[1567]! } + public var Passport_Identity_IssueDatePlaceholder: String { return self._s[1568]! } + public var Channel_BanUser_Unban: String { return self._s[1570]! } + public var Channel_EditAdmin_PermissionPostMessages: String { return self._s[1571]! } + public var Group_Username_CreatePublicLinkHelp: String { return self._s[1572]! } + public var TwoStepAuth_ConfirmEmailCodePlaceholder: String { return self._s[1573]! } + public var Passport_Identity_Name: String { return self._s[1575]! } + public var Conversation_BlockUser: String { return self._s[1576]! } + public var Month_GenJanuary: String { return self._s[1577]! } + public var ChatSettings_TextSize: String { return self._s[1578]! } + public var Notification_PassportValuePhone: String { return self._s[1579]! } + public var Passport_Language_ne: String { return self._s[1580]! } + public var Notification_CallBack: String { return self._s[1581]! } + public var TwoStepAuth_EmailHelp: String { return self._s[1582]! } public func Time_YesterdayAt(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1582]!, self._r[1582]!, [_0]) + return formatWithArgumentRanges(self._s[1583]!, self._r[1583]!, [_0]) } - public var Channel_Info_Management: String { return self._s[1583]! } - public var Passport_FieldIdentityUploadHelp: String { return self._s[1584]! } - public var Stickers_FrequentlyUsed: String { return self._s[1585]! } - public var Channel_BanUser_PermissionSendMessages: String { return self._s[1586]! } - public var Passport_Address_OneOfTypeUtilityBill: String { return self._s[1588]! } - public var Passport_Address_EditResidentialAddress: String { return self._s[1589]! } - public var PrivacyPolicy_DeclineTitle: String { return self._s[1590]! } + public var Channel_Info_Management: String { return self._s[1584]! } + public var Passport_FieldIdentityUploadHelp: String { return self._s[1585]! } + public var Stickers_FrequentlyUsed: String { return self._s[1586]! } + public var Channel_BanUser_PermissionSendMessages: String { return self._s[1587]! } + public var Passport_Address_OneOfTypeUtilityBill: String { return self._s[1589]! } + public var Passport_Address_EditResidentialAddress: String { return self._s[1590]! } + public var PrivacyPolicy_DeclineTitle: String { return self._s[1591]! } public func Checkout_SavePasswordTimeoutAndTouchId(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1591]!, self._r[1591]!, [_0]) + return formatWithArgumentRanges(self._s[1592]!, self._r[1592]!, [_0]) } - public var PhotoEditor_QualityMedium: String { return self._s[1592]! } - public var InfoPlist_NSMicrophoneUsageDescription: String { return self._s[1593]! } + public var PhotoEditor_QualityMedium: String { return self._s[1593]! } + public var InfoPlist_NSMicrophoneUsageDescription: String { return self._s[1594]! } public func Conversation_RestrictedInlineTimed(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1594]!, self._r[1594]!, [_0]) + return formatWithArgumentRanges(self._s[1595]!, self._r[1595]!, [_0]) } - public var Conversation_StatusKickedFromChannel: String { return self._s[1595]! } - public var CheckoutInfo_ReceiverInfoName: String { return self._s[1596]! } - public var Group_ErrorSendRestrictedStickers: String { return self._s[1597]! } - public var Conversation_LinkDialogOpen: String { return self._s[1599]! } - public var Settings_Username: String { return self._s[1600]! } - public var Wallpaper_Wallpaper: String { return self._s[1602]! } + public var Conversation_StatusKickedFromChannel: String { return self._s[1596]! } + public var CheckoutInfo_ReceiverInfoName: String { return self._s[1597]! } + public var Group_ErrorSendRestrictedStickers: String { return self._s[1598]! } + public var Conversation_LinkDialogOpen: String { return self._s[1600]! } + public var Settings_Username: String { return self._s[1601]! } + public var Wallpaper_Wallpaper: String { return self._s[1603]! } public func PINNED_GIF(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1604]!, self._r[1604]!, [_1]) + return formatWithArgumentRanges(self._s[1605]!, self._r[1605]!, [_1]) } - public var SocksProxySetup_UseProxy: String { return self._s[1605]! } - public var UserInfo_ShareMyContactInfo: String { return self._s[1606]! } - public var MessageTimer_Forever: String { return self._s[1607]! } - public var Privacy_Calls_WhoCanCallMe: String { return self._s[1608]! } - public var PhotoEditor_DiscardChanges: String { return self._s[1609]! } - public var AuthSessions_TerminateOtherSessionsHelp: String { return self._s[1610]! } - public var Passport_Language_da: String { return self._s[1611]! } - public var SocksProxySetup_PortPlaceholder: String { return self._s[1612]! } + public var SocksProxySetup_UseProxy: String { return self._s[1606]! } + public var UserInfo_ShareMyContactInfo: String { return self._s[1607]! } + public var MessageTimer_Forever: String { return self._s[1608]! } + public var Privacy_Calls_WhoCanCallMe: String { return self._s[1609]! } + public var PhotoEditor_DiscardChanges: String { return self._s[1610]! } + public var AuthSessions_TerminateOtherSessionsHelp: String { return self._s[1611]! } + public var Passport_Language_da: String { return self._s[1612]! } + public var SocksProxySetup_PortPlaceholder: String { return self._s[1613]! } public func SecretGIF_NotViewedYet(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1613]!, self._r[1613]!, [_0]) + return formatWithArgumentRanges(self._s[1614]!, self._r[1614]!, [_0]) } - public var Passport_Address_EditPassportRegistration: String { return self._s[1614]! } + public var Passport_Address_EditPassportRegistration: String { return self._s[1615]! } public func Channel_AdminLog_MessageChangedGroupAbout(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1616]!, self._r[1616]!, [_0]) + return formatWithArgumentRanges(self._s[1617]!, self._r[1617]!, [_0]) } - public var Passport_Identity_ResidenceCountryPlaceholder: String { return self._s[1618]! } - public var Conversation_SearchByName_Prefix: String { return self._s[1619]! } + public var Passport_Identity_ResidenceCountryPlaceholder: String { return self._s[1619]! } + public var Conversation_SearchByName_Prefix: String { return self._s[1620]! } public func PINNED_AUDIO(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1620]!, self._r[1620]!, [_1]) + return formatWithArgumentRanges(self._s[1621]!, self._r[1621]!, [_1]) } - public var Conversation_EmptyGifPanelPlaceholder: String { return self._s[1621]! } - public var Cache_ByPeerHeader: String { return self._s[1622]! } + public var Conversation_EmptyGifPanelPlaceholder: String { return self._s[1622]! } + public var Cache_ByPeerHeader: String { return self._s[1623]! } public func Conversation_EncryptedPlaceholderTitleIncoming(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1623]!, self._r[1623]!, [_0]) + return formatWithArgumentRanges(self._s[1624]!, self._r[1624]!, [_0]) } - public var ChatSettings_AutoDownloadDocuments: String { return self._s[1624]! } - public var Notification_PinnedMessage: String { return self._s[1627]! } - public var Call_EncryptionKey_Title: String { return self._s[1630]! } - public var Watch_UserInfo_Service: String { return self._s[1631]! } - public var Conversation_Unpin: String { return self._s[1634]! } - public var CancelResetAccount_Title: String { return self._s[1635]! } - public var Map_LiveLocationFor15Minutes: String { return self._s[1636]! } + public var ChatSettings_AutoDownloadDocuments: String { return self._s[1625]! } + public var Notification_PinnedMessage: String { return self._s[1628]! } + public var Call_EncryptionKey_Title: String { return self._s[1631]! } + public var Watch_UserInfo_Service: String { return self._s[1632]! } + public var Conversation_Unpin: String { return self._s[1635]! } + public var CancelResetAccount_Title: String { return self._s[1636]! } + public var Map_LiveLocationFor15Minutes: String { return self._s[1637]! } public func Time_PreciseDate_m8(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1638]!, self._r[1638]!, [_1, _2, _3]) + return formatWithArgumentRanges(self._s[1639]!, self._r[1639]!, [_1, _2, _3]) } - public var Group_Members_AddMemberBotErrorNotAllowed: String { return self._s[1639]! } - public var CallSettings_Title: String { return self._s[1640]! } - public var PasscodeSettings_EncryptDataHelp: String { return self._s[1642]! } - public var AutoDownloadSettings_Contacts: String { return self._s[1643]! } - public var Passport_Identity_DocumentDetails: String { return self._s[1644]! } - public var LoginPassword_PasswordHelp: String { return self._s[1645]! } - public var PrivacyLastSeenSettings_CustomShareSettings_Delete: String { return self._s[1646]! } - public var Checkout_TotalPaidAmount: String { return self._s[1647]! } + public var Group_Members_AddMemberBotErrorNotAllowed: String { return self._s[1640]! } + public var CallSettings_Title: String { return self._s[1641]! } + public var PasscodeSettings_EncryptDataHelp: String { return self._s[1643]! } + public var AutoDownloadSettings_Contacts: String { return self._s[1644]! } + public var Passport_Identity_DocumentDetails: String { return self._s[1645]! } + public var LoginPassword_PasswordHelp: String { return self._s[1646]! } + public var PrivacyLastSeenSettings_CustomShareSettings_Delete: String { return self._s[1647]! } + public var Checkout_TotalPaidAmount: String { return self._s[1648]! } public func FileSize_KB(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1648]!, self._r[1648]!, [_0]) + return formatWithArgumentRanges(self._s[1649]!, self._r[1649]!, [_0]) } - public var PasscodeSettings_ChangePasscode: String { return self._s[1649]! } - public var Conversation_SecretLinkPreviewAlert: String { return self._s[1651]! } - public var Privacy_SecretChatsLinkPreviews: String { return self._s[1652]! } - public var Contacts_InviteFriends: String { return self._s[1654]! } - public var Map_ChooseLocationTitle: String { return self._s[1655]! } - public var Calls_RatingFeedback: String { return self._s[1657]! } - public var GroupInfo_BroadcastListNamePlaceholder: String { return self._s[1658]! } - public var NotificationsSound_Pulse: String { return self._s[1659]! } - public var Watch_LastSeen_Lately: String { return self._s[1660]! } - public var Widget_NoUsers: String { return self._s[1663]! } - public var NotificationsSound_Circles: String { return self._s[1665]! } - public var PrivacyLastSeenSettings_AlwaysShareWith_Title: String { return self._s[1667]! } + public var PasscodeSettings_ChangePasscode: String { return self._s[1650]! } + public var Conversation_SecretLinkPreviewAlert: String { return self._s[1652]! } + public var Privacy_SecretChatsLinkPreviews: String { return self._s[1653]! } + public var Contacts_InviteFriends: String { return self._s[1655]! } + public var Map_ChooseLocationTitle: String { return self._s[1656]! } + public var Calls_RatingFeedback: String { return self._s[1658]! } + public var GroupInfo_BroadcastListNamePlaceholder: String { return self._s[1659]! } + public var NotificationsSound_Pulse: String { return self._s[1660]! } + public var Watch_LastSeen_Lately: String { return self._s[1661]! } + public var Widget_NoUsers: String { return self._s[1664]! } + public var NotificationsSound_Circles: String { return self._s[1666]! } + public var PrivacyLastSeenSettings_AlwaysShareWith_Title: String { return self._s[1668]! } public func CHANNEL_MESSAGE_GEOLIVE(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1668]!, self._r[1668]!, [_1]) + return formatWithArgumentRanges(self._s[1669]!, self._r[1669]!, [_1]) } - public var TwoStepAuth_RecoveryCodeExpired: String { return self._s[1669]! } + public var TwoStepAuth_RecoveryCodeExpired: String { return self._s[1670]! } public func CHAT_MESSAGE_GIF(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1670]!, self._r[1670]!, [_1, _2]) + return formatWithArgumentRanges(self._s[1671]!, self._r[1671]!, [_1, _2]) } - public var Passport_Identity_CountryPlaceholder: String { return self._s[1672]! } - public var Conversation_FileDropbox: String { return self._s[1674]! } - public var Notifications_ExceptionsUnmuted: String { return self._s[1675]! } - public var Tour_Text3: String { return self._s[1677]! } - public var Login_ResetAccountProtected_Title: String { return self._s[1679]! } - public var ChatAdmins_AllMembersAreAdminsOnHelp: String { return self._s[1680]! } + public var Passport_Identity_CountryPlaceholder: String { return self._s[1673]! } + public var Conversation_FileDropbox: String { return self._s[1675]! } + public var Notifications_ExceptionsUnmuted: String { return self._s[1676]! } + public var Tour_Text3: String { return self._s[1678]! } + public var Login_ResetAccountProtected_Title: String { return self._s[1680]! } + public var ChatAdmins_AllMembersAreAdminsOnHelp: String { return self._s[1681]! } public func Conversation_LiveLocationYouAnd(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1682]!, self._r[1682]!, [_0]) + return formatWithArgumentRanges(self._s[1683]!, self._r[1683]!, [_0]) } - public var GroupInfo_AddParticipantTitle: String { return self._s[1683]! } - public var Checkout_ShippingOption_Title: String { return self._s[1684]! } - public var ChatSettings_AutoDownloadTitle: String { return self._s[1685]! } + public var GroupInfo_AddParticipantTitle: String { return self._s[1684]! } + public var Checkout_ShippingOption_Title: String { return self._s[1685]! } + public var ChatSettings_AutoDownloadTitle: String { return self._s[1686]! } public func DialogList_SingleTypingSuffix(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1686]!, self._r[1686]!, [_0]) + return formatWithArgumentRanges(self._s[1687]!, self._r[1687]!, [_0]) } public func CHAT_MESSAGE_ROUND(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1687]!, self._r[1687]!, [_1, _2]) + return formatWithArgumentRanges(self._s[1688]!, self._r[1688]!, [_1, _2]) } - public var PrivacyLastSeenSettings_NeverShareWith_Placeholder: String { return self._s[1688]! } - public var Appearance_PreviewIncomingText: String { return self._s[1690]! } - public var ChannelInfo_ConfirmLeave: String { return self._s[1691]! } - public var MediaPicker_MomentsDateRangeSameMonthYearFormat: String { return self._s[1692]! } - public var Passport_Identity_DocumentNumberPlaceholder: String { return self._s[1693]! } - public var Channel_AdminLogFilter_EventsNewMembers: String { return self._s[1694]! } - public var PasscodeSettings_AutoLock_IfAwayFor_5minutes: String { return self._s[1695]! } - public var GroupInfo_SetGroupPhotoStop: String { return self._s[1696]! } - public var Notification_SecretChatScreenshot: String { return self._s[1697]! } - public var Passport_Address_City: String { return self._s[1699]! } - public var InfoPlist_NSPhotoLibraryAddUsageDescription: String { return self._s[1700]! } - public var SocksProxySetup_SecretPlaceholder: String { return self._s[1701]! } - public var AccessDenied_LocationDisabled: String { return self._s[1702]! } - public var SocksProxySetup_HostnamePlaceholder: String { return self._s[1704]! } - public var GroupInfo_Sound: String { return self._s[1705]! } - public var Stickers_RemoveFromFavorites: String { return self._s[1706]! } - public var Contacts_Title: String { return self._s[1707]! } - public var Passport_Language_fr: String { return self._s[1708]! } + public var PrivacyLastSeenSettings_NeverShareWith_Placeholder: String { return self._s[1689]! } + public var Appearance_PreviewIncomingText: String { return self._s[1691]! } + public var ChannelInfo_ConfirmLeave: String { return self._s[1692]! } + public var MediaPicker_MomentsDateRangeSameMonthYearFormat: String { return self._s[1693]! } + public var Passport_Identity_DocumentNumberPlaceholder: String { return self._s[1694]! } + public var Channel_AdminLogFilter_EventsNewMembers: String { return self._s[1695]! } + public var PasscodeSettings_AutoLock_IfAwayFor_5minutes: String { return self._s[1696]! } + public var GroupInfo_SetGroupPhotoStop: String { return self._s[1697]! } + public var Notification_SecretChatScreenshot: String { return self._s[1698]! } + public var Passport_Address_City: String { return self._s[1700]! } + public var InfoPlist_NSPhotoLibraryAddUsageDescription: String { return self._s[1701]! } + public var SocksProxySetup_SecretPlaceholder: String { return self._s[1702]! } + public var AccessDenied_LocationDisabled: String { return self._s[1703]! } + public var SocksProxySetup_HostnamePlaceholder: String { return self._s[1705]! } + public var GroupInfo_Sound: String { return self._s[1706]! } + public var Stickers_RemoveFromFavorites: String { return self._s[1707]! } + public var Contacts_Title: String { return self._s[1708]! } + public var Passport_Language_fr: String { return self._s[1709]! } public func CHAT_TITLE_EDITED(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1709]!, self._r[1709]!, [_1, _2]) + return formatWithArgumentRanges(self._s[1710]!, self._r[1710]!, [_1, _2]) } - public var Notifications_ResetAllNotifications: String { return self._s[1710]! } - public var PrivacySettings_SecurityTitle: String { return self._s[1713]! } - public var Checkout_NewCard_Title: String { return self._s[1714]! } - public var Login_HaveNotReceivedCodeInternal: String { return self._s[1715]! } - public var Conversation_ForwardChats: String { return self._s[1716]! } - public var Settings_FAQ: String { return self._s[1719]! } - public var AutoDownloadSettings_DocumentsTitle: String { return self._s[1720]! } - public var Conversation_ContextMenuForward: String { return self._s[1721]! } - public var PrivacyPolicy_Title: String { return self._s[1726]! } - public var Notifications_TextTone: String { return self._s[1727]! } - public var Profile_CreateNewContact: String { return self._s[1728]! } - public var AutoNightTheme_AutomaticSection: String { return self._s[1730]! } - public var Channel_Username_InvalidCharacters: String { return self._s[1732]! } + public var Notifications_ResetAllNotifications: String { return self._s[1711]! } + public var PrivacySettings_SecurityTitle: String { return self._s[1714]! } + public var Checkout_NewCard_Title: String { return self._s[1715]! } + public var Login_HaveNotReceivedCodeInternal: String { return self._s[1716]! } + public var Conversation_ForwardChats: String { return self._s[1717]! } + public var Settings_FAQ: String { return self._s[1720]! } + public var AutoDownloadSettings_DocumentsTitle: String { return self._s[1721]! } + public var Conversation_ContextMenuForward: String { return self._s[1722]! } + public var PrivacyPolicy_Title: String { return self._s[1727]! } + public var Notifications_TextTone: String { return self._s[1728]! } + public var Profile_CreateNewContact: String { return self._s[1729]! } + public var AutoNightTheme_AutomaticSection: String { return self._s[1731]! } + public var Channel_Username_InvalidCharacters: String { return self._s[1733]! } public func Channel_AdminLog_MessageChangedChannelUsername(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1733]!, self._r[1733]!, [_0]) + return formatWithArgumentRanges(self._s[1734]!, self._r[1734]!, [_0]) } - public var Channel_AdminLog_CanInviteUsers: String { return self._s[1734]! } - public var Conversation_MessageDeliveryFailed: String { return self._s[1735]! } - public var TextFormat_Italic: String { return self._s[1736]! } - public var Bot_Unblock: String { return self._s[1737]! } - public var Watch_ChatList_NoConversationsText: String { return self._s[1738]! } - public var Weekday_Wednesday: String { return self._s[1739]! } - public var Settings_About_Help: String { return self._s[1740]! } - public var SearchImages_Title: String { return self._s[1741]! } - public var Conversation_ClousStorageInfo_Description1: String { return self._s[1742]! } - public var ExplicitContent_AlertTitle: String { return self._s[1743]! } + public var Channel_AdminLog_CanInviteUsers: String { return self._s[1735]! } + public var Conversation_MessageDeliveryFailed: String { return self._s[1736]! } + public var TextFormat_Italic: String { return self._s[1737]! } + public var Bot_Unblock: String { return self._s[1738]! } + public var Watch_ChatList_NoConversationsText: String { return self._s[1739]! } + public var Weekday_Wednesday: String { return self._s[1740]! } + public var Settings_About_Help: String { return self._s[1741]! } + public var SearchImages_Title: String { return self._s[1742]! } + public var Conversation_ClousStorageInfo_Description1: String { return self._s[1743]! } + public var ExplicitContent_AlertTitle: String { return self._s[1744]! } public func Time_PreciseDate_m5(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1744]!, self._r[1744]!, [_1, _2, _3]) + return formatWithArgumentRanges(self._s[1745]!, self._r[1745]!, [_1, _2, _3]) } - public var Weekday_Thursday: String { return self._s[1745]! } - public var Channel_Members_AddMembersHelp: String { return self._s[1746]! } + public var Weekday_Thursday: String { return self._s[1746]! } + public var Channel_Members_AddMembersHelp: String { return self._s[1747]! } public func Checkout_SavePasswordTimeout(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1747]!, self._r[1747]!, [_0]) + return formatWithArgumentRanges(self._s[1748]!, self._r[1748]!, [_0]) } - public var Passport_RequestedInformation: String { return self._s[1748]! } - public var Login_PhoneAndCountryHelp: String { return self._s[1749]! } + public var Passport_RequestedInformation: String { return self._s[1749]! } + public var Login_PhoneAndCountryHelp: String { return self._s[1750]! } public func CHAT_MESSAGE_AUDIO(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1750]!, self._r[1750]!, [_1, _2]) + return formatWithArgumentRanges(self._s[1751]!, self._r[1751]!, [_1, _2]) } - public var Conversation_EncryptionProcessing: String { return self._s[1751]! } - public var PhotoEditor_EnhanceTool: String { return self._s[1754]! } - public var Channel_Setup_Title: String { return self._s[1755]! } - public var Conversation_SearchPlaceholder: String { return self._s[1756]! } - public var AccessDenied_LocationAlwaysDenied: String { return self._s[1757]! } - public var Checkout_ErrorGeneric: String { return self._s[1758]! } - public var Passport_Language_hu: String { return self._s[1759]! } + public var Conversation_EncryptionProcessing: String { return self._s[1752]! } + public var PhotoEditor_EnhanceTool: String { return self._s[1755]! } + public var Channel_Setup_Title: String { return self._s[1756]! } + public var Conversation_SearchPlaceholder: String { return self._s[1757]! } + public var AccessDenied_LocationAlwaysDenied: String { return self._s[1758]! } + public var Checkout_ErrorGeneric: String { return self._s[1759]! } + public var Passport_Language_hu: String { return self._s[1760]! } public func Passport_Identity_UploadOneOfScan(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1761]!, self._r[1761]!, [_0]) + return formatWithArgumentRanges(self._s[1762]!, self._r[1762]!, [_0]) } - public var Conversation_CloudStorageInfo_Title: String { return self._s[1764]! } - public var PhotoEditor_CropAspectRatioSquare: String { return self._s[1765]! } + public var Conversation_CloudStorageInfo_Title: String { return self._s[1765]! } + public var PhotoEditor_CropAspectRatioSquare: String { return self._s[1766]! } public func Notification_Exceptions_MutedUntil(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1766]!, self._r[1766]!, [_0]) + return formatWithArgumentRanges(self._s[1767]!, self._r[1767]!, [_0]) } - public var Conversation_ClearPrivateHistory: String { return self._s[1767]! } - public var ContactInfo_PhoneLabelHome: String { return self._s[1768]! } - public var PrivacySettings_LastSeenContacts: String { return self._s[1769]! } + public var Conversation_ClearPrivateHistory: String { return self._s[1768]! } + public var ContactInfo_PhoneLabelHome: String { return self._s[1769]! } + public var PrivacySettings_LastSeenContacts: String { return self._s[1770]! } public func ChangePhone_ErrorOccupied(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1770]!, self._r[1770]!, [_0]) + return formatWithArgumentRanges(self._s[1771]!, self._r[1771]!, [_0]) } - public var Passport_Language_cs: String { return self._s[1771]! } - public var Message_PinnedAnimationMessage: String { return self._s[1772]! } - public var Passport_Identity_ReverseSideHelp: String { return self._s[1774]! } - public var Embed_PlayingInPIP: String { return self._s[1776]! } - public var AutoNightTheme_ScheduleSection: String { return self._s[1777]! } + public var Passport_Language_cs: String { return self._s[1772]! } + public var Message_PinnedAnimationMessage: String { return self._s[1773]! } + public var Passport_Identity_ReverseSideHelp: String { return self._s[1775]! } + public var Embed_PlayingInPIP: String { return self._s[1777]! } + public var AutoNightTheme_ScheduleSection: String { return self._s[1778]! } public func Call_EmojiDescription(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1778]!, self._r[1778]!, [_0]) + return formatWithArgumentRanges(self._s[1779]!, self._r[1779]!, [_0]) } - public var MediaPicker_LivePhotoDescription: String { return self._s[1779]! } + public var MediaPicker_LivePhotoDescription: String { return self._s[1780]! } public func Channel_AdminLog_MessageRestrictedName(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1780]!, self._r[1780]!, [_1]) + return formatWithArgumentRanges(self._s[1781]!, self._r[1781]!, [_1]) } - public var Notification_PaymentSent: String { return self._s[1781]! } - public var PhotoEditor_CurvesGreen: String { return self._s[1782]! } - public var SaveIncomingPhotosSettings_Title: String { return self._s[1783]! } + public var Notification_PaymentSent: String { return self._s[1782]! } + public var PhotoEditor_CurvesGreen: String { return self._s[1783]! } + public var SaveIncomingPhotosSettings_Title: String { return self._s[1784]! } public func ApplyLanguage_UnsufficientDataText(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1784]!, self._r[1784]!, [_1]) + return formatWithArgumentRanges(self._s[1785]!, self._r[1785]!, [_1]) } public func CHAT_MESSAGE_GEOLIVE(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1785]!, self._r[1785]!, [_1, _2]) + return formatWithArgumentRanges(self._s[1786]!, self._r[1786]!, [_1, _2]) } - public var NetworkUsageSettings_CallDataSection: String { return self._s[1786]! } - public var PasscodeSettings_HelpTop: String { return self._s[1787]! } - public var Passport_Address_TypeRentalAgreement: String { return self._s[1788]! } - public var ReportPeer_ReasonOther_Placeholder: String { return self._s[1789]! } - public var CheckoutInfo_ErrorPhoneInvalid: String { return self._s[1790]! } - public var Call_Accept: String { return self._s[1792]! } - public var Month_GenMarch: String { return self._s[1793]! } - public var PhotoEditor_ShadowsTool: String { return self._s[1794]! } - public var LoginPassword_Title: String { return self._s[1795]! } - public var Watch_Conversation_GroupInfo: String { return self._s[1796]! } - public var CallSettings_Always: String { return self._s[1797]! } - public var TwoStepAuth_SetupHint: String { return self._s[1798]! } - public var ConversationProfile_UsersTooMuchError: String { return self._s[1799]! } - public var Login_PhoneTitle: String { return self._s[1800]! } - public var Passport_FieldPhoneHelp: String { return self._s[1801]! } - public var Weekday_ShortSunday: String { return self._s[1802]! } - public var Passport_InfoFAQ_URL: String { return self._s[1803]! } - public var ContactInfo_Job: String { return self._s[1805]! } - public var UserInfo_InviteBotToGroup: String { return self._s[1806]! } - public var TwoStepAuth_PasswordRemovePassportConfirmation: String { return self._s[1807]! } - public var Passport_DeletePersonalDetailsConfirmation: String { return self._s[1808]! } - public var Passport_Identity_AddInternalPassport: String { return self._s[1810]! } - public var MediaPicker_AddCaption: String { return self._s[1811]! } - public var CallSettings_TabIconDescription: String { return self._s[1812]! } - public var Privacy_GroupsAndChannels_AlwaysAllow: String { return self._s[1813]! } - public var Passport_Identity_TypePersonalDetails: String { return self._s[1814]! } - public var DialogList_SearchSectionRecent: String { return self._s[1815]! } - public var PrivacyPolicy_DeclineMessage: String { return self._s[1816]! } - public var LastSeen_WithinAWeek: String { return self._s[1818]! } - public var ChannelMembers_GroupAdminsTitle: String { return self._s[1819]! } - public var Conversation_CloudStorage_ChatStatus: String { return self._s[1821]! } - public var Passport_Address_TypeResidentialAddress: String { return self._s[1822]! } - public var Conversation_StatusLeftGroup: String { return self._s[1823]! } - public var SocksProxySetup_ProxyDetailsTitle: String { return self._s[1824]! } - public var PhotoEditor_BlurToolRadial: String { return self._s[1827]! } - public var Conversation_ContextMenuCopy: String { return self._s[1828]! } - public var AccessDenied_CallMicrophone: String { return self._s[1829]! } + public var NetworkUsageSettings_CallDataSection: String { return self._s[1787]! } + public var PasscodeSettings_HelpTop: String { return self._s[1788]! } + public var Passport_Address_TypeRentalAgreement: String { return self._s[1789]! } + public var ReportPeer_ReasonOther_Placeholder: String { return self._s[1790]! } + public var CheckoutInfo_ErrorPhoneInvalid: String { return self._s[1791]! } + public var Call_Accept: String { return self._s[1793]! } + public var Month_GenMarch: String { return self._s[1794]! } + public var PhotoEditor_ShadowsTool: String { return self._s[1795]! } + public var LoginPassword_Title: String { return self._s[1796]! } + public var Watch_Conversation_GroupInfo: String { return self._s[1797]! } + public var CallSettings_Always: String { return self._s[1798]! } + public var TwoStepAuth_SetupHint: String { return self._s[1799]! } + public var ConversationProfile_UsersTooMuchError: String { return self._s[1800]! } + public var Login_PhoneTitle: String { return self._s[1801]! } + public var Passport_FieldPhoneHelp: String { return self._s[1802]! } + public var Weekday_ShortSunday: String { return self._s[1803]! } + public var Passport_InfoFAQ_URL: String { return self._s[1804]! } + public var ContactInfo_Job: String { return self._s[1806]! } + public var UserInfo_InviteBotToGroup: String { return self._s[1807]! } + public var TwoStepAuth_PasswordRemovePassportConfirmation: String { return self._s[1808]! } + public var Passport_DeletePersonalDetailsConfirmation: String { return self._s[1809]! } + public var Passport_Identity_AddInternalPassport: String { return self._s[1811]! } + public var MediaPicker_AddCaption: String { return self._s[1812]! } + public var CallSettings_TabIconDescription: String { return self._s[1813]! } + public var Privacy_GroupsAndChannels_AlwaysAllow: String { return self._s[1814]! } + public var Passport_Identity_TypePersonalDetails: String { return self._s[1815]! } + public var DialogList_SearchSectionRecent: String { return self._s[1816]! } + public var PrivacyPolicy_DeclineMessage: String { return self._s[1817]! } + public var LastSeen_WithinAWeek: String { return self._s[1819]! } + public var ChannelMembers_GroupAdminsTitle: String { return self._s[1820]! } + public var Conversation_CloudStorage_ChatStatus: String { return self._s[1822]! } + public var Passport_Address_TypeResidentialAddress: String { return self._s[1823]! } + public var Conversation_StatusLeftGroup: String { return self._s[1824]! } + public var SocksProxySetup_ProxyDetailsTitle: String { return self._s[1825]! } + public var PhotoEditor_BlurToolRadial: String { return self._s[1828]! } + public var Conversation_ContextMenuCopy: String { return self._s[1829]! } + public var AccessDenied_CallMicrophone: String { return self._s[1830]! } public func Time_PreciseDate_m2(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1830]!, self._r[1830]!, [_1, _2, _3]) + return formatWithArgumentRanges(self._s[1831]!, self._r[1831]!, [_1, _2, _3]) } - public var Login_InvalidFirstNameError: String { return self._s[1831]! } - public var Notifications_Badge_CountUnreadMessages_InfoOn: String { return self._s[1832]! } - public var Checkout_PaymentMethod_New: String { return self._s[1833]! } - public var ShareMenu_CopyShareLinkGame: String { return self._s[1834]! } - public var PhotoEditor_QualityTool: String { return self._s[1835]! } - public var Login_SendCodeViaSms: String { return self._s[1836]! } + public var Login_InvalidFirstNameError: String { return self._s[1832]! } + public var Notifications_Badge_CountUnreadMessages_InfoOn: String { return self._s[1833]! } + public var Checkout_PaymentMethod_New: String { return self._s[1834]! } + public var ShareMenu_CopyShareLinkGame: String { return self._s[1835]! } + public var PhotoEditor_QualityTool: String { return self._s[1836]! } + public var Login_SendCodeViaSms: String { return self._s[1837]! } public func CHAT_MESSAGE_CONTACT(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1837]!, self._r[1837]!, [_1, _2]) + return formatWithArgumentRanges(self._s[1838]!, self._r[1838]!, [_1, _2]) } - public var Login_EmailNotConfiguredError: String { return self._s[1838]! } - public var PrivacyPolicy_Accept: String { return self._s[1839]! } - public var Notifications_ExceptionsMessagePlaceholder: String { return self._s[1840]! } - public var InfoPlist_NSLocationAlwaysUsageDescription: String { return self._s[1841]! } - public var AutoNightTheme_Automatic: String { return self._s[1842]! } - public var Channel_Username_InvalidStartsWithNumber: String { return self._s[1843]! } - public var Privacy_ContactsSyncHelp: String { return self._s[1844]! } - public var Cache_Help: String { return self._s[1845]! } - public var Passport_Language_fa: String { return self._s[1846]! } - public var Login_ResetAccountProtected_TimerTitle: String { return self._s[1847]! } - public var PrivacySettings_LastSeen: String { return self._s[1848]! } + public var Login_EmailNotConfiguredError: String { return self._s[1839]! } + public var PrivacyPolicy_Accept: String { return self._s[1840]! } + public var Notifications_ExceptionsMessagePlaceholder: String { return self._s[1841]! } + public var InfoPlist_NSLocationAlwaysUsageDescription: String { return self._s[1842]! } + public var AutoNightTheme_Automatic: String { return self._s[1843]! } + public var Channel_Username_InvalidStartsWithNumber: String { return self._s[1844]! } + public var Privacy_ContactsSyncHelp: String { return self._s[1845]! } + public var Cache_Help: String { return self._s[1846]! } + public var Passport_Language_fa: String { return self._s[1847]! } + public var Login_ResetAccountProtected_TimerTitle: String { return self._s[1848]! } + public var PrivacySettings_LastSeen: String { return self._s[1849]! } public func DialogList_MultipleTyping(_ _0: String, _ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1849]!, self._r[1849]!, [_0, _1]) + return formatWithArgumentRanges(self._s[1850]!, self._r[1850]!, [_0, _1]) } - public var Channel_EditAdmin_PermissionInviteUsers: String { return self._s[1850]! } - public var Preview_SaveGif: String { return self._s[1852]! } - public var Profile_About: String { return self._s[1853]! } - public var Channel_About_Placeholder: String { return self._s[1854]! } - public var Login_InfoTitle: String { return self._s[1855]! } + public var Channel_EditAdmin_PermissionInviteUsers: String { return self._s[1851]! } + public var Preview_SaveGif: String { return self._s[1853]! } + public var Profile_About: String { return self._s[1854]! } + public var Channel_About_Placeholder: String { return self._s[1855]! } + public var Login_InfoTitle: String { return self._s[1856]! } public func TwoStepAuth_SetupPendingEmail(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1856]!, self._r[1856]!, [_0]) + return formatWithArgumentRanges(self._s[1857]!, self._r[1857]!, [_0]) } - public var Watch_Suggestion_CantTalk: String { return self._s[1858]! } - public var ContactInfo_Title: String { return self._s[1859]! } - public var Media_ShareThisVideo: String { return self._s[1860]! } - public var Weekday_ShortFriday: String { return self._s[1861]! } - public var AccessDenied_Contacts: String { return self._s[1862]! } - public var Notification_CallIncomingShort: String { return self._s[1863]! } - public var Group_Setup_TypePublic: String { return self._s[1864]! } - public var Notifications_MessageNotificationsExceptions: String { return self._s[1865]! } - public var Notifications_Badge_IncludeChannels: String { return self._s[1866]! } - public var Notifications_MessageNotificationsPreview: String { return self._s[1869]! } - public var ConversationProfile_ErrorCreatingConversation: String { return self._s[1870]! } - public var Group_ErrorAddTooMuchBots: String { return self._s[1871]! } - public var Privacy_GroupsAndChannels_CustomShareHelp: String { return self._s[1872]! } - public var Permissions_CellularDataAllowInSettings_v0: String { return self._s[1873]! } - public var DialogList_Typing: String { return self._s[1874]! } - public var Checkout_Phone: String { return self._s[1877]! } - public var Login_InfoFirstNamePlaceholder: String { return self._s[1880]! } - public var Privacy_Calls_Integration: String { return self._s[1881]! } - public var Notifications_PermissionsAllow: String { return self._s[1882]! } - public var TwoStepAuth_AddHintDescription: String { return self._s[1886]! } - public var Settings_ChatSettings: String { return self._s[1887]! } + public var Watch_Suggestion_CantTalk: String { return self._s[1859]! } + public var ContactInfo_Title: String { return self._s[1860]! } + public var Media_ShareThisVideo: String { return self._s[1861]! } + public var Weekday_ShortFriday: String { return self._s[1862]! } + public var AccessDenied_Contacts: String { return self._s[1863]! } + public var Notification_CallIncomingShort: String { return self._s[1864]! } + public var Group_Setup_TypePublic: String { return self._s[1865]! } + public var Notifications_MessageNotificationsExceptions: String { return self._s[1866]! } + public var Notifications_Badge_IncludeChannels: String { return self._s[1867]! } + public var Notifications_MessageNotificationsPreview: String { return self._s[1870]! } + public var ConversationProfile_ErrorCreatingConversation: String { return self._s[1871]! } + public var Group_ErrorAddTooMuchBots: String { return self._s[1872]! } + public var Privacy_GroupsAndChannels_CustomShareHelp: String { return self._s[1873]! } + public var Permissions_CellularDataAllowInSettings_v0: String { return self._s[1874]! } + public var DialogList_Typing: String { return self._s[1875]! } + public var Checkout_Phone: String { return self._s[1878]! } + public var Login_InfoFirstNamePlaceholder: String { return self._s[1881]! } + public var Privacy_Calls_Integration: String { return self._s[1882]! } + public var Notifications_PermissionsAllow: String { return self._s[1883]! } + public var TwoStepAuth_AddHintDescription: String { return self._s[1887]! } + public var Settings_ChatSettings: String { return self._s[1888]! } public func Channel_AdminLog_MessageInvitedNameUsername(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1888]!, self._r[1888]!, [_1, _2]) + return formatWithArgumentRanges(self._s[1889]!, self._r[1889]!, [_1, _2]) } - public var Login_ContinueWithLocalization: String { return self._s[1890]! } - public var Watch_Message_ForwardedFrom: String { return self._s[1891]! } - public var TwoStepAuth_EnterEmailCode: String { return self._s[1893]! } - public var Conversation_Unblock: String { return self._s[1894]! } - public var PrivacySettings_DataSettings: String { return self._s[1895]! } - public var Notifications_InAppNotificationsVibrate: String { return self._s[1896]! } + public var Login_ContinueWithLocalization: String { return self._s[1891]! } + public var Watch_Message_ForwardedFrom: String { return self._s[1892]! } + public var TwoStepAuth_EnterEmailCode: String { return self._s[1894]! } + public var Conversation_Unblock: String { return self._s[1895]! } + public var PrivacySettings_DataSettings: String { return self._s[1896]! } + public var Notifications_InAppNotificationsVibrate: String { return self._s[1897]! } public func Privacy_GroupsAndChannels_InviteToChannelError(_ _0: String, _ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1897]!, self._r[1897]!, [_0, _1]) + return formatWithArgumentRanges(self._s[1898]!, self._r[1898]!, [_0, _1]) } - public var PrivacySettings_Passcode: String { return self._s[1900]! } + public var PrivacySettings_Passcode: String { return self._s[1901]! } public func ENCRYPTION_ACCEPT(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1901]!, self._r[1901]!, [_1]) + return formatWithArgumentRanges(self._s[1902]!, self._r[1902]!, [_1]) } - public var Passport_Language_dz: String { return self._s[1902]! } - public var Passport_Language_tk: String { return self._s[1903]! } + public var Passport_Language_dz: String { return self._s[1903]! } + public var Passport_Language_tk: String { return self._s[1904]! } public func Login_EmailCodeSubject(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1904]!, self._r[1904]!, [_0]) + return formatWithArgumentRanges(self._s[1905]!, self._r[1905]!, [_0]) } - public var InfoPlist_NSPhotoLibraryUsageDescription: String { return self._s[1905]! } - public var Conversation_ContextMenuReply: String { return self._s[1906]! } - public var Tour_Title1: String { return self._s[1907]! } + public var InfoPlist_NSPhotoLibraryUsageDescription: String { return self._s[1906]! } + public var Conversation_ContextMenuReply: String { return self._s[1907]! } + public var Tour_Title1: String { return self._s[1908]! } public func MESSAGE_STICKER(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1908]!, self._r[1908]!, [_1, _2]) + return formatWithArgumentRanges(self._s[1909]!, self._r[1909]!, [_1, _2]) } - public var Conversation_ClearGroupHistory: String { return self._s[1910]! } + public var Conversation_ClearGroupHistory: String { return self._s[1911]! } public func Checkout_PasswordEntry_Text(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1911]!, self._r[1911]!, [_0]) + return formatWithArgumentRanges(self._s[1912]!, self._r[1912]!, [_0]) } - public var Call_RateCall: String { return self._s[1912]! } - public var Passport_PasswordCompleteSetup: String { return self._s[1913]! } - public var Conversation_InputTextSilentBroadcastPlaceholder: String { return self._s[1914]! } - public var UserInfo_LastNamePlaceholder: String { return self._s[1916]! } + public var Call_RateCall: String { return self._s[1913]! } + public var Passport_PasswordCompleteSetup: String { return self._s[1914]! } + public var Conversation_InputTextSilentBroadcastPlaceholder: String { return self._s[1915]! } + public var UserInfo_LastNamePlaceholder: String { return self._s[1917]! } public func Login_WillCallYou(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1918]!, self._r[1918]!, [_0]) + return formatWithArgumentRanges(self._s[1919]!, self._r[1919]!, [_0]) } - public var Compose_Create: String { return self._s[1919]! } - public var Contacts_InviteToTelegram: String { return self._s[1920]! } - public var GroupInfo_Notifications: String { return self._s[1921]! } - public var Message_PinnedLiveLocationMessage: String { return self._s[1923]! } - public var Month_GenApril: String { return self._s[1924]! } - public var Appearance_AutoNightTheme: String { return self._s[1925]! } - public var ChatSettings_AutomaticAudioDownload: String { return self._s[1927]! } - public var Login_CodeSentSms: String { return self._s[1929]! } + public var Compose_Create: String { return self._s[1920]! } + public var Contacts_InviteToTelegram: String { return self._s[1921]! } + public var GroupInfo_Notifications: String { return self._s[1922]! } + public var Message_PinnedLiveLocationMessage: String { return self._s[1924]! } + public var Month_GenApril: String { return self._s[1925]! } + public var Appearance_AutoNightTheme: String { return self._s[1926]! } + public var ChatSettings_AutomaticAudioDownload: String { return self._s[1928]! } + public var Login_CodeSentSms: String { return self._s[1930]! } public func UserInfo_UnblockConfirmation(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1930]!, self._r[1930]!, [_0]) + return formatWithArgumentRanges(self._s[1931]!, self._r[1931]!, [_0]) } - public var Passport_Language_hr: String { return self._s[1931]! } + public var Passport_Language_hr: String { return self._s[1932]! } public func Channel_AdminLog_MessageRestrictedNewSetting(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1932]!, self._r[1932]!, [_0]) + return formatWithArgumentRanges(self._s[1933]!, self._r[1933]!, [_0]) } - public var GroupInfo_InviteLink_CopyLink: String { return self._s[1933]! } - public var Conversation_InputTextBroadcastPlaceholder: String { return self._s[1934]! } - public var Privacy_SecretChatsTitle: String { return self._s[1935]! } - public var Notification_SecretChatMessageScreenshotSelf: String { return self._s[1937]! } - public var GroupInfo_AddUserLeftError: String { return self._s[1938]! } - public var Preview_DeleteGif: String { return self._s[1939]! } - public var Group_ErrorNotMutualContact: String { return self._s[1940]! } - public var Notification_MessageLifetime5s: String { return self._s[1941]! } + public var GroupInfo_InviteLink_CopyLink: String { return self._s[1934]! } + public var Conversation_InputTextBroadcastPlaceholder: String { return self._s[1935]! } + public var Privacy_SecretChatsTitle: String { return self._s[1936]! } + public var Notification_SecretChatMessageScreenshotSelf: String { return self._s[1938]! } + public var GroupInfo_AddUserLeftError: String { return self._s[1939]! } + public var Preview_DeleteGif: String { return self._s[1940]! } + public var Group_ErrorNotMutualContact: String { return self._s[1941]! } + public var Notification_MessageLifetime5s: String { return self._s[1942]! } public func Watch_LastSeen_AtDate(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1942]!, self._r[1942]!, [_0]) + return formatWithArgumentRanges(self._s[1943]!, self._r[1943]!, [_0]) } - public var Passport_Address_AddBankStatement: String { return self._s[1944]! } - public var Notification_CallIncoming: String { return self._s[1945]! } - public var TwoStepAuth_RecoveryCodeHelp: String { return self._s[1947]! } - public var Passport_Address_Postcode: String { return self._s[1949]! } + public var Passport_Address_AddBankStatement: String { return self._s[1945]! } + public var Notification_CallIncoming: String { return self._s[1946]! } + public var TwoStepAuth_RecoveryCodeHelp: String { return self._s[1948]! } + public var Passport_Address_Postcode: String { return self._s[1950]! } public func LastSeen_YesterdayAt(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1950]!, self._r[1950]!, [_0]) + return formatWithArgumentRanges(self._s[1951]!, self._r[1951]!, [_0]) } - public var Checkout_NewCard_SaveInfoHelp: String { return self._s[1951]! } + public var Checkout_NewCard_SaveInfoHelp: String { return self._s[1952]! } public func Cache_Clear(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1952]!, self._r[1952]!, [_0]) + return formatWithArgumentRanges(self._s[1953]!, self._r[1953]!, [_0]) } - public var Bot_GroupStatusDoesNotReadHistory: String { return self._s[1953]! } - public var Username_Placeholder: String { return self._s[1954]! } - public var Passport_FieldAddressUploadHelp: String { return self._s[1955]! } - public var Permissions_NotificationsAllowInSettings_v0: String { return self._s[1956]! } - public var Passport_PasswordDescription: String { return self._s[1958]! } - public var Channel_MessagePhotoUpdated: String { return self._s[1959]! } - public var MediaPicker_TapToUngroupDescription: String { return self._s[1960]! } - public var AttachmentMenu_PhotoOrVideo: String { return self._s[1961]! } - public var Conversation_ContextMenuMore: String { return self._s[1962]! } - public var Privacy_PaymentsClearInfo: String { return self._s[1963]! } - public var CallSettings_TabIcon: String { return self._s[1964]! } - public var KeyCommand_Find: String { return self._s[1965]! } - public var Message_PinnedGame: String { return self._s[1966]! } - public var Notifications_Badge_CountUnreadMessages_InfoOff: String { return self._s[1967]! } - public var Login_CallRequestState2: String { return self._s[1969]! } - public var CheckoutInfo_ReceiverInfoNamePlaceholder: String { return self._s[1971]! } + public var Bot_GroupStatusDoesNotReadHistory: String { return self._s[1954]! } + public var Username_Placeholder: String { return self._s[1955]! } + public var Passport_FieldAddressUploadHelp: String { return self._s[1956]! } + public var Permissions_NotificationsAllowInSettings_v0: String { return self._s[1957]! } + public var Passport_PasswordDescription: String { return self._s[1959]! } + public var Channel_MessagePhotoUpdated: String { return self._s[1960]! } + public var MediaPicker_TapToUngroupDescription: String { return self._s[1961]! } + public var AttachmentMenu_PhotoOrVideo: String { return self._s[1962]! } + public var Conversation_ContextMenuMore: String { return self._s[1963]! } + public var Privacy_PaymentsClearInfo: String { return self._s[1964]! } + public var CallSettings_TabIcon: String { return self._s[1965]! } + public var KeyCommand_Find: String { return self._s[1966]! } + public var Message_PinnedGame: String { return self._s[1967]! } + public var Notifications_Badge_CountUnreadMessages_InfoOff: String { return self._s[1968]! } + public var Login_CallRequestState2: String { return self._s[1970]! } + public var CheckoutInfo_ReceiverInfoNamePlaceholder: String { return self._s[1972]! } public func Checkout_PayPrice(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1973]!, self._r[1973]!, [_0]) + return formatWithArgumentRanges(self._s[1974]!, self._r[1974]!, [_0]) } - public var Conversation_InstantPagePreview: String { return self._s[1974]! } + public var Conversation_InstantPagePreview: String { return self._s[1975]! } public func DialogList_SingleUploadingVideoSuffix(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1975]!, self._r[1975]!, [_0]) + return formatWithArgumentRanges(self._s[1976]!, self._r[1976]!, [_0]) } - public var SecretTimer_VideoDescription: String { return self._s[1978]! } - public var Passport_Language_es: String { return self._s[1979]! } - public var Permissions_ContactsAllow_v0: String { return self._s[1981]! } - public var Conversation_EditingMessageMediaEditCurrentVideo: String { return self._s[1982]! } - public var WebPreview_GettingLinkInfo: String { return self._s[1983]! } - public var Watch_UserInfo_Unmute: String { return self._s[1984]! } - public var GroupInfo_ChannelListNamePlaceholder: String { return self._s[1985]! } - public var ChatList_ReadAll: String { return self._s[1987]! } - public var AccessDenied_CameraRestricted: String { return self._s[1988]! } + public var SecretTimer_VideoDescription: String { return self._s[1979]! } + public var Passport_Language_es: String { return self._s[1980]! } + public var Permissions_ContactsAllow_v0: String { return self._s[1982]! } + public var Conversation_EditingMessageMediaEditCurrentVideo: String { return self._s[1983]! } + public var WebPreview_GettingLinkInfo: String { return self._s[1984]! } + public var Watch_UserInfo_Unmute: String { return self._s[1985]! } + public var GroupInfo_ChannelListNamePlaceholder: String { return self._s[1986]! } + public var ChatList_ReadAll: String { return self._s[1988]! } + public var AccessDenied_CameraRestricted: String { return self._s[1989]! } public func Conversation_Kilobytes(_ _0: Int) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1990]!, self._r[1990]!, ["\(_0)"]) + return formatWithArgumentRanges(self._s[1991]!, self._r[1991]!, ["\(_0)"]) } - public var Contacts_SearchLabel: String { return self._s[1992]! } - public var Settings_CopyUsername: String { return self._s[1993]! } + public var Contacts_SearchLabel: String { return self._s[1993]! } + public var Settings_CopyUsername: String { return self._s[1994]! } public func MESSAGE_GEOLIVE(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[1994]!, self._r[1994]!, [_1]) + return formatWithArgumentRanges(self._s[1995]!, self._r[1995]!, [_1]) } - public var Map_OpenInYandexNavigator: String { return self._s[1995]! } - public var PasscodeSettings_EncryptData: String { return self._s[1996]! } - public var Notifications_GroupNotificationsPreview: String { return self._s[1997]! } - public var DialogList_AdNoticeAlert: String { return self._s[1998]! } + public var Map_OpenInYandexNavigator: String { return self._s[1996]! } + public var PasscodeSettings_EncryptData: String { return self._s[1997]! } + public var Notifications_GroupNotificationsPreview: String { return self._s[1998]! } + public var DialogList_AdNoticeAlert: String { return self._s[1999]! } public func CHAT_DELETE_MEMBER(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2000]!, self._r[2000]!, [_1, _2, _3]) + return formatWithArgumentRanges(self._s[2001]!, self._r[2001]!, [_1, _2, _3]) } - public var CheckoutInfo_ShippingInfoAddress1: String { return self._s[2001]! } - public var CheckoutInfo_ShippingInfoAddress2: String { return self._s[2002]! } - public var Localization_LanguageCustom: String { return self._s[2003]! } - public var Passport_Identity_TypeDriversLicenseUploadScan: String { return self._s[2004]! } + public var CheckoutInfo_ShippingInfoAddress1: String { return self._s[2002]! } + public var CheckoutInfo_ShippingInfoAddress2: String { return self._s[2003]! } + public var Localization_LanguageCustom: String { return self._s[2004]! } + public var Passport_Identity_TypeDriversLicenseUploadScan: String { return self._s[2005]! } public func CHAT_CREATED(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2005]!, self._r[2005]!, [_1, _2]) + return formatWithArgumentRanges(self._s[2006]!, self._r[2006]!, [_1, _2]) } - public var Passport_Address_OneOfTypePassportRegistration: String { return self._s[2007]! } - public var Conversation_InfoGroup: String { return self._s[2008]! } - public var Compose_NewMessage: String { return self._s[2009]! } - public var FastTwoStepSetup_HintPlaceholder: String { return self._s[2010]! } - public var ChatSettings_AutoDownloadVideoMessages: String { return self._s[2011]! } + public var Passport_Address_OneOfTypePassportRegistration: String { return self._s[2008]! } + public var Conversation_InfoGroup: String { return self._s[2009]! } + public var Compose_NewMessage: String { return self._s[2010]! } + public var FastTwoStepSetup_HintPlaceholder: String { return self._s[2011]! } + public var ChatSettings_AutoDownloadVideoMessages: String { return self._s[2012]! } public func Passport_Scans_ScanIndex(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2012]!, self._r[2012]!, [_0]) + return formatWithArgumentRanges(self._s[2013]!, self._r[2013]!, [_0]) } - public var Channel_AdminLog_CanDeleteMessages: String { return self._s[2013]! } - public var Login_CancelSignUpConfirmation: String { return self._s[2014]! } - public var ChangePhoneNumberCode_Help: String { return self._s[2015]! } - public var PrivacySettings_DeleteAccountHelp: String { return self._s[2016]! } - public var Channel_BlackList_Title: String { return self._s[2017]! } - public var UserInfo_PhoneCall: String { return self._s[2018]! } - public var Passport_Address_OneOfTypeBankStatement: String { return self._s[2020]! } - public var State_connecting: String { return self._s[2021]! } + public var Channel_AdminLog_CanDeleteMessages: String { return self._s[2014]! } + public var Login_CancelSignUpConfirmation: String { return self._s[2015]! } + public var ChangePhoneNumberCode_Help: String { return self._s[2016]! } + public var PrivacySettings_DeleteAccountHelp: String { return self._s[2017]! } + public var Channel_BlackList_Title: String { return self._s[2018]! } + public var UserInfo_PhoneCall: String { return self._s[2019]! } + public var Passport_Address_OneOfTypeBankStatement: String { return self._s[2021]! } + public var State_connecting: String { return self._s[2022]! } public func DialogList_SingleRecordingAudioSuffix(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2022]!, self._r[2022]!, [_0]) + return formatWithArgumentRanges(self._s[2023]!, self._r[2023]!, [_0]) } - public var Notifications_GroupNotifications: String { return self._s[2023]! } - public var Passport_Identity_EditPassport: String { return self._s[2024]! } - public var EnterPasscode_RepeatNewPasscode: String { return self._s[2026]! } - public var Localization_EnglishLanguageName: String { return self._s[2027]! } - public var Share_AuthDescription: String { return self._s[2028]! } - public var Passport_Identity_Surname: String { return self._s[2029]! } - public var Compose_TokenListPlaceholder: String { return self._s[2030]! } - public var Passport_Identity_OneOfTypePassport: String { return self._s[2031]! } - public var Settings_AboutEmpty: String { return self._s[2032]! } - public var Conversation_Unmute: String { return self._s[2033]! } - public var Login_CodeSentCall: String { return self._s[2036]! } - public var ContactInfo_PhoneLabelHomeFax: String { return self._s[2037]! } - public var ChatSettings_Appearance: String { return self._s[2038]! } - public var Appearance_PickAccentColor: String { return self._s[2039]! } - public var Notification_CallMissed: String { return self._s[2040]! } - public var Channel_AdminLogFilter_EventsInfo: String { return self._s[2041]! } - public var ChatAdmins_AdminLabel: String { return self._s[2043]! } - public var KeyCommand_JumpToNextChat: String { return self._s[2044]! } - public var ChangePhoneNumberCode_CodePlaceholder: String { return self._s[2046]! } - public var Month_GenJune: String { return self._s[2047]! } - public var Watch_Location_Current: String { return self._s[2048]! } + public var Notifications_GroupNotifications: String { return self._s[2024]! } + public var Passport_Identity_EditPassport: String { return self._s[2025]! } + public var EnterPasscode_RepeatNewPasscode: String { return self._s[2027]! } + public var Localization_EnglishLanguageName: String { return self._s[2028]! } + public var Share_AuthDescription: String { return self._s[2029]! } + public var Passport_Identity_Surname: String { return self._s[2030]! } + public var Compose_TokenListPlaceholder: String { return self._s[2031]! } + public var Passport_Identity_OneOfTypePassport: String { return self._s[2032]! } + public var Settings_AboutEmpty: String { return self._s[2033]! } + public var Conversation_Unmute: String { return self._s[2034]! } + public var Login_CodeSentCall: String { return self._s[2037]! } + public var ContactInfo_PhoneLabelHomeFax: String { return self._s[2038]! } + public var ChatSettings_Appearance: String { return self._s[2039]! } + public var Appearance_PickAccentColor: String { return self._s[2040]! } + public var Notification_CallMissed: String { return self._s[2041]! } + public var Channel_AdminLogFilter_EventsInfo: String { return self._s[2042]! } + public var ChatAdmins_AdminLabel: String { return self._s[2044]! } + public var KeyCommand_JumpToNextChat: String { return self._s[2045]! } + public var ChangePhoneNumberCode_CodePlaceholder: String { return self._s[2047]! } + public var Month_GenJune: String { return self._s[2048]! } + public var Watch_Location_Current: String { return self._s[2049]! } public func PINNED_ROUND(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2049]!, self._r[2049]!, [_1]) + return formatWithArgumentRanges(self._s[2050]!, self._r[2050]!, [_1]) } - public var GroupInfo_DeleteAndExit: String { return self._s[2050]! } + public var GroupInfo_DeleteAndExit: String { return self._s[2051]! } public func Conversation_Moderate_DeleteAllMessages(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2051]!, self._r[2051]!, [_0]) + return formatWithArgumentRanges(self._s[2052]!, self._r[2052]!, [_0]) } - public var Call_ReportPlaceholder: String { return self._s[2052]! } - public var MaskStickerSettings_Info: String { return self._s[2053]! } + public var Call_ReportPlaceholder: String { return self._s[2053]! } + public var MaskStickerSettings_Info: String { return self._s[2054]! } public func GroupInfo_AddParticipantConfirmation(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2054]!, self._r[2054]!, [_0]) + return formatWithArgumentRanges(self._s[2055]!, self._r[2055]!, [_0]) } - public var Checkout_NewCard_PostcodeTitle: String { return self._s[2055]! } - public var Passport_Address_RegionPlaceholder: String { return self._s[2057]! } - public var Contacts_ShareTelegram: String { return self._s[2058]! } - public var EnterPasscode_EnterNewPasscodeNew: String { return self._s[2059]! } - public var Channel_ErrorAccessDenied: String { return self._s[2060]! } - public var Stickers_GroupChooseStickerPack: String { return self._s[2062]! } - public var Call_ConnectionErrorTitle: String { return self._s[2063]! } - public var UserInfo_NotificationsEnable: String { return self._s[2064]! } - public var Tour_Text4: String { return self._s[2067]! } + public var Checkout_NewCard_PostcodeTitle: String { return self._s[2056]! } + public var Passport_Address_RegionPlaceholder: String { return self._s[2058]! } + public var Contacts_ShareTelegram: String { return self._s[2059]! } + public var EnterPasscode_EnterNewPasscodeNew: String { return self._s[2060]! } + public var Channel_ErrorAccessDenied: String { return self._s[2061]! } + public var Stickers_GroupChooseStickerPack: String { return self._s[2063]! } + public var Call_ConnectionErrorTitle: String { return self._s[2064]! } + public var UserInfo_NotificationsEnable: String { return self._s[2065]! } + public var Tour_Text4: String { return self._s[2068]! } public func CHANNEL_MESSAGE_NOTEXT(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2069]!, self._r[2069]!, [_1]) + return formatWithArgumentRanges(self._s[2070]!, self._r[2070]!, [_1]) } - public var Profile_MessageLifetime2s: String { return self._s[2070]! } - public var Notification_MessageLifetime2s: String { return self._s[2071]! } + public var Profile_MessageLifetime2s: String { return self._s[2071]! } + public var Notification_MessageLifetime2s: String { return self._s[2072]! } public func Time_PreciseDate_m10(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2072]!, self._r[2072]!, [_1, _2, _3]) + return formatWithArgumentRanges(self._s[2073]!, self._r[2073]!, [_1, _2, _3]) } - public var Cache_ClearCache: String { return self._s[2073]! } - public var AutoNightTheme_UpdateLocation: String { return self._s[2074]! } - public var Permissions_NotificationsUnreachableText_v0: String { return self._s[2075]! } + public var Cache_ClearCache: String { return self._s[2074]! } + public var AutoNightTheme_UpdateLocation: String { return self._s[2075]! } + public var Permissions_NotificationsUnreachableText_v0: String { return self._s[2076]! } public func Channel_AdminLog_MessageChangedGroupUsername(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2077]!, self._r[2077]!, [_0]) + return formatWithArgumentRanges(self._s[2078]!, self._r[2078]!, [_0]) } - public var Channel_AdminLog_EmptyFilterTitle: String { return self._s[2079]! } - public var SocksProxySetup_TypeSocks: String { return self._s[2080]! } - public var AutoNightTheme_Title: String { return self._s[2081]! } - public var InstantPage_FeedbackButton: String { return self._s[2082]! } - public var Passport_FieldAddress: String { return self._s[2083]! } - public var Month_ShortMarch: String { return self._s[2084]! } - public var SocksProxySetup_UsernamePlaceholder: String { return self._s[2085]! } - public var Conversation_ShareInlineBotLocationConfirmation: String { return self._s[2086]! } - public var Passport_FloodError: String { return self._s[2087]! } - public var SecretGif_Title: String { return self._s[2088]! } - public var Passport_Language_th: String { return self._s[2090]! } - public var Passport_Address_Address: String { return self._s[2091]! } - public var Login_InvalidLastNameError: String { return self._s[2092]! } - public var Notifications_InAppNotificationsPreview: String { return self._s[2093]! } - public var ShareMenu_Send: String { return self._s[2094]! } - public var Month_GenNovember: String { return self._s[2097]! } - public var Checkout_Email: String { return self._s[2099]! } - public var NotificationsSound_Tritone: String { return self._s[2100]! } - public var StickerPacksSettings_ManagingHelp: String { return self._s[2102]! } - public var ChangePhoneNumberNumber_Help: String { return self._s[2105]! } + public var Channel_AdminLog_EmptyFilterTitle: String { return self._s[2080]! } + public var SocksProxySetup_TypeSocks: String { return self._s[2081]! } + public var AutoNightTheme_Title: String { return self._s[2082]! } + public var InstantPage_FeedbackButton: String { return self._s[2083]! } + public var Passport_FieldAddress: String { return self._s[2084]! } + public var Month_ShortMarch: String { return self._s[2085]! } + public var SocksProxySetup_UsernamePlaceholder: String { return self._s[2086]! } + public var Conversation_ShareInlineBotLocationConfirmation: String { return self._s[2087]! } + public var Passport_FloodError: String { return self._s[2088]! } + public var SecretGif_Title: String { return self._s[2089]! } + public var Passport_Language_th: String { return self._s[2091]! } + public var Passport_Address_Address: String { return self._s[2092]! } + public var Login_InvalidLastNameError: String { return self._s[2093]! } + public var Notifications_InAppNotificationsPreview: String { return self._s[2094]! } + public var ShareMenu_Send: String { return self._s[2095]! } + public var Month_GenNovember: String { return self._s[2098]! } + public var Checkout_Email: String { return self._s[2100]! } + public var NotificationsSound_Tritone: String { return self._s[2101]! } + public var StickerPacksSettings_ManagingHelp: String { return self._s[2103]! } + public var ChangePhoneNumberNumber_Help: String { return self._s[2106]! } public func Checkout_LiabilityAlert(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2106]!, self._r[2106]!, [_1, _1, _1, _2]) + return formatWithArgumentRanges(self._s[2107]!, self._r[2107]!, [_1, _1, _1, _2]) } - public var DialogList_You: String { return self._s[2107]! } - public var MediaPicker_Send: String { return self._s[2110]! } - public var Call_AudioRouteSpeaker: String { return self._s[2111]! } - public var Watch_UserInfo_Title: String { return self._s[2112]! } - public var Appearance_AccentColor: String { return self._s[2113]! } + public var DialogList_You: String { return self._s[2108]! } + public var MediaPicker_Send: String { return self._s[2111]! } + public var Call_AudioRouteSpeaker: String { return self._s[2112]! } + public var Watch_UserInfo_Title: String { return self._s[2113]! } + public var Appearance_AccentColor: String { return self._s[2114]! } public func Login_EmailPhoneSubject(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2114]!, self._r[2114]!, [_0]) + return formatWithArgumentRanges(self._s[2115]!, self._r[2115]!, [_0]) } - public var Permissions_ContactsAllowInSettings_v0: String { return self._s[2115]! } - public var Conversation_ClousStorageInfo_Description2: String { return self._s[2116]! } - public var WebSearch_RecentClearConfirmation: String { return self._s[2117]! } - public var Notification_CallOutgoing: String { return self._s[2118]! } - public var PrivacySettings_PasscodeAndFaceId: String { return self._s[2119]! } - public var Call_RecordingDisabledMessage: String { return self._s[2120]! } - public var PrivacyLastSeenSettings_CustomHelp: String { return self._s[2121]! } - public var Channel_EditAdmin_PermissionAddAdmins: String { return self._s[2122]! } - public var Date_DialogDateFormat: String { return self._s[2123]! } - public var Notifications_InAppNotifications: String { return self._s[2124]! } + public var Permissions_ContactsAllowInSettings_v0: String { return self._s[2116]! } + public var Conversation_ClousStorageInfo_Description2: String { return self._s[2117]! } + public var WebSearch_RecentClearConfirmation: String { return self._s[2118]! } + public var Notification_CallOutgoing: String { return self._s[2119]! } + public var PrivacySettings_PasscodeAndFaceId: String { return self._s[2120]! } + public var Call_RecordingDisabledMessage: String { return self._s[2121]! } + public var PrivacyLastSeenSettings_CustomHelp: String { return self._s[2122]! } + public var Channel_EditAdmin_PermissionAddAdmins: String { return self._s[2123]! } + public var Date_DialogDateFormat: String { return self._s[2124]! } + public var Notifications_InAppNotifications: String { return self._s[2125]! } public func Settings_ApplyProxyAlert(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2125]!, self._r[2125]!, [_1, _2]) + return formatWithArgumentRanges(self._s[2126]!, self._r[2126]!, [_1, _2]) } - public var NewContact_Title: String { return self._s[2126]! } - public var Conversation_ViewContactDetails: String { return self._s[2127]! } - public var Checkout_NewCard_CardholderNameTitle: String { return self._s[2128]! } - public var Passport_Identity_ExpiryDateNone: String { return self._s[2129]! } - public var PrivacySettings_Title: String { return self._s[2130]! } - public var Conversation_SilentBroadcastTooltipOff: String { return self._s[2133]! } + public var NewContact_Title: String { return self._s[2127]! } + public var Conversation_ViewContactDetails: String { return self._s[2128]! } + public var Checkout_NewCard_CardholderNameTitle: String { return self._s[2129]! } + public var Passport_Identity_ExpiryDateNone: String { return self._s[2130]! } + public var PrivacySettings_Title: String { return self._s[2131]! } + public var Conversation_SilentBroadcastTooltipOff: String { return self._s[2134]! } public func CHANNEL_MESSAGE_CONTACT(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2134]!, self._r[2134]!, [_1]) + return formatWithArgumentRanges(self._s[2135]!, self._r[2135]!, [_1]) } - public var Contacts_PhoneNumber: String { return self._s[2135]! } - public var Map_ShowPlaces: String { return self._s[2137]! } - public var ChatAdmins_Title: String { return self._s[2138]! } - public var InstantPage_Reference: String { return self._s[2140]! } - public var Camera_FlashOff: String { return self._s[2141]! } - public var Watch_UserInfo_Block: String { return self._s[2142]! } - public var ChatSettings_Stickers: String { return self._s[2143]! } - public var ChatSettings_DownloadInBackground: String { return self._s[2144]! } + public var Contacts_PhoneNumber: String { return self._s[2136]! } + public var Map_ShowPlaces: String { return self._s[2138]! } + public var ChatAdmins_Title: String { return self._s[2139]! } + public var InstantPage_Reference: String { return self._s[2141]! } + public var Camera_FlashOff: String { return self._s[2142]! } + public var Watch_UserInfo_Block: String { return self._s[2143]! } + public var ChatSettings_Stickers: String { return self._s[2144]! } + public var ChatSettings_DownloadInBackground: String { return self._s[2145]! } public func UserInfo_BlockConfirmation(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2145]!, self._r[2145]!, [_0]) + return formatWithArgumentRanges(self._s[2146]!, self._r[2146]!, [_0]) } - public var Login_CheckOtherSessionMessages: String { return self._s[2146]! } - public var Settings_ViewPhoto: String { return self._s[2147]! } - public var AutoDownloadSettings_Cellular: String { return self._s[2148]! } + public var Login_CheckOtherSessionMessages: String { return self._s[2147]! } + public var Settings_ViewPhoto: String { return self._s[2148]! } + public var AutoDownloadSettings_Cellular: String { return self._s[2149]! } public func Target_InviteToGroupConfirmation(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2150]!, self._r[2150]!, [_0]) + return formatWithArgumentRanges(self._s[2151]!, self._r[2151]!, [_0]) } - public var Privacy_DeleteDrafts: String { return self._s[2151]! } + public var Privacy_DeleteDrafts: String { return self._s[2152]! } public func LastSeen_AtDate(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2152]!, self._r[2152]!, [_0]) + return formatWithArgumentRanges(self._s[2153]!, self._r[2153]!, [_0]) } - public var DialogList_SavedMessagesHelp: String { return self._s[2153]! } - public var DialogList_SavedMessages: String { return self._s[2154]! } - public var GroupInfo_UpgradeButton: String { return self._s[2155]! } + public var DialogList_SavedMessagesHelp: String { return self._s[2154]! } + public var DialogList_SavedMessages: String { return self._s[2155]! } + public var GroupInfo_UpgradeButton: String { return self._s[2156]! } public func CHAT_MESSAGE_GAME(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2156]!, self._r[2156]!, [_1, _2, _3]) + return formatWithArgumentRanges(self._s[2157]!, self._r[2157]!, [_1, _2, _3]) } - public var DialogList_Pin: String { return self._s[2157]! } + public var DialogList_Pin: String { return self._s[2158]! } public func ForwardedAuthors2(_ _0: String, _ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2158]!, self._r[2158]!, [_0, _1]) + return formatWithArgumentRanges(self._s[2159]!, self._r[2159]!, [_0, _1]) } - public var Notification_Exceptions_AlwaysOn: String { return self._s[2159]! } - public var UserInfo_NotificationsDisable: String { return self._s[2160]! } - public var Paint_Outlined: String { return self._s[2161]! } - public var Activity_PlayingGame: String { return self._s[2162]! } - public var SearchImages_NoImagesFound: String { return self._s[2163]! } - public var SocksProxySetup_ProxyType: String { return self._s[2164]! } - public var AppleWatch_ReplyPresetsHelp: String { return self._s[2166]! } - public var Settings_AppLanguage: String { return self._s[2167]! } - public var TwoStepAuth_ResetAccountHelp: String { return self._s[2168]! } - public var Common_ChoosePhoto: String { return self._s[2169]! } - public var Privacy_Calls_AlwaysAllow: String { return self._s[2170]! } - public var Activity_UploadingVideo: String { return self._s[2171]! } - public var ChannelInfo_DeleteChannelConfirmation: String { return self._s[2172]! } - public var NetworkUsageSettings_Wifi: String { return self._s[2173]! } - public var Channel_BanUser_PermissionReadMessages: String { return self._s[2174]! } - public var Checkout_PayWithTouchId: String { return self._s[2175]! } - public var Notifications_ExceptionsNone: String { return self._s[2177]! } + public var Notification_Exceptions_AlwaysOn: String { return self._s[2160]! } + public var UserInfo_NotificationsDisable: String { return self._s[2161]! } + public var Paint_Outlined: String { return self._s[2162]! } + public var Activity_PlayingGame: String { return self._s[2163]! } + public var SearchImages_NoImagesFound: String { return self._s[2164]! } + public var SocksProxySetup_ProxyType: String { return self._s[2165]! } + public var AppleWatch_ReplyPresetsHelp: String { return self._s[2167]! } + public var Settings_AppLanguage: String { return self._s[2168]! } + public var TwoStepAuth_ResetAccountHelp: String { return self._s[2169]! } + public var Common_ChoosePhoto: String { return self._s[2170]! } + public var Privacy_Calls_AlwaysAllow: String { return self._s[2171]! } + public var Activity_UploadingVideo: String { return self._s[2172]! } + public var ChannelInfo_DeleteChannelConfirmation: String { return self._s[2173]! } + public var NetworkUsageSettings_Wifi: String { return self._s[2174]! } + public var Channel_BanUser_PermissionReadMessages: String { return self._s[2175]! } + public var Checkout_PayWithTouchId: String { return self._s[2176]! } + public var Notifications_ExceptionsNone: String { return self._s[2178]! } public func Message_ForwardedMessageShort(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2178]!, self._r[2178]!, [_0]) + return formatWithArgumentRanges(self._s[2179]!, self._r[2179]!, [_0]) } - public var AuthSessions_IncompleteAttempts: String { return self._s[2179]! } - public var Passport_Address_Region: String { return self._s[2183]! } - public var PhotoEditor_TiltShift: String { return self._s[2184]! } - public var Settings_FAQ_URL: String { return self._s[2185]! } - public var Passport_Language_sl: String { return self._s[2186]! } - public var Settings_PrivacySettings: String { return self._s[2188]! } - public var SharedMedia_TitleLink: String { return self._s[2189]! } - public var Passport_Identity_TypePassportUploadScan: String { return self._s[2190]! } - public var Settings_SetProfilePhoto: String { return self._s[2191]! } - public var Channel_About_Help: String { return self._s[2192]! } - public var AttachmentMenu_SendAsFiles: String { return self._s[2193]! } - public var Passport_Address_AddTemporaryRegistration: String { return self._s[2195]! } - public var PrivacySettings_DeleteAccountTitle: String { return self._s[2196]! } - public var AccessDenied_VideoMessageCamera: String { return self._s[2198]! } - public var Map_OpenInYandexMaps: String { return self._s[2200]! } - public var PhotoEditor_SaturationTool: String { return self._s[2201]! } - public var Notification_Exceptions_NewException_NotificationHeader: String { return self._s[2202]! } - public var Appearance_TextSize: String { return self._s[2203]! } - public var Channel_Username_InvalidTooShort: String { return self._s[2205]! } - public var Passport_PassportInformation: String { return self._s[2208]! } - public var WatchRemote_AlertTitle: String { return self._s[2209]! } - public var Privacy_GroupsAndChannels_NeverAllow: String { return self._s[2210]! } - public var ConvertToSupergroup_HelpText: String { return self._s[2212]! } + public var AuthSessions_IncompleteAttempts: String { return self._s[2180]! } + public var Passport_Address_Region: String { return self._s[2184]! } + public var PhotoEditor_TiltShift: String { return self._s[2185]! } + public var Settings_FAQ_URL: String { return self._s[2186]! } + public var Passport_Language_sl: String { return self._s[2187]! } + public var Settings_PrivacySettings: String { return self._s[2189]! } + public var SharedMedia_TitleLink: String { return self._s[2190]! } + public var Passport_Identity_TypePassportUploadScan: String { return self._s[2191]! } + public var Settings_SetProfilePhoto: String { return self._s[2192]! } + public var Channel_About_Help: String { return self._s[2193]! } + public var AttachmentMenu_SendAsFiles: String { return self._s[2194]! } + public var Passport_Address_AddTemporaryRegistration: String { return self._s[2196]! } + public var PrivacySettings_DeleteAccountTitle: String { return self._s[2197]! } + public var AccessDenied_VideoMessageCamera: String { return self._s[2199]! } + public var Map_OpenInYandexMaps: String { return self._s[2201]! } + public var PhotoEditor_SaturationTool: String { return self._s[2202]! } + public var Notification_Exceptions_NewException_NotificationHeader: String { return self._s[2203]! } + public var Appearance_TextSize: String { return self._s[2204]! } + public var Channel_Username_InvalidTooShort: String { return self._s[2206]! } + public var Passport_PassportInformation: String { return self._s[2209]! } + public var WatchRemote_AlertTitle: String { return self._s[2210]! } + public var Privacy_GroupsAndChannels_NeverAllow: String { return self._s[2211]! } + public var ConvertToSupergroup_HelpText: String { return self._s[2213]! } public func Time_MonthOfYear_m7(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2213]!, self._r[2213]!, [_0]) + return formatWithArgumentRanges(self._s[2214]!, self._r[2214]!, [_0]) } - public var Privacy_GroupsAndChannels_CustomHelp: String { return self._s[2214]! } - public var TwoStepAuth_RecoveryCodeInvalid: String { return self._s[2216]! } - public var AccessDenied_CameraDisabled: String { return self._s[2217]! } + public var Privacy_GroupsAndChannels_CustomHelp: String { return self._s[2215]! } + public var TwoStepAuth_RecoveryCodeInvalid: String { return self._s[2217]! } + public var AccessDenied_CameraDisabled: String { return self._s[2218]! } public func Channel_Username_UsernameIsAvailable(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2218]!, self._r[2218]!, [_0]) + return formatWithArgumentRanges(self._s[2219]!, self._r[2219]!, [_0]) } - public var PhotoEditor_ContrastTool: String { return self._s[2221]! } - public var DialogList_Draft: String { return self._s[2222]! } - public var Privacy_TopPeersDelete: String { return self._s[2224]! } - public var LoginPassword_PasswordPlaceholder: String { return self._s[2225]! } - public var Passport_Identity_TypeIdentityCardUploadScan: String { return self._s[2226]! } - public var WebSearch_RecentSectionClear: String { return self._s[2227]! } - public var Watch_ChatList_NoConversationsTitle: String { return self._s[2229]! } - public var Common_Done: String { return self._s[2230]! } - public var AuthSessions_EmptyText: String { return self._s[2231]! } - public var Conversation_ShareBotContactConfirmation: String { return self._s[2232]! } - public var Tour_Title5: String { return self._s[2233]! } + public var PhotoEditor_ContrastTool: String { return self._s[2222]! } + public var DialogList_Draft: String { return self._s[2223]! } + public var Privacy_TopPeersDelete: String { return self._s[2225]! } + public var LoginPassword_PasswordPlaceholder: String { return self._s[2226]! } + public var Passport_Identity_TypeIdentityCardUploadScan: String { return self._s[2227]! } + public var WebSearch_RecentSectionClear: String { return self._s[2228]! } + public var Watch_ChatList_NoConversationsTitle: String { return self._s[2230]! } + public var Common_Done: String { return self._s[2231]! } + public var AuthSessions_EmptyText: String { return self._s[2232]! } + public var Conversation_ShareBotContactConfirmation: String { return self._s[2233]! } + public var Tour_Title5: String { return self._s[2234]! } public func Map_DirectionsDriveEta(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2234]!, self._r[2234]!, [_0]) + return formatWithArgumentRanges(self._s[2235]!, self._r[2235]!, [_0]) } - public var ApplyLanguage_UnsufficientDataTitle: String { return self._s[2235]! } - public var Conversation_LinkDialogSave: String { return self._s[2236]! } - public var GroupInfo_ActionRestrict: String { return self._s[2237]! } - public var Checkout_Title: String { return self._s[2238]! } - public var Channel_AdminLog_CanChangeInfo: String { return self._s[2241]! } - public var Notification_RenamedGroup: String { return self._s[2242]! } - public var Checkout_PayWithFaceId: String { return self._s[2243]! } - public var Channel_BanList_BlockedTitle: String { return self._s[2244]! } - public var Checkout_WebConfirmation_Title: String { return self._s[2246]! } - public var Notifications_MessageNotificationsAlert: String { return self._s[2247]! } - public var Profile_AddToExisting: String { return self._s[2249]! } + public var ApplyLanguage_UnsufficientDataTitle: String { return self._s[2236]! } + public var Conversation_LinkDialogSave: String { return self._s[2237]! } + public var GroupInfo_ActionRestrict: String { return self._s[2238]! } + public var Checkout_Title: String { return self._s[2239]! } + public var Channel_AdminLog_CanChangeInfo: String { return self._s[2242]! } + public var Notification_RenamedGroup: String { return self._s[2243]! } + public var Checkout_PayWithFaceId: String { return self._s[2244]! } + public var Channel_BanList_BlockedTitle: String { return self._s[2245]! } + public var Checkout_WebConfirmation_Title: String { return self._s[2247]! } + public var Notifications_MessageNotificationsAlert: String { return self._s[2248]! } + public var Profile_AddToExisting: String { return self._s[2250]! } public func Profile_CreateEncryptedChatOutdatedError(_ _0: String, _ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2250]!, self._r[2250]!, [_0, _1]) + return formatWithArgumentRanges(self._s[2251]!, self._r[2251]!, [_0, _1]) } - public var Cache_Files: String { return self._s[2251]! } - public var Permissions_PrivacyPolicy: String { return self._s[2252]! } - public var SocksProxySetup_ConnectAndSave: String { return self._s[2253]! } - public var UserInfo_NotificationsDefaultDisabled: String { return self._s[2254]! } - public var Calls_NoCallsPlaceholder: String { return self._s[2257]! } - public var Channel_Username_RevokeExistingUsernamesInfo: String { return self._s[2258]! } - public var Notifications_ExceptionsGroupPlaceholder: String { return self._s[2260]! } - public var Passport_FieldAddressHelp: String { return self._s[2261]! } - public var Privacy_GroupsAndChannels_InviteToChannelMultipleError: String { return self._s[2262]! } + public var Cache_Files: String { return self._s[2252]! } + public var Permissions_PrivacyPolicy: String { return self._s[2253]! } + public var SocksProxySetup_ConnectAndSave: String { return self._s[2254]! } + public var UserInfo_NotificationsDefaultDisabled: String { return self._s[2255]! } + public var Calls_NoCallsPlaceholder: String { return self._s[2258]! } + public var Channel_Username_RevokeExistingUsernamesInfo: String { return self._s[2259]! } + public var Notifications_ExceptionsGroupPlaceholder: String { return self._s[2261]! } + public var Passport_FieldAddressHelp: String { return self._s[2262]! } + public var Privacy_GroupsAndChannels_InviteToChannelMultipleError: String { return self._s[2263]! } public func Login_TermsOfService_ProceedBot(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2263]!, self._r[2263]!, [_0]) + return formatWithArgumentRanges(self._s[2264]!, self._r[2264]!, [_0]) } - public var Channel_AdminLog_EmptyTitle: String { return self._s[2264]! } - public var Privacy_Calls_NeverAllow_Title: String { return self._s[2266]! } - public var Login_UnknownError: String { return self._s[2267]! } - public var Group_UpgradeNoticeText2: String { return self._s[2269]! } - public var Watch_Compose_AddContact: String { return self._s[2270]! } - public var Web_Error: String { return self._s[2271]! } - public var Profile_MessageLifetime1h: String { return self._s[2272]! } - public var CheckoutInfo_ReceiverInfoEmailPlaceholder: String { return self._s[2273]! } - public var Channel_Username_CheckingUsername: String { return self._s[2274]! } + public var Channel_AdminLog_EmptyTitle: String { return self._s[2265]! } + public var Privacy_Calls_NeverAllow_Title: String { return self._s[2267]! } + public var Login_UnknownError: String { return self._s[2268]! } + public var Group_UpgradeNoticeText2: String { return self._s[2270]! } + public var Watch_Compose_AddContact: String { return self._s[2271]! } + public var Web_Error: String { return self._s[2272]! } + public var Profile_MessageLifetime1h: String { return self._s[2273]! } + public var CheckoutInfo_ReceiverInfoEmailPlaceholder: String { return self._s[2274]! } + public var Channel_Username_CheckingUsername: String { return self._s[2275]! } public func PINNED_GAME(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2275]!, self._r[2275]!, [_1]) + return formatWithArgumentRanges(self._s[2276]!, self._r[2276]!, [_1]) } - public var Channel_AboutItem: String { return self._s[2276]! } - public var Privacy_GroupsAndChannels_AlwaysAllow_Placeholder: String { return self._s[2278]! } - public var GroupInfo_SharedMedia: String { return self._s[2279]! } + public var Channel_AboutItem: String { return self._s[2277]! } + public var Privacy_GroupsAndChannels_AlwaysAllow_Placeholder: String { return self._s[2279]! } + public var GroupInfo_SharedMedia: String { return self._s[2280]! } public func Channel_AdminLog_MessagePromotedName(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2280]!, self._r[2280]!, [_1]) + return formatWithArgumentRanges(self._s[2281]!, self._r[2281]!, [_1]) } - public var Call_PhoneCallInProgressMessage: String { return self._s[2281]! } - public var GroupInfo_InviteLink_RevokeAlert_Text: String { return self._s[2282]! } - public var Conversation_SearchByName_Placeholder: String { return self._s[2283]! } - public var Group_UpgradeNoticeHeader: String { return self._s[2284]! } - public var Channel_Management_AddModerator: String { return self._s[2285]! } - public var StickerPacksSettings_ShowStickersButton: String { return self._s[2286]! } - public var NotificationsSound_Hello: String { return self._s[2287]! } + public var Call_PhoneCallInProgressMessage: String { return self._s[2282]! } + public var GroupInfo_InviteLink_RevokeAlert_Text: String { return self._s[2283]! } + public var Conversation_SearchByName_Placeholder: String { return self._s[2284]! } + public var Group_UpgradeNoticeHeader: String { return self._s[2285]! } + public var Channel_Management_AddModerator: String { return self._s[2286]! } + public var StickerPacksSettings_ShowStickersButton: String { return self._s[2287]! } + public var NotificationsSound_Hello: String { return self._s[2288]! } public func CHAT_MESSAGE_GEO(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2288]!, self._r[2288]!, [_1, _2]) + return formatWithArgumentRanges(self._s[2289]!, self._r[2289]!, [_1, _2]) } - public var SocksProxySetup_SavedProxies: String { return self._s[2289]! } - public var Channel_Stickers_Placeholder: String { return self._s[2291]! } + public var SocksProxySetup_SavedProxies: String { return self._s[2290]! } + public var Channel_Stickers_Placeholder: String { return self._s[2292]! } public func Login_EmailCodeBody(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2292]!, self._r[2292]!, [_0]) + return formatWithArgumentRanges(self._s[2293]!, self._r[2293]!, [_0]) } - public var PrivacyPolicy_DeclineDeclineAndDelete: String { return self._s[2293]! } - public var Channel_Management_AddModeratorHelp: String { return self._s[2294]! } - public var ContactInfo_BirthdayLabel: String { return self._s[2295]! } - public var ChangePhoneNumberCode_RequestingACall: String { return self._s[2296]! } - public var AutoDownloadSettings_Channels: String { return self._s[2297]! } - public var Passport_Language_mn: String { return self._s[2298]! } - public var Notifications_ResetAllNotificationsHelp: String { return self._s[2301]! } - public var Passport_Language_ja: String { return self._s[2303]! } - public var Settings_About_Title: String { return self._s[2304]! } - public var Settings_NotificationsAndSounds: String { return self._s[2305]! } - public var ChannelInfo_DeleteGroup: String { return self._s[2306]! } - public var Settings_BlockedUsers: String { return self._s[2307]! } + public var PrivacyPolicy_DeclineDeclineAndDelete: String { return self._s[2294]! } + public var Channel_Management_AddModeratorHelp: String { return self._s[2295]! } + public var ContactInfo_BirthdayLabel: String { return self._s[2296]! } + public var ChangePhoneNumberCode_RequestingACall: String { return self._s[2297]! } + public var AutoDownloadSettings_Channels: String { return self._s[2298]! } + public var Passport_Language_mn: String { return self._s[2299]! } + public var Notifications_ResetAllNotificationsHelp: String { return self._s[2302]! } + public var Passport_Language_ja: String { return self._s[2304]! } + public var Settings_About_Title: String { return self._s[2305]! } + public var Settings_NotificationsAndSounds: String { return self._s[2306]! } + public var ChannelInfo_DeleteGroup: String { return self._s[2307]! } + public var Settings_BlockedUsers: String { return self._s[2308]! } public func Time_MonthOfYear_m4(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2308]!, self._r[2308]!, [_0]) + return formatWithArgumentRanges(self._s[2309]!, self._r[2309]!, [_0]) } - public var Passport_Address_AddResidentialAddress: String { return self._s[2309]! } - public var Channel_Username_Title: String { return self._s[2310]! } + public var Passport_Address_AddResidentialAddress: String { return self._s[2310]! } + public var Channel_Username_Title: String { return self._s[2311]! } public func Notification_RemovedGroupPhoto(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2311]!, self._r[2311]!, [_0]) + return formatWithArgumentRanges(self._s[2312]!, self._r[2312]!, [_0]) } - public var AttachmentMenu_File: String { return self._s[2313]! } - public var AppleWatch_Title: String { return self._s[2314]! } - public var Activity_RecordingVideoMessage: String { return self._s[2315]! } - public var Weekday_Saturday: String { return self._s[2316]! } - public var Profile_CreateEncryptedChatError: String { return self._s[2317]! } - public var Common_Next: String { return self._s[2319]! } - public var Channel_Stickers_YourStickers: String { return self._s[2321]! } - public var Call_AudioRouteHeadphones: String { return self._s[2322]! } - public var TwoStepAuth_EnterPasswordForgot: String { return self._s[2324]! } - public var Watch_Contacts_NoResults: String { return self._s[2326]! } - public var PhotoEditor_TintTool: String { return self._s[2328]! } - public var LoginPassword_ResetAccount: String { return self._s[2330]! } - public var Settings_SavedMessages: String { return self._s[2331]! } - public var StickerPack_Add: String { return self._s[2332]! } - public var Your_cards_number_is_invalid: String { return self._s[2333]! } - public var Checkout_TotalAmount: String { return self._s[2334]! } + public var AttachmentMenu_File: String { return self._s[2314]! } + public var AppleWatch_Title: String { return self._s[2315]! } + public var Activity_RecordingVideoMessage: String { return self._s[2316]! } + public var Weekday_Saturday: String { return self._s[2317]! } + public var Profile_CreateEncryptedChatError: String { return self._s[2318]! } + public var Common_Next: String { return self._s[2320]! } + public var Channel_Stickers_YourStickers: String { return self._s[2322]! } + public var Call_AudioRouteHeadphones: String { return self._s[2323]! } + public var TwoStepAuth_EnterPasswordForgot: String { return self._s[2325]! } + public var Watch_Contacts_NoResults: String { return self._s[2327]! } + public var PhotoEditor_TintTool: String { return self._s[2329]! } + public var LoginPassword_ResetAccount: String { return self._s[2331]! } + public var Settings_SavedMessages: String { return self._s[2332]! } + public var StickerPack_Add: String { return self._s[2333]! } + public var Your_cards_number_is_invalid: String { return self._s[2334]! } + public var Checkout_TotalAmount: String { return self._s[2335]! } public func ChangePhoneNumberCode_CallTimer(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2335]!, self._r[2335]!, [_0]) + return formatWithArgumentRanges(self._s[2336]!, self._r[2336]!, [_0]) } - public var ChatSettings_ConnectionType_UseSocks5: String { return self._s[2336]! } + public var ChatSettings_ConnectionType_UseSocks5: String { return self._s[2337]! } public func CHANNEL_MESSAGE_STICKER(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2338]!, self._r[2338]!, [_1, _2]) + return formatWithArgumentRanges(self._s[2339]!, self._r[2339]!, [_1, _2]) } public func Conversation_RestrictedTextTimed(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2339]!, self._r[2339]!, [_0]) + return formatWithArgumentRanges(self._s[2340]!, self._r[2340]!, [_0]) } - public var GroupInfo_InviteLink_ShareLink: String { return self._s[2340]! } - public var StickerPack_Share: String { return self._s[2341]! } - public var Passport_DeleteAddress: String { return self._s[2342]! } - public var Settings_Passport: String { return self._s[2343]! } - public var SharedMedia_EmptyFilesText: String { return self._s[2344]! } - public var Conversation_DeleteMessagesForMe: String { return self._s[2345]! } - public var PasscodeSettings_AutoLock_IfAwayFor_1hour: String { return self._s[2346]! } - public var Contacts_PermissionsText: String { return self._s[2347]! } - public var Group_Setup_HistoryVisible: String { return self._s[2348]! } - public var Passport_Address_AddRentalAgreement: String { return self._s[2350]! } - public var SocksProxySetup_Title: String { return self._s[2351]! } - public var Notification_Mute1h: String { return self._s[2352]! } + public var GroupInfo_InviteLink_ShareLink: String { return self._s[2341]! } + public var StickerPack_Share: String { return self._s[2342]! } + public var Passport_DeleteAddress: String { return self._s[2343]! } + public var Settings_Passport: String { return self._s[2344]! } + public var SharedMedia_EmptyFilesText: String { return self._s[2345]! } + public var Conversation_DeleteMessagesForMe: String { return self._s[2346]! } + public var PasscodeSettings_AutoLock_IfAwayFor_1hour: String { return self._s[2347]! } + public var Contacts_PermissionsText: String { return self._s[2348]! } + public var Group_Setup_HistoryVisible: String { return self._s[2349]! } + public var Passport_Address_AddRentalAgreement: String { return self._s[2351]! } + public var SocksProxySetup_Title: String { return self._s[2352]! } + public var Notification_Mute1h: String { return self._s[2353]! } public func Passport_Email_CodeHelp(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2353]!, self._r[2353]!, [_0]) + return formatWithArgumentRanges(self._s[2354]!, self._r[2354]!, [_0]) } - public var FastTwoStepSetup_PasswordSection: String { return self._s[2354]! } - public var NetworkUsageSettings_ResetStatsConfirmation: String { return self._s[2357]! } - public var InfoPlist_NSFaceIDUsageDescription: String { return self._s[2359]! } - public var DialogList_NoMessagesText: String { return self._s[2360]! } - public var Privacy_ContactsResetConfirmation: String { return self._s[2361]! } - public var Privacy_Calls_P2PHelp: String { return self._s[2362]! } - public var Your_cards_expiration_year_is_invalid: String { return self._s[2364]! } - public var Common_TakePhotoOrVideo: String { return self._s[2365]! } - public var Call_StatusBusy: String { return self._s[2366]! } - public var Conversation_PinnedMessage: String { return self._s[2367]! } - public var AutoDownloadSettings_VoiceMessagesTitle: String { return self._s[2368]! } - public var TwoStepAuth_SetupPasswordConfirmFailed: String { return self._s[2369]! } - public var AppleWatch_ReplyPresets: String { return self._s[2370]! } - public var Passport_DiscardMessageDescription: String { return self._s[2372]! } - public var Login_NetworkError: String { return self._s[2373]! } + public var FastTwoStepSetup_PasswordSection: String { return self._s[2355]! } + public var NetworkUsageSettings_ResetStatsConfirmation: String { return self._s[2358]! } + public var InfoPlist_NSFaceIDUsageDescription: String { return self._s[2360]! } + public var DialogList_NoMessagesText: String { return self._s[2361]! } + public var Privacy_ContactsResetConfirmation: String { return self._s[2362]! } + public var Privacy_Calls_P2PHelp: String { return self._s[2363]! } + public var Your_cards_expiration_year_is_invalid: String { return self._s[2365]! } + public var Common_TakePhotoOrVideo: String { return self._s[2366]! } + public var Call_StatusBusy: String { return self._s[2367]! } + public var Conversation_PinnedMessage: String { return self._s[2368]! } + public var AutoDownloadSettings_VoiceMessagesTitle: String { return self._s[2369]! } + public var TwoStepAuth_SetupPasswordConfirmFailed: String { return self._s[2370]! } + public var AppleWatch_ReplyPresets: String { return self._s[2371]! } + public var Passport_DiscardMessageDescription: String { return self._s[2373]! } + public var Login_NetworkError: String { return self._s[2374]! } public func Notification_PinnedRoundMessage(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2374]!, self._r[2374]!, [_0]) - } - public func Channel_AdminLog_MessageRemovedChannelUsername(_ _0: String) -> (String, [(Int, NSRange)]) { return formatWithArgumentRanges(self._s[2375]!, self._r[2375]!, [_0]) } - public var SocksProxySetup_PasswordPlaceholder: String { return self._s[2376]! } + public func Channel_AdminLog_MessageRemovedChannelUsername(_ _0: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[2376]!, self._r[2376]!, [_0]) + } + public var SocksProxySetup_PasswordPlaceholder: String { return self._s[2377]! } public func CONTACT_JOINED(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2378]!, self._r[2378]!, [_1]) + return formatWithArgumentRanges(self._s[2379]!, self._r[2379]!, [_1]) } - public var Login_ResetAccountProtected_LimitExceeded: String { return self._s[2379]! } + public var Login_ResetAccountProtected_LimitExceeded: String { return self._s[2380]! } public func Watch_LastSeen_YesterdayAt(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2381]!, self._r[2381]!, [_0]) + return formatWithArgumentRanges(self._s[2382]!, self._r[2382]!, [_0]) } - public var Call_ConnectionErrorMessage: String { return self._s[2382]! } - public var Compose_GroupTokenListPlaceholder: String { return self._s[2384]! } - public var ConversationMedia_Title: String { return self._s[2385]! } - public var EncryptionKey_Title: String { return self._s[2387]! } - public var TwoStepAuth_EnterPasswordTitle: String { return self._s[2388]! } - public var Notification_Exceptions_AddException: String { return self._s[2389]! } - public var Profile_MessageLifetime1m: String { return self._s[2390]! } + public var Call_ConnectionErrorMessage: String { return self._s[2383]! } + public var Compose_GroupTokenListPlaceholder: String { return self._s[2385]! } + public var ConversationMedia_Title: String { return self._s[2386]! } + public var EncryptionKey_Title: String { return self._s[2388]! } + public var TwoStepAuth_EnterPasswordTitle: String { return self._s[2389]! } + public var Notification_Exceptions_AddException: String { return self._s[2390]! } + public var Profile_MessageLifetime1m: String { return self._s[2391]! } public func Channel_AdminLog_MessageUnkickedName(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2391]!, self._r[2391]!, [_1]) + return formatWithArgumentRanges(self._s[2392]!, self._r[2392]!, [_1]) } - public var Month_GenMay: String { return self._s[2392]! } + public var Month_GenMay: String { return self._s[2393]! } public func LiveLocationUpdated_TodayAt(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2393]!, self._r[2393]!, [_0]) + return formatWithArgumentRanges(self._s[2394]!, self._r[2394]!, [_0]) } - public var ChannelMembers_WhoCanAddMembersAllHelp: String { return self._s[2394]! } - public var Conversation_EmptyPlaceholder: String { return self._s[2396]! } - public var Passport_Address_AddPassportRegistration: String { return self._s[2397]! } - public var Notifications_ChannelNotificationsAlert: String { return self._s[2398]! } - public var Camera_TapAndHoldForVideo: String { return self._s[2399]! } - public var Channel_JoinChannel: String { return self._s[2401]! } - public var Appearance_Animations: String { return self._s[2404]! } + public var ChannelMembers_WhoCanAddMembersAllHelp: String { return self._s[2395]! } + public var Conversation_EmptyPlaceholder: String { return self._s[2397]! } + public var Passport_Address_AddPassportRegistration: String { return self._s[2398]! } + public var Notifications_ChannelNotificationsAlert: String { return self._s[2399]! } + public var Camera_TapAndHoldForVideo: String { return self._s[2400]! } + public var Channel_JoinChannel: String { return self._s[2402]! } + public var Appearance_Animations: String { return self._s[2405]! } public func Notification_MessageLifetimeChanged(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2405]!, self._r[2405]!, [_1, _2]) + return formatWithArgumentRanges(self._s[2406]!, self._r[2406]!, [_1, _2]) } - public var Stickers_GroupStickers: String { return self._s[2407]! } - public var ConvertToSupergroup_HelpTitle: String { return self._s[2409]! } - public var Passport_Address_Street: String { return self._s[2410]! } - public var Conversation_AddContact: String { return self._s[2411]! } - public var Login_PhonePlaceholder: String { return self._s[2412]! } - public var Channel_Members_InviteLink: String { return self._s[2414]! } - public var Bot_Stop: String { return self._s[2415]! } - public var Notification_PassportValueAddress: String { return self._s[2417]! } - public var Month_ShortJuly: String { return self._s[2418]! } - public var Passport_Address_TypeTemporaryRegistrationUploadScan: String { return self._s[2419]! } - public var Channel_AdminLog_BanSendMedia: String { return self._s[2420]! } - public var Passport_Identity_ReverseSide: String { return self._s[2421]! } - public var Watch_Stickers_Recents: String { return self._s[2424]! } - public var PrivacyLastSeenSettings_EmpryUsersPlaceholder: String { return self._s[2426]! } - public var Map_SendThisLocation: String { return self._s[2427]! } + public var Stickers_GroupStickers: String { return self._s[2408]! } + public var ConvertToSupergroup_HelpTitle: String { return self._s[2410]! } + public var Passport_Address_Street: String { return self._s[2411]! } + public var Conversation_AddContact: String { return self._s[2412]! } + public var Login_PhonePlaceholder: String { return self._s[2413]! } + public var Channel_Members_InviteLink: String { return self._s[2415]! } + public var Bot_Stop: String { return self._s[2416]! } + public var Notification_PassportValueAddress: String { return self._s[2418]! } + public var Month_ShortJuly: String { return self._s[2419]! } + public var Passport_Address_TypeTemporaryRegistrationUploadScan: String { return self._s[2420]! } + public var Channel_AdminLog_BanSendMedia: String { return self._s[2421]! } + public var Passport_Identity_ReverseSide: String { return self._s[2422]! } + public var Watch_Stickers_Recents: String { return self._s[2425]! } + public var PrivacyLastSeenSettings_EmpryUsersPlaceholder: String { return self._s[2427]! } + public var Map_SendThisLocation: String { return self._s[2428]! } public func Time_MonthOfYear_m1(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2428]!, self._r[2428]!, [_0]) - } - public func InviteText_SingleContact(_ _0: String) -> (String, [(Int, NSRange)]) { return formatWithArgumentRanges(self._s[2429]!, self._r[2429]!, [_0]) } - public var ConvertToSupergroup_Note: String { return self._s[2430]! } + public func InviteText_SingleContact(_ _0: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[2430]!, self._r[2430]!, [_0]) + } + public var ConvertToSupergroup_Note: String { return self._s[2431]! } public func FileSize_MB(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2431]!, self._r[2431]!, [_0]) + return formatWithArgumentRanges(self._s[2432]!, self._r[2432]!, [_0]) } - public var NetworkUsageSettings_GeneralDataSection: String { return self._s[2432]! } + public var NetworkUsageSettings_GeneralDataSection: String { return self._s[2433]! } public func Compatibility_SecretMediaVersionTooLow(_ _0: String, _ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2433]!, self._r[2433]!, [_0, _1]) + return formatWithArgumentRanges(self._s[2434]!, self._r[2434]!, [_0, _1]) } - public var Login_CallRequestState3: String { return self._s[2435]! } + public var Login_CallRequestState3: String { return self._s[2436]! } public func CHANNEL_MESSAGE_GEO(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2437]!, self._r[2437]!, [_1]) + return formatWithArgumentRanges(self._s[2438]!, self._r[2438]!, [_1]) } - public var PasscodeSettings_UnlockWithFaceId: String { return self._s[2438]! } - public var Channel_AdminLogFilter_Title: String { return self._s[2439]! } - public var Notifications_GroupNotificationsExceptions: String { return self._s[2443]! } + public var PasscodeSettings_UnlockWithFaceId: String { return self._s[2439]! } + public var Channel_AdminLogFilter_Title: String { return self._s[2440]! } + public var Notifications_GroupNotificationsExceptions: String { return self._s[2444]! } public func FileSize_B(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2444]!, self._r[2444]!, [_0]) + return formatWithArgumentRanges(self._s[2445]!, self._r[2445]!, [_0]) } - public var Passport_CorrectErrors: String { return self._s[2445]! } + public var Passport_CorrectErrors: String { return self._s[2446]! } public func Channel_MessageTitleUpdated(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2446]!, self._r[2446]!, [_0]) + return formatWithArgumentRanges(self._s[2447]!, self._r[2447]!, [_0]) } - public var Map_SendMyCurrentLocation: String { return self._s[2447]! } - public var Permissions_NotificationsText_v0: String { return self._s[2448]! } - public var LoginPassword_FloodError: String { return self._s[2449]! } - public var Group_Setup_HistoryHiddenHelp: String { return self._s[2451]! } + public var Map_SendMyCurrentLocation: String { return self._s[2448]! } + public var SharedMedia_SearchNoResults: String { return self._s[2449]! } + public var Permissions_NotificationsText_v0: String { return self._s[2450]! } + public var LoginPassword_FloodError: String { return self._s[2451]! } + public var Group_Setup_HistoryHiddenHelp: String { return self._s[2453]! } public func TwoStepAuth_PendingEmailHelp(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2452]!, self._r[2452]!, [_0]) - } - public var Passport_Language_bn: String { return self._s[2453]! } - public func DialogList_SingleUploadingPhotoSuffix(_ _0: String) -> (String, [(Int, NSRange)]) { return formatWithArgumentRanges(self._s[2454]!, self._r[2454]!, [_0]) } - public func Notification_PinnedAudioMessage(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2455]!, self._r[2455]!, [_0]) - } - public func Channel_AdminLog_MessageChangedGroupStickerPack(_ _0: String) -> (String, [(Int, NSRange)]) { + public var Passport_Language_bn: String { return self._s[2455]! } + public func DialogList_SingleUploadingPhotoSuffix(_ _0: String) -> (String, [(Int, NSRange)]) { return formatWithArgumentRanges(self._s[2456]!, self._r[2456]!, [_0]) } - public var GroupInfo_InvitationLinkGroupFull: String { return self._s[2459]! } - public var Group_EditAdmin_PermissionChangeInfo: String { return self._s[2461]! } - public var Contacts_PermissionsAllow: String { return self._s[2462]! } - public var ReportPeer_ReasonCopyright: String { return self._s[2463]! } - public var Channel_EditAdmin_PermissinAddAdminOn: String { return self._s[2464]! } - public var Paint_Duplicate: String { return self._s[2465]! } - public var Notification_ChannelMigratedFrom: String { return self._s[2466]! } - public var Passport_Address_Country: String { return self._s[2467]! } - public var Notification_RenamedChannel: String { return self._s[2468]! } - public var CheckoutInfo_ErrorPostcodeInvalid: String { return self._s[2469]! } - public var Group_MessagePhotoUpdated: String { return self._s[2470]! } - public var Channel_BanUser_PermissionSendMedia: String { return self._s[2471]! } - public var Conversation_ContextMenuBan: String { return self._s[2472]! } - public var TwoStepAuth_EmailSent: String { return self._s[2473]! } - public var Passport_Language_is: String { return self._s[2474]! } - public var Tour_Text5: String { return self._s[2476]! } + public func Notification_PinnedAudioMessage(_ _0: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[2457]!, self._r[2457]!, [_0]) + } + public func Channel_AdminLog_MessageChangedGroupStickerPack(_ _0: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[2458]!, self._r[2458]!, [_0]) + } + public var GroupInfo_InvitationLinkGroupFull: String { return self._s[2461]! } + public var Group_EditAdmin_PermissionChangeInfo: String { return self._s[2463]! } + public var Contacts_PermissionsAllow: String { return self._s[2464]! } + public var ReportPeer_ReasonCopyright: String { return self._s[2465]! } + public var Channel_EditAdmin_PermissinAddAdminOn: String { return self._s[2466]! } + public var Paint_Duplicate: String { return self._s[2467]! } + public var Notification_ChannelMigratedFrom: String { return self._s[2468]! } + public var Passport_Address_Country: String { return self._s[2469]! } + public var Notification_RenamedChannel: String { return self._s[2470]! } + public var CheckoutInfo_ErrorPostcodeInvalid: String { return self._s[2471]! } + public var Group_MessagePhotoUpdated: String { return self._s[2472]! } + public var Channel_BanUser_PermissionSendMedia: String { return self._s[2473]! } + public var Conversation_ContextMenuBan: String { return self._s[2474]! } + public var TwoStepAuth_EmailSent: String { return self._s[2475]! } + public var Passport_Language_is: String { return self._s[2476]! } + public var Tour_Text5: String { return self._s[2478]! } public func Call_GroupFormat(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2478]!, self._r[2478]!, [_1, _2]) + return formatWithArgumentRanges(self._s[2480]!, self._r[2480]!, [_1, _2]) } - public var Paint_Edit: String { return self._s[2480]! } - public var LoginPassword_ForgotPassword: String { return self._s[2483]! } - public var GroupInfo_GroupNamePlaceholder: String { return self._s[2484]! } + public var Paint_Edit: String { return self._s[2482]! } + public var LoginPassword_ForgotPassword: String { return self._s[2485]! } + public var GroupInfo_GroupNamePlaceholder: String { return self._s[2486]! } public func Notification_Kicked(_ _0: String, _ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2485]!, self._r[2485]!, [_0, _1]) + return formatWithArgumentRanges(self._s[2487]!, self._r[2487]!, [_0, _1]) } - public var Conversation_InputTextCaptionPlaceholder: String { return self._s[2486]! } - public var AutoDownloadSettings_VideoMessagesTitle: String { return self._s[2487]! } - public var Passport_Language_uz: String { return self._s[2488]! } - public var Conversation_PinMessageAlertGroup: String { return self._s[2489]! } - public var Map_StopLiveLocation: String { return self._s[2491]! } - public var PasscodeSettings_Help: String { return self._s[2493]! } - public var NotificationsSound_Input: String { return self._s[2494]! } - public var Share_Title: String { return self._s[2496]! } - public var Login_TermsOfServiceAgree: String { return self._s[2497]! } - public var Channel_AdminLog_TitleSelectedEvents: String { return self._s[2498]! } - public var EnterPasscode_EnterTitle: String { return self._s[2499]! } - public var Channel_EditAdmin_PermissionEditMessages: String { return self._s[2500]! } + public var Conversation_InputTextCaptionPlaceholder: String { return self._s[2488]! } + public var AutoDownloadSettings_VideoMessagesTitle: String { return self._s[2489]! } + public var Passport_Language_uz: String { return self._s[2490]! } + public var Conversation_PinMessageAlertGroup: String { return self._s[2491]! } + public var Map_StopLiveLocation: String { return self._s[2493]! } + public var PasscodeSettings_Help: String { return self._s[2495]! } + public var NotificationsSound_Input: String { return self._s[2496]! } + public var Share_Title: String { return self._s[2498]! } + public var Login_TermsOfServiceAgree: String { return self._s[2499]! } + public var Channel_AdminLog_TitleSelectedEvents: String { return self._s[2500]! } + public var EnterPasscode_EnterTitle: String { return self._s[2501]! } + public var Channel_EditAdmin_PermissionEditMessages: String { return self._s[2502]! } public func Call_PrivacyErrorMessage(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2501]!, self._r[2501]!, [_0]) + return formatWithArgumentRanges(self._s[2503]!, self._r[2503]!, [_0]) } - public var Settings_CopyPhoneNumber: String { return self._s[2502]! } - public var NotificationsSound_Keys: String { return self._s[2503]! } + public var Settings_CopyPhoneNumber: String { return self._s[2504]! } + public var NotificationsSound_Keys: String { return self._s[2505]! } public func Call_ParticipantVersionOutdatedError(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2504]!, self._r[2504]!, [_0]) + return formatWithArgumentRanges(self._s[2506]!, self._r[2506]!, [_0]) } - public var Notification_MessageLifetime1w: String { return self._s[2505]! } - public var Message_Video: String { return self._s[2506]! } + public var Notification_MessageLifetime1w: String { return self._s[2507]! } + public var Message_Video: String { return self._s[2508]! } public func Notification_JoinedChat(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2509]!, self._r[2509]!, [_0]) + return formatWithArgumentRanges(self._s[2511]!, self._r[2511]!, [_0]) } public func PrivacySettings_LastSeenContactsPlus(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2510]!, self._r[2510]!, [_0]) + return formatWithArgumentRanges(self._s[2512]!, self._r[2512]!, [_0]) } - public var Passport_Language_mk: String { return self._s[2511]! } - public var Conversation_SilentBroadcastTooltipOn: String { return self._s[2513]! } - public var PrivacyPolicy_Decline: String { return self._s[2514]! } - public var Passport_Identity_DoesNotExpire: String { return self._s[2515]! } - public var Channel_AdminLogFilter_EventsRestrictions: String { return self._s[2516]! } - public var Permissions_SiriAllow_v0: String { return self._s[2517]! } + public var Passport_Language_mk: String { return self._s[2513]! } + public var Conversation_SilentBroadcastTooltipOn: String { return self._s[2515]! } + public var PrivacyPolicy_Decline: String { return self._s[2516]! } + public var Passport_Identity_DoesNotExpire: String { return self._s[2517]! } + public var Channel_AdminLogFilter_EventsRestrictions: String { return self._s[2518]! } + public var Permissions_SiriAllow_v0: String { return self._s[2519]! } public func CHAT_MESSAGE_STICKER(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2518]!, self._r[2518]!, [_1, _2, _3]) + return formatWithArgumentRanges(self._s[2520]!, self._r[2520]!, [_1, _2, _3]) } public func CHANNEL_MESSAGES(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2519]!, self._r[2519]!, [_1, _2]) + return formatWithArgumentRanges(self._s[2521]!, self._r[2521]!, [_1, _2]) } public func Notification_RenamedChat(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2520]!, self._r[2520]!, [_0]) + return formatWithArgumentRanges(self._s[2522]!, self._r[2522]!, [_0]) } - public var Paint_Regular: String { return self._s[2521]! } - public var ChatSettings_AutoDownloadReset: String { return self._s[2522]! } - public var BlockedUsers_SelectUserTitle: String { return self._s[2523]! } - public var GroupInfo_InviteByLink: String { return self._s[2525]! } - public var MessageTimer_Custom: String { return self._s[2526]! } - public var UserInfo_NotificationsDefaultEnabled: String { return self._s[2527]! } - public var Passport_Address_TypeTemporaryRegistration: String { return self._s[2529]! } - public var Channel_Username_InvalidTaken: String { return self._s[2530]! } - public var Conversation_ClousStorageInfo_Description3: String { return self._s[2531]! } + public var Paint_Regular: String { return self._s[2523]! } + public var ChatSettings_AutoDownloadReset: String { return self._s[2524]! } + public var BlockedUsers_SelectUserTitle: String { return self._s[2525]! } + public var GroupInfo_InviteByLink: String { return self._s[2527]! } + public var MessageTimer_Custom: String { return self._s[2528]! } + public var UserInfo_NotificationsDefaultEnabled: String { return self._s[2529]! } + public var Passport_Address_TypeTemporaryRegistration: String { return self._s[2531]! } + public var Channel_Username_InvalidTaken: String { return self._s[2532]! } + public var Conversation_ClousStorageInfo_Description3: String { return self._s[2533]! } public func CHANNEL_MESSAGE_VIDEO(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2532]!, self._r[2532]!, [_1]) + return formatWithArgumentRanges(self._s[2534]!, self._r[2534]!, [_1]) } - public var Settings_ChatBackground: String { return self._s[2533]! } - public var Channel_Subscribers_Title: String { return self._s[2534]! } - public var ApplyLanguage_ChangeLanguageTitle: String { return self._s[2535]! } - public var Watch_ConnectionDescription: String { return self._s[2536]! } - public var EditProfile_Title: String { return self._s[2540]! } - public var NotificationsSound_Bamboo: String { return self._s[2542]! } - public var Channel_AdminLog_MessagePreviousMessage: String { return self._s[2543]! } - public var Login_SmsRequestState2: String { return self._s[2544]! } - public var Passport_Language_ar: String { return self._s[2545]! } - public var Conversation_MessageDialogEdit: String { return self._s[2546]! } - public var Common_Close: String { return self._s[2547]! } + public var Settings_ChatBackground: String { return self._s[2535]! } + public var Channel_Subscribers_Title: String { return self._s[2536]! } + public var ApplyLanguage_ChangeLanguageTitle: String { return self._s[2537]! } + public var Watch_ConnectionDescription: String { return self._s[2538]! } + public var EditProfile_Title: String { return self._s[2542]! } + public var NotificationsSound_Bamboo: String { return self._s[2544]! } + public var Channel_AdminLog_MessagePreviousMessage: String { return self._s[2545]! } + public var Login_SmsRequestState2: String { return self._s[2546]! } + public var Passport_Language_ar: String { return self._s[2547]! } + public var Conversation_MessageDialogEdit: String { return self._s[2548]! } + public var Common_Close: String { return self._s[2549]! } public func Channel_AdminLog_MessageToggleInvitesOff(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2551]!, self._r[2551]!, [_0]) - } - public var UserInfo_About_Placeholder: String { return self._s[2552]! } - public func Conversation_FileHowToText(_ _0: String) -> (String, [(Int, NSRange)]) { return formatWithArgumentRanges(self._s[2553]!, self._r[2553]!, [_0]) } - public var Channel_Info_Banned: String { return self._s[2555]! } + public var UserInfo_About_Placeholder: String { return self._s[2554]! } + public func Conversation_FileHowToText(_ _0: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[2555]!, self._r[2555]!, [_0]) + } + public var Channel_Info_Banned: String { return self._s[2557]! } public func Time_MonthOfYear_m11(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2556]!, self._r[2556]!, [_0]) + return formatWithArgumentRanges(self._s[2558]!, self._r[2558]!, [_0]) } - public var Passport_Language_my: String { return self._s[2557]! } + public var Passport_Language_my: String { return self._s[2559]! } public func Time_PreciseDate_m9(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2558]!, self._r[2558]!, [_1, _2, _3]) + return formatWithArgumentRanges(self._s[2560]!, self._r[2560]!, [_1, _2, _3]) } - public var Preview_CopyAddress: String { return self._s[2559]! } + public var Preview_CopyAddress: String { return self._s[2561]! } public func DialogList_SinglePlayingGameSuffix(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2560]!, self._r[2560]!, [_0]) + return formatWithArgumentRanges(self._s[2562]!, self._r[2562]!, [_0]) } - public var KeyCommand_JumpToPreviousChat: String { return self._s[2561]! } - public var UserInfo_BotSettings: String { return self._s[2562]! } - public var Username_TooManyPublicUsernamesError: String { return self._s[2564]! } - public var Passport_PasswordCreate: String { return self._s[2565]! } - public var LiveLocation_MenuStopAll: String { return self._s[2566]! } - public var Message_PinnedLocationMessage: String { return self._s[2567]! } - public var Map_Satellite: String { return self._s[2568]! } - public var StickerSettings_MaskContextInfo: String { return self._s[2569]! } - public var TwoStepAuth_EnterPasswordInvalid: String { return self._s[2570]! } + public var KeyCommand_JumpToPreviousChat: String { return self._s[2563]! } + public var UserInfo_BotSettings: String { return self._s[2564]! } + public var Username_TooManyPublicUsernamesError: String { return self._s[2566]! } + public var Passport_PasswordCreate: String { return self._s[2567]! } + public var LiveLocation_MenuStopAll: String { return self._s[2568]! } + public var Message_PinnedLocationMessage: String { return self._s[2569]! } + public var Map_Satellite: String { return self._s[2570]! } + public var StickerSettings_MaskContextInfo: String { return self._s[2571]! } + public var TwoStepAuth_EnterPasswordInvalid: String { return self._s[2572]! } public func Notification_PinnedTextMessage(_ _0: String, _ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2571]!, self._r[2571]!, [_0, _1]) + return formatWithArgumentRanges(self._s[2573]!, self._r[2573]!, [_0, _1]) } - public var Notifications_ChannelNotificationsHelp: String { return self._s[2572]! } - public var Privacy_Calls_P2PContacts: String { return self._s[2573]! } - public var NotificationsSound_None: String { return self._s[2574]! } - public var AccessDenied_VoiceMicrophone: String { return self._s[2576]! } + public var Notifications_ChannelNotificationsHelp: String { return self._s[2574]! } + public var Privacy_Calls_P2PContacts: String { return self._s[2575]! } + public var NotificationsSound_None: String { return self._s[2576]! } + public var AccessDenied_VoiceMicrophone: String { return self._s[2578]! } public func ApplyLanguage_ChangeLanguageAlreadyActive(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2577]!, self._r[2577]!, [_1]) + return formatWithArgumentRanges(self._s[2579]!, self._r[2579]!, [_1]) } - public var Cache_Indexing: String { return self._s[2578]! } - public var DialogList_RecentTitlePeople: String { return self._s[2580]! } - public var DialogList_EncryptionRejected: String { return self._s[2581]! } - public var Passport_ScanPassportHelp: String { return self._s[2582]! } - public var Application_Name: String { return self._s[2583]! } - public var Channel_AdminLogFilter_ChannelEventsInfo: String { return self._s[2584]! } - public var Passport_Identity_TranslationHelp: String { return self._s[2586]! } + public var Cache_Indexing: String { return self._s[2580]! } + public var DialogList_RecentTitlePeople: String { return self._s[2582]! } + public var DialogList_EncryptionRejected: String { return self._s[2583]! } + public var Passport_ScanPassportHelp: String { return self._s[2584]! } + public var Application_Name: String { return self._s[2585]! } + public var Channel_AdminLogFilter_ChannelEventsInfo: String { return self._s[2586]! } + public var Passport_Identity_TranslationHelp: String { return self._s[2588]! } public func Notification_JoinedGroupByLink(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2587]!, self._r[2587]!, [_0]) + return formatWithArgumentRanges(self._s[2589]!, self._r[2589]!, [_0]) } public func DialogList_EncryptedChatStartedOutgoing(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2588]!, self._r[2588]!, [_0]) + return formatWithArgumentRanges(self._s[2590]!, self._r[2590]!, [_0]) } - public var Channel_EditAdmin_PermissionDeleteMessages: String { return self._s[2589]! } - public var Privacy_ChatsTitle: String { return self._s[2590]! } - public var DialogList_ClearHistoryConfirmation: String { return self._s[2591]! } - public var Watch_Suggestion_HoldOn: String { return self._s[2592]! } - public var SocksProxySetup_RequiredCredentials: String { return self._s[2593]! } - public var Passport_Address_TypeRentalAgreementUploadScan: String { return self._s[2594]! } - public var TwoStepAuth_EmailSkipAlert: String { return self._s[2595]! } - public var Channel_Setup_TypePublic: String { return self._s[2598]! } + public var Channel_EditAdmin_PermissionDeleteMessages: String { return self._s[2591]! } + public var Privacy_ChatsTitle: String { return self._s[2592]! } + public var DialogList_ClearHistoryConfirmation: String { return self._s[2593]! } + public var Watch_Suggestion_HoldOn: String { return self._s[2594]! } + public var SocksProxySetup_RequiredCredentials: String { return self._s[2595]! } + public var Passport_Address_TypeRentalAgreementUploadScan: String { return self._s[2596]! } + public var TwoStepAuth_EmailSkipAlert: String { return self._s[2597]! } + public var Channel_Setup_TypePublic: String { return self._s[2600]! } public func Channel_AdminLog_MessageToggleInvitesOn(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2599]!, self._r[2599]!, [_0]) + return formatWithArgumentRanges(self._s[2601]!, self._r[2601]!, [_0]) } - public var Channel_TypeSetup_Title: String { return self._s[2601]! } - public var Map_OpenInMaps: String { return self._s[2603]! } - public var NotificationsSound_Tremolo: String { return self._s[2605]! } + public var Channel_TypeSetup_Title: String { return self._s[2603]! } + public var Map_OpenInMaps: String { return self._s[2605]! } + public var NotificationsSound_Tremolo: String { return self._s[2607]! } public func Date_ChatDateHeaderYear(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2606]!, self._r[2606]!, [_1, _2, _3]) + return formatWithArgumentRanges(self._s[2608]!, self._r[2608]!, [_1, _2, _3]) } - public var ConversationProfile_UnknownAddMemberError: String { return self._s[2607]! } - public var Passport_PasswordHelp: String { return self._s[2608]! } - public var Login_CodeExpiredError: String { return self._s[2609]! } - public var Channel_EditAdmin_PermissionChangeInfo: String { return self._s[2610]! } - public var Passport_Identity_ScansHelp: String { return self._s[2611]! } - public var Passport_Language_lo: String { return self._s[2612]! } - public var Camera_FlashAuto: String { return self._s[2613]! } - public var Common_Cancel: String { return self._s[2614]! } - public var DialogList_SavedMessagesTooltip: String { return self._s[2615]! } - public var TwoStepAuth_SetupPasswordTitle: String { return self._s[2616]! } - public var Conversation_ReportSpamConfirmation: String { return self._s[2617]! } - public var ChatSettings_Title: String { return self._s[2619]! } - public var Passport_PasswordReset: String { return self._s[2620]! } - public var SocksProxySetup_TypeNone: String { return self._s[2621]! } - public var PhoneNumberHelp_Help: String { return self._s[2623]! } - public var Checkout_EnterPassword: String { return self._s[2624]! } - public var Share_AuthTitle: String { return self._s[2626]! } - public var Activity_UploadingDocument: String { return self._s[2627]! } - public var State_Connecting: String { return self._s[2628]! } - public var Profile_MessageLifetime1w: String { return self._s[2629]! } - public var Conversation_ContextMenuReport: String { return self._s[2630]! } - public var CheckoutInfo_ReceiverInfoPhone: String { return self._s[2631]! } - public var AutoNightTheme_ScheduledTo: String { return self._s[2632]! } - public var AuthSessions_Terminate: String { return self._s[2633]! } - public var Checkout_NewCard_CardholderNamePlaceholder: String { return self._s[2634]! } - public var KeyCommand_JumpToPreviousUnreadChat: String { return self._s[2635]! } - public var PhotoEditor_Set: String { return self._s[2636]! } - public var Login_PadPhoneHelp: String { return self._s[2637]! } - public var PrivacyPolicy_DeclineLastWarning: String { return self._s[2640]! } - public var NotificationsSound_Complete: String { return self._s[2641]! } - public var Group_Info_AdminLog: String { return self._s[2642]! } - public var Channel_AdminLog_InfoPanelAlertText: String { return self._s[2643]! } - public var Conversation_Admin: String { return self._s[2645]! } - public var Conversation_GifTooltip: String { return self._s[2646]! } - public var Passport_NotLoggedInMessage: String { return self._s[2647]! } - public var Profile_MessageLifetimeForever: String { return self._s[2648]! } - public var SharedMedia_EmptyTitle: String { return self._s[2650]! } - public var Channel_Edit_PrivatePublicLinkAlert: String { return self._s[2651]! } - public var Username_Help: String { return self._s[2652]! } - public var DialogList_LanguageTooltip: String { return self._s[2654]! } - public var Map_LoadError: String { return self._s[2655]! } - public var Notification_Exceptions_NewException: String { return self._s[2656]! } - public var TwoStepAuth_EmailTitle: String { return self._s[2657]! } - public var WatchRemote_AlertText: String { return self._s[2658]! } - public var ChatSettings_ConnectionType_Title: String { return self._s[2660]! } + public var ConversationProfile_UnknownAddMemberError: String { return self._s[2609]! } + public var Passport_PasswordHelp: String { return self._s[2610]! } + public var Login_CodeExpiredError: String { return self._s[2611]! } + public var Channel_EditAdmin_PermissionChangeInfo: String { return self._s[2612]! } + public var Passport_Identity_ScansHelp: String { return self._s[2613]! } + public var Passport_Language_lo: String { return self._s[2614]! } + public var Camera_FlashAuto: String { return self._s[2615]! } + public var Common_Cancel: String { return self._s[2616]! } + public var DialogList_SavedMessagesTooltip: String { return self._s[2617]! } + public var TwoStepAuth_SetupPasswordTitle: String { return self._s[2618]! } + public var Conversation_ReportSpamConfirmation: String { return self._s[2619]! } + public var ChatSettings_Title: String { return self._s[2621]! } + public var Passport_PasswordReset: String { return self._s[2622]! } + public var SocksProxySetup_TypeNone: String { return self._s[2623]! } + public var PhoneNumberHelp_Help: String { return self._s[2625]! } + public var Checkout_EnterPassword: String { return self._s[2626]! } + public var Share_AuthTitle: String { return self._s[2628]! } + public var Activity_UploadingDocument: String { return self._s[2629]! } + public var State_Connecting: String { return self._s[2630]! } + public var Profile_MessageLifetime1w: String { return self._s[2631]! } + public var Conversation_ContextMenuReport: String { return self._s[2632]! } + public var CheckoutInfo_ReceiverInfoPhone: String { return self._s[2633]! } + public var AutoNightTheme_ScheduledTo: String { return self._s[2634]! } + public var AuthSessions_Terminate: String { return self._s[2635]! } + public var Checkout_NewCard_CardholderNamePlaceholder: String { return self._s[2636]! } + public var KeyCommand_JumpToPreviousUnreadChat: String { return self._s[2637]! } + public var PhotoEditor_Set: String { return self._s[2638]! } + public var Login_PadPhoneHelp: String { return self._s[2639]! } + public var PrivacyPolicy_DeclineLastWarning: String { return self._s[2642]! } + public var NotificationsSound_Complete: String { return self._s[2643]! } + public var Group_Info_AdminLog: String { return self._s[2644]! } + public var Channel_AdminLog_InfoPanelAlertText: String { return self._s[2645]! } + public var Conversation_Admin: String { return self._s[2647]! } + public var Conversation_GifTooltip: String { return self._s[2648]! } + public var Passport_NotLoggedInMessage: String { return self._s[2649]! } + public var Profile_MessageLifetimeForever: String { return self._s[2650]! } + public var SharedMedia_EmptyTitle: String { return self._s[2652]! } + public var Channel_Edit_PrivatePublicLinkAlert: String { return self._s[2653]! } + public var Username_Help: String { return self._s[2654]! } + public var DialogList_LanguageTooltip: String { return self._s[2656]! } + public var Map_LoadError: String { return self._s[2657]! } + public var Notification_Exceptions_NewException: String { return self._s[2658]! } + public var TwoStepAuth_EmailTitle: String { return self._s[2659]! } + public var WatchRemote_AlertText: String { return self._s[2660]! } + public var ChatSettings_ConnectionType_Title: String { return self._s[2662]! } public func LOCKED_MESSAGE(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2661]!, self._r[2661]!, [_1]) + return formatWithArgumentRanges(self._s[2663]!, self._r[2663]!, [_1]) } - public var Passport_Address_CountryPlaceholder: String { return self._s[2662]! } + public var Passport_Address_CountryPlaceholder: String { return self._s[2664]! } public func DialogList_AwaitingEncryption(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2663]!, self._r[2663]!, [_0]) + return formatWithArgumentRanges(self._s[2665]!, self._r[2665]!, [_0]) } public func Time_PreciseDate_m6(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2664]!, self._r[2664]!, [_1, _2, _3]) + return formatWithArgumentRanges(self._s[2666]!, self._r[2666]!, [_1, _2, _3]) } - public var Group_AdminLog_EmptyText: String { return self._s[2665]! } - public var AccessDenied_VideoMicrophone: String { return self._s[2667]! } - public var Conversation_ContextMenuStickerPackAdd: String { return self._s[2668]! } - public var Cache_ClearNone: String { return self._s[2669]! } - public var SocksProxySetup_FailedToConnect: String { return self._s[2670]! } - public var Permissions_NotificationsTitle_v0: String { return self._s[2671]! } + public var Group_AdminLog_EmptyText: String { return self._s[2667]! } + public var AccessDenied_VideoMicrophone: String { return self._s[2669]! } + public var Conversation_ContextMenuStickerPackAdd: String { return self._s[2670]! } + public var Cache_ClearNone: String { return self._s[2671]! } + public var SocksProxySetup_FailedToConnect: String { return self._s[2672]! } + public var Permissions_NotificationsTitle_v0: String { return self._s[2673]! } public func Channel_AdminLog_MessageEdited(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2672]!, self._r[2672]!, [_0]) - } - public var Passport_Identity_Country: String { return self._s[2673]! } - public func Notification_CreatedChat(_ _0: String) -> (String, [(Int, NSRange)]) { return formatWithArgumentRanges(self._s[2674]!, self._r[2674]!, [_0]) } - public var AccessDenied_Settings: String { return self._s[2675]! } - public var Passport_Address_TypeUtilityBillUploadScan: String { return self._s[2676]! } - public var Month_ShortMay: String { return self._s[2677]! } - public var Compose_NewGroup: String { return self._s[2678]! } - public var Group_Setup_TypePrivate: String { return self._s[2680]! } - public var Login_PadPhoneHelpTitle: String { return self._s[2681]! } - public var Appearance_ThemeDayClassic: String { return self._s[2682]! } - public var Channel_AdminLog_MessagePreviousCaption: String { return self._s[2683]! } - public var Privacy_GroupsAndChannels_WhoCanAddMe: String { return self._s[2684]! } - public var Conversation_typing: String { return self._s[2686]! } - public var Paint_Masks: String { return self._s[2687]! } - public var Username_InvalidTaken: String { return self._s[2688]! } - public var TwoStepAuth_EmailAddSuccess: String { return self._s[2689]! } - public func CHAT_PHOTO_EDITED(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2690]!, self._r[2690]!, [_1, _2]) + public var Passport_Identity_Country: String { return self._s[2675]! } + public func Notification_CreatedChat(_ _0: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[2676]!, self._r[2676]!, [_0]) } - public var Call_StatusNoAnswer: String { return self._s[2691]! } - public var Passport_Identity_Selfie: String { return self._s[2692]! } - public var Login_InfoLastNamePlaceholder: String { return self._s[2693]! } - public var Privacy_SecretChatsLinkPreviewsHelp: String { return self._s[2694]! } - public var Conversation_ClearSecretHistory: String { return self._s[2695]! } - public var NetworkUsageSettings_Title: String { return self._s[2697]! } - public var Your_cards_security_code_is_invalid: String { return self._s[2699]! } + public var AccessDenied_Settings: String { return self._s[2677]! } + public var Passport_Address_TypeUtilityBillUploadScan: String { return self._s[2678]! } + public var Month_ShortMay: String { return self._s[2679]! } + public var Compose_NewGroup: String { return self._s[2680]! } + public var Group_Setup_TypePrivate: String { return self._s[2682]! } + public var Login_PadPhoneHelpTitle: String { return self._s[2683]! } + public var Appearance_ThemeDayClassic: String { return self._s[2684]! } + public var Channel_AdminLog_MessagePreviousCaption: String { return self._s[2685]! } + public var Privacy_GroupsAndChannels_WhoCanAddMe: String { return self._s[2686]! } + public var Conversation_typing: String { return self._s[2688]! } + public var Paint_Masks: String { return self._s[2689]! } + public var Username_InvalidTaken: String { return self._s[2690]! } + public var TwoStepAuth_EmailAddSuccess: String { return self._s[2691]! } + public func CHAT_PHOTO_EDITED(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[2692]!, self._r[2692]!, [_1, _2]) + } + public var Call_StatusNoAnswer: String { return self._s[2693]! } + public var Passport_Identity_Selfie: String { return self._s[2694]! } + public var Login_InfoLastNamePlaceholder: String { return self._s[2695]! } + public var Privacy_SecretChatsLinkPreviewsHelp: String { return self._s[2696]! } + public var Conversation_ClearSecretHistory: String { return self._s[2697]! } + public var NetworkUsageSettings_Title: String { return self._s[2699]! } + public var Your_cards_security_code_is_invalid: String { return self._s[2701]! } public func Notification_LeftChannel(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2701]!, self._r[2701]!, [_0]) + return formatWithArgumentRanges(self._s[2703]!, self._r[2703]!, [_0]) } public func Call_CallInProgressMessage(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2702]!, self._r[2702]!, [_1, _2]) + return formatWithArgumentRanges(self._s[2704]!, self._r[2704]!, [_1, _2]) } - public var SaveIncomingPhotosSettings_From: String { return self._s[2704]! } - public var Map_LiveLocationTitle: String { return self._s[2705]! } - public var Login_InfoAvatarAdd: String { return self._s[2706]! } - public var Passport_Identity_FilesView: String { return self._s[2707]! } - public var UserInfo_GenericPhoneLabel: String { return self._s[2708]! } - public var Privacy_Calls_NeverAllow: String { return self._s[2709]! } + public var SaveIncomingPhotosSettings_From: String { return self._s[2706]! } + public var Map_LiveLocationTitle: String { return self._s[2707]! } + public var Login_InfoAvatarAdd: String { return self._s[2708]! } + public var Passport_Identity_FilesView: String { return self._s[2709]! } + public var UserInfo_GenericPhoneLabel: String { return self._s[2710]! } + public var Privacy_Calls_NeverAllow: String { return self._s[2711]! } public func Contacts_AddPhoneNumber(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2710]!, self._r[2710]!, [_0]) + return formatWithArgumentRanges(self._s[2712]!, self._r[2712]!, [_0]) } - public var TwoStepAuth_ConfirmationText: String { return self._s[2711]! } - public var ChatSettings_AutomaticVideoMessageDownload: String { return self._s[2712]! } - public var Channel_AdminLogFilter_AdminsAll: String { return self._s[2713]! } - public var Tour_Title2: String { return self._s[2714]! } - public var Conversation_FileOpenIn: String { return self._s[2715]! } - public var Checkout_ErrorPrecheckoutFailed: String { return self._s[2716]! } - public var Wallpaper_Set: String { return self._s[2717]! } - public var Passport_Identity_Translations: String { return self._s[2719]! } + public var TwoStepAuth_ConfirmationText: String { return self._s[2713]! } + public var ChatSettings_AutomaticVideoMessageDownload: String { return self._s[2714]! } + public var Channel_AdminLogFilter_AdminsAll: String { return self._s[2715]! } + public var Tour_Title2: String { return self._s[2716]! } + public var Conversation_FileOpenIn: String { return self._s[2717]! } + public var Checkout_ErrorPrecheckoutFailed: String { return self._s[2718]! } + public var Wallpaper_Set: String { return self._s[2719]! } + public var Passport_Identity_Translations: String { return self._s[2721]! } public func Channel_AdminLog_MessageChangedChannelAbout(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2720]!, self._r[2720]!, [_0]) + return formatWithArgumentRanges(self._s[2722]!, self._r[2722]!, [_0]) } - public var Channel_LeaveChannel: String { return self._s[2721]! } + public var Channel_LeaveChannel: String { return self._s[2723]! } public func PINNED_INVOICE(_ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2722]!, self._r[2722]!, [_1]) + return formatWithArgumentRanges(self._s[2724]!, self._r[2724]!, [_1]) } - public var PhotoEditor_HighlightsTint: String { return self._s[2723]! } - public var Passport_Email_Delete: String { return self._s[2724]! } - public var Conversation_Mute: String { return self._s[2726]! } - public var Channel_AdminLog_CanSendMessages: String { return self._s[2728]! } + public var PhotoEditor_HighlightsTint: String { return self._s[2725]! } + public var Passport_Email_Delete: String { return self._s[2726]! } + public var Conversation_Mute: String { return self._s[2728]! } + public var Channel_AdminLog_CanSendMessages: String { return self._s[2730]! } public func Notification_PassportValuesSentMessage(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2729]!, self._r[2729]!, [_1, _2]) + return formatWithArgumentRanges(self._s[2731]!, self._r[2731]!, [_1, _2]) } - public var Calls_CallTabDescription: String { return self._s[2730]! } - public var Passport_Identity_NativeNameHelp: String { return self._s[2731]! } - public var Common_No: String { return self._s[2732]! } - public var Weekday_Sunday: String { return self._s[2733]! } - public var Notification_Reply: String { return self._s[2734]! } - public var Conversation_ViewMessage: String { return self._s[2735]! } + public var Calls_CallTabDescription: String { return self._s[2732]! } + public var Passport_Identity_NativeNameHelp: String { return self._s[2733]! } + public var Common_No: String { return self._s[2734]! } + public var Weekday_Sunday: String { return self._s[2735]! } + public var Notification_Reply: String { return self._s[2736]! } + public var Conversation_ViewMessage: String { return self._s[2737]! } public func Checkout_SavePasswordTimeoutAndFaceId(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2736]!, self._r[2736]!, [_0]) + return formatWithArgumentRanges(self._s[2738]!, self._r[2738]!, [_0]) } public func Map_LiveLocationPrivateDescription(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2737]!, self._r[2737]!, [_0]) + return formatWithArgumentRanges(self._s[2739]!, self._r[2739]!, [_0]) } - public var Message_PinnedDocumentMessage: String { return self._s[2738]! } - public var DialogList_TabTitle: String { return self._s[2740]! } - public var Passport_FieldEmail: String { return self._s[2741]! } - public var Conversation_UnpinMessageAlert: String { return self._s[2742]! } - public var Passport_Address_TypeBankStatement: String { return self._s[2743]! } - public var Passport_Identity_ExpiryDate: String { return self._s[2744]! } - public var Privacy_Calls_P2P: String { return self._s[2745]! } + public var Message_PinnedDocumentMessage: String { return self._s[2740]! } + public var DialogList_TabTitle: String { return self._s[2742]! } + public var Passport_FieldEmail: String { return self._s[2743]! } + public var Conversation_UnpinMessageAlert: String { return self._s[2744]! } + public var Passport_Address_TypeBankStatement: String { return self._s[2745]! } + public var Passport_Identity_ExpiryDate: String { return self._s[2746]! } + public var Privacy_Calls_P2P: String { return self._s[2747]! } public func CancelResetAccount_Success(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2747]!, self._r[2747]!, [_0]) + return formatWithArgumentRanges(self._s[2749]!, self._r[2749]!, [_0]) } - public var SocksProxySetup_UseForCallsHelp: String { return self._s[2748]! } - public var EnterPasscode_ChangeTitle: String { return self._s[2749]! } - public var Passport_InfoText: String { return self._s[2750]! } - public var Checkout_NewCard_SaveInfoEnableHelp: String { return self._s[2751]! } + public var SocksProxySetup_UseForCallsHelp: String { return self._s[2750]! } + public var EnterPasscode_ChangeTitle: String { return self._s[2751]! } + public var Passport_InfoText: String { return self._s[2752]! } + public var Checkout_NewCard_SaveInfoEnableHelp: String { return self._s[2753]! } public func Time_PreciseDate_m3(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2752]!, self._r[2752]!, [_1, _2, _3]) + return formatWithArgumentRanges(self._s[2754]!, self._r[2754]!, [_1, _2, _3]) } - public var Passport_Identity_EditDriversLicense: String { return self._s[2753]! } - public var Conversation_TapAndHoldToRecord: String { return self._s[2754]! } + public var Passport_Identity_EditDriversLicense: String { return self._s[2755]! } + public var Conversation_TapAndHoldToRecord: String { return self._s[2756]! } public func Notification_CallTimeFormat(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2755]!, self._r[2755]!, [_1, _2]) + return formatWithArgumentRanges(self._s[2757]!, self._r[2757]!, [_1, _2]) } public func Generic_OpenHiddenLinkAlert(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2757]!, self._r[2757]!, [_0]) + return formatWithArgumentRanges(self._s[2759]!, self._r[2759]!, [_0]) } - public var DialogList_Unread: String { return self._s[2758]! } - public var User_DeletedAccount: String { return self._s[2759]! } + public var DialogList_Unread: String { return self._s[2760]! } + public var User_DeletedAccount: String { return self._s[2761]! } public func Watch_Time_ShortYesterdayAt(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2760]!, self._r[2760]!, [_0]) + return formatWithArgumentRanges(self._s[2762]!, self._r[2762]!, [_0]) } - public var UserInfo_NotificationsDefault: String { return self._s[2761]! } - public var SharedMedia_CategoryMedia: String { return self._s[2762]! } - public var SocksProxySetup_ProxyStatusUnavailable: String { return self._s[2763]! } - public var Channel_AdminLog_MessageRestrictedForever: String { return self._s[2764]! } - public var Watch_ChatList_Compose: String { return self._s[2765]! } - public var Notifications_MessageNotificationsExceptionsHelp: String { return self._s[2766]! } - public var Watch_Microphone_Access: String { return self._s[2767]! } - public var Group_Setup_HistoryHeader: String { return self._s[2768]! } - public var Activity_UploadingPhoto: String { return self._s[2769]! } - public var Conversation_Edit: String { return self._s[2771]! } - public var Group_ErrorSendRestrictedMedia: String { return self._s[2772]! } - public var Login_TermsOfServiceDecline: String { return self._s[2773]! } - public var Message_PinnedContactMessage: String { return self._s[2774]! } + public var UserInfo_NotificationsDefault: String { return self._s[2763]! } + public var SharedMedia_CategoryMedia: String { return self._s[2764]! } + public var SocksProxySetup_ProxyStatusUnavailable: String { return self._s[2765]! } + public var Channel_AdminLog_MessageRestrictedForever: String { return self._s[2766]! } + public var Watch_ChatList_Compose: String { return self._s[2767]! } + public var Notifications_MessageNotificationsExceptionsHelp: String { return self._s[2768]! } + public var Watch_Microphone_Access: String { return self._s[2769]! } + public var Group_Setup_HistoryHeader: String { return self._s[2770]! } + public var Activity_UploadingPhoto: String { return self._s[2771]! } + public var Conversation_Edit: String { return self._s[2773]! } + public var Group_ErrorSendRestrictedMedia: String { return self._s[2774]! } + public var Login_TermsOfServiceDecline: String { return self._s[2775]! } + public var Message_PinnedContactMessage: String { return self._s[2776]! } public func Channel_AdminLog_MessageRestrictedNameUsername(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2775]!, self._r[2775]!, [_1, _2]) + return formatWithArgumentRanges(self._s[2777]!, self._r[2777]!, [_1, _2]) } - public var TwoStepAuth_AdditionalPassword: String { return self._s[2777]! } - public var Passport_Phone_EnterOtherNumber: String { return self._s[2778]! } - public var TwoStepAuth_RecoveryEmailAddDescription: String { return self._s[2779]! } - public var Passport_FieldPhone: String { return self._s[2780]! } - public var Message_PinnedPhotoMessage: String { return self._s[2781]! } - public var InfoPlist_NSCameraUsageDescription: String { return self._s[2783]! } - public var Conversation_Call: String { return self._s[2784]! } - public var Common_TakePhoto: String { return self._s[2786]! } - public var Channel_NotificationLoading: String { return self._s[2787]! } + public var TwoStepAuth_AdditionalPassword: String { return self._s[2779]! } + public var Passport_Phone_EnterOtherNumber: String { return self._s[2780]! } + public var TwoStepAuth_RecoveryEmailAddDescription: String { return self._s[2781]! } + public var Passport_FieldPhone: String { return self._s[2782]! } + public var Message_PinnedPhotoMessage: String { return self._s[2783]! } + public var InfoPlist_NSCameraUsageDescription: String { return self._s[2785]! } + public var Conversation_Call: String { return self._s[2786]! } + public var Common_TakePhoto: String { return self._s[2788]! } + public var Channel_NotificationLoading: String { return self._s[2789]! } public func Notification_Exceptions_Sound(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2788]!, self._r[2788]!, [_0]) - } - public var Permissions_SiriTitle_v0: String { return self._s[2789]! } - public func Login_ResetAccountProtected_Text(_ _0: String) -> (String, [(Int, NSRange)]) { return formatWithArgumentRanges(self._s[2790]!, self._r[2790]!, [_0]) } - public var Channel_MessagePhotoRemoved: String { return self._s[2791]! } - public var Common_edit: String { return self._s[2792]! } - public var PrivacySettings_AuthSessions: String { return self._s[2793]! } - public var Month_ShortJune: String { return self._s[2794]! } - public var PrivacyLastSeenSettings_AlwaysShareWith_Placeholder: String { return self._s[2795]! } - public var Call_ReportSend: String { return self._s[2796]! } - public var Watch_LastSeen_JustNow: String { return self._s[2797]! } - public var Notifications_MessageNotifications: String { return self._s[2798]! } - public var BroadcastListInfo_AddRecipient: String { return self._s[2800]! } - public var Group_Status: String { return self._s[2801]! } + public var Permissions_SiriTitle_v0: String { return self._s[2791]! } + public func Login_ResetAccountProtected_Text(_ _0: String) -> (String, [(Int, NSRange)]) { + return formatWithArgumentRanges(self._s[2792]!, self._r[2792]!, [_0]) + } + public var Channel_MessagePhotoRemoved: String { return self._s[2793]! } + public var Common_edit: String { return self._s[2794]! } + public var PrivacySettings_AuthSessions: String { return self._s[2795]! } + public var Month_ShortJune: String { return self._s[2796]! } + public var PrivacyLastSeenSettings_AlwaysShareWith_Placeholder: String { return self._s[2797]! } + public var Call_ReportSend: String { return self._s[2798]! } + public var Watch_LastSeen_JustNow: String { return self._s[2799]! } + public var Notifications_MessageNotifications: String { return self._s[2800]! } + public var BroadcastListInfo_AddRecipient: String { return self._s[2802]! } + public var Group_Status: String { return self._s[2803]! } public func AutoNightTheme_LocationHelp(_ _0: String, _ _1: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2802]!, self._r[2802]!, [_0, _1]) + return formatWithArgumentRanges(self._s[2804]!, self._r[2804]!, [_0, _1]) } - public var ShareMenu_ShareTo: String { return self._s[2803]! } - public var Conversation_Moderate_Ban: String { return self._s[2804]! } + public var ShareMenu_ShareTo: String { return self._s[2805]! } + public var Conversation_Moderate_Ban: String { return self._s[2806]! } public func Conversation_DeleteMessagesFor(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2805]!, self._r[2805]!, [_0]) + return formatWithArgumentRanges(self._s[2807]!, self._r[2807]!, [_0]) } - public var SharedMedia_ViewInChat: String { return self._s[2806]! } - public var Map_LiveLocationFor8Hours: String { return self._s[2807]! } + public var SharedMedia_ViewInChat: String { return self._s[2808]! } + public var Map_LiveLocationFor8Hours: String { return self._s[2809]! } public func Map_AccurateTo(_ _0: String) -> (String, [(Int, NSRange)]) { - return formatWithArgumentRanges(self._s[2809]!, self._r[2809]!, [_0]) + return formatWithArgumentRanges(self._s[2811]!, self._r[2811]!, [_0]) } - public var Appearance_ReduceMotion: String { return self._s[2810]! } - public var Map_OpenInHereMaps: String { return self._s[2811]! } - public var Channel_Setup_TypePublicHelp: String { return self._s[2812]! } - public var Passport_Identity_EditInternalPassport: String { return self._s[2813]! } - public var PhotoEditor_Skip: String { return self._s[2814]! } - public func Notifications_Exceptions(_ value: Int32) -> String { + public var Appearance_ReduceMotion: String { return self._s[2812]! } + public var Map_OpenInHereMaps: String { return self._s[2813]! } + public var Channel_Setup_TypePublicHelp: String { return self._s[2814]! } + public var Passport_Identity_EditInternalPassport: String { return self._s[2815]! } + public var PhotoEditor_Skip: String { return self._s[2816]! } + public func MessageTimer_ShortHours(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) return String(format: self._ps[0 * 6 + Int(form.rawValue)]!, "\(value)") } - public func Map_ETAMinutes(_ value: Int32) -> String { + public func ForwardedAudios(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) return String(format: self._ps[1 * 6 + Int(form.rawValue)]!, "\(value)") } - public func PrivacyLastSeenSettings_AddUsers(_ value: Int32) -> String { + public func MessageTimer_ShortWeeks(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) return String(format: self._ps[2 * 6 + Int(form.rawValue)]!, "\(value)") } - public func ForwardedPhotos(_ value: Int32) -> String { + public func Call_Seconds(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) return String(format: self._ps[3 * 6 + Int(form.rawValue)]!, "\(value)") } - public func ForwardedVideos(_ value: Int32) -> String { + public func Notification_GameScoreSelfSimple(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) return String(format: self._ps[4 * 6 + Int(form.rawValue)]!, "\(value)") } - public func Conversation_LiveLocationMembersCount(_ value: Int32) -> String { + public func PrivacyLastSeenSettings_AddUsers(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) return String(format: self._ps[5 * 6 + Int(form.rawValue)]!, "\(value)") } - public func Notifications_ExceptionMuteExpires_Days(_ value: Int32) -> String { + public func Watch_UserInfo_Mute(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) return String(format: self._ps[6 * 6 + Int(form.rawValue)]!, "\(value)") } - public func StickerPack_AddMaskCount(_ value: Int32) -> String { + public func Contacts_ImportersCount(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) return String(format: self._ps[7 * 6 + Int(form.rawValue)]!, "\(value)") } - public func QuickSend_Photos(_ value: Int32) -> String { + public func ForwardedFiles(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) return String(format: self._ps[8 * 6 + Int(form.rawValue)]!, "\(value)") } - public func ForwardedStickers(_ value: Int32) -> String { + public func AttachmentMenu_SendItem(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) return String(format: self._ps[9 * 6 + Int(form.rawValue)]!, "\(value)") } - public func Notification_GameScoreSelfExtended(_ value: Int32) -> String { + public func Forward_ConfirmMultipleFiles(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) return String(format: self._ps[10 * 6 + Int(form.rawValue)]!, "\(value)") } - public func AttachmentMenu_SendPhoto(_ value: Int32) -> String { + public func Passport_Scans(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) return String(format: self._ps[11 * 6 + Int(form.rawValue)]!, "\(value)") } - public func Media_SharePhoto(_ value: Int32) -> String { + public func ServiceMessage_GameScoreSelfSimple(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) return String(format: self._ps[12 * 6 + Int(form.rawValue)]!, "\(value)") } - public func MessageTimer_Seconds(_ value: Int32) -> String { + public func SharedMedia_File(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) return String(format: self._ps[13 * 6 + Int(form.rawValue)]!, "\(value)") } - public func SharedMedia_Generic(_ value: Int32) -> String { + public func QuickSend_Photos(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) return String(format: self._ps[14 * 6 + Int(form.rawValue)]!, "\(value)") } - public func SharedMedia_DeleteItemsConfirmation(_ value: Int32) -> String { + public func StickerPack_RemoveStickerCount(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) return String(format: self._ps[15 * 6 + Int(form.rawValue)]!, "\(value)") } - public func Media_ShareItem(_ value: Int32) -> String { + public func Call_Minutes(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) return String(format: self._ps[16 * 6 + Int(form.rawValue)]!, "\(value)") } - public func MessageTimer_Months(_ value: Int32) -> String { + public func LiveLocation_MenuChatsCount(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) return String(format: self._ps[17 * 6 + Int(form.rawValue)]!, "\(value)") } - public func Conversation_StatusOnline(_ value: Int32) -> String { + public func LiveLocationUpdated_MinutesAgo(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) return String(format: self._ps[18 * 6 + Int(form.rawValue)]!, "\(value)") } - public func StickerPack_AddStickerCount(_ value: Int32) -> String { + public func Media_ShareVideo(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) return String(format: self._ps[19 * 6 + Int(form.rawValue)]!, "\(value)") } - public func SharedMedia_File(_ value: Int32) -> String { + public func ChatList_DeleteConfirmation(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) return String(format: self._ps[20 * 6 + Int(form.rawValue)]!, "\(value)") } - public func ForwardedAuthorsOthers(_ value: Int32) -> String { + public func MessageTimer_Minutes(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) return String(format: self._ps[21 * 6 + Int(form.rawValue)]!, "\(value)") } - public func ForwardedContacts(_ value: Int32) -> String { + public func Conversation_LiveLocationMembersCount(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) return String(format: self._ps[22 * 6 + Int(form.rawValue)]!, "\(value)") } - public func Notification_GameScoreSimple(_ value: Int32) -> String { + public func Notifications_ExceptionMuteExpires_Days(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) return String(format: self._ps[23 * 6 + Int(form.rawValue)]!, "\(value)") } - public func ChatList_DeleteConfirmation(_ value: Int32) -> String { + public func Notifications_Exceptions(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) return String(format: self._ps[24 * 6 + Int(form.rawValue)]!, "\(value)") } - public func MuteFor_Hours(_ value: Int32) -> String { + public func Watch_LastSeen_HoursAgo(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) return String(format: self._ps[25 * 6 + Int(form.rawValue)]!, "\(value)") } - public func MuteExpires_Hours(_ value: Int32) -> String { + public func StickerPack_StickerCount(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) return String(format: self._ps[26 * 6 + Int(form.rawValue)]!, "\(value)") } - public func Conversation_StatusMembers(_ value: Int32) -> String { + public func SharedMedia_Photo(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) return String(format: self._ps[27 * 6 + Int(form.rawValue)]!, "\(value)") } - public func MuteExpires_Days(_ value: Int32) -> String { + public func Call_ShortSeconds(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) return String(format: self._ps[28 * 6 + Int(form.rawValue)]!, "\(value)") } - public func Notification_GameScoreSelfSimple(_ value: Int32) -> String { + public func MessageTimer_Weeks(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) return String(format: self._ps[29 * 6 + Int(form.rawValue)]!, "\(value)") } - public func Watch_LastSeen_MinutesAgo(_ value: Int32) -> String { + public func SharedMedia_Generic(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) return String(format: self._ps[30 * 6 + Int(form.rawValue)]!, "\(value)") } - public func Call_Seconds(_ value: Int32) -> String { + public func LastSeen_HoursAgo(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) return String(format: self._ps[31 * 6 + Int(form.rawValue)]!, "\(value)") } - public func Media_ShareVideo(_ value: Int32) -> String { + public func StickerPack_AddStickerCount(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) return String(format: self._ps[32 * 6 + Int(form.rawValue)]!, "\(value)") } - public func Invitation_Members(_ value: Int32) -> String { + public func MessageTimer_Years(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) return String(format: self._ps[33 * 6 + Int(form.rawValue)]!, "\(value)") } - public func AttachmentMenu_SendGif(_ value: Int32) -> String { + public func SharedMedia_Video(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) return String(format: self._ps[34 * 6 + Int(form.rawValue)]!, "\(value)") } - public func ServiceMessage_GameScoreExtended(_ value: Int32) -> String { + public func Conversation_StatusSubscribers(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) return String(format: self._ps[35 * 6 + Int(form.rawValue)]!, "\(value)") } - public func ServiceMessage_GameScoreSimple(_ value: Int32) -> String { + public func ForwardedVideoMessages(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) return String(format: self._ps[36 * 6 + Int(form.rawValue)]!, "\(value)") } - public func Notifications_ExceptionMuteExpires_Minutes(_ value: Int32) -> String { + public func Map_ETAHours(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) return String(format: self._ps[37 * 6 + Int(form.rawValue)]!, "\(value)") } - public func Contacts_ImportersCount(_ value: Int32) -> String { + public func Notification_GameScoreSimple(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) return String(format: self._ps[38 * 6 + Int(form.rawValue)]!, "\(value)") } - public func AttachmentMenu_SendVideo(_ value: Int32) -> String { + public func AttachmentMenu_SendPhoto(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) return String(format: self._ps[39 * 6 + Int(form.rawValue)]!, "\(value)") } - public func MessageTimer_ShortWeeks(_ value: Int32) -> String { + public func AttachmentMenu_SendVideo(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) return String(format: self._ps[40 * 6 + Int(form.rawValue)]!, "\(value)") } - public func ForwardedVideoMessages(_ value: Int32) -> String { + public func ServiceMessage_GameScoreExtended(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) return String(format: self._ps[41 * 6 + Int(form.rawValue)]!, "\(value)") } - public func Watch_LastSeen_HoursAgo(_ value: Int32) -> String { + public func ForwardedContacts(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) return String(format: self._ps[42 * 6 + Int(form.rawValue)]!, "\(value)") } - public func Forward_ConfirmMultipleFiles(_ value: Int32) -> String { + public func ForwardedVideos(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) return String(format: self._ps[43 * 6 + Int(form.rawValue)]!, "\(value)") } - public func LiveLocationUpdated_MinutesAgo(_ value: Int32) -> String { + public func Media_SharePhoto(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) return String(format: self._ps[44 * 6 + Int(form.rawValue)]!, "\(value)") } - public func PasscodeSettings_FailedAttempts(_ value: Int32) -> String { + public func InviteText_ContactsCountText(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) return String(format: self._ps[45 * 6 + Int(form.rawValue)]!, "\(value)") } - public func Call_ShortSeconds(_ value: Int32) -> String { + public func GroupInfo_ParticipantCount(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) return String(format: self._ps[46 * 6 + Int(form.rawValue)]!, "\(value)") } - public func Conversation_StatusSubscribers(_ value: Int32) -> String { + public func LastSeen_MinutesAgo(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) return String(format: self._ps[47 * 6 + Int(form.rawValue)]!, "\(value)") } - public func SharedMedia_Video(_ value: Int32) -> String { + public func ForwardedStickers(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) return String(format: self._ps[48 * 6 + Int(form.rawValue)]!, "\(value)") } - public func MessageTimer_ShortHours(_ value: Int32) -> String { + public func MessageTimer_ShortSeconds(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) return String(format: self._ps[49 * 6 + Int(form.rawValue)]!, "\(value)") } - public func ServiceMessage_GameScoreSelfSimple(_ value: Int32) -> String { + public func Notification_GameScoreExtended(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) return String(format: self._ps[50 * 6 + Int(form.rawValue)]!, "\(value)") } - public func SharedMedia_Link(_ value: Int32) -> String { + public func UserCount(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) return String(format: self._ps[51 * 6 + Int(form.rawValue)]!, "\(value)") } - public func Watch_UserInfo_Mute(_ value: Int32) -> String { + public func ForwardedPhotos(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) return String(format: self._ps[52 * 6 + Int(form.rawValue)]!, "\(value)") } - public func StickerPack_StickerCount(_ value: Int32) -> String { + public func StickerPack_RemoveMaskCount(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) return String(format: self._ps[53 * 6 + Int(form.rawValue)]!, "\(value)") } - public func MessageTimer_Weeks(_ value: Int32) -> String { + public func StickerPack_AddMaskCount(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) return String(format: self._ps[54 * 6 + Int(form.rawValue)]!, "\(value)") } - public func InviteText_ContactsCountText(_ value: Int32) -> String { + public func ForwardedLocations(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) return String(format: self._ps[55 * 6 + Int(form.rawValue)]!, "\(value)") } - public func ForwardedFiles(_ value: Int32) -> String { + public func ServiceMessage_GameScoreSelfExtended(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) return String(format: self._ps[56 * 6 + Int(form.rawValue)]!, "\(value)") } - public func AttachmentMenu_SendItem(_ value: Int32) -> String { + public func MuteFor_Days(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) return String(format: self._ps[57 * 6 + Int(form.rawValue)]!, "\(value)") } - public func ForwardedLocations(_ value: Int32) -> String { + public func ForwardedAuthorsOthers(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) return String(format: self._ps[58 * 6 + Int(form.rawValue)]!, "\(value)") } - public func GroupInfo_ParticipantCount(_ value: Int32) -> String { + public func Notifications_ExceptionMuteExpires_Hours(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) return String(format: self._ps[59 * 6 + Int(form.rawValue)]!, "\(value)") } - public func LastSeen_HoursAgo(_ value: Int32) -> String { + public func AttachmentMenu_SendGif(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) return String(format: self._ps[60 * 6 + Int(form.rawValue)]!, "\(value)") } - public func MuteExpires_Minutes(_ value: Int32) -> String { + public func MuteFor_Hours(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) return String(format: self._ps[61 * 6 + Int(form.rawValue)]!, "\(value)") } - public func ForwardedMessages(_ value: Int32) -> String { + public func SharedMedia_DeleteItemsConfirmation(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) return String(format: self._ps[62 * 6 + Int(form.rawValue)]!, "\(value)") } - public func Call_Minutes(_ value: Int32) -> String { + public func Media_ShareItem(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) return String(format: self._ps[63 * 6 + Int(form.rawValue)]!, "\(value)") } - public func MessageTimer_Years(_ value: Int32) -> String { + public func ServiceMessage_GameScoreSimple(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) return String(format: self._ps[64 * 6 + Int(form.rawValue)]!, "\(value)") } - public func MessageTimer_ShortSeconds(_ value: Int32) -> String { + public func DialogList_LiveLocationChatsCount(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) return String(format: self._ps[65 * 6 + Int(form.rawValue)]!, "\(value)") } - public func MessageTimer_Days(_ value: Int32) -> String { + public func MuteExpires_Days(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) return String(format: self._ps[66 * 6 + Int(form.rawValue)]!, "\(value)") } - public func Map_ETAHours(_ value: Int32) -> String { + public func Call_ShortMinutes(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) return String(format: self._ps[67 * 6 + Int(form.rawValue)]!, "\(value)") } - public func Notification_GameScoreExtended(_ value: Int32) -> String { + public func Watch_LastSeen_MinutesAgo(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) return String(format: self._ps[68 * 6 + Int(form.rawValue)]!, "\(value)") } - public func Passport_Scans(_ value: Int32) -> String { + public func Notification_GameScoreSelfExtended(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) return String(format: self._ps[69 * 6 + Int(form.rawValue)]!, "\(value)") } - public func ForwardedGifs(_ value: Int32) -> String { + public func Map_ETAMinutes(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) return String(format: self._ps[70 * 6 + Int(form.rawValue)]!, "\(value)") } - public func ServiceMessage_GameScoreSelfExtended(_ value: Int32) -> String { + public func MessageTimer_Hours(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) return String(format: self._ps[71 * 6 + Int(form.rawValue)]!, "\(value)") } - public func MessageTimer_Minutes(_ value: Int32) -> String { + public func Conversation_StatusMembers(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) return String(format: self._ps[72 * 6 + Int(form.rawValue)]!, "\(value)") } - public func StickerPack_RemoveStickerCount(_ value: Int32) -> String { + public func Conversation_StatusOnline(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) return String(format: self._ps[73 * 6 + Int(form.rawValue)]!, "\(value)") } - public func MessageTimer_ShortDays(_ value: Int32) -> String { + public func MuteExpires_Minutes(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) return String(format: self._ps[74 * 6 + Int(form.rawValue)]!, "\(value)") } - public func StickerPack_RemoveMaskCount(_ value: Int32) -> String { + public func ForwardedMessages(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) return String(format: self._ps[75 * 6 + Int(form.rawValue)]!, "\(value)") } - public func UserCount(_ value: Int32) -> String { + public func MessageTimer_Months(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) return String(format: self._ps[76 * 6 + Int(form.rawValue)]!, "\(value)") } - public func ForwardedAudios(_ value: Int32) -> String { + public func Invitation_Members(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) return String(format: self._ps[77 * 6 + Int(form.rawValue)]!, "\(value)") } - public func MessageTimer_ShortMinutes(_ value: Int32) -> String { + public func MessageTimer_Seconds(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) return String(format: self._ps[78 * 6 + Int(form.rawValue)]!, "\(value)") } - public func Notifications_ExceptionMuteExpires_Hours(_ value: Int32) -> String { + public func MessageTimer_ShortDays(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) return String(format: self._ps[79 * 6 + Int(form.rawValue)]!, "\(value)") } - public func LastSeen_MinutesAgo(_ value: Int32) -> String { + public func SharedMedia_Link(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) return String(format: self._ps[80 * 6 + Int(form.rawValue)]!, "\(value)") } - public func Call_ShortMinutes(_ value: Int32) -> String { + public func MessageTimer_ShortMinutes(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) return String(format: self._ps[81 * 6 + Int(form.rawValue)]!, "\(value)") } - public func DialogList_LiveLocationChatsCount(_ value: Int32) -> String { + public func Notifications_ExceptionMuteExpires_Minutes(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) return String(format: self._ps[82 * 6 + Int(form.rawValue)]!, "\(value)") } - public func MuteFor_Days(_ value: Int32) -> String { + public func MessageTimer_Days(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) return String(format: self._ps[83 * 6 + Int(form.rawValue)]!, "\(value)") } - public func LiveLocation_MenuChatsCount(_ value: Int32) -> String { + public func ForwardedGifs(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) return String(format: self._ps[84 * 6 + Int(form.rawValue)]!, "\(value)") } - public func SharedMedia_Photo(_ value: Int32) -> String { + public func MuteExpires_Hours(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) return String(format: self._ps[85 * 6 + Int(form.rawValue)]!, "\(value)") } - public func MessageTimer_Hours(_ value: Int32) -> String { + public func PasscodeSettings_FailedAttempts(_ value: Int32) -> String { let form = presentationStringsPluralizationForm(self.lc, value) return String(format: self._ps[86 * 6 + Int(form.rawValue)]!, "\(value)") } diff --git a/TelegramUI/PresentationTheme.swift b/TelegramUI/PresentationTheme.swift index 41ab3c17b5..9abe1d94b4 100644 --- a/TelegramUI/PresentationTheme.swift +++ b/TelegramUI/PresentationTheme.swift @@ -720,7 +720,7 @@ public final class PresentationThemeChatInputPanel { } public final class PresentationThemeInputMediaPanel { - public let panelSerapatorColor: UIColor + public let panelSeparatorColor: UIColor public let panelIconColor: UIColor public let panelHighlightedIconBackgroundColor: UIColor public let stickersBackgroundColor: UIColor @@ -731,8 +731,8 @@ public final class PresentationThemeInputMediaPanel { public let stickersSearchControlColor: UIColor public let gifsBackgroundColor: UIColor - public init(panelSerapatorColor: UIColor, panelIconColor: UIColor, panelHighlightedIconBackgroundColor: UIColor, stickersBackgroundColor: UIColor, stickersSectionTextColor: UIColor, stickersSearchBackgroundColor: UIColor, stickersSearchPlaceholderColor: UIColor, stickersSearchPrimaryColor: UIColor, stickersSearchControlColor: UIColor, gifsBackgroundColor: UIColor) { - self.panelSerapatorColor = panelSerapatorColor + public init(panelSeparatorColor: UIColor, panelIconColor: UIColor, panelHighlightedIconBackgroundColor: UIColor, stickersBackgroundColor: UIColor, stickersSectionTextColor: UIColor, stickersSearchBackgroundColor: UIColor, stickersSearchPlaceholderColor: UIColor, stickersSearchPrimaryColor: UIColor, stickersSearchControlColor: UIColor, gifsBackgroundColor: UIColor) { + self.panelSeparatorColor = panelSeparatorColor self.panelIconColor = panelIconColor self.panelHighlightedIconBackgroundColor = panelHighlightedIconBackgroundColor self.stickersBackgroundColor = stickersBackgroundColor @@ -746,7 +746,7 @@ public final class PresentationThemeInputMediaPanel { } public final class PresentationThemeInputButtonPanel { - public let panelSerapatorColor: UIColor + public let panelSeparatorColor: UIColor public let panelBackgroundColor: UIColor public let buttonFillColor: UIColor public let buttonStrokeColor: UIColor @@ -754,8 +754,8 @@ public final class PresentationThemeInputButtonPanel { public let buttonHighlightedStrokeColor: UIColor public let buttonTextColor: UIColor - public init(panelSerapatorColor: UIColor, panelBackgroundColor: UIColor, buttonFillColor: UIColor, buttonStrokeColor: UIColor, buttonHighlightedFillColor: UIColor, buttonHighlightedStrokeColor: UIColor, buttonTextColor: UIColor) { - self.panelSerapatorColor = panelSerapatorColor + public init(panelSeparatorColor: UIColor, panelBackgroundColor: UIColor, buttonFillColor: UIColor, buttonStrokeColor: UIColor, buttonHighlightedFillColor: UIColor, buttonHighlightedStrokeColor: UIColor, buttonTextColor: UIColor) { + self.panelSeparatorColor = panelSeparatorColor self.panelBackgroundColor = panelBackgroundColor self.buttonFillColor = buttonFillColor self.buttonStrokeColor = buttonStrokeColor diff --git a/TelegramUI/PresentationThemeEssentialGraphics.swift b/TelegramUI/PresentationThemeEssentialGraphics.swift index 9cf68975c2..9efda98a13 100644 --- a/TelegramUI/PresentationThemeEssentialGraphics.swift +++ b/TelegramUI/PresentationThemeEssentialGraphics.swift @@ -229,7 +229,7 @@ public final class PrincipalThemeAdditionalGraphics { context.setFillColor(serviceColor.fill.cgColor) context.fillEllipse(in: CGRect(origin: CGPoint(), size: size)) })!.stretchableImage(withLeftCapWidth: 8, topCapHeight: 8) - self.chatFreeformContentAdditionalInfoBackgroundImage = generateStretchableFilledCircleImage(radius: 4.0, color: serviceColor.fill)! + self.chatFreeformContentAdditionalInfoBackgroundImage = generateStretchableFilledCircleImage(radius: 10.0, color: serviceColor.fill)! self.chatEmptyItemBackgroundImage = generateStretchableFilledCircleImage(radius: 14.0, color: serviceColor.fill)! self.chatLoadingIndicatorBackgroundImage = generateStretchableFilledCircleImage(diameter: 30.0, color: serviceColor.fill)! diff --git a/TelegramUI/ProxyServerActionSheetController.swift b/TelegramUI/ProxyServerActionSheetController.swift index a93a84e386..fca49041d9 100644 --- a/TelegramUI/ProxyServerActionSheetController.swift +++ b/TelegramUI/ProxyServerActionSheetController.swift @@ -7,8 +7,7 @@ import UIKit import SwiftSignalKit final class ProxyServerActionSheetController: ActionSheetController { - private let theme: PresentationTheme - private let strings: PresentationStrings + private var presentationDisposable: Disposable? private let _ready = Promise() override var ready: Promise { @@ -17,9 +16,10 @@ final class ProxyServerActionSheetController: ActionSheetController { private var isDismissed: Bool = false - init(account: Account, theme: PresentationTheme, strings: PresentationStrings, server: ProxyServerSettings) { - self.theme = theme - self.strings = strings + init(account: Account, server: ProxyServerSettings) { + let presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 } + let theme = presentationData.theme + let strings = presentationData.strings let sheetTheme = ActionSheetControllerTheme(presentationTheme: theme) super.init(theme: sheetTheme) @@ -51,11 +51,21 @@ final class ProxyServerActionSheetController: ActionSheetController { }) ]) ]) + + self.presentationDisposable = account.telegramApplicationContext.presentationData.start(next: { [weak self] presentationData in + if let strongSelf = self { + strongSelf.theme = ActionSheetControllerTheme(presentationTheme: presentationData.theme) + } + }) } required init(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } + + deinit { + self.presentationDisposable?.dispose() + } } private final class ProxyServerInfoItem: ActionSheetItem { diff --git a/TelegramUI/RadialStatusNode.swift b/TelegramUI/RadialStatusNode.swift index f33d7159b2..a20c30ec67 100644 --- a/TelegramUI/RadialStatusNode.swift +++ b/TelegramUI/RadialStatusNode.swift @@ -125,7 +125,11 @@ public enum RadialStatusNodeState: Equatable { } public final class RadialStatusNode: ASControlNode { - private var backgroundNodeColor: UIColor + var backgroundNodeColor: UIColor { + didSet { + self.transitionToBackgroundColor(state.backgroundColor(color: self.backgroundNodeColor), animated: false, completion: {}) + } + } private(set) var state: RadialStatusNodeState = .none diff --git a/TelegramUI/Resources/PresentationStrings.mapping b/TelegramUI/Resources/PresentationStrings.mapping index 8bef8e8e07..ad3ad6f42d 100644 Binary files a/TelegramUI/Resources/PresentationStrings.mapping and b/TelegramUI/Resources/PresentationStrings.mapping differ diff --git a/TelegramUI/SearchBarNode.swift b/TelegramUI/SearchBarNode.swift index 97878e26ef..cd2a3646ca 100644 --- a/TelegramUI/SearchBarNode.swift +++ b/TelegramUI/SearchBarNode.swift @@ -60,6 +60,22 @@ private class SearchBarTextField: UITextField { fatalError("init(coder:) has not been implemented") } + override var keyboardAppearance: UIKeyboardAppearance { + get { + return super.keyboardAppearance + } + set { + let resigning = self.isFirstResponder + if resigning { + self.resignFirstResponder() + } + super.keyboardAppearance = newValue + if resigning { + self.becomeFirstResponder() + } + } + } + override func textRect(forBounds bounds: CGRect) -> CGRect { if bounds.size.width.isZero { return CGRect(origin: CGPoint(), size: CGSize()) @@ -197,8 +213,8 @@ class SearchBarNode: ASDisplayNode, UITextFieldDelegate { didSet { if self.activity != oldValue { if self.activity { - if self.activityIndicator == nil { - let activityIndicator = ActivityIndicator(type: .custom(self.theme.inputIcon, 13.0, 1.0, false)) + if self.activityIndicator == nil, let theme = self.theme { + let activityIndicator = ActivityIndicator(type: .custom(theme.inputIcon, 13.0, 1.0, false)) self.activityIndicator = activityIndicator self.addSubnode(activityIndicator) if let (boundingSize, leftInset, rightInset) = self.validLayout { @@ -225,56 +241,39 @@ class SearchBarNode: ASDisplayNode, UITextFieldDelegate { private var validLayout: (CGSize, CGFloat, CGFloat)? - private var theme: SearchBarNodeTheme - private var strings: PresentationStrings + private var theme: SearchBarNodeTheme? + private var strings: PresentationStrings? init(theme: SearchBarNodeTheme, strings: PresentationStrings) { - self.theme = theme - self.strings = strings - self.backgroundNode = ASDisplayNode() self.backgroundNode.isLayerBacked = true - self.backgroundNode.backgroundColor = theme.background self.separatorNode = ASDisplayNode() self.separatorNode.isLayerBacked = true - self.separatorNode.backgroundColor = theme.separator self.textBackgroundNode = ASImageNode() self.textBackgroundNode.isLayerBacked = false self.textBackgroundNode.displaysAsynchronously = false self.textBackgroundNode.displayWithoutProcessing = true - self.textBackgroundNode.image = generateBackground(backgroundColor: theme.background, foregroundColor: theme.inputFill) self.iconNode = ASImageNode() self.iconNode.isLayerBacked = true self.iconNode.displaysAsynchronously = false self.iconNode.displayWithoutProcessing = true - self.iconNode.image = generateLoupeIcon(color: theme.inputIcon) self.textField = SearchBarTextField() self.textField.autocorrectionType = .no self.textField.returnKeyType = .search self.textField.font = Font.regular(14.0) - self.textField.textColor = theme.primaryText self.clearButton = HighlightableButtonNode() self.clearButton.imageNode.displaysAsynchronously = false self.clearButton.imageNode.displayWithoutProcessing = true self.clearButton.displaysAsynchronously = false - self.clearButton.setImage(generateClearIcon(color: theme.inputClear), for: []) self.clearButton.isHidden = true - switch theme.keyboard { - case .light: - self.textField.keyboardAppearance = .default - case .dark: - self.textField.keyboardAppearance = .dark - } - self.cancelButton = HighlightableButtonNode() self.cancelButton.hitTestSlop = UIEdgeInsets(top: -8.0, left: -8.0, bottom: -8.0, right: -8.0) - self.cancelButton.setAttributedTitle(NSAttributedString(string: strings.Common_Cancel, font: Font.regular(17.0), textColor: theme.accent), for: []) self.cancelButton.displaysAsynchronously = false super.init() @@ -297,15 +296,32 @@ class SearchBarNode: ASDisplayNode, UITextFieldDelegate { self.cancelButton.addTarget(self, action: #selector(self.cancelPressed), forControlEvents: .touchUpInside) self.clearButton.addTarget(self, action: #selector(self.clearPressed), forControlEvents: .touchUpInside) + + self.updateThemeAndStrings(theme: theme, strings: strings) } func updateThemeAndStrings(theme: SearchBarNodeTheme, strings: PresentationStrings) { - if self.theme !== theme { + if self.theme !== theme || self.strings !== strings { self.cancelButton.setAttributedTitle(NSAttributedString(string: strings.Common_Cancel, font: Font.regular(17.0), textColor: theme.accent), for: []) + } + if self.theme !== theme { self.backgroundNode.backgroundColor = theme.background self.separatorNode.backgroundColor = theme.separator self.textBackgroundNode.image = generateBackground(backgroundColor: theme.background, foregroundColor: theme.inputFill) + self.textField.textColor = theme.primaryText + self.clearButton.setImage(generateClearIcon(color: theme.inputClear), for: []) + self.iconNode.image = generateLoupeIcon(color: theme.inputIcon) + switch theme.keyboard { + case .light: + self.textField.keyboardAppearance = .default + case .dark: + self.textField.keyboardAppearance = .dark + } + + if let activityIndicator = self.activityIndicator { + activityIndicator.type = .custom(theme.inputIcon, 13.0, 1.0, false) + } } self.theme = theme diff --git a/TelegramUI/SearchDisplayController.swift b/TelegramUI/SearchDisplayController.swift index a39a21bdf1..dd11bf2cba 100644 --- a/TelegramUI/SearchDisplayController.swift +++ b/TelegramUI/SearchDisplayController.swift @@ -105,7 +105,7 @@ final class SearchDisplayController { } func deactivate(placeholder: SearchBarPlaceholderNode?, animated: Bool = true) { - searchBar.deactivate() + self.searchBar.deactivate() if let placeholder = placeholder { let searchBar = self.searchBar diff --git a/TelegramUI/SecureIdAuthControllerNode.swift b/TelegramUI/SecureIdAuthControllerNode.swift index 7e305227aa..7752bc2076 100644 --- a/TelegramUI/SecureIdAuthControllerNode.swift +++ b/TelegramUI/SecureIdAuthControllerNode.swift @@ -450,19 +450,20 @@ final class SecureIdAuthControllerNode: ViewControllerTracingNode { switch field { case let .identity(personalDetails, document): if let document = document { - var hasValueType: (document: SecureIdRequestedIdentityDocument, selfie: Bool, translation: Bool)? + var hasValueType: (document: SecureIdRequestedIdentityDocument, requireSelfie: Bool, hasSelfie: Bool, requireTranslation: Bool, hasTranslation: Bool)? switch document { case let .just(type): if let value = findValue(formData.values, key: type.document.valueKey)?.1 { + let data = extractSecureIdValueAdditionalData(value.value) switch value.value { case .passport: - hasValueType = (.passport, type.selfie, type.translation) + hasValueType = (.passport, type.selfie, data.selfie, type.translation, data.translation) case .idCard: - hasValueType = (.idCard, type.selfie, type.translation) + hasValueType = (.idCard, type.selfie, data.selfie, type.translation, data.translation) case .driversLicense: - hasValueType = (.driversLicense, type.selfie, type.translation) + hasValueType = (.driversLicense, type.selfie, data.selfie, type.translation, data.translation) case .internalPassport: - hasValueType = (.internalPassport, type.selfie, type.translation) + hasValueType = (.internalPassport, type.selfie, data.selfie, type.translation, data.translation) default: break } @@ -481,13 +482,13 @@ final class SecureIdAuthControllerNode: ViewControllerTracingNode { if hasValueType == nil || dataFilled { switch value.value { case .passport: - hasValueType = (.passport, type.selfie, type.translation) + hasValueType = (.passport, type.selfie, data.selfie, type.translation, data.translation) case .idCard: - hasValueType = (.idCard, type.selfie, type.translation) + hasValueType = (.idCard, type.selfie, data.selfie, type.translation, data.translation) case .driversLicense: - hasValueType = (.driversLicense, type.selfie, type.translation) + hasValueType = (.driversLicense, type.selfie, data.selfie, type.translation, data.translation) case .internalPassport: - hasValueType = (.internalPassport, type.selfie, type.translation) + hasValueType = (.internalPassport, type.selfie, data.selfie, type.translation, data.translation) default: break } @@ -499,8 +500,15 @@ final class SecureIdAuthControllerNode: ViewControllerTracingNode { } } } - if let (hasValueType, selfie, translation) = hasValueType { - self.interaction.present(SecureIdDocumentFormController(account: self.account, context: context, requestedData: .identity(details: personalDetails, document: hasValueType, selfie: selfie, translations: translation), primaryLanguageByCountry: encryptedFormData.primaryLanguageByCountry, values: formData.values, updatedValues: { values in + if let (hasValueType, requireSelfie, hasSelfie, requireTranslation, hasTranslation) = hasValueType { + var scrollTo: SecureIdDocumentFormScrollToSubject? + if requireSelfie && !hasSelfie { + scrollTo = .selfie + } + else if requireTranslation && !hasTranslation { + scrollTo = .translation + } + self.interaction.present(SecureIdDocumentFormController(account: self.account, context: context, requestedData: .identity(details: personalDetails, document: hasValueType, selfie: requireSelfie, translations: requireTranslation), scrollTo: scrollTo, primaryLanguageByCountry: encryptedFormData.primaryLanguageByCountry, values: formData.values, updatedValues: { values in var keys: [SecureIdValueKey] = [] if personalDetails != nil { keys.append(.personalDetails) @@ -518,21 +526,22 @@ final class SecureIdAuthControllerNode: ViewControllerTracingNode { } case let .address(addressDetails, document): if let document = document { - var hasValueType: (document: SecureIdRequestedAddressDocument, translation: Bool)? + var hasValueType: (document: SecureIdRequestedAddressDocument, requireTranslation: Bool, hasTranslation: Bool)? switch document { case let .just(type): if let value = findValue(formData.values, key: type.document.valueKey)?.1 { + let data = extractSecureIdValueAdditionalData(value.value) switch value.value { case .utilityBill: - hasValueType = (.utilityBill, type.translation) + hasValueType = (.utilityBill, type.translation, data.translation) case .bankStatement: - hasValueType = (.bankStatement, type.translation) + hasValueType = (.bankStatement, type.translation, data.translation) case .rentalAgreement: - hasValueType = (.rentalAgreement, type.translation) + hasValueType = (.rentalAgreement, type.translation, data.translation) case .passportRegistration: - hasValueType = (.passportRegistration, type.translation) + hasValueType = (.passportRegistration, type.translation, data.translation) case .temporaryRegistration: - hasValueType = (.temporaryRegistration, type.translation) + hasValueType = (.temporaryRegistration, type.translation, data.translation) default: break } @@ -549,15 +558,15 @@ final class SecureIdAuthControllerNode: ViewControllerTracingNode { if hasValueType == nil || dataFilled { switch value.value { case .utilityBill: - hasValueType = (.utilityBill, type.translation) + hasValueType = (.utilityBill, type.translation, data.translation) case .bankStatement: - hasValueType = (.bankStatement, type.translation) + hasValueType = (.bankStatement, type.translation, data.translation) case .rentalAgreement: - hasValueType = (.rentalAgreement, type.translation) + hasValueType = (.rentalAgreement, type.translation, data.translation) case .passportRegistration: - hasValueType = (.passportRegistration, type.translation) + hasValueType = (.passportRegistration, type.translation, data.translation) case .temporaryRegistration: - hasValueType = (.temporaryRegistration, type.translation) + hasValueType = (.temporaryRegistration, type.translation, data.translation) default: break } @@ -569,8 +578,12 @@ final class SecureIdAuthControllerNode: ViewControllerTracingNode { } } } - if let (hasValueType, translation) = hasValueType { - self.interaction.present(SecureIdDocumentFormController(account: self.account, context: context, requestedData: .address(details: addressDetails, document: hasValueType, translations: translation), primaryLanguageByCountry: encryptedFormData.primaryLanguageByCountry, values: formData.values, updatedValues: { values in + if let (hasValueType, requireTranslation, hasTranslation) = hasValueType { + var scrollTo: SecureIdDocumentFormScrollToSubject? + if requireTranslation && !hasTranslation { + scrollTo = .translation + } + self.interaction.present(SecureIdDocumentFormController(account: self.account, context: context, requestedData: .address(details: addressDetails, document: hasValueType, translations: requireTranslation), scrollTo: scrollTo, primaryLanguageByCountry: encryptedFormData.primaryLanguageByCountry, values: formData.values, updatedValues: { values in var keys: [SecureIdValueKey] = [] if addressDetails { keys.append(.address) @@ -654,7 +667,7 @@ final class SecureIdAuthControllerNode: ViewControllerTracingNode { if itemsForField.count == 1 { completionImpl(itemsForField[0].1) } else { - let controller = SecureIdDocumentTypeSelectionController(theme: self.presentationData.theme, strings: self.presentationData.strings, field: field, currentValues: formData.values, completion: completionImpl) + let controller = SecureIdDocumentTypeSelectionController(account: self.account, field: field, currentValues: formData.values, completion: completionImpl) self.interaction.present(controller, nil) } } diff --git a/TelegramUI/SecureIdDocumentFormController.swift b/TelegramUI/SecureIdDocumentFormController.swift index 034985e947..afb78d87bf 100644 --- a/TelegramUI/SecureIdDocumentFormController.swift +++ b/TelegramUI/SecureIdDocumentFormController.swift @@ -5,6 +5,11 @@ import SwiftSignalKit import Postbox import TelegramCore +enum SecureIdDocumentFormScrollToSubject { + case selfie + case translation +} + enum SecureIdDocumentFormRequestedData { case identity(details: ParsedRequestedPersonalDetails?, document: SecureIdRequestedIdentityDocument?, selfie: Bool, translations: Bool) case address(details: Bool, document: SecureIdRequestedAddressDocument?, translations: Bool) @@ -20,10 +25,11 @@ final class SecureIdDocumentFormController: FormController Void) { + init(account: Account, context: SecureIdAccessContext, requestedData: SecureIdDocumentFormRequestedData, requestOptionalData: Bool = false, scrollTo: SecureIdDocumentFormScrollToSubject? = nil, primaryLanguageByCountry: [String: String], values: [SecureIdValueWithContext], updatedValues: @escaping ([SecureIdValueWithContext]) -> Void) { self.account = account self.presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 } self.context = context @@ -32,6 +38,7 @@ final class SecureIdDocumentFormController: FormController Void fileprivate let openDocument: (SecureIdVerificationDocument) -> Void + fileprivate let deleteDocument: (SecureIdVerificationDocument) -> Void fileprivate let updateText: (SecureIdDocumentFormTextField, String) -> Void fileprivate let selectNextInputItem: (SecureIdDocumentFormEntry) -> Void fileprivate let endEditing: () -> Void @@ -56,11 +57,12 @@ final class SecureIdDocumentFormParams { fileprivate let scanPassport: () -> Void fileprivate let deleteValue: () -> Void - fileprivate init(account: Account, context: SecureIdAccessContext, addFile: @escaping (SecureIdAddFileTarget) -> Void, openDocument: @escaping (SecureIdVerificationDocument) -> Void, updateText: @escaping (SecureIdDocumentFormTextField, String) -> Void, selectNextInputItem: @escaping (SecureIdDocumentFormEntry) -> Void, endEditing: @escaping () -> Void, activateSelection: @escaping (SecureIdDocumentFormSelectionField) -> Void, scanPassport: @escaping () -> Void, deleteValue: @escaping () -> Void) { + fileprivate init(account: Account, context: SecureIdAccessContext, addFile: @escaping (SecureIdAddFileTarget) -> Void, openDocument: @escaping (SecureIdVerificationDocument) -> Void, deleteDocument: @escaping (SecureIdVerificationDocument) -> Void, updateText: @escaping (SecureIdDocumentFormTextField, String) -> Void, selectNextInputItem: @escaping (SecureIdDocumentFormEntry) -> Void, endEditing: @escaping () -> Void, activateSelection: @escaping (SecureIdDocumentFormSelectionField) -> Void, scanPassport: @escaping () -> Void, deleteValue: @escaping () -> Void) { self.account = account self.context = context self.addFile = addFile self.openDocument = openDocument + self.deleteDocument = deleteDocument self.updateText = updateText self.selectNextInputItem = selectNextInputItem self.endEditing = endEditing @@ -1802,6 +1804,8 @@ enum SecureIdDocumentFormEntry: FormControllerEntry { case let .scan(index, document, error): return SecureIdValueFormFileItem(account: params.account, context: params.context, document: document, placeholder: nil, title: strings.Passport_Scans_ScanIndex("\(index + 1)").0, label: error.flatMap(SecureIdValueFormFileItemLabel.error) ?? .timestamp, activated: { params.openDocument(document) + }, deleted: { + params.deleteDocument(document) }) case let .addScan(hasAny): return FormControllerActionItem(type: .accent, title: hasAny ? strings.Passport_Scans_UploadNew : strings.Passport_Scans_Upload, fullTopInset: true, activated: { @@ -2027,6 +2031,10 @@ enum SecureIdDocumentFormEntry: FormControllerEntry { } else { params.addFile(.selfie) } + }, deleted: { + if let document = document { + params.deleteDocument(document) + } }) case let .frontSide(_, type, document, error): let label: SecureIdValueFormFileItemLabel @@ -2061,6 +2069,10 @@ enum SecureIdDocumentFormEntry: FormControllerEntry { } else { params.addFile(.frontSide(type)) } + }, deleted: { + if let document = document { + params.deleteDocument(document) + } }) case let .backSide(_, type, document, error): let label: SecureIdValueFormFileItemLabel @@ -2077,6 +2089,10 @@ enum SecureIdDocumentFormEntry: FormControllerEntry { } else { params.addFile(.backSide(type)) } + }, deleted: { + if let document = document { + params.deleteDocument(document) + } }) case let .documentsInfo(category): let text: String @@ -2092,6 +2108,8 @@ enum SecureIdDocumentFormEntry: FormControllerEntry { case let .translation(index, document, error): return SecureIdValueFormFileItem(account: params.account, context: params.context, document: document, placeholder: nil, title: strings.Passport_Scans_ScanIndex("\(index + 1)").0, label: error.flatMap(SecureIdValueFormFileItemLabel.error) ?? .timestamp, activated: { params.openDocument(document) + }, deleted: { + params.deleteDocument(document) }) case let .addTranslation(hasAny): return FormControllerActionItem(type: .accent, title: hasAny ? strings.Passport_Scans_UploadNew : strings.Passport_Scans_Upload, fullTopInset: true, activated: { @@ -2128,6 +2146,8 @@ final class SecureIdDocumentFormControllerNode: FormControllerNode Void)? var dismiss: (() -> Void)? + var initiallyScrollTo: SecureIdDocumentFormScrollToSubject? + private let actionDisposable = MetaDisposable() private let hiddenItemDisposable = MetaDisposable() @@ -2154,6 +2174,10 @@ final class SecureIdDocumentFormControllerNode: FormControllerNode Bool in + switch subject { + case .selfie: + if case .selfie = entry { + self.scrollToItemNode(itemNode) + return false + } + case .translation: + if case .translationsHeader = entry { + self.scrollToItemNode(itemNode) + return false + } + } + return true + } + } } diff --git a/TelegramUI/SecureIdDocumentTypeSelectionController.swift b/TelegramUI/SecureIdDocumentTypeSelectionController.swift index 9e92e24054..897ef44b18 100644 --- a/TelegramUI/SecureIdDocumentTypeSelectionController.swift +++ b/TelegramUI/SecureIdDocumentTypeSelectionController.swift @@ -70,8 +70,7 @@ func documentSelectionItemsForField(field: SecureIdParsedRequestedFormField, str } final class SecureIdDocumentTypeSelectionController: ActionSheetController { - private let theme: PresentationTheme - private let strings: PresentationStrings + private var presentationDisposable: Disposable? private let completion: (SecureIdDocumentFormRequestedData) -> Void private let _ready = Promise() @@ -79,13 +78,21 @@ final class SecureIdDocumentTypeSelectionController: ActionSheetController { return self._ready } - init(theme: PresentationTheme, strings: PresentationStrings, field: SecureIdParsedRequestedFormField, currentValues: [SecureIdValueWithContext], completion: @escaping (SecureIdDocumentFormRequestedData) -> Void) { - self.theme = theme - self.strings = strings + init(account: Account, field: SecureIdParsedRequestedFormField, currentValues: [SecureIdValueWithContext], completion: @escaping (SecureIdDocumentFormRequestedData) -> Void) { + let presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 } + let theme = presentationData.theme + let strings = presentationData.strings + self.completion = completion super.init(theme: ActionSheetControllerTheme(presentationTheme: theme)) + self.presentationDisposable = account.telegramApplicationContext.presentationData.start(next: { [weak self] presentationData in + if let strongSelf = self { + strongSelf.theme = ActionSheetControllerTheme(presentationTheme: presentationData.theme) + } + }) + self._ready.set(.single(true)) var items: [ActionSheetItem] = [] @@ -108,4 +115,8 @@ final class SecureIdDocumentTypeSelectionController: ActionSheetController { required init(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } + + deinit { + self.presentationDisposable?.dispose() + } } diff --git a/TelegramUI/SecureIdValueFormFileItem.swift b/TelegramUI/SecureIdValueFormFileItem.swift index 467b56cb6b..1741070bfb 100644 --- a/TelegramUI/SecureIdValueFormFileItem.swift +++ b/TelegramUI/SecureIdValueFormFileItem.swift @@ -12,6 +12,10 @@ enum SecureIdValueFormFileItemLabel { case text(String) } +private enum RevealOptionKey: Int32 { + case delete +} + final class SecureIdValueFormFileItem: FormControllerItem { let account: Account let context: SecureIdAccessContext @@ -20,8 +24,9 @@ final class SecureIdValueFormFileItem: FormControllerItem { let title: String let label: SecureIdValueFormFileItemLabel let activated: () -> Void + let deleted: () -> Void - init(account: Account, context: SecureIdAccessContext, document: SecureIdVerificationDocument?, placeholder: UIImage?, title: String, label: SecureIdValueFormFileItemLabel, activated: @escaping () -> Void) { + init(account: Account, context: SecureIdAccessContext, document: SecureIdVerificationDocument?, placeholder: UIImage?, title: String, label: SecureIdValueFormFileItemLabel, activated: @escaping () -> Void, deleted: @escaping () -> Void) { self.account = account self.context = context self.document = document @@ -29,6 +34,7 @@ final class SecureIdValueFormFileItem: FormControllerItem { self.title = title self.label = label self.activated = activated + self.deleted = deleted } func node() -> ASDisplayNode & FormControllerItemNode { @@ -46,7 +52,7 @@ final class SecureIdValueFormFileItem: FormControllerItem { } } -final class SecureIdValueFormFileItemNode: FormBlockItemNode { +final class SecureIdValueFormFileItemNode: FormEditableBlockItemNode { private let titleNode: ImmediateTextNode private let labelNode: ImmediateTextNode let imageNode: TransformImageNode @@ -113,9 +119,13 @@ final class SecureIdValueFormFileItemNode: FormBlockItemNode deliverOnMainQueue).start(next: { result in switch result { @@ -668,7 +668,7 @@ public func settingsController(account: Account, accountManager: AccountManager) let _ = currentAvatarMixin.swap(nil) updateState { if let profileImage = peer?.smallProfileImage { - return $0.withUpdatedUpdatingAvatar(.image(profileImage)) + return $0.withUpdatedUpdatingAvatar(.image(profileImage, false)) } else { return $0.withUpdatedUpdatingAvatar(.none) } diff --git a/TelegramUI/ShareActionButtonNode.swift b/TelegramUI/ShareActionButtonNode.swift index 9a94ea59d9..ee69ee0c67 100644 --- a/TelegramUI/ShareActionButtonNode.swift +++ b/TelegramUI/ShareActionButtonNode.swift @@ -3,12 +3,22 @@ import AsyncDisplayKit import Display final class ShareActionButtonNode: HighlightTrackingButtonNode { - private let badgeTextColor: UIColor - private let badgeLabel: TextNode private var badgeText: NSAttributedString? private let badgeBackground: ASImageNode + var badgeBackgroundColor: UIColor { + didSet { + self.badgeBackground.image = generateStretchableFilledCircleImage(diameter: 22.0, color: self.badgeBackgroundColor) + } + } + + var badgeTextColor: UIColor { + didSet { + self.setNeedsLayout() + } + } + var badge: String? { didSet { if self.badge != oldValue { @@ -28,6 +38,7 @@ final class ShareActionButtonNode: HighlightTrackingButtonNode { } init(badgeBackgroundColor: UIColor, badgeTextColor: UIColor) { + self.badgeBackgroundColor = badgeBackgroundColor self.badgeTextColor = badgeTextColor self.badgeLabel = TextNode() diff --git a/TelegramUI/ShareController.swift b/TelegramUI/ShareController.swift index f9ee8ae072..4dd796d2ec 100644 --- a/TelegramUI/ShareController.swift +++ b/TelegramUI/ShareController.swift @@ -178,6 +178,8 @@ public final class ShareController: ViewController { private let account: Account private var presentationData: PresentationData + private var presentationDataDisposable: Disposable? + private let externalShare: Bool private let immediateExternalShare: Bool private let subject: ShareControllerSubject @@ -199,6 +201,13 @@ public final class ShareController: ViewController { super.init(navigationBarPresentationData: nil) + self.presentationDataDisposable = (self.account.telegramApplicationContext.presentationData + |> deliverOnMainQueue).start(next: { [weak self] presentationData in + if let strongSelf = self { + strongSelf.controllerNode.updatePresentationData(presentationData) + } + }) + switch subject { case let .url(text): self.defaultAction = ShareControllerAction(title: self.presentationData.strings.ShareMenu_CopyShareLink, action: { [weak self] in diff --git a/TelegramUI/ShareControllerNode.swift b/TelegramUI/ShareControllerNode.swift index 622baf16eb..4dd631c0b3 100644 --- a/TelegramUI/ShareControllerNode.swift +++ b/TelegramUI/ShareControllerNode.swift @@ -127,7 +127,7 @@ final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate self.actionButtonNode.titleNode.displaysAsynchronously = false self.actionButtonNode.setBackgroundImage(highlightedHalfRoundedBackground, for: .highlighted) - self.inputFieldNode = ShareInputFieldNode(theme: self.presentationData.theme, placeholder: self.presentationData.strings.ShareMenu_Comment) + self.inputFieldNode = ShareInputFieldNode(theme: ShareInputFieldNodeTheme(presentationTheme: self.presentationData.theme), placeholder: self.presentationData.strings.ShareMenu_Comment) self.inputFieldNode.alpha = 0.0 self.actionSeparatorNode = ASDisplayNode() @@ -231,6 +231,40 @@ final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate } } + func updatePresentationData(_ presentationData: PresentationData) { + self.presentationData = presentationData + + 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.cancelButtonNode.setBackgroundImage(roundedBackground, for: .normal) + self.cancelButtonNode.setBackgroundImage(highlightedRoundedBackground, for: .highlighted) + + self.contentBackgroundNode.image = roundedBackground + self.actionsBackgroundNode.image = halfRoundedBackground + self.actionButtonNode.setBackgroundImage(highlightedHalfRoundedBackground, for: .highlighted) + self.actionSeparatorNode.backgroundColor = presentationData.theme.actionSheet.opaqueItemSeparatorColor + self.cancelButtonNode.setTitle(presentationData.strings.Common_Cancel, with: Font.medium(20.0), with: presentationData.theme.actionSheet.standardActionTextColor, for: .normal) + + self.actionButtonNode.badgeBackgroundColor = presentationData.theme.actionSheet.controlAccentColor + self.actionButtonNode.badgeTextColor = presentationData.theme.actionSheet.opaqueItemBackgroundColor + } + func setActionNodesHidden(_ hidden: Bool, inputField: Bool = false, actions: Bool = false) { func updateActionNodesAlpha(_ nodes: [ASDisplayNode], alpha: CGFloat) { for node in nodes { diff --git a/TelegramUI/ShareInputFieldNode.swift b/TelegramUI/ShareInputFieldNode.swift index fd4e69c67c..d2a10aae92 100644 --- a/TelegramUI/ShareInputFieldNode.swift +++ b/TelegramUI/ShareInputFieldNode.swift @@ -6,10 +6,51 @@ private func generateClearIcon(color: UIColor) -> UIImage? { return generateTintedImage(image: UIImage(bundleImageName: "Components/Search Bar/Clear"), color: color) } +final class ShareInputFieldNodeTheme: Equatable { + let backgroundColor: UIColor + let textColor: UIColor + let placeholderColor: UIColor + let clearButtonColor: UIColor + let keyboardAppearance: UIKeyboardAppearance + + public init(backgroundColor: UIColor, textColor: UIColor, placeholderColor: UIColor, clearButtonColor: UIColor, keyboardAppearance: UIKeyboardAppearance) { + self.backgroundColor = backgroundColor + self.textColor = textColor + self.placeholderColor = placeholderColor + self.clearButtonColor = clearButtonColor + self.keyboardAppearance = keyboardAppearance + } + + public static func ==(lhs: ShareInputFieldNodeTheme, rhs: ShareInputFieldNodeTheme) -> Bool { + if lhs.backgroundColor != rhs.backgroundColor { + return false + } + if lhs.textColor != rhs.textColor { + return false + } + if lhs.placeholderColor != rhs.placeholderColor { + return false + } + if lhs.clearButtonColor != rhs.clearButtonColor { + return false + } + if lhs.keyboardAppearance != rhs.keyboardAppearance { + return false + } + return true + } +} + +extension ShareInputFieldNodeTheme { + convenience init(presentationTheme theme: PresentationTheme) { + self.init(backgroundColor: theme.actionSheet.inputBackgroundColor, textColor: theme.actionSheet.inputTextColor, placeholderColor: theme.actionSheet.inputPlaceholderColor, clearButtonColor: theme.actionSheet.inputClearButtonColor, keyboardAppearance: .default) + } +} + final class ShareInputFieldNode: ASDisplayNode, ASEditableTextNodeDelegate { - private let theme: PresentationTheme + private let theme: ShareInputFieldNodeTheme private let backgroundNode: ASImageNode - private let textInputNode: ASEditableTextNode + private let textInputNode: EditableTextNode private let placeholderNode: ASTextNode private let clearButton: HighlightableButtonNode @@ -23,35 +64,41 @@ final class ShareInputFieldNode: ASDisplayNode, ASEditableTextNodeDelegate { return self.textInputNode.attributedText?.string ?? "" } - init(theme: PresentationTheme, placeholder: String) { + var placeholder: String = "" { + didSet { + self.placeholderNode.attributedText = NSAttributedString(string: self.placeholder, font: Font.regular(17.0), textColor: self.theme.placeholderColor) + } + } + + init(theme: ShareInputFieldNodeTheme, placeholder: String) { self.theme = theme self.backgroundNode = ASImageNode() self.backgroundNode.isLayerBacked = true self.backgroundNode.displaysAsynchronously = false self.backgroundNode.displayWithoutProcessing = true - self.backgroundNode.image = generateStretchableFilledCircleImage(diameter: 16.0, color: theme.actionSheet.inputBackgroundColor) + self.backgroundNode.image = generateStretchableFilledCircleImage(diameter: 16.0, color: theme.backgroundColor) - self.textInputNode = ASEditableTextNode() - let textColor: UIColor = theme.actionSheet.inputTextColor + self.textInputNode = EditableTextNode() + let textColor: UIColor = theme.textColor let keyboardAppearance: UIKeyboardAppearance = UIKeyboardAppearance.default - textInputNode.typingAttributes = [NSAttributedStringKey.font.rawValue: Font.regular(17.0), NSAttributedStringKey.foregroundColor.rawValue: textColor] - textInputNode.clipsToBounds = true - textInputNode.hitTestSlop = UIEdgeInsets(top: -5.0, left: -5.0, bottom: -5.0, right: -5.0) - textInputNode.keyboardAppearance = keyboardAppearance - textInputNode.textContainerInset = UIEdgeInsets(top: self.inputInsets.top, left: 0.0, bottom: self.inputInsets.bottom, right: 0.0) - textInputNode.keyboardAppearance = theme.chatList.searchBarKeyboardColor.keyboardAppearance + self.textInputNode.typingAttributes = [NSAttributedStringKey.font.rawValue: Font.regular(17.0), NSAttributedStringKey.foregroundColor.rawValue: textColor] + self.textInputNode.clipsToBounds = true + self.textInputNode.hitTestSlop = UIEdgeInsets(top: -5.0, left: -5.0, bottom: -5.0, right: -5.0) + self.textInputNode.keyboardAppearance = keyboardAppearance + self.textInputNode.textContainerInset = UIEdgeInsets(top: self.inputInsets.top, left: 0.0, bottom: self.inputInsets.bottom, right: 0.0) + self.textInputNode.keyboardAppearance = theme.keyboardAppearance self.placeholderNode = ASTextNode() self.placeholderNode.isUserInteractionEnabled = false self.placeholderNode.displaysAsynchronously = false - self.placeholderNode.attributedText = NSAttributedString(string: placeholder, font: Font.regular(17.0), textColor: theme.actionSheet.inputPlaceholderColor) + self.placeholderNode.attributedText = NSAttributedString(string: placeholder, font: Font.regular(17.0), textColor: theme.placeholderColor) self.clearButton = HighlightableButtonNode() self.clearButton.imageNode.displaysAsynchronously = false self.clearButton.imageNode.displayWithoutProcessing = true self.clearButton.displaysAsynchronously = false - self.clearButton.setImage(generateClearIcon(color: theme.actionSheet.inputClearButtonColor), for: []) + self.clearButton.setImage(generateClearIcon(color: theme.clearButtonColor), for: []) self.clearButton.isHidden = true super.init() diff --git a/TelegramUI/StickerPackPreviewController.swift b/TelegramUI/StickerPackPreviewController.swift index e301bd862c..d27939a137 100644 --- a/TelegramUI/StickerPackPreviewController.swift +++ b/TelegramUI/StickerPackPreviewController.swift @@ -16,7 +16,6 @@ final class StickerPackPreviewController: ViewController { private let account: Account private weak var parentNavigationController: NavigationController? - private var presentationData: PresentationData private let stickerPack: StickerPackReference private var stickerPackContentsValue: LoadedStickerPack? @@ -29,6 +28,8 @@ final class StickerPackPreviewController: ViewController { private let openMentionDisposable = MetaDisposable() + private var presentationDataDisposable: Disposable? + var sendSticker: ((FileMediaReference) -> Void)? { didSet { if self.isNodeLoaded { @@ -50,13 +51,18 @@ final class StickerPackPreviewController: ViewController { self.stickerPack = stickerPack - self.presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 } - super.init(navigationBarPresentationData: nil) self.statusBar.statusBarStyle = .Ignore self.stickerPackContents.set(loadedStickerPack(postbox: account.postbox, network: account.network, reference: stickerPack, forceActualized: true)) + + self.presentationDataDisposable = (self.account.telegramApplicationContext.presentationData + |> deliverOnMainQueue).start(next: { [weak self] presentationData in + if let strongSelf = self { + strongSelf.controllerNode.updatePresentationData(presentationData) + } + }) } required init(coder aDecoder: NSCoder) { @@ -67,6 +73,7 @@ final class StickerPackPreviewController: ViewController { self.stickerPackDisposable.dispose() self.stickerPackInstalledDisposable.dispose() self.openMentionDisposable.dispose() + self.presentationDataDisposable?.dispose() } override func loadDisplayNode() { @@ -123,7 +130,8 @@ final class StickerPackPreviewController: ViewController { self.stickerPackDisposable.set((self.stickerPackContents.get() |> deliverOnMainQueue).start(next: { [weak self] next in if let strongSelf = self { if case .none = next { - strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.presentationData.theme), title: nil, text: strongSelf.presentationData.strings.StickerPack_ErrorNotFound, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + let presentationData = strongSelf.account.telegramApplicationContext.currentPresentationData.with { $0 } + strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: nil, text: presentationData.strings.StickerPack_ErrorNotFound, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root)) strongSelf.dismiss() } else { strongSelf.controllerNode.updateStickerPack(next) diff --git a/TelegramUI/StickerPackPreviewControllerNode.swift b/TelegramUI/StickerPackPreviewControllerNode.swift index 195edecdc8..5c310ba322 100644 --- a/TelegramUI/StickerPackPreviewControllerNode.swift +++ b/TelegramUI/StickerPackPreviewControllerNode.swift @@ -39,7 +39,7 @@ private struct StickerPackPreviewGridTransaction { final class StickerPackPreviewControllerNode: ViewControllerTracingNode, UIScrollViewDelegate { private let account: Account private let openShare: () -> Void - private let presentationData: PresentationData + private var presentationData: PresentationData private var containerLayout: (ContainerViewLayout, CGFloat)? @@ -82,24 +82,6 @@ final class StickerPackPreviewControllerNode: ViewControllerTracingNode, UIScrol self.openShare = openShare self.presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 } - 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) - - let roundedBackground = generateStretchableFilledCircleImage(radius: 16.0, color: self.presentationData.theme.actionSheet.opaqueItemBackgroundColor) - let highlightedRoundedBackground = generateStretchableFilledCircleImage(radius: 16.0, color: self.presentationData.theme.actionSheet.opaqueItemHighlightedBackgroundColor) - self.wrappingScrollNode = ASScrollNode() self.wrappingScrollNode.view.alwaysBounceVertical = true self.wrappingScrollNode.view.delaysContentTouches = false @@ -110,8 +92,6 @@ final class StickerPackPreviewControllerNode: ViewControllerTracingNode, UIScrol 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 @@ -119,32 +99,26 @@ final class StickerPackPreviewControllerNode: ViewControllerTracingNode, UIScrol self.contentBackgroundNode = ASImageNode() self.contentBackgroundNode.displaysAsynchronously = false self.contentBackgroundNode.displayWithoutProcessing = true - self.contentBackgroundNode.image = roundedBackground self.contentGridNode = GridNode() self.installActionButtonNode = HighlightTrackingButtonNode() self.installActionButtonNode.displaysAsynchronously = false self.installActionButtonNode.titleNode.displaysAsynchronously = false - self.installActionButtonNode.setBackgroundImage(halfRoundedBackground, for: .normal) - self.installActionButtonNode.setBackgroundImage(highlightedHalfRoundedBackground, for: .highlighted) self.contentTitleNode = ImmediateTextNode() self.contentTitleNode.displaysAsynchronously = false self.contentTitleNode.maximumNumberOfLines = 1 self.contentShareButtonNode = HighlightableButtonNode() - self.contentShareButtonNode.setImage(generateTintedImage(image: UIImage(bundleImageName: "Share/ShareIcon"), color: self.presentationData.theme.actionSheet.controlAccentColor), for: []) self.contentShareButtonNode.isHidden = true self.contentSeparatorNode = ASDisplayNode() self.contentSeparatorNode.isLayerBacked = true - self.contentSeparatorNode.backgroundColor = self.presentationData.theme.actionSheet.opaqueItemSeparatorColor self.installActionSeparatorNode = ASDisplayNode() self.installActionSeparatorNode.isLayerBacked = true self.installActionSeparatorNode.displaysAsynchronously = false - self.installActionSeparatorNode.backgroundColor = self.presentationData.theme.actionSheet.opaqueItemSeparatorColor super.init() @@ -165,8 +139,6 @@ final class StickerPackPreviewControllerNode: ViewControllerTracingNode, UIScrol 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) @@ -186,7 +158,6 @@ final class StickerPackPreviewControllerNode: ViewControllerTracingNode, UIScrol self?.gridPresentationLayoutUpdated(presentationLayout, transition: transition) } - self.contentTitleNode.linkHighlightColor = self.presentationData.theme.actionSheet.controlAccentColor.withAlphaComponent(0.5) self.contentTitleNode.highlightAttributeAction = { attributes in if let _ = attributes[NSAttributedStringKey(rawValue: TelegramTextAttributes.PeerTextMention)] { return NSAttributedStringKey(rawValue: TelegramTextAttributes.PeerTextMention) @@ -215,34 +186,34 @@ final class StickerPackPreviewControllerNode: ViewControllerTracingNode, UIScrol if let itemNode = strongSelf.contentGridNode.itemNodeAtPoint(point) as? StickerPackPreviewGridItemNode, let item = itemNode.stickerPackItem { return strongSelf.account.postbox.transaction { transaction -> Bool in return getIsStickerSaved(transaction: transaction, fileId: item.file.fileId) - } - |> deliverOnMainQueue - |> map { isStarred -> (ASDisplayNode, PeekControllerContent)? in - if let strongSelf = self { - var menuItems: [PeekControllerMenuItem] = [] - if let stickerPack = strongSelf.stickerPack, case let .result(info, _, _) = stickerPack, info.id.namespace == Namespaces.ItemCollection.CloudStickerPacks { - if strongSelf.sendSticker != nil { - menuItems.append(PeekControllerMenuItem(title: strongSelf.presentationData.strings.ShareMenu_Send, color: .accent, font: .bold, action: { - if let strongSelf = self { - strongSelf.sendSticker?(.standalone(media: item.file)) - } - })) - } - menuItems.append(PeekControllerMenuItem(title: isStarred ? strongSelf.presentationData.strings.Stickers_RemoveFromFavorites : strongSelf.presentationData.strings.Stickers_AddToFavorites, color: isStarred ? .destructive : .accent, action: { - if let strongSelf = self { - if isStarred { - let _ = removeSavedSticker(postbox: strongSelf.account.postbox, mediaId: item.file.fileId).start() - } else { - let _ = addSavedSticker(postbox: strongSelf.account.postbox, network: strongSelf.account.network, file: item.file).start() - } - } - })) - menuItems.append(PeekControllerMenuItem(title: strongSelf.presentationData.strings.Common_Cancel, color: .accent, action: {})) + } + |> deliverOnMainQueue + |> map { isStarred -> (ASDisplayNode, PeekControllerContent)? in + if let strongSelf = self { + var menuItems: [PeekControllerMenuItem] = [] + if let stickerPack = strongSelf.stickerPack, case let .result(info, _, _) = stickerPack, info.id.namespace == Namespaces.ItemCollection.CloudStickerPacks { + if strongSelf.sendSticker != nil { + menuItems.append(PeekControllerMenuItem(title: strongSelf.presentationData.strings.ShareMenu_Send, color: .accent, font: .bold, action: { + if let strongSelf = self { + strongSelf.sendSticker?(.standalone(media: item.file)) + } + })) } - return (itemNode, StickerPreviewPeekContent(account: strongSelf.account, item: .pack(item), menu: menuItems)) - } else { - return nil + menuItems.append(PeekControllerMenuItem(title: isStarred ? strongSelf.presentationData.strings.Stickers_RemoveFromFavorites : strongSelf.presentationData.strings.Stickers_AddToFavorites, color: isStarred ? .destructive : .accent, action: { + if let strongSelf = self { + if isStarred { + let _ = removeSavedSticker(postbox: strongSelf.account.postbox, mediaId: item.file.fileId).start() + } else { + let _ = addSavedSticker(postbox: strongSelf.account.postbox, network: strongSelf.account.network, file: item.file).start() + } + } + })) + menuItems.append(PeekControllerMenuItem(title: strongSelf.presentationData.strings.Common_Cancel, color: .accent, action: {})) } + return (itemNode, StickerPreviewPeekContent(account: strongSelf.account, item: .pack(item), menu: menuItems)) + } else { + return nil + } } } } @@ -265,6 +236,50 @@ final class StickerPackPreviewControllerNode: ViewControllerTracingNode, UIScrol strongSelf.updatePreviewingItem(item: item, animated: true) } }, activateBySingleTap: true)) + + self.updatePresentationData(self.presentationData) + } + + func updatePresentationData(_ presentationData: PresentationData) { + self.presentationData = presentationData + + let theme = 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) + + let roundedBackground = generateStretchableFilledCircleImage(radius: 16.0, color: presentationData.theme.actionSheet.opaqueItemBackgroundColor) + let highlightedRoundedBackground = generateStretchableFilledCircleImage(radius: 16.0, color: presentationData.theme.actionSheet.opaqueItemHighlightedBackgroundColor) + + self.contentBackgroundNode.image = roundedBackground + self.contentShareButtonNode.setImage(generateTintedImage(image: UIImage(bundleImageName: "Share/ShareIcon"), color: presentationData.theme.actionSheet.controlAccentColor), for: []) + + self.cancelButtonNode.setBackgroundImage(roundedBackground, for: .normal) + self.cancelButtonNode.setBackgroundImage(highlightedRoundedBackground, for: .highlighted) + + self.installActionButtonNode.setBackgroundImage(halfRoundedBackground, for: .normal) + self.installActionButtonNode.setBackgroundImage(highlightedHalfRoundedBackground, for: .highlighted) + + self.contentSeparatorNode.backgroundColor = presentationData.theme.actionSheet.opaqueItemSeparatorColor + self.installActionSeparatorNode.backgroundColor = presentationData.theme.actionSheet.opaqueItemSeparatorColor + + self.cancelButtonNode.setTitle(presentationData.strings.Common_Cancel, with: Font.medium(20.0), with: presentationData.theme.actionSheet.standardActionTextColor, for: .normal) + + self.contentTitleNode.linkHighlightColor = presentationData.theme.actionSheet.controlAccentColor.withAlphaComponent(0.5) + + if let (layout, navigationBarHeight) = self.containerLayout { + self.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .immediate) + } } func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) { diff --git a/TelegramUI/StickerPaneSearchBarNode.swift b/TelegramUI/StickerPaneSearchBarNode.swift index 6558fd1c6c..fea30a23ee 100644 --- a/TelegramUI/StickerPaneSearchBarNode.swift +++ b/TelegramUI/StickerPaneSearchBarNode.swift @@ -58,6 +58,22 @@ private class StickerPaneSearchBarTextField: UITextField { required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } + + override var keyboardAppearance: UIKeyboardAppearance { + get { + return super.keyboardAppearance + } + set { + let resigning = self.isFirstResponder + if resigning { + self.resignFirstResponder() + } + super.keyboardAppearance = newValue + if resigning { + self.becomeFirstResponder() + } + } + } override func textRect(forBounds bounds: CGRect) -> CGRect { if bounds.size.width.isZero { @@ -159,7 +175,7 @@ class StickerPaneSearchBarNode: ASDisplayNode, UITextFieldDelegate { didSet { if self.activity != oldValue { if self.activity { - if self.activityIndicator == nil { + if self.activityIndicator == nil, let theme = self.theme { let activityIndicator = ActivityIndicator(type: .custom(theme.chat.inputMediaPanel.stickersSearchControlColor, 13.0, 1.0, false)) self.activityIndicator = activityIndicator self.addSubnode(activityIndicator) @@ -177,57 +193,38 @@ class StickerPaneSearchBarNode: ASDisplayNode, UITextFieldDelegate { } private var validLayout: (CGSize, CGFloat, CGFloat)? + private var theme: PresentationTheme? - private var theme: PresentationTheme - private var strings: PresentationStrings - - init(theme: PresentationTheme, strings: PresentationStrings) { - self.theme = theme - self.strings = strings - + override init() { self.backgroundNode = ASDisplayNode() self.backgroundNode.isLayerBacked = true - //self.backgroundNode.backgroundColor = theme.chat.inputMediaPanel.stickersBackgroundColor self.separatorNode = ASDisplayNode() self.separatorNode.isLayerBacked = true - self.separatorNode.backgroundColor = theme.chat.inputMediaPanel.panelSerapatorColor self.textBackgroundNode = ASImageNode() self.textBackgroundNode.isLayerBacked = false self.textBackgroundNode.displaysAsynchronously = false self.textBackgroundNode.displayWithoutProcessing = true - self.textBackgroundNode.image = generateStretchableFilledCircleImage(diameter: 33.0, color: theme.chat.inputMediaPanel.stickersSearchBackgroundColor) self.iconNode = ASImageNode() self.iconNode.isUserInteractionEnabled = false self.iconNode.displaysAsynchronously = false self.iconNode.displayWithoutProcessing = true - self.iconNode.image = generateLoupeIcon(color: theme.chat.inputMediaPanel.stickersSearchControlColor) self.textField = StickerPaneSearchBarTextField() self.textField.autocorrectionType = .no self.textField.returnKeyType = .done self.textField.font = Font.regular(14.0) - self.textField.textColor = theme.chat.inputMediaPanel.stickersSearchPrimaryColor self.clearButton = HighlightableButtonNode() self.clearButton.imageNode.displaysAsynchronously = false self.clearButton.imageNode.displayWithoutProcessing = true self.clearButton.displaysAsynchronously = false - self.clearButton.setImage(generateClearIcon(color: theme.chat.inputMediaPanel.stickersSearchControlColor), for: []) self.clearButton.isHidden = true - switch theme.chatList.searchBarKeyboardColor { - case .light: - self.textField.keyboardAppearance = .default - case .dark: - self.textField.keyboardAppearance = .dark - } - self.cancelButton = ASButtonNode() self.cancelButton.hitTestSlop = UIEdgeInsets(top: -8.0, left: -8.0, bottom: -8.0, right: -8.0) - self.cancelButton.setAttributedTitle(NSAttributedString(string: strings.Common_Cancel, font: Font.regular(17.0), textColor: theme.chat.inputPanel.panelControlAccentColor), for: []) self.cancelButton.displaysAsynchronously = false super.init() @@ -252,6 +249,31 @@ class StickerPaneSearchBarNode: ASDisplayNode, UITextFieldDelegate { self.clearButton.addTarget(self, action: #selector(self.clearPressed), forControlEvents: .touchUpInside) } + func updateThemeAndStrings(theme: PresentationTheme, strings: PresentationStrings) { + self.theme = theme + + if let activityIndicator = self.activityIndicator { + activityIndicator.type = .custom(theme.chat.inputMediaPanel.stickersSearchControlColor, 13.0, 1.0, false) + } + self.separatorNode.backgroundColor = theme.chat.inputMediaPanel.panelSeparatorColor + self.textBackgroundNode.image = generateStretchableFilledCircleImage(diameter: 33.0, color: theme.chat.inputMediaPanel.stickersSearchBackgroundColor) + self.textField.textColor = theme.chat.inputMediaPanel.stickersSearchPrimaryColor + self.iconNode.image = generateLoupeIcon(color: theme.chat.inputMediaPanel.stickersSearchControlColor) + self.clearButton.setImage(generateClearIcon(color: theme.chat.inputMediaPanel.stickersSearchControlColor), for: []) + self.cancelButton.setAttributedTitle(NSAttributedString(string: strings.Common_Cancel, font: Font.regular(17.0), textColor: theme.chat.inputPanel.panelControlAccentColor), for: []) + + switch theme.chatList.searchBarKeyboardColor { + case .light: + self.textField.keyboardAppearance = .default + case .dark: + self.textField.keyboardAppearance = .dark + } + + if let (boundingSize, leftInset, rightInset) = self.validLayout { + self.updateLayout(boundingSize: boundingSize, leftInset: leftInset, rightInset: rightInset, transition: .immediate) + } + } + func updateLayout(boundingSize: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) { self.validLayout = (boundingSize, leftInset, rightInset) diff --git a/TelegramUI/StickerPaneSearchContainerNode.swift b/TelegramUI/StickerPaneSearchContainerNode.swift index 412de32b4c..3f88ea0d53 100644 --- a/TelegramUI/StickerPaneSearchContainerNode.swift +++ b/TelegramUI/StickerPaneSearchContainerNode.swift @@ -135,8 +135,6 @@ private func preparedChatMediaInputGridEntryTransition(account: Account, theme: final class StickerPaneSearchContainerNode: ASDisplayNode { private let account: Account - private let theme: PresentationTheme - private let strings: PresentationStrings private let controllerInteraction: ChatControllerInteraction private let inputNodeInteraction: ChatMediaInputNodeInteraction @@ -160,19 +158,16 @@ final class StickerPaneSearchContainerNode: ASDisplayNode { init(account: Account, theme: PresentationTheme, strings: PresentationStrings, controllerInteraction: ChatControllerInteraction, inputNodeInteraction: ChatMediaInputNodeInteraction, cancel: @escaping () -> Void) { self.account = account - self.theme = theme - self.strings = strings self.controllerInteraction = controllerInteraction self.inputNodeInteraction = inputNodeInteraction self.backgroundNode = ASDisplayNode() - self.backgroundNode.backgroundColor = theme.chat.inputMediaPanel.stickersBackgroundColor self.trendingPane = ChatMediaInputTrendingPane(account: account, controllerInteraction: controllerInteraction, getItemIsPreviewed: { [weak inputNodeInteraction] item in return inputNodeInteraction?.previewedStickerPackItem == .pack(item) }) - self.searchBar = StickerPaneSearchBarNode(theme: theme, strings: strings) + self.searchBar = StickerPaneSearchBarNode() self.gridNode = GridNode() @@ -180,12 +175,10 @@ final class StickerPaneSearchContainerNode: ASDisplayNode { self.notFoundNode.displayWithoutProcessing = true self.notFoundNode.displaysAsynchronously = false self.notFoundNode.clipsToBounds = false - self.notFoundNode.image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Media/StickersNotFoundIcon"), color: theme.list.freeMonoIcon) self.notFoundLabel = ImmediateTextNode() self.notFoundLabel.displaysAsynchronously = false self.notFoundLabel.isUserInteractionEnabled = false - self.notFoundLabel.attributedText = NSAttributedString(string: strings.Stickers_NoStickersFound, font: Font.medium(14.0), textColor: theme.list.freeTextColor) self.notFoundNode.addSubnode(self.notFoundLabel) self.gridNode.isHidden = true @@ -209,7 +202,6 @@ final class StickerPaneSearchContainerNode: ASDisplayNode { self?.searchBar.deactivate(clear: false) } - self.searchBar.placeholderString = NSAttributedString(string: strings.Stickers_Search, font: Font.regular(14.0), textColor: theme.chat.inputMediaPanel.stickersSearchPlaceholderColor) self.searchBar.cancel = { cancel() } @@ -371,12 +363,22 @@ final class StickerPaneSearchContainerNode: ASDisplayNode { self._ready.set(self.trendingPane.ready) self.trendingPane.activate() + + self.updateThemeAndStrings(theme: theme, strings: strings) } deinit { self.searchDisposable.dispose() } + func updateThemeAndStrings(theme: PresentationTheme, strings: PresentationStrings) { + self.backgroundNode.backgroundColor = theme.chat.inputMediaPanel.stickersBackgroundColor + self.notFoundNode.image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Media/StickersNotFoundIcon"), color: theme.list.freeMonoIcon) + self.notFoundLabel.attributedText = NSAttributedString(string: strings.Stickers_NoStickersFound, font: Font.medium(14.0), textColor: theme.list.freeTextColor) + self.searchBar.updateThemeAndStrings(theme: theme, strings: strings) + self.searchBar.placeholderString = NSAttributedString(string: strings.Stickers_Search, font: Font.regular(14.0), textColor: theme.chat.inputMediaPanel.stickersSearchPlaceholderColor) + } + func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, inputHeight: CGFloat, transition: ContainedViewLayoutTransition) { let firstLayout = self.validLayout == nil self.validLayout = size diff --git a/TelegramUI/StickerPreviewPeekContent.swift b/TelegramUI/StickerPreviewPeekContent.swift index 73029c1f7d..fced05cbaf 100644 --- a/TelegramUI/StickerPreviewPeekContent.swift +++ b/TelegramUI/StickerPreviewPeekContent.swift @@ -46,6 +46,10 @@ final class StickerPreviewPeekContent: PeekControllerContent { return StickerPreviewPeekContentNode(account: self.account, item: self.item) } + func topAccessoryNode() -> ASDisplayNode? { + return nil + } + func isEqual(to: PeekControllerContent) -> Bool { if let to = to as? StickerPreviewPeekContent { return self.item == to.item diff --git a/TelegramUI/TelegramApplicationContext.swift b/TelegramUI/TelegramApplicationContext.swift index e0ecda28b4..602ad0f823 100644 --- a/TelegramUI/TelegramApplicationContext.swift +++ b/TelegramUI/TelegramApplicationContext.swift @@ -148,7 +148,7 @@ public final class TelegramApplicationContext { if let account = account { self._presentationData.set(.single(initialPresentationDataAndSettings.presentationData) - |> then(updatedPresentationData(postbox: account.postbox))) + |> then(updatedPresentationData(postbox: account.postbox, applicationBindings: applicationBindings))) self._automaticMediaDownloadSettings.set(.single(initialPresentationDataAndSettings.automaticMediaDownloadSettings) |> then(updatedAutomaticMediaDownloadSettings(postbox: account.postbox))) } else { self._presentationData.set(.single(initialPresentationDataAndSettings.presentationData)) diff --git a/TelegramUI/ThemeAccentColorActionSheet.swift b/TelegramUI/ThemeAccentColorActionSheet.swift index 1182255c8e..a022e200d1 100644 --- a/TelegramUI/ThemeAccentColorActionSheet.swift +++ b/TelegramUI/ThemeAccentColorActionSheet.swift @@ -3,22 +3,28 @@ import Display import AsyncDisplayKit import UIKit import SwiftSignalKit -import Photos +import TelegramCore final class ThemeAccentColorActionSheet: ActionSheetController { - private let theme: PresentationTheme - private let strings: PresentationStrings + private var presentationDisposable: Disposable? private let _ready = Promise() override var ready: Promise { return self._ready } - init(theme: PresentationTheme, strings: PresentationStrings, currentValue: Int32, applyValue: @escaping (Int32) -> Void) { - self.theme = theme - self.strings = strings + init(account: Account, currentValue: Int32, applyValue: @escaping (Int32) -> Void) { + let presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 } + let theme = presentationData.theme + let strings = presentationData.strings super.init(theme: ActionSheetControllerTheme(presentationTheme: theme)) + + self.presentationDisposable = account.telegramApplicationContext.presentationData.start(next: { [weak self] presentationData in + if let strongSelf = self { + strongSelf.theme = ActionSheetControllerTheme(presentationTheme: presentationData.theme) + } + }) self._ready.set(.single(true)) @@ -41,6 +47,10 @@ final class ThemeAccentColorActionSheet: ActionSheetController { required init(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } + + deinit { + self.presentationDisposable?.dispose() + } } private final class ThemeAccentColorActionSheetItem: ActionSheetItem { diff --git a/TelegramUI/ThemeAutoNightSettingsController.swift b/TelegramUI/ThemeAutoNightSettingsController.swift index 58b456a10e..189c2c990d 100644 --- a/TelegramUI/ThemeAutoNightSettingsController.swift +++ b/TelegramUI/ThemeAutoNightSettingsController.swift @@ -3,7 +3,6 @@ import Display import SwiftSignalKit import Postbox import TelegramCore -import CoreLocation import TelegramUIPrivateModule @@ -18,22 +17,6 @@ private enum TimeBasedManualField { case to } -private func reverseGeocodeLocation(latitude: Double, longitude: Double) -> Signal { - return Signal { subscriber in - let geocoder = CLGeocoder() - geocoder.reverseGeocodeLocation(CLLocation(latitude: latitude, longitude: longitude), completionHandler: { placemarks, _ in - if let placemarks = placemarks, let locality = placemarks.first?.locality { - subscriber.putNext(locality) - subscriber.putCompletion() - } - }) - - return ActionDisposable { - - } - } -} - private final class ThemeAutoNightSettingsControllerArguments { let updateMode: (TriggerMode) -> Void let updateTimeBasedAutomatic: (Bool) -> Void @@ -477,7 +460,7 @@ public func themeAutoNightSettingsController(account: Account) -> ViewController } let presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 } - presentControllerImpl?(ThemeAutoNightTimeSelectionActionSheet(theme: presentationData.theme, strings: presentationData.strings, currentValue: currentValue, applyValue: { value in + presentControllerImpl?(ThemeAutoNightTimeSelectionActionSheet(account: account, currentValue: currentValue, applyValue: { value in guard let value = value else { return } diff --git a/TelegramUI/ThemeAutoNightTimeSelectionActionSheet.swift b/TelegramUI/ThemeAutoNightTimeSelectionActionSheet.swift index 0136da4f6b..ec711b4336 100644 --- a/TelegramUI/ThemeAutoNightTimeSelectionActionSheet.swift +++ b/TelegramUI/ThemeAutoNightTimeSelectionActionSheet.swift @@ -3,23 +3,29 @@ import Display import AsyncDisplayKit import UIKit import SwiftSignalKit -import Photos +import TelegramCore final class ThemeAutoNightTimeSelectionActionSheet: ActionSheetController { - private let theme: PresentationTheme - private let strings: PresentationStrings + private var presentationDisposable: Disposable? private let _ready = Promise() override var ready: Promise { return self._ready } - init(theme: PresentationTheme, strings: PresentationStrings, currentValue: Int32, emptyTitle: String? = nil, applyValue: @escaping (Int32?) -> Void) { - self.theme = theme - self.strings = strings + init(account: Account, currentValue: Int32, emptyTitle: String? = nil, applyValue: @escaping (Int32?) -> Void) { + let presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 } + let theme = presentationData.theme + let strings = presentationData.strings super.init(theme: ActionSheetControllerTheme(presentationTheme: theme)) + self.presentationDisposable = account.telegramApplicationContext.presentationData.start(next: { [weak self] presentationData in + if let strongSelf = self { + strongSelf.theme = ActionSheetControllerTheme(presentationTheme: presentationData.theme) + } + }) + self._ready.set(.single(true)) var updatedValue = currentValue @@ -50,6 +56,10 @@ final class ThemeAutoNightTimeSelectionActionSheet: ActionSheetController { required init(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } + + deinit { + self.presentationDisposable?.dispose() + } } private final class ThemeAutoNightTimeSelectionActionSheetItem: ActionSheetItem { diff --git a/TelegramUI/ThemeSettingsChatPreviewItem.swift b/TelegramUI/ThemeSettingsChatPreviewItem.swift index e51dbae6cc..fdab5d4468 100644 --- a/TelegramUI/ThemeSettingsChatPreviewItem.swift +++ b/TelegramUI/ThemeSettingsChatPreviewItem.swift @@ -100,6 +100,7 @@ class ThemeSettingsChatPreviewItemNode: ListViewItemNode { }, navigateToFirstDateMessage: { _ in }, requestRedeliveryOfFailedMessages: { _ in }, addContact: { _ in + }, rateCall: { _ in }, requestMessageUpdate: { _ in }, cancelInteractiveKeyboardGestures: { }, automaticMediaDownloadSettings: AutomaticMediaDownloadSettings.defaultSettings) diff --git a/TelegramUI/ThemeSettingsController.swift b/TelegramUI/ThemeSettingsController.swift index 8a95de01ec..70b5e9b0d4 100644 --- a/TelegramUI/ThemeSettingsController.swift +++ b/TelegramUI/ThemeSettingsController.swift @@ -278,7 +278,7 @@ public func themeSettingsController(account: Account) -> ViewController { pushControllerImpl?(ThemeGridController(account: account)) }, openAccentColor: { color in let presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 } - presentControllerImpl?(ThemeAccentColorActionSheet(theme: presentationData.theme, strings: presentationData.strings, currentValue: color, applyValue: { color in + presentControllerImpl?(ThemeAccentColorActionSheet(account: account, currentValue: color, applyValue: { color in let _ = updatePresentationThemeSettingsInteractively(postbox: account.postbox, { current in return PresentationThemeSettings(chatWallpaper: current.chatWallpaper, theme: current.theme, themeAccentColor: color, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, disableAnimations: current.disableAnimations) }).start() diff --git a/TelegramUI/ThemedTextAlertController.swift b/TelegramUI/ThemedTextAlertController.swift new file mode 100644 index 0000000000..c4bcad8f64 --- /dev/null +++ b/TelegramUI/ThemedTextAlertController.swift @@ -0,0 +1,17 @@ +import Foundation +import Display +import TelegramCore + +public func textAlertController(account: Account, title: String?, text: String, actions: [TextAlertAction], actionLayout: TextAlertContentActionLayout = .horizontal) -> AlertController { + let presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 } + let theme = presentationData.theme + + let controller = standardTextAlertController(theme: AlertControllerTheme(presentationTheme: theme), title: title, text: text, actions: actions) + _ = account.telegramApplicationContext.presentationData.start(next: { [weak controller] presentationData in + if let strongController = controller { + strongController.theme = AlertControllerTheme(presentationTheme: presentationData.theme) + } + }) + + return controller +} diff --git a/TelegramUI/UIImage+WebP.m b/TelegramUI/UIImage+WebP.m index a9e1812fce..89623f9750 100644 --- a/TelegramUI/UIImage+WebP.m +++ b/TelegramUI/UIImage+WebP.m @@ -78,6 +78,7 @@ + (NSData *)convertToWebP:(UIImage *)image quality:(CGFloat)quality error:(NSError **)error { WebPPreset preset = WEBP_PRESET_DEFAULT; CGImageRef webPImageRef = image.CGImage; + size_t webPBytesPerRow = CGImageGetBytesPerRow(webPImageRef); size_t webPImageWidth = CGImageGetWidth(webPImageRef); @@ -100,9 +101,6 @@ } config.method = 6; - //if (configBlock) { - // configBlock(&config); - //} if (!WebPValidateConfig(&config)) { NSMutableDictionary *errorDetail = [NSMutableDictionary dictionary]; @@ -128,7 +126,8 @@ pic.height = (int)webPImageHeight; pic.colorspace = WEBP_YUV420; - WebPPictureImportRGBA(&pic, webPImageData, (int)webPBytesPerRow); + WebPPictureImportBGRA(&pic, webPImageData, (int)webPBytesPerRow); + //WebPPictureImportRGBA(&pic, webPImageData, (int)webPBytesPerRow); WebPPictureARGBToYUVA(&pic, WEBP_YUV420); WebPCleanupTransparentArea(&pic); diff --git a/TelegramUI/UniversalVideoNode.swift b/TelegramUI/UniversalVideoNode.swift index b67d45d08f..dac73d68cf 100644 --- a/TelegramUI/UniversalVideoNode.swift +++ b/TelegramUI/UniversalVideoNode.swift @@ -85,6 +85,7 @@ final class UniversalVideoNode: ASDisplayNode { private let autoplay: Bool private let snapshotContentWhenGone: Bool + private var contentNode: (UniversalVideoContentNode & ASDisplayNode)? private var contentNodeId: Int32? diff --git a/TelegramUI/UserInfoController.swift b/TelegramUI/UserInfoController.swift index 0e0fe3396b..27dc25ad59 100644 --- a/TelegramUI/UserInfoController.swift +++ b/TelegramUI/UserInfoController.swift @@ -928,7 +928,7 @@ public func userInfoController(account: Account, peerId: PeerId, mode: UserInfoC } else { text = presentationData.strings.UserInfo_UnblockConfirmation(peer.displayTitle).0 } - presentControllerImpl?(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_No, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Yes, action: { + presentControllerImpl?(textAlertController(account: account, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_No, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Yes, action: { updatePeerBlockedDisposable.set(requestUpdatePeerIsBlocked(account: account, peerId: peer.id, isBlocked: value).start()) })]), nil) } diff --git a/TelegramUI/UsernameSetupController.swift b/TelegramUI/UsernameSetupController.swift index 25169eb0c2..c9f02e0b76 100644 --- a/TelegramUI/UsernameSetupController.swift +++ b/TelegramUI/UsernameSetupController.swift @@ -96,7 +96,7 @@ private enum UsernameSetupEntry: ItemListNodeEntry { case .available: string = NSAttributedString(string: text, textColor: theme.list.freeTextSuccessColor) case .invalid, .taken: - string = NSAttributedString(string: text, textColor: theme.list.freeTextSuccessColor) + string = NSAttributedString(string: text, textColor: theme.list.freeTextErrorColor) } case .checking: string = NSAttributedString(string: text, textColor: theme.list.freeTextColor) diff --git a/TelegramUI/WebP.swift b/TelegramUI/WebP.swift index f8f4d2859d..3ea07e86e1 100644 --- a/TelegramUI/WebP.swift +++ b/TelegramUI/WebP.swift @@ -1,6 +1,7 @@ import UIKit import SwiftSignalKit import LegacyComponents +import Display private func scaleImage(_ image: UIImage, dimensions: CGSize) -> UIImage? { if #available(iOSApplicationExtension 10.0, *) { @@ -22,7 +23,14 @@ func convertToWebP(image: UIImage, targetSize: CGSize?, quality: CGFloat) -> Sig } return Signal { subscriber in - if let data = try? UIImage.convert(toWebP: image, quality: quality * 100.0) { + let context = DrawingContext(size: image.size, scale: 0.0, clear: true) + context.withFlippedContext({ context in + if let cgImage = image.cgImage { + context.draw(cgImage, in: CGRect(x: 0.0, y: 0.0, width: image.size.width, height: image.size.height)) + } + }) + let processedImage = context.generateImage()! + if let data = try? UIImage.convert(toWebP: processedImage, quality: quality * 100.0) { subscriber.putNext(data) } subscriber.putCompletion() diff --git a/TelegramUI/WebSearchController.swift b/TelegramUI/WebSearchController.swift index 8a116e359f..38e7477720 100644 --- a/TelegramUI/WebSearchController.swift +++ b/TelegramUI/WebSearchController.swift @@ -27,12 +27,16 @@ private func requestContextResults(account: Account, botId: PeerId, query: Strin final class WebSearchControllerInteraction { let openResult: (ChatContextResult) -> Void + let setSearchQuery: (String) -> Void + let deleteRecentQuery: (String) -> Void let toggleSelection: ([String], Bool) -> Void let sendSelected: (ChatContextResultCollection) -> Void var selectionState: WebSearchSelectionState? - init(openResult: @escaping (ChatContextResult) -> Void, toggleSelection: @escaping ([String], Bool) -> Void, sendSelected: @escaping (ChatContextResultCollection) -> Void) { + init(openResult: @escaping (ChatContextResult) -> Void, setSearchQuery: @escaping (String) -> Void, deleteRecentQuery: @escaping (String) -> Void, toggleSelection: @escaping ([String], Bool) -> Void, sendSelected: @escaping (ChatContextResultCollection) -> Void) { self.openResult = openResult + self.setSearchQuery = setSearchQuery + self.deleteRecentQuery = deleteRecentQuery self.toggleSelection = toggleSelection self.sendSelected = sendSelected } @@ -115,6 +119,16 @@ final class WebSearchController: ViewController { self.controllerInteraction = WebSearchControllerInteraction(openResult: { result in + }, setSearchQuery: { [weak self] query in + if let strongSelf = self { + strongSelf.navigationContentNode?.setQuery(query) + strongSelf.updateSearchQuery(query) + strongSelf.navigationContentNode?.deactivate() + } + }, deleteRecentQuery: { [weak self] query in + if let strongSelf = self { + _ = removeRecentWebSearchQuery(postbox: strongSelf.account.postbox, string: query).start() + } }, toggleSelection: { [weak self] ids, value in if let strongSelf = self { strongSelf.updateInterfaceState { $0.withToggledSelectedMessages(ids, value: value) } @@ -145,6 +159,12 @@ final class WebSearchController: ViewController { } } + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + + self.navigationContentNode?.activate() + } + override public func loadDisplayNode() { self.displayNode = WebSearchControllerNode(account: self.account, theme: self.interfaceState.presentationData.theme, strings: interfaceState.presentationData.strings, controllerInteraction: self.controllerInteraction!) self.controllerNode.requestUpdateInterfaceState = { [weak self] animated, f in @@ -157,6 +177,11 @@ final class WebSearchController: ViewController { strongSelf.dismiss() } } + self.controllerNode.dismissInput = { [weak self] in + if let strongSelf = self { + strongSelf.navigationContentNode?.deactivate() + } + } self.controllerNode.updateInterfaceState(self.interfaceState, animated: false) self._ready.set(.single(true)) @@ -185,12 +210,18 @@ final class WebSearchController: ViewController { } private func updateSearchQuery(_ query: String) { + if !query.isEmpty { + let _ = addRecentWebSearchQuery(postbox: self.account.postbox, string: query).start() + } + let mode = self.interfaceStatePromise.get() |> map { state -> WebSearchMode? in return state.state?.mode } |> distinctUntilChanged + self.updateInterfaceState { $0.withUpdatedQuery(query) } + var results = mode |> mapToSignal { mode -> (Signal<(ChatPresentationInputQueryResult?) -> ChatPresentationInputQueryResult?, NoError>) in if let mode = mode { @@ -223,6 +254,8 @@ final class WebSearchController: ViewController { if let results = results { strongSelf.controllerNode.updateResults(results) } + } else { + strongSelf.controllerNode.updateResults(nil) } } })) @@ -318,6 +351,7 @@ final class WebSearchController: ViewController { } override public func dismiss(completion: (() -> Void)? = nil) { + self.navigationContentNode?.deactivate() self.controllerNode.animateOut(completion: { [weak self] in self?.presentingViewController?.dismiss(animated: false, completion: nil) completion?() diff --git a/TelegramUI/WebSearchControllerNode.swift b/TelegramUI/WebSearchControllerNode.swift index cc48837af9..15129d8486 100644 --- a/TelegramUI/WebSearchControllerNode.swift +++ b/TelegramUI/WebSearchControllerNode.swift @@ -46,16 +46,6 @@ private struct WebSearchTransition { let hasMore: Bool } -private final class HorizontalListContextResultsOpaqueState { - let entryCount: Int - let hasMore: Bool - - init(entryCount: Int, hasMore: Bool) { - self.entryCount = entryCount - self.hasMore = hasMore - } -} - private func preparedTransition(from fromEntries: [WebSearchEntry], to toEntries: [WebSearchEntry], hasMore: Bool, account: Account, theme: PresentationTheme, interfaceState: WebSearchInterfaceState, controllerInteraction: WebSearchControllerInteraction) -> WebSearchTransition { let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries) @@ -70,6 +60,56 @@ private func gridNodeLayoutForContainerLayout(size: CGSize) -> GridNodeLayoutTyp return .fixed(itemSize: CGSize(width: side, height: side), fillWidth: true, lineSpacing: 1.0, itemSpacing: 1.0) } + +private struct WebSearchRecentQueryStableId: Hashable { + let query: String + + var hashValue: Int { + return query.hashValue + } + + static func ==(lhs: WebSearchRecentQueryStableId, rhs: WebSearchRecentQueryStableId) -> Bool { + return lhs.query == rhs.query + } +} + +private struct WebSearchRecentQueryEntry: Comparable, Identifiable { + let index: Int + let query: String + + var stableId: WebSearchRecentQueryStableId { + return WebSearchRecentQueryStableId(query: self.query) + } + + static func ==(lhs: WebSearchRecentQueryEntry, rhs: WebSearchRecentQueryEntry) -> Bool { + return lhs.index == rhs.index && lhs.query == rhs.query + } + + static func <(lhs: WebSearchRecentQueryEntry, rhs: WebSearchRecentQueryEntry) -> Bool { + return lhs.index < rhs.index + } + + func item(account: Account, theme: PresentationTheme, strings: PresentationStrings, controllerInteraction: WebSearchControllerInteraction, header: ListViewItemHeader) -> ListViewItem { + return WebSearchRecentQueryItem(account: account, theme: theme, strings: strings, query: self.query, controllerInteraction: controllerInteraction, header: header) + } +} + +private struct WebSearchRecentTransition { + let deletions: [ListViewDeleteItem] + let insertions: [ListViewInsertItem] + let updates: [ListViewUpdateItem] +} + +private func preparedWebSearchRecentTransition(from fromEntries: [WebSearchRecentQueryEntry], to toEntries: [WebSearchRecentQueryEntry], account: Account, theme: PresentationTheme, strings: PresentationStrings, controllerInteraction: WebSearchControllerInteraction, header: ListViewItemHeader) -> WebSearchRecentTransition { + let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries) + + let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) } + let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, theme: theme, strings: strings, controllerInteraction: controllerInteraction, header: header), directionHint: nil) } + let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, theme: theme, strings: strings, controllerInteraction: controllerInteraction, header: header), directionHint: nil) } + + return WebSearchRecentTransition(deletions: deletions, insertions: insertions, updates: updates) +} + class WebSearchControllerNode: ASDisplayNode { private let account: Account private var theme: PresentationTheme @@ -90,6 +130,10 @@ class WebSearchControllerNode: ASDisplayNode { private let attributionNode: ASImageNode + private let recentQueriesPlaceholder: ImmediateTextNode + private let recentQueriesNode: ListView + private var enqueuedRecentTransitions: [(WebSearchRecentTransition, Bool)] = [] + private let gridNode: GridNode private var enqueuedTransitions: [(WebSearchTransition, Bool)] = [] private var dequeuedInitialTransitionOnLayout = false @@ -97,18 +141,21 @@ class WebSearchControllerNode: ASDisplayNode { private var currentExternalResults: ChatContextResultCollection? private var currentProcessedResults: ChatContextResultCollection? private var currentEntries: [WebSearchEntry]? + private var hasMore = false private var isLoadingMore = false - private let results = ValuePromise(ignoreRepeated: true) - + private let results = ValuePromise(nil, ignoreRepeated: true) private let disposable = MetaDisposable() private let loadMoreDisposable = MetaDisposable() + private let recentDisposable = MetaDisposable() + private var containerLayout: (ContainerViewLayout, CGFloat)? var requestUpdateInterfaceState: (Bool, (WebSearchInterfaceState) -> WebSearchInterfaceState) -> Void = { _, _ in } var cancel: (() -> Void)? + var dismissInput: (() -> Void)? init(account: Account, theme: PresentationTheme, strings: PresentationStrings, controllerInteraction: WebSearchControllerInteraction) { self.account = account @@ -136,6 +183,11 @@ class WebSearchControllerNode: ASDisplayNode { self.gridNode = GridNode() self.gridNode.backgroundColor = theme.list.plainBackgroundColor + self.recentQueriesNode = ListView() + self.recentQueriesNode.backgroundColor = theme.list.plainBackgroundColor + + self.recentQueriesPlaceholder = ImmediateTextNode() + super.init() self.setViewBlock({ @@ -143,6 +195,7 @@ class WebSearchControllerNode: ASDisplayNode { }) self.addSubnode(self.gridNode) + self.addSubnode(self.recentQueriesNode) self.addSubnode(self.segmentedBackgroundNode) self.addSubnode(self.segmentedSeparatorNode) self.view.addSubview(self.segmentedControl) @@ -165,18 +218,41 @@ class WebSearchControllerNode: ASDisplayNode { } })) + let previousRecentItems = Atomic<[WebSearchRecentQueryEntry]?>(value: nil) + self.recentDisposable.set((combineLatest(webSearchRecentQueries(postbox: self.account.postbox), self.webSearchInterfaceStatePromise.get()) + |> deliverOnMainQueue).start(next: { [weak self] queries, interfaceState in + if let strongSelf = self { + var entries: [WebSearchRecentQueryEntry] = [] + for i in 0 ..< queries.count { + entries.append(WebSearchRecentQueryEntry(index: i, query: queries[i])) + } + + let header = ChatListSearchItemHeader(type: .recentPeers, theme: interfaceState.presentationData.theme, strings:interfaceState.presentationData.strings, actionTitle: strings.WebSearch_RecentSectionClear.uppercased(), action: { + _ = clearRecentWebSearchQueries(postbox: strongSelf.account.postbox).start() + }) + + let previousEntries = previousRecentItems.swap(entries) + + let transition = preparedWebSearchRecentTransition(from: previousEntries ?? [], to: entries, account: strongSelf.account, theme: interfaceState.presentationData.theme, strings: interfaceState.presentationData.strings, controllerInteraction: strongSelf.controllerInteraction, header: header) + strongSelf.enqueueRecentTransition(transition, firstTime: previousEntries == nil) + } + })) + self.gridNode.visibleItemsUpdated = { [weak self] visibleItems in - //state.hasMore && - //if visibleItems.bottom.0 <= state.entryCount - 10 { - // strongSelf.loadMore() - //} + if let strongSelf = self, let bottom = visibleItems.bottom, let entries = strongSelf.currentEntries { + if bottom.0 <= entries.count { + strongSelf.loadMore() + } + } } -// let selectorRecogizner = ChatGridLiveSelectorRecognizer(target: self, action: #selector(self.panGesture(_:))) -// selectorRecogizner.shouldBegin = { [weak controllerInteraction] in -// return controllerInteraction?.selectionState != nil -// } -// self.view.addGestureRecognizer(selectorRecogizner) + self.gridNode.scrollingInitiated = { [weak self] in + self?.dismissInput?() + } + + self.recentQueriesNode.beganInteractiveDragging = { [weak self] in + self?.dismissInput?() + } } deinit { @@ -235,6 +311,8 @@ class WebSearchControllerNode: ASDisplayNode { transition.updateFrame(view: self.segmentedControl, frame: CGRect(origin: CGPoint(x: layout.safeInsets.left + floor((layout.size.width - layout.safeInsets.left - layout.safeInsets.right - controlSize.width) / 2.0), y: panelY + floor((segmentedHeight - controlSize.height) / 2.0)), size: controlSize)) + insets.top -= 4.0 + let toolbarHeight: CGFloat = 44.0 let toolbarY = layout.size.height - toolbarHeight - insets.bottom transition.updateFrame(node: self.toolbarBackgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: toolbarY), size: CGSize(width: layout.size.width, height: toolbarHeight + insets.bottom))) @@ -260,6 +338,31 @@ class WebSearchControllerNode: ASDisplayNode { insets.bottom += toolbarHeight self.gridNode.transaction(GridNodeTransaction(deleteItems: [], insertItems: [], updateItems: [], scrollToItem: nil, updateLayout: GridNodeUpdateLayout(layout: GridNodeLayout(size: layout.size, insets: insets, preloadSize: 400.0, type: gridNodeLayoutForContainerLayout(size: layout.size)), transition: .immediate), itemTransition: .immediate, stationaryItems: .none,updateFirstIndexInSectionOffset: nil), completion: { _ in }) + var duration: Double = 0.0 + var curve: UInt = 0 + switch transition { + case .immediate: + break + case let .animated(animationDuration, animationCurve): + duration = animationDuration + switch animationCurve { + case .easeInOut: + break + case .spring: + curve = 7 + } + } + + let listViewCurve: ListViewAnimationCurve + if curve == 7 { + listViewCurve = .Spring(duration: duration) + } else { + listViewCurve = .Default(duration: duration) + } + + self.recentQueriesNode.frame = CGRect(origin: CGPoint(), size: layout.size) + self.recentQueriesNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous], scrollToItem: nil, updateSizeAndInsets: ListViewUpdateSizeAndInsets(size: layout.size, insets: insets, duration: duration, curve: listViewCurve), stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in }) + if !self.dequeuedInitialTransitionOnLayout { self.dequeuedInitialTransitionOnLayout = true self.dequeueTransition() @@ -279,7 +382,7 @@ class WebSearchControllerNode: ASDisplayNode { } } - func updateResults(_ results: ChatContextResultCollection) { + func updateResults(_ results: ChatContextResultCollection?) { if self.currentExternalResults == results { return } @@ -291,6 +394,10 @@ class WebSearchControllerNode: ASDisplayNode { self.results.set(results) } + func clearResults() { + self.results.set(nil) + } + private func loadMore() { guard !self.isLoadingMore, let currentProcessedResults = self.currentProcessedResults, let nextOffset = currentProcessedResults.nextOffset else { return @@ -315,24 +422,31 @@ class WebSearchControllerNode: ASDisplayNode { })) } - private func updateInternalResults(_ results: ChatContextResultCollection, interfaceState: WebSearchInterfaceState) { + private func updateInternalResults(_ results: ChatContextResultCollection?, interfaceState: WebSearchInterfaceState) { var entries: [WebSearchEntry] = [] - var index = 0 - var resultIds = Set() - for result in results.results { - let entry = WebSearchEntry(index: index, result: result) - if resultIds.contains(entry.stableId) { - continue - } else { - resultIds.insert(entry.stableId) + var hasMore = false + if let state = interfaceState.state, state.query.isEmpty { + } else if let results = results { + hasMore = results.nextOffset != nil + + var index = 0 + var resultIds = Set() + for result in results.results { + let entry = WebSearchEntry(index: index, result: result) + if resultIds.contains(entry.stableId) { + continue + } else { + resultIds.insert(entry.stableId) + } + entries.append(entry) + index += 1 } - entries.append(entry) - index += 1 } let firstTime = self.currentEntries == nil - let transition = preparedTransition(from: self.currentEntries ?? [], to: entries, hasMore: results.nextOffset != nil, account: self.account, theme: interfaceState.presentationData.theme, interfaceState: interfaceState, controllerInteraction: self.controllerInteraction) + let transition = preparedTransition(from: self.currentEntries ?? [], to: entries, hasMore: hasMore, account: self.account, theme: interfaceState.presentationData.theme, interfaceState: interfaceState, controllerInteraction: self.controllerInteraction) self.currentEntries = entries + self.enqueueTransition(transition, firstTime: firstTime) } @@ -355,10 +469,41 @@ class WebSearchControllerNode: ASDisplayNode { } } + if let state = self.webSearchInterfaceState.state { + self.recentQueriesNode.isHidden = !state.query.isEmpty + } + + self.hasMore = transition.hasMore self.gridNode.transaction(GridNodeTransaction(deleteItems: transition.deleteItems, insertItems: transition.insertItems, updateItems: transition.updateItems, scrollToItem: nil, updateLayout: nil, itemTransition: .immediate, stationaryItems: .none, updateFirstIndexInSectionOffset: nil), completion: completion) } } + private func enqueueRecentTransition(_ transition: WebSearchRecentTransition, firstTime: Bool) { + enqueuedRecentTransitions.append((transition, firstTime)) + + if self.containerLayout != nil { + while !self.enqueuedRecentTransitions.isEmpty { + self.dequeueRecentTransition() + } + } + } + + private func dequeueRecentTransition() { + if let (transition, firstTime) = self.enqueuedRecentTransitions.first { + self.enqueuedRecentTransitions.remove(at: 0) + + var options = ListViewDeleteAndInsertOptions() + if firstTime { + options.insert(.PreferSynchronousDrawing) + } else { + options.insert(.AnimateInsertion) + } + + self.recentQueriesNode.transaction(deleteIndices: transition.deletions, insertIndicesAndItems: transition.insertions, updateIndicesAndItems: transition.updates, options: options, updateSizeAndInsets: nil, updateOpaqueState: nil, completion: { _ in + }) + } + } + @objc private func indexChanged() { self.requestUpdateInterfaceState(true) { current in if let mode = WebSearchMode(rawValue: Int32(self.segmentedControl.selectedSegmentIndex)) { diff --git a/TelegramUI/WebSearchInterfaceState.swift b/TelegramUI/WebSearchInterfaceState.swift index 8e769a0982..c0525ff5cc 100644 --- a/TelegramUI/WebSearchInterfaceState.swift +++ b/TelegramUI/WebSearchInterfaceState.swift @@ -19,6 +19,7 @@ enum WebSearchMode: Int32 { struct WebSearchInterfaceInnerState: Equatable { let mode: WebSearchMode + let query: String let selectionState: WebSearchSelectionState } @@ -37,13 +38,13 @@ struct WebSearchInterfaceState: Equatable { } func withUpdatedMode(_ mode: WebSearchMode) -> WebSearchInterfaceState { - return WebSearchInterfaceState(state: WebSearchInterfaceInnerState(mode: mode, selectionState: self.state?.selectionState ?? WebSearchSelectionState(selectedIds: [])), presentationData: self.presentationData) + return WebSearchInterfaceState(state: WebSearchInterfaceInnerState(mode: mode, query: self.state?.query ?? "", selectionState: self.state?.selectionState ?? WebSearchSelectionState(selectedIds: [])), presentationData: self.presentationData) } - func withUpdatedSelectionState(_ selectionState: WebSearchSelectionState) -> WebSearchInterfaceState { - return WebSearchInterfaceState(state: WebSearchInterfaceInnerState(mode: self.state?.mode ?? .images, selectionState: selectionState), presentationData: self.presentationData) + func withUpdatedQuery(_ query: String) -> WebSearchInterfaceState { + return WebSearchInterfaceState(state: WebSearchInterfaceInnerState(mode: self.state?.mode ?? .images, query: query, selectionState: self.state?.selectionState ?? WebSearchSelectionState(selectedIds: [])), presentationData: self.presentationData) } - + func withToggledSelectedMessages(_ ids: [String], value: Bool) -> WebSearchInterfaceState { var selectedIds = Set() if let selectionState = self.state?.selectionState { @@ -56,7 +57,7 @@ struct WebSearchInterfaceState: Equatable { selectedIds.remove(id) } } - return WebSearchInterfaceState(state: WebSearchInterfaceInnerState(mode: self.state?.mode ?? .images, selectionState: WebSearchSelectionState(selectedIds: selectedIds)), presentationData: self.presentationData) + return WebSearchInterfaceState(state: WebSearchInterfaceInnerState(mode: self.state?.mode ?? .images, query: self.state?.query ?? "", selectionState: WebSearchSelectionState(selectedIds: selectedIds)), presentationData: self.presentationData) } func withUpdatedPresentationData(_ presentationData: PresentationData) -> WebSearchInterfaceState { diff --git a/TelegramUI/WebSearchNavigationContentNode.swift b/TelegramUI/WebSearchNavigationContentNode.swift index b0b7b3b43f..92c6fa6cbf 100644 --- a/TelegramUI/WebSearchNavigationContentNode.swift +++ b/TelegramUI/WebSearchNavigationContentNode.swift @@ -44,6 +44,10 @@ final class WebSearchNavigationContentNode: NavigationBarContentNode { self.searchBar.activity = activity } + func setQuery(_ query: String) { + self.searchBar.text = query + } + override func layout() { super.layout() diff --git a/TelegramUI/WebSearchRecentQueries.swift b/TelegramUI/WebSearchRecentQueries.swift new file mode 100644 index 0000000000..64c637238e --- /dev/null +++ b/TelegramUI/WebSearchRecentQueries.swift @@ -0,0 +1,72 @@ +import Foundation +import Postbox +import SwiftSignalKit + +private struct WebSearchRecentQueryItemId { + public let rawValue: MemoryBuffer + + var value: String { + return String(data: self.rawValue.makeData(), encoding: .utf8) ?? "" + } + + init(_ rawValue: MemoryBuffer) { + self.rawValue = rawValue + } + + init?(_ value: String) { + if let data = value.data(using: .utf8) { + self.rawValue = MemoryBuffer(data: data) + } else { + return nil + } + } +} + +final class RecentWebSearchQueryItem: OrderedItemListEntryContents { + init() { + } + + public init(decoder: PostboxDecoder) { + } + + public func encode(_ encoder: PostboxEncoder) { + } +} + +func addRecentWebSearchQuery(postbox: Postbox, string: String) -> Signal { + return postbox.transaction { transaction in + if let itemId = WebSearchRecentQueryItemId(string) { + transaction.addOrMoveToFirstPositionOrderedItemListItem(collectionId: ApplicationSpecificOrderedItemListCollectionId.webSearchRecentQueries, item: OrderedItemListEntry(id: itemId.rawValue, contents: RecentWebSearchQueryItem()), removeTailIfCountExceeds: 100) + } + } +} + +func removeRecentWebSearchQuery(postbox: Postbox, string: String) -> Signal { + return postbox.transaction { transaction -> Void in + if let itemId = WebSearchRecentQueryItemId(string) { + transaction.removeOrderedItemListItem(collectionId: ApplicationSpecificOrderedItemListCollectionId.webSearchRecentQueries, itemId: itemId.rawValue) + } + } +} + +func clearRecentWebSearchQueries(postbox: Postbox) -> Signal { + return postbox.transaction { transaction -> Void in + transaction.replaceOrderedItemListItems(collectionId: ApplicationSpecificOrderedItemListCollectionId.webSearchRecentQueries, items: []) + } +} + +func webSearchRecentQueries(postbox: Postbox) -> Signal<[String], NoError> { + return postbox.combinedView(keys: [.orderedItemList(id: ApplicationSpecificOrderedItemListCollectionId.webSearchRecentQueries)]) + |> mapToSignal { view -> Signal<[String], NoError> in + return postbox.transaction { transaction -> [String] in + var result: [String] = [] + if let view = view.views[.orderedItemList(id: ApplicationSpecificOrderedItemListCollectionId.webSearchRecentQueries)] as? OrderedItemListView { + for item in view.items { + let value = WebSearchRecentQueryItemId(item.id).value + result.append(value) + } + } + return result + } + } +} diff --git a/TelegramUI/WebSearchRecentQueriesNode.swift b/TelegramUI/WebSearchRecentQueriesNode.swift deleted file mode 100644 index 81f72fba9a..0000000000 --- a/TelegramUI/WebSearchRecentQueriesNode.swift +++ /dev/null @@ -1,5 +0,0 @@ -import UIKit - -class WebSearchRecentQueriesNode: NSObject { - -} diff --git a/TelegramUI/WebSearchRecentQueryItem.swift b/TelegramUI/WebSearchRecentQueryItem.swift new file mode 100644 index 0000000000..f1feee0a62 --- /dev/null +++ b/TelegramUI/WebSearchRecentQueryItem.swift @@ -0,0 +1,310 @@ +import Foundation +import UIKit +import AsyncDisplayKit +import Postbox +import Display +import SwiftSignalKit +import TelegramCore + +private enum RevealOptionKey: Int32 { + case delete +} + +class WebSearchRecentQueryItem: ListViewItem { + let theme: PresentationTheme + let strings: PresentationStrings + let account: Account + let query: String + let controllerInteraction: WebSearchControllerInteraction + + let header: ListViewItemHeader? + + init(account: Account, theme: PresentationTheme, strings: PresentationStrings, query: String, controllerInteraction: WebSearchControllerInteraction, header: ListViewItemHeader) { + self.theme = theme + self.strings = strings + self.account = account + self.query = query + self.controllerInteraction = controllerInteraction + self.header = header + } + + func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal?, (ListViewItemApply) -> Void)) -> Void) { + async { + let node = WebSearchRecentQueryItemNode() + let makeLayout = node.asyncLayout() + let (nodeLayout, nodeApply) = makeLayout(self, params, nextItem == nil, previousItem == nil) + node.contentSize = nodeLayout.contentSize + node.insets = nodeLayout.insets + + completion(node, nodeApply) + } + } + + func updateNode(async: @escaping (@escaping () -> Void) -> Void, node: @escaping () -> ListViewItemNode, params: ListViewItemLayoutParams, previousItem: ListViewItem?, nextItem: ListViewItem?, animation: ListViewItemUpdateAnimation, completion: @escaping (ListViewItemNodeLayout, @escaping (ListViewItemApply) -> Void) -> Void) { + Queue.mainQueue().async { + if let nodeValue = node() as? WebSearchRecentQueryItemNode { + let layout = nodeValue.asyncLayout() + async { + let (nodeLayout, apply) = layout(self, params, nextItem == nil, previousItem == nil) + Queue.mainQueue().async { + completion(nodeLayout, { info in + apply().1(info) + }) + } + } + } + } + } + + var selectable: Bool { + return true + } + + func selected(listView: ListView) { + listView.clearHighlightAnimated(true) + self.controllerInteraction.setSearchQuery(self.query) + } +} + +private let separatorHeight = 1.0 / UIScreen.main.scale + +class WebSearchRecentQueryItemNode: ItemListRevealOptionsItemNode { + private let backgroundNode: ASDisplayNode + private let separatorNode: ASDisplayNode + private let highlightedBackgroundNode: ASDisplayNode + private var textNode: TextNode? + + private var item: WebSearchRecentQueryItem? + + required init() { + self.backgroundNode = ASDisplayNode() + self.backgroundNode.isLayerBacked = true + + self.separatorNode = ASDisplayNode() + self.separatorNode.isLayerBacked = true + + self.highlightedBackgroundNode = ASDisplayNode() + self.highlightedBackgroundNode.isLayerBacked = true + + super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false) + + self.addSubnode(self.backgroundNode) + self.addSubnode(self.separatorNode) + } + + override func layoutForParams(_ params: ListViewItemLayoutParams, item: ListViewItem, previousItem: ListViewItem?, nextItem: ListViewItem?) { + if let item = self.item { + let makeLayout = self.asyncLayout() + let (nodeLayout, nodeApply) = makeLayout(item, params, nextItem == nil, previousItem == nil) + self.contentSize = nodeLayout.contentSize + self.insets = nodeLayout.insets + let _ = nodeApply() + } + } + + override func setHighlighted(_ highlighted: Bool, at point: CGPoint, animated: Bool) { + super.setHighlighted(highlighted, at: point, animated: animated) + + if highlighted { + self.highlightedBackgroundNode.alpha = 1.0 + if self.highlightedBackgroundNode.supernode == nil { + self.insertSubnode(self.highlightedBackgroundNode, aboveSubnode: self.separatorNode) + } + } else { + if self.highlightedBackgroundNode.supernode != nil { + if animated { + self.highlightedBackgroundNode.layer.animateAlpha(from: self.highlightedBackgroundNode.alpha, to: 0.0, duration: 0.4, completion: { [weak self] completed in + if let strongSelf = self { + if completed { + strongSelf.highlightedBackgroundNode.removeFromSupernode() + } + } + }) + self.highlightedBackgroundNode.alpha = 0.0 + } else { + self.highlightedBackgroundNode.removeFromSupernode() + } + } + } + } + + func asyncLayout() -> (_ item: WebSearchRecentQueryItem, _ params: ListViewItemLayoutParams, _ last: Bool, _ firstWithHeader: Bool) -> (ListViewItemNodeLayout, () -> (Signal?, (ListViewItemApply) -> Void)) { + let currentItem = self.item + + let textLayout = TextNode.asyncLayout(self.textNode) + + return { [weak self] item, params, last, firstWithHeader in + + let leftInset: CGFloat = 15.0 + params.leftInset + let rightInset: CGFloat = params.rightInset + + let attributedString = NSAttributedString(string: item.query, font: Font.regular(17.0), textColor: .black) + let textApply = textLayout(TextNodeLayoutArguments(attributedString: attributedString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - rightInset - 15.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) + + let nodeLayout = ListViewItemNodeLayout(contentSize: CGSize(width: params.width, height: 44.0), insets: UIEdgeInsets(top: firstWithHeader ? 29.0 : 0.0, left: 0.0, bottom: 0.0, right: 0.0)) + + return (nodeLayout, { [weak self] in + var updatedTheme: PresentationTheme? + if currentItem?.theme !== item.theme { + updatedTheme = item.theme + } + + return (nil, { _ in + if let strongSelf = self { + strongSelf.item = item + + if let _ = updatedTheme { + strongSelf.separatorNode.backgroundColor = item.theme.list.itemPlainSeparatorColor + strongSelf.backgroundNode.backgroundColor = item.theme.list.plainBackgroundColor + strongSelf.highlightedBackgroundNode.backgroundColor = item.theme.list.itemHighlightedBackgroundColor + } + + let (textLayout, textApply) = textApply + let textNode = textApply() + if strongSelf.textNode == nil { + strongSelf.textNode = textNode + strongSelf.addSubnode(textNode) + } + + let textFrame = CGRect(origin: CGPoint(x: leftInset, y: floorToScreenPixels((44.0 - textLayout.size.height) / 2.0)), size: textLayout.size) + textNode.frame = textFrame + + let separatorHeight = UIScreenPixel + let topHighlightInset: CGFloat = (firstWithHeader || !nodeLayout.insets.top.isZero) ? 0.0 : separatorHeight + + strongSelf.backgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: nodeLayout.contentSize.width, height: nodeLayout.contentSize.height)) + strongSelf.highlightedBackgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -nodeLayout.insets.top - topHighlightInset), size: CGSize(width: nodeLayout.size.width, height: nodeLayout.size.height + topHighlightInset)) + strongSelf.separatorNode.frame = CGRect(origin: CGPoint(x: leftInset, y: nodeLayout.contentSize.height - separatorHeight), size: CGSize(width: nodeLayout.size.width, height: separatorHeight)) + strongSelf.separatorNode.isHidden = last + + strongSelf.updateLayout(size: nodeLayout.contentSize, leftInset: params.leftInset, rightInset: params.rightInset) + + strongSelf.setRevealOptions((left: [], right: [ItemListRevealOption(key: RevealOptionKey.delete.rawValue, title: item.strings.Common_Delete, icon: .none, color: item.theme.list.itemDisclosureActions.destructive.fillColor, textColor: item.theme.list.itemDisclosureActions.destructive.foregroundColor)])) + } + }) + }) + } + } + + override func animateInsertion(_ currentTimestamp: Double, duration: Double, short: Bool) { + self.layer.animateAlpha(from: 0.0, to: 1.0, duration: duration * 0.5) + } + + override func animateRemoved(_ currentTimestamp: Double, duration: Double) { + self.layer.animateAlpha(from: 1.0, to: 0.0, duration: duration * 0.5, removeOnCompletion: false) + } + + override public func header() -> ListViewItemHeader? { + if let item = self.item { + return item.header + } else { + return nil + } + } + + override func updateRevealOffset(offset: CGFloat, transition: ContainedViewLayoutTransition) { + super.updateRevealOffset(offset: offset, transition: transition) + +// if let _ = self.item, let params = self.layoutParams?.5 { +// let editingOffset: CGFloat +// if let selectableControlNode = self.selectableControlNode { +// editingOffset = selectableControlNode.bounds.size.width +// var selectableControlFrame = selectableControlNode.frame +// selectableControlFrame.origin.x = params.leftInset + offset +// transition.updateFrame(node: selectableControlNode, frame: selectableControlFrame) +// } else { +// editingOffset = 0.0 +// } +// +// if let reorderControlNode = self.reorderControlNode { +// var reorderControlFrame = reorderControlNode.frame +// reorderControlFrame.origin.x = params.width - params.rightInset - reorderControlFrame.size.width + offset +// transition.updateFrame(node: reorderControlNode, frame: reorderControlFrame) +// } +// +// let leftInset: CGFloat = params.leftInset + 78.0 +// +// let rawContentRect = CGRect(origin: CGPoint(x: 2.0, y: 8.0), size: CGSize(width: params.width - leftInset - params.rightInset - 10.0 - 1.0 - editingOffset, height: itemHeight - 12.0 - 9.0)) +// +// let contentRect = rawContentRect.offsetBy(dx: editingOffset + leftInset + offset, dy: 0.0) +// +// var avatarFrame = self.avatarNode.frame +// avatarFrame.origin.x = leftInset - 78.0 + editingOffset + 10.0 + offset +// transition.updateFrame(node: self.avatarNode, frame: avatarFrame) +// if let multipleAvatarsNode = self.multipleAvatarsNode { +// transition.updateFrame(node: multipleAvatarsNode, frame: avatarFrame) +// } +// +// var titleOffset: CGFloat = 0.0 +// if let secretIconNode = self.secretIconNode, let image = secretIconNode.image { +// transition.updateFrame(node: secretIconNode, frame: CGRect(origin: CGPoint(x: contentRect.minX, y: secretIconNode.frame.minY), size: image.size)) +// titleOffset += image.size.width + 3.0 +// } +// +// let titleFrame = self.titleNode.frame +// transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(x: contentRect.origin.x + titleOffset, y: titleFrame.origin.y), size: titleFrame.size)) +// +// let authorFrame = self.authorNode.frame +// transition.updateFrame(node: self.authorNode, frame: CGRect(origin: CGPoint(x: contentRect.origin.x, y: authorFrame.origin.y), size: authorFrame.size)) +// +// transition.updateFrame(node: self.inputActivitiesNode, frame: CGRect(origin: CGPoint(x: contentRect.origin.x, y: self.inputActivitiesNode.frame.minY), size: self.inputActivitiesNode.bounds.size)) +// +// let textFrame = self.textNode.frame +// transition.updateFrame(node: self.textNode, frame: CGRect(origin: CGPoint(x: contentRect.origin.x, y: textFrame.origin.y), size: textFrame.size)) +// +// let dateFrame = self.dateNode.frame +// transition.updateFrame(node: self.dateNode, frame: CGRect(origin: CGPoint(x: contentRect.origin.x + contentRect.size.width - dateFrame.size.width, y: dateFrame.minY), size: dateFrame.size)) +// +// let statusFrame = self.statusNode.frame +// transition.updateFrame(node: self.statusNode, frame: CGRect(origin: CGPoint(x: contentRect.origin.x + contentRect.size.width - dateFrame.size.width - 2.0 - statusFrame.size.width, y: statusFrame.minY), size: statusFrame.size)) +// +// var nextTitleIconOrigin: CGFloat = contentRect.origin.x + titleFrame.size.width + 3.0 + titleOffset +// +// if let verificationIconNode = self.verificationIconNode { +// transition.updateFrame(node: verificationIconNode, frame: CGRect(origin: CGPoint(x: nextTitleIconOrigin, y: verificationIconNode.frame.origin.y), size: verificationIconNode.bounds.size)) +// nextTitleIconOrigin += verificationIconNode.bounds.size.width + 5.0 +// } +// +// let mutedIconFrame = self.mutedIconNode.frame +// transition.updateFrame(node: self.mutedIconNode, frame: CGRect(origin: CGPoint(x: nextTitleIconOrigin, y: contentRect.origin.y + 6.0), size: mutedIconFrame.size)) +// nextTitleIconOrigin += mutedIconFrame.size.width + 3.0 +// +// let badgeBackgroundFrame = self.badgeBackgroundNode.frame +// let updatedBadgeBackgroundFrame = CGRect(origin: CGPoint(x: contentRect.maxX - badgeBackgroundFrame.size.width, y: contentRect.maxY - badgeBackgroundFrame.size.height - 2.0), size: badgeBackgroundFrame.size) +// transition.updateFrame(node: self.badgeBackgroundNode, frame: updatedBadgeBackgroundFrame) +// +// if self.mentionBadgeNode.supernode != nil { +// let mentionBadgeSize = self.mentionBadgeNode.bounds.size +// let mentionBadgeOffset: CGFloat +// if updatedBadgeBackgroundFrame.size.width.isZero || self.badgeBackgroundNode.image == nil { +// mentionBadgeOffset = contentRect.maxX - mentionBadgeSize.width +// } else { +// mentionBadgeOffset = contentRect.maxX - updatedBadgeBackgroundFrame.size.width - 6.0 - mentionBadgeSize.width +// } +// +// let badgeBackgroundWidth = mentionBadgeSize.width +// let badgeBackgroundFrame = CGRect(x: mentionBadgeOffset, y: self.mentionBadgeNode.frame.origin.y, width: badgeBackgroundWidth, height: mentionBadgeSize.height) +// transition.updateFrame(node: self.mentionBadgeNode, frame: badgeBackgroundFrame) +// } +// +// let badgeTextFrame = self.badgeTextNode.frame +// transition.updateFrame(node: self.badgeTextNode, frame: CGRect(origin: CGPoint(x: updatedBadgeBackgroundFrame.midX - badgeTextFrame.size.width / 2.0, y: badgeTextFrame.minY), size: badgeTextFrame.size)) +// } + } + + override func revealOptionSelected(_ option: ItemListRevealOption, animated: Bool) { + var close = true + if let item = self.item { + switch option.key { + case RevealOptionKey.delete.rawValue: + item.controllerInteraction.deleteRecentQuery(item.query) + default: + break + } + } + if close { + self.setRevealOptionsOpened(false, animated: true) + self.revealOptionsInteractivelyClosed() + } + } +}