diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
index 52390c7ed3..4b118ca6f5 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -37,4 +37,4 @@ Remove, if not applicable
**iOS version**: `13.X`
-**App version:** `5.X`
+**App version:** `7.X`
diff --git a/Makefile b/Makefile
index 80c8001593..db34652315 100644
--- a/Makefile
+++ b/Makefile
@@ -3,7 +3,7 @@
include Utils.makefile
-APP_VERSION="6.3"
+APP_VERSION="7.0"
CORE_COUNT=$(shell sysctl -n hw.logicalcpu)
CORE_COUNT_MINUS_ONE=$(shell expr ${CORE_COUNT} \- 1)
diff --git a/Random.txt b/Random.txt
new file mode 100644
index 0000000000..b87a57f138
--- /dev/null
+++ b/Random.txt
@@ -0,0 +1 @@
+LS3VfNVetXNy6mHmek8hegOh0wsvmJs0hcrc7PLA9eI=
diff --git a/Telegram/NotificationService/Serialization.m b/Telegram/NotificationService/Serialization.m
index c585f0d0c3..3d92dca7b9 100644
--- a/Telegram/NotificationService/Serialization.m
+++ b/Telegram/NotificationService/Serialization.m
@@ -3,7 +3,7 @@
@implementation Serialization
- (NSUInteger)currentLayer {
- return 116;
+ return 117;
}
- (id _Nullable)parseMessage:(NSData * _Nullable)data {
diff --git a/Telegram/Telegram-iOS/ca.lproj/InfoPlist.strings b/Telegram/Telegram-iOS/ca.lproj/InfoPlist.strings
index fe8899ddd2..f57e0cecfb 100644
--- a/Telegram/Telegram-iOS/ca.lproj/InfoPlist.strings
+++ b/Telegram/Telegram-iOS/ca.lproj/InfoPlist.strings
@@ -4,7 +4,7 @@
"NSLocationWhenInUseUsageDescription" = "Si envieu la vostra ubicació als amics, Telegram requereix accés per a mostra-los un mapa.";
"NSLocationAlwaysAndWhenInUseUsageDescription" = "Si trieu de compartir la vostra ubicació en directe amb amics en un xat, Telegram requereix accés en segon pla a la vostra ubicació per a actualitzar-la durant la compartició en directe.";
"NSLocationAlwaysUsageDescription" = "Si trieu de compartir la vostra ubicació en directe amb amics en un xat, Telegram requereix de tenir accés en segon pla a la vostra ubicació per a actualitzar-la durant la compartició en directe. També necessiteu això per a enviar ubicacions des d'un Apple Watch.";
-"NSCameraUsageDescription" = "Ens cal això perquè pugueu fer i compartir fotos i vídeos.";
+"NSCameraUsageDescription" = "Ens cal això perquè pugueu fer i compartir fotos i vídeos, així com fer videotrucades.";
"NSPhotoLibraryUsageDescription" = "Ens cal això perquè pugueu compartir fotos i vídeos de la biblioteca de fotos.";
"NSPhotoLibraryAddUsageDescription" = "Ens cal això perquè així pugueu desar fotos i vídeos a la biblioteca de fotos.";
"NSMicrophoneUsageDescription" = "Ens cal això perquè pugueu enregistrar i compartir missatges de veu i vídeos amb so.";
diff --git a/Telegram/Telegram-iOS/en.lproj/InfoPlist.strings b/Telegram/Telegram-iOS/en.lproj/InfoPlist.strings
index 21c893850f..ca33866340 100644
--- a/Telegram/Telegram-iOS/en.lproj/InfoPlist.strings
+++ b/Telegram/Telegram-iOS/en.lproj/InfoPlist.strings
@@ -4,7 +4,7 @@
"NSLocationWhenInUseUsageDescription" = "When you send your location to your friends, Telegram needs access to show them a map.";
"NSLocationAlwaysAndWhenInUseUsageDescription" = "When you choose to share your Live Location with friends in a chat, Telegram needs background access to your location to keep them updated for the duration of the live sharing.";
"NSLocationAlwaysUsageDescription" = "When you choose to share your live location with friends in a chat, Telegram needs background access to your location to keep them updated for the duration of the live sharing. You also need this to send locations from an Apple Watch.";
-"NSCameraUsageDescription" = "We need this so that you can take and share photos and videos.";
+"NSCameraUsageDescription" = "We need this so that you can take and share photos and videos, as well as make video calls.";
"NSPhotoLibraryUsageDescription" = "We need this so that you can share photos and videos from your photo library.";
"NSPhotoLibraryAddUsageDescription" = "We need this so that you can save photos and videos to your photo library.";
"NSMicrophoneUsageDescription" = "We need this so that you can record and share voice messages and videos with sound.";
diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings
index 0d3c7a5bd8..2a1989b53e 100644
--- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings
+++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings
@@ -215,7 +215,9 @@
"PUSH_AUTH_REGION" = "New login|from unrecognized device %1$@, location: %2$@";
"PUSH_PHONE_CALL_REQUEST" = "%1$@|is calling you!";
+"PUSH_VIDEO_CALL_REQUEST" = "%1$@|is calling you!";
"PUSH_PHONE_CALL_MISSED" = "%1$@|You missed a call";
+"PUSH_VIDEO_CALL_MISSED" = "%1$@|You missed a video call";
"PUSH_MESSAGE_GAME_SCORE" = "%1$@ scored %3$@ in game %2$@";
"PUSH_MESSAGE_VIDEOS" = "%1$@ sent you %2$@ videos";
@@ -2293,7 +2295,9 @@ Unused sets are archived when you add more.";
"Notification.CallIncoming" = "Incoming Call";
"Notification.VideoCallIncoming" = "Incoming Video Call";
"Notification.CallMissed" = "Missed Call";
+"Notification.VideoCallMissed" = "Missed Video Call";
"Notification.CallCanceled" = "Cancelled Call";
+"Notification.VideoCallCanceled" = "Cancelled Video Call";
"Notification.CallOutgoingShort" = "Outgoing";
"Notification.CallIncomingShort" = "Incoming";
"Notification.CallMissedShort" = "Missed";
@@ -2470,6 +2474,7 @@ Unused sets are archived when you add more.";
"Call.CallInProgressTitle" = "Call in Progress";
"Call.CallInProgressMessage" = "Finish call with %1$@ and start a new one with %2$@?";
+"Call.ExternalCallInProgressMessage" = "Please finish the current call first.";
"Call.Message" = "Message";
@@ -3023,7 +3028,7 @@ Unused sets are archived when you add more.";
"InfoPlist.NSContactsUsageDescription" = "Telegram will continuously upload your contacts to its heavily encrypted cloud servers to let you connect with your friends across all your devices.";
"InfoPlist.NSLocationWhenInUseUsageDescription" = "When you send your location to your friends, Telegram needs access to show them a map.";
-"InfoPlist.NSCameraUsageDescription" = "We need this so that you can take and share photos and videos.";
+"InfoPlist.NSCameraUsageDescription" = "We need this so that you can take and share photos and videos, as well as make video calls.";
"InfoPlist.NSPhotoLibraryUsageDescription" = "We need this so that you can share photos and videos from your photo library.";
"InfoPlist.NSPhotoLibraryAddUsageDescription" = "We need this so that you can save photos and videos to your photo library.";
"InfoPlist.NSMicrophoneUsageDescription" = "We need this so that you can record and share voice messages and videos with sound.";
@@ -4788,6 +4793,7 @@ Any member of this group will be able to see messages in the channel.";
"ContactList.Context.SendMessage" = "Send Message";
"ContactList.Context.StartSecretChat" = "Start Secret Chat";
"ContactList.Context.Call" = "Call";
+"ContactList.Context.VideoCall" = "Video Call";
"Theme.Context.Apply" = "Apply";
@@ -5332,7 +5338,7 @@ Any member of this group will be able to see messages in the channel.";
"PeerInfo.ButtonMessage" = "Message";
"PeerInfo.ButtonDiscuss" = "Discuss";
"PeerInfo.ButtonCall" = "Call";
-"PeerInfo.ButtonVideoCall" = "Video Call";
+"PeerInfo.ButtonVideoCall" = "Video";
"PeerInfo.ButtonMute" = "Mute";
"PeerInfo.ButtonUnmute" = "Unmute";
"PeerInfo.ButtonMore" = "More";
@@ -5633,7 +5639,7 @@ Any member of this group will be able to see messages in the channel.";
"PrivacySettings.AutoArchive" = "Archive and Mute";
"PrivacySettings.AutoArchiveInfo" = "Automatically archive and mute new chats, groups and channels from non-contacts.";
-"Call.RemoteVideoPaused" = "%@'s video paused";
+"Call.RemoteVideoPaused" = "%@'s video is paused";
"Settings.SetProfilePhotoOrVideo" = "Set Photo or Video";
"Settings.SetNewProfilePhotoOrVideo" = "Set New Photo or Video";
@@ -5696,3 +5702,36 @@ Any member of this group will be able to see messages in the channel.";
"EditProfile.NameAndPhotoOrVideoHelp" = "Enter your name and add an optional profile photo or video.";
"Settings.RemoveConfirmation" = "Remove";
+
+"Conversation.ContextMenuOpenProfile" = "Open Profile";
+"Conversation.ContextMenuSendMessage" = "Send Message";
+"Conversation.ContextMenuMention" = "Mention";
+
+"Conversation.ContextMenuOpenChannelProfile" = "Open Profile";
+"Conversation.ContextMenuOpenChannel" = "Open Channel";
+
+"Cache.KeepMediaHelp" = "Photos, videos and other files from cloud chats that you have **not accessed** during this period will be removed from this device to save disk space.";
+
+
+"Cache.MaximumCacheSize" = "Maximum Cache Size";
+"Cache.NoLimit" = "No Limit";
+"Cache.MaximumCacheSizeHelp" = "If your cache size exceeds this limit, the oldest media will be deleted.\n\nAll media will stay in the Telegram cloud and can be re-downloaded if you need it again.";
+
+"Stats.MessageTitle" = "Message Statistics";
+"Stats.MessageOverview" = "Overview";
+"Stats.MessageInteractionsTitle" = "Interactions";
+"Stats.MessagePublicForwardsTitle" = "Public Shares";
+
+"Call.CameraTooltip" = "Tap here to turn on your camera";
+"Call.CameraConfirmationText" = "Switch to video call?";
+"Call.CameraConfirmationConfirm" = "Switch";
+
+"Call.YourMicrophoneOff" = "Your microphone is off";
+"Call.MicrophoneOff" = "%@'s microphone is off";
+"Call.CameraOff" = "%@'s camera is off";
+"Call.BatteryLow" = "%@'s battery level is low";
+
+"Call.Audio" = "audio";
+"Call.AudioRouteMute" = "Mute Yourself";
+
+"AccessDenied.VideoCallCamera" = "Telegram needs access to your camera to make video calls.\n\nPlease go to Settings > Privacy > Camera and set Telegram to ON.";
diff --git a/Telegram/Telegram-iOS/es.lproj/InfoPlist.strings b/Telegram/Telegram-iOS/es.lproj/InfoPlist.strings
index dd4511b397..672fbd1a9c 100644
--- a/Telegram/Telegram-iOS/es.lproj/InfoPlist.strings
+++ b/Telegram/Telegram-iOS/es.lproj/InfoPlist.strings
@@ -4,7 +4,7 @@
"NSLocationWhenInUseUsageDescription" = "Cuando envías tu ubicación a tus amigos, Telegram necesita acceso para mostrarles un mapa.";
"NSLocationAlwaysAndWhenInUseUsageDescription" = "Cuando eliges compartir tu ubicación en tiempo real con amigos en un chat, Telegram necesita acceso en segundo plano a tu ubicación para mantenerla actualizada mientras la función esté en uso.";
"NSLocationAlwaysUsageDescription" = "Cuando envías tu ubicación a tus amigos, Telegram necesita acceso para mostrarles un mapa. También es requerido para enviar ubicaciones desde un Apple Watch.";
-"NSCameraUsageDescription" = "Es requerido para que puedas hacer fotos y vídeos.";
+"NSCameraUsageDescription" = "Necesitamos esto para que puedas tomar y compartir fotos y videos, así como para realizar videollamadas.";
"NSPhotoLibraryUsageDescription" = "Es requerido para que puedas compartir fotos y vídeos desde tu biblioteca de fotos.";
"NSPhotoLibraryAddUsageDescription" = "Necesitamos esto para que puedas guardar fotos y videos en tu biblioteca de fotos.";
"NSMicrophoneUsageDescription" = "Es requerido para que puedas grabar y compartir mensajes de voz y vídeos con sonido.";
diff --git a/Telegram/Telegram-iOS/fa.lproj/AppIntentVocabulary.plist b/Telegram/Telegram-iOS/fa.lproj/AppIntentVocabulary.plist
new file mode 100644
index 0000000000..504ece4483
--- /dev/null
+++ b/Telegram/Telegram-iOS/fa.lproj/AppIntentVocabulary.plist
@@ -0,0 +1,17 @@
+
+
+
+
+ IntentPhrases
+
+
+ IntentName
+ INSendMessageIntent
+ IntentExamples
+
+ Send a Telegram message to Alex saying I'll be there in 10 minutes
+
+
+
+
+
diff --git a/Telegram/Telegram-iOS/fa.lproj/InfoPlist.strings b/Telegram/Telegram-iOS/fa.lproj/InfoPlist.strings
new file mode 100644
index 0000000000..d23604b69e
--- /dev/null
+++ b/Telegram/Telegram-iOS/fa.lproj/InfoPlist.strings
@@ -0,0 +1,12 @@
+/* Localized versions of Info.plist keys */
+
+"NSContactsUsageDescription" = "تلگرام به طور مداوم مخاطبین شما را به سرورهای اَبری و رمزگذاری شده خود آپلود میکند تا شما بتوانید در تمامی دستگاههای خود با دوستانتان ارتباط برقرار کنید.";
+"NSLocationWhenInUseUsageDescription" = "هنگامی که شما موقعیت مکانی خود را برای دوستانتان ارسال میکنید، تلگرام جهت نشان دادن نقشه به آنها نیازمند دسترسی است.";
+"NSLocationAlwaysAndWhenInUseUsageDescription" = "در صورتی که تصمیم بگیرید موقعیت مکانی لحظهای خود را در یک گفتگو با دوستانتان به اشتراک بگذارید، تلگرام جهت بهروزرسانی این اطلاعات در طول به اشتراک گذاری لحظهای، باید در پسزمینه به موقعیت مکانی شما دسترسی داشته باشد.";
+"NSLocationAlwaysUsageDescription" = "در صورتی که شما تصمیم بگیرید موقعیت مکانی لحظهای خود را در یک گفتگو با دوستانتان به اشتراک بگذارید، تلگرام جهت بهروزرسانی این اطلاعات در طول به اشتراک گذاری لحظهای، باید در پسزمینه به موقعیت مکانی شما دسترسی داشته باشد. شما همچنین برای ارسال موقعیت مکانی از ساعت اَپل به این دسترسی نیاز دارید.";
+"NSCameraUsageDescription" = "ما به این دسترسی نیاز داریم تا شما بتوانید عکس و ویدیو گرفته و آنها را به اشتراک بگذارید.";
+"NSPhotoLibraryUsageDescription" = "ما به این دسترسی نیاز داریم تا شما بتوانید از گالری تصاویر خود عکس و ویدیو به اشتراک بگذارید.";
+"NSPhotoLibraryAddUsageDescription" = "ما به این دسترسی نیاز داریم تا شما بتوانید عکسها و ویدیوها را در گالری تصاویر خود ذخیره کنید.";
+"NSMicrophoneUsageDescription" = "ما به این دسترسی نیاز داریم تا شما بتوانید پیامهای صوتی و ویدیوهای صدادار را ضبط کرده و به اشتراک بگذارید.";
+"NSSiriUsageDescription" = "شما میتوانید با استفاده از Siri پیام ارسال کنید.";
+"NSFaceIDUsageDescription" = "شما میتوانید با استفاده از Face ID قفل برنامه را باز کنید.";
diff --git a/Telegram/Telegram-iOS/fa.lproj/Localizable.strings b/Telegram/Telegram-iOS/fa.lproj/Localizable.strings
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/Telegram/Telegram-iOS/it.lproj/InfoPlist.strings b/Telegram/Telegram-iOS/it.lproj/InfoPlist.strings
index 9e1ae9a309..a9a5ed1752 100644
--- a/Telegram/Telegram-iOS/it.lproj/InfoPlist.strings
+++ b/Telegram/Telegram-iOS/it.lproj/InfoPlist.strings
@@ -4,7 +4,7 @@
"NSLocationWhenInUseUsageDescription" = "Quando invii la tua posizione ai tuoi amici, Telegram ha bisogno di accedere per mostrare loro la mappa.";
"NSLocationAlwaysUsageDescription" = "Quando invii la tua posizione ai tuoi amici, Telegram ha bisogno di accedere per mostrare loro la mappa. Ti serve anche per inviare posizioni da Apple Watch.";
"NSLocationAlwaysAndWhenInUseUsageDescription" = "Quando scegli di condividere la tua Posizione Attuale con gli amici in una chat, Telegram ha bisogno dell'accesso in background alla tua posizione per tenerli aggiornati durante la durata della condivisione della posizione.";
-"NSCameraUsageDescription" = "Ci serve per farti scattare, registrare e condividere foto e video.";
+"NSCameraUsageDescription" = "Ci serve per farti scattare, registrare e condividere foto e video, oltre che per fare videochiamate.";
"NSPhotoLibraryUsageDescription" = "Ci serve per farti condividere foto e video dalla tua libreria foto.";
"NSPhotoLibraryAddUsageDescription" = "Ci serve per farti salvare foto e video nella tua libreria foto.";
"NSMicrophoneUsageDescription" = "Ci serve per farti registrare e condividere messaggi vocali e video con il sonoro.";
diff --git a/Telegram/Telegram-iOS/ms.lproj/InfoPlist.strings b/Telegram/Telegram-iOS/ms.lproj/InfoPlist.strings
index 10ef1e9f5f..c6d7bf72f9 100644
--- a/Telegram/Telegram-iOS/ms.lproj/InfoPlist.strings
+++ b/Telegram/Telegram-iOS/ms.lproj/InfoPlist.strings
@@ -4,7 +4,7 @@
"NSLocationWhenInUseUsageDescription" = "Bila anda hantar lokasi anda kepada rakan anda, Telegram perlukan akses untuk tunjuk peta.";
"NSLocationAlwaysAndWhenInUseUsageDescription" = "Bila anda pilih untuk kongsi Lokasi Langsung anda dengan rakan dalam chat, Telegram perlu akses latar belakang ke lokasi anda agar lokasi anda sentiasa dikemaskini ketika perkongsian.";
"NSLocationAlwaysUsageDescription" = "Bila anda pilih untuk kongsi lokasi langsung anda dengan rakan dalam chat, Telegram perlu akses latar belakang agar lokasi anda sentiasa dikemaskini. Anda juga harus hantar lokasi anda ke Jam Apple.";
-"NSCameraUsageDescription" = "Kita perlukan ini agar anda boleh ambil dan kongsi foto dan video.";
+"NSCameraUsageDescription" = "Kita perlukan ini agar anda boleh ambil dan kongsi foto dan video, dan juga buat panggilan video.";
"NSPhotoLibraryUsageDescription" = "Kita perlu ini agar anda boleh kongsi foto dan video dari librari foto anda.";
"NSPhotoLibraryAddUsageDescription" = "Kita perlu ini agar anda boleh simpan foto dan video ke librari foto anda.";
"NSMicrophoneUsageDescription" = "Kita perlu ini agar anda boleh rekod dan kongsi mesej suara dan video dengan suara.";
diff --git a/Telegram/Telegram-iOS/pl.lproj/AppIntentVocabulary.plist b/Telegram/Telegram-iOS/pl.lproj/AppIntentVocabulary.plist
new file mode 100644
index 0000000000..504ece4483
--- /dev/null
+++ b/Telegram/Telegram-iOS/pl.lproj/AppIntentVocabulary.plist
@@ -0,0 +1,17 @@
+
+
+
+
+ IntentPhrases
+
+
+ IntentName
+ INSendMessageIntent
+ IntentExamples
+
+ Send a Telegram message to Alex saying I'll be there in 10 minutes
+
+
+
+
+
diff --git a/Telegram/Telegram-iOS/pl.lproj/InfoPlist.strings b/Telegram/Telegram-iOS/pl.lproj/InfoPlist.strings
new file mode 100644
index 0000000000..e2edd5f990
--- /dev/null
+++ b/Telegram/Telegram-iOS/pl.lproj/InfoPlist.strings
@@ -0,0 +1,12 @@
+/* Localized versions of Info.plist keys */
+
+"NSContactsUsageDescription" = "Telegram będzie nieprzerwanie przesyłać kontakty do silnie zaszyfrowanych serwerów w chmurze, aby umożliwić ci połączenie się ze znajomymi na wszystkich urządzeniach.";
+"NSLocationWhenInUseUsageDescription" = "Gdy wysyłasz swoją lokalizację znajomym, Telegram potrzebuje dostępu, aby pokazać im mapę.";
+"NSLocationAlwaysAndWhenInUseUsageDescription" = "Gdy zdecydujesz się udostępnić swoją „lokalizację na żywo” znajomym podczas czatu, Telegram potrzebuje dostępu w tle do twojej lokalizacji, aby zapewnić jej aktualizację przez cały czas udostępniania „na żywo”.";
+"NSLocationAlwaysUsageDescription" = "Gdy zdecydujesz się udostępnić swoją „lokalizację na żywo” znajomym podczas czatu, Telegram potrzebuje dostępu w tle do twojej lokalizacji, aby zapewnić jej aktualizację przez cały czas udostępniania „na żywo”. Jest to również potrzebne do wysyłania lokalizacji z Apple Watch.";
+"NSCameraUsageDescription" = "Potrzebujemy tego, aby można było robić i udostępniać zdjęcia i wideo, a także prowadzić rozmowy wideo.";
+"NSPhotoLibraryUsageDescription" = "Potrzebujemy tego, aby można było udostępniać zdjęcia i wideo ze swojej biblioteki zdjęć.";
+"NSPhotoLibraryAddUsageDescription" = "Potrzebujemy tego, aby można było zapisywać zdjęcia i wideo w swojej bibliotece zdjęć.";
+"NSMicrophoneUsageDescription" = "Potrzebujemy tego, aby można było nagrywać i udostępniać wiadomości głosowe i wideo z dźwiękiem.";
+"NSSiriUsageDescription" = "Możesz użyć Siri do wysyłania wiadomości.";
+"NSFaceIDUsageDescription" = "Możesz użyć Face ID, aby odblokować aplikację.";
diff --git a/Telegram/Telegram-iOS/pl.lproj/Localizable.strings b/Telegram/Telegram-iOS/pl.lproj/Localizable.strings
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/Telegram/Telegram-iOS/tr.lproj/InfoPlist.strings b/Telegram/Telegram-iOS/tr.lproj/InfoPlist.strings
index 1ba2967139..283b1f2a25 100644
--- a/Telegram/Telegram-iOS/tr.lproj/InfoPlist.strings
+++ b/Telegram/Telegram-iOS/tr.lproj/InfoPlist.strings
@@ -4,7 +4,7 @@
"NSLocationWhenInUseUsageDescription" = "Konumunuzu arkadaşlarınıza gönderdiğinizde, Telegram'ın onlara bir harita göstermesi için erişmesi gerekiyor.";
"NSLocationAlwaysAndWhenInUseUsageDescription" = "Canlı Konumunuzu arkadaşlarınızla bir sohbette paylaşmayı seçtiğinizde, Telegram'ın canlı paylaşım süresince onları güncel tutmak için konumunuza arka plan erişimi olması gerekir.";
"NSLocationAlwaysUsageDescription" = "Canlı konumunuzu bir sohbette arkadaşlarınızla paylaşmayı seçtiğinizde, Telegram'ın canlı paylaşım süresince konumunuzu güncel tutması için bir arka plan erişimi gerekir. Ayrıca Apple Watch'dan konum göndermek için de buna ihtiyacınız var.";
-"NSCameraUsageDescription" = "Fotoğraf ve video çekip paylaşabilmeniz için buna ihtiyacımız var.";
+"NSCameraUsageDescription" = "Fotoğraf ve video çekip paylaşabilmeniz ve görüntülü arama yapabilmeniz için buna ihtiyacımız var.";
"NSPhotoLibraryUsageDescription" = "Fotoğraf arşivinizdeki fotoğraf ve videoları paylaşabilmeniz için buna ihtiyacımız var.";
"NSPhotoLibraryAddUsageDescription" = "Fotoğraf arşivine fotoğraf ve video kaydedebilmeniz için buna ihtiyacımız var.";
"NSMicrophoneUsageDescription" = "Sesli mesajları ve videoları ses ile kaydedebilmeniz ve paylaşabilmeniz için buna ihtiyacımız var.";
diff --git a/Telegram/Telegram-iOS/uz.lproj/AppIntentVocabulary.plist b/Telegram/Telegram-iOS/uz.lproj/AppIntentVocabulary.plist
new file mode 100644
index 0000000000..504ece4483
--- /dev/null
+++ b/Telegram/Telegram-iOS/uz.lproj/AppIntentVocabulary.plist
@@ -0,0 +1,17 @@
+
+
+
+
+ IntentPhrases
+
+
+ IntentName
+ INSendMessageIntent
+ IntentExamples
+
+ Send a Telegram message to Alex saying I'll be there in 10 minutes
+
+
+
+
+
diff --git a/Telegram/Telegram-iOS/uz.lproj/InfoPlist.strings b/Telegram/Telegram-iOS/uz.lproj/InfoPlist.strings
new file mode 100644
index 0000000000..ec5ffb6584
--- /dev/null
+++ b/Telegram/Telegram-iOS/uz.lproj/InfoPlist.strings
@@ -0,0 +1,12 @@
+/* Localized versions of Info.plist keys */
+
+"NSContactsUsageDescription" = "Telegram barcha qurilmalaringizdan doʻstlaringiz bilan bogʻlana olishingiz uchun muntazam ravishda kontaktlaringizni kuchli shifrlanadigan bulut serverlariga yuklaydi.";
+"NSLocationWhenInUseUsageDescription" = "Joylashuvingizni doʻstlaringizga yuborganingizda Telegram ularga xaritani koʻrsatishi uchun ruxsat kerak boʻladi.";
+"NSLocationAlwaysAndWhenInUseUsageDescription" = "Chatda doʻstlarga Jonli joylashuvingizni ulashishni tanlaganingizda, Telegram joylashuvingizni jonli ulashish davomida yangilab turishi uchun undan fonda foydalanishi kerak.";
+"NSLocationAlwaysUsageDescription" = "Chatda doʻstlarga Jonli joylashuvingizni ulashishni tanlaganingizda, Telegram joylashuvingizni jonli ulashish davomida yangilab turishi uchun undan fonda foydalanishi kerak. Sizga bu Apple Watchdan joylashuvlarni yuborish uchun ham kerak.";
+"NSCameraUsageDescription" = "Bu bizga rasm va videolarga olish, shuningdek, video chaqiruvlar qilish va ularni ulashishingiz uchun kerak.";
+"NSPhotoLibraryUsageDescription" = "Bu bizga galereyangizdan rasm va videolaringizni ulasha olishingiz uchun kerak.";
+"NSPhotoLibraryAddUsageDescription" = "Bu bizga rasm va videolaringizni galereyangizga saqlay olishingiz uchun kerak.";
+"NSMicrophoneUsageDescription" = "Bu bizga ovozli xabarlarni tovush bilan yozishingiz va ulashingiz uchun kerak.";
+"NSSiriUsageDescription" = "Siz xabarlar yuborish uchun Siridan foydalanishingiz mumkin.";
+"NSFaceIDUsageDescription" = "Siz ilovani Face ID yordamida ochishingiz mumkin.";
diff --git a/Telegram/Telegram-iOS/uz.lproj/Localizable.strings b/Telegram/Telegram-iOS/uz.lproj/Localizable.strings
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/bazel-Telegram-iOS-Beta b/bazel-Telegram-iOS-Beta
deleted file mode 120000
index e30c8faa42..0000000000
--- a/bazel-Telegram-iOS-Beta
+++ /dev/null
@@ -1 +0,0 @@
-/private/var/tmp/_bazel_ilya/b5422c0ab62ebf818a9d47ff552063a2/execroot/__main__
\ No newline at end of file
diff --git a/build-system/generate-xcode-project.sh b/build-system/generate-xcode-project.sh
index 44665420c2..420ff2b136 100755
--- a/build-system/generate-xcode-project.sh
+++ b/build-system/generate-xcode-project.sh
@@ -17,11 +17,14 @@ fi
XCODE_VERSION=$(cat "build-system/xcode_version")
INSTALLED_XCODE_VERSION=$(echo `plutil -p \`xcode-select -p\`/../Info.plist | grep -e CFBundleShortVersionString | sed 's/[^0-9\.]*//g'`)
-if [ "$INSTALLED_XCODE_VERSION" != "$XCODE_VERSION" ]; then
- echo "Xcode $XCODE_VERSION required, $INSTALLED_XCODE_VERSION installed (at $(xcode-select -p))"
- exit 1
+if [ "$IGNORE_XCODE_VERSION_MISMATCH" = "1" ]; then
+ XCODE_VERSION="$INSTALLED_XCODE_VERSION"
+else
+ if [ "$INSTALLED_XCODE_VERSION" != "$XCODE_VERSION" ]; then
+ echo "Xcode $XCODE_VERSION required, $INSTALLED_XCODE_VERSION installed (at $(xcode-select -p))"
+ exit 1
+ fi
fi
-
GEN_DIRECTORY="build-input/gen/project"
mkdir -p "$GEN_DIRECTORY"
diff --git a/submodules/AccountContext/Sources/AccountContext.swift b/submodules/AccountContext/Sources/AccountContext.swift
index 19f7d7932a..ed6306da0d 100644
--- a/submodules/AccountContext/Sources/AccountContext.swift
+++ b/submodules/AccountContext/Sources/AccountContext.swift
@@ -394,6 +394,12 @@ public enum ContactListPeerId: Hashable {
case deviceContact(DeviceContactStableId)
}
+public enum ContactListAction: Equatable {
+ case generic
+ case voiceCall
+ case videoCall
+}
+
public enum ContactListPeer: Equatable {
case peer(peer: Peer, isGlobal: Bool, participantCount: Int32?)
case deviceContact(DeviceContactStableId, DeviceContactBasicData)
@@ -440,14 +446,16 @@ public final class ContactSelectionControllerParams {
public let title: (PresentationStrings) -> String
public let options: [ContactListAdditionalOption]
public let displayDeviceContacts: Bool
+ public let displayCallIcons: Bool
public let confirmation: (ContactListPeer) -> Signal
- public init(context: AccountContext, autoDismiss: Bool = true, title: @escaping (PresentationStrings) -> String, options: [ContactListAdditionalOption] = [], displayDeviceContacts: Bool = false, confirmation: @escaping (ContactListPeer) -> Signal = { _ in .single(true) }) {
+ public init(context: AccountContext, autoDismiss: Bool = true, title: @escaping (PresentationStrings) -> String, options: [ContactListAdditionalOption] = [], displayDeviceContacts: Bool = false, displayCallIcons: Bool = false, confirmation: @escaping (ContactListPeer) -> Signal = { _ in .single(true) }) {
self.context = context
self.autoDismiss = autoDismiss
self.title = title
self.options = options
self.displayDeviceContacts = displayDeviceContacts
+ self.displayCallIcons = displayCallIcons
self.confirmation = confirmation
}
}
@@ -647,6 +655,7 @@ public protocol AccountContext: class {
var currentLimitsConfiguration: Atomic { get }
var currentContentSettings: Atomic { get }
+ var currentAppConfiguration: Atomic { get }
func storeSecureIdPassword(password: String)
func getStoredSecureIdPassword() -> String?
diff --git a/submodules/AccountContext/Sources/ChatListController.swift b/submodules/AccountContext/Sources/ChatListController.swift
index ad9ac4ee69..ec9fb811e8 100644
--- a/submodules/AccountContext/Sources/ChatListController.swift
+++ b/submodules/AccountContext/Sources/ChatListController.swift
@@ -10,5 +10,5 @@ public protocol ChatListController: ViewController {
func activateSearch()
func deactivateSearch(animated: Bool)
func activateCompose()
- func maybeAskForPeerChatRemoval(peer: RenderedPeer, deleteGloballyIfPossible: Bool, completion: @escaping (Bool) -> Void, removed: @escaping () -> Void)
+ func maybeAskForPeerChatRemoval(peer: RenderedPeer, joined: Bool, deleteGloballyIfPossible: Bool, completion: @escaping (Bool) -> Void, removed: @escaping () -> Void)
}
diff --git a/submodules/AccountContext/Sources/ContactSelectionController.swift b/submodules/AccountContext/Sources/ContactSelectionController.swift
index f480206f36..b99e4c7252 100644
--- a/submodules/AccountContext/Sources/ContactSelectionController.swift
+++ b/submodules/AccountContext/Sources/ContactSelectionController.swift
@@ -3,7 +3,7 @@ import Display
import SwiftSignalKit
public protocol ContactSelectionController: ViewController {
- var result: Signal { get }
+ var result: Signal<(ContactListPeer, ContactListAction)?, NoError> { get }
var displayProgress: Bool { get set }
var dismissed: (() -> Void)? { get set }
diff --git a/submodules/AccountContext/Sources/FetchMediaUtils.swift b/submodules/AccountContext/Sources/FetchMediaUtils.swift
index e580697411..52b7487363 100644
--- a/submodules/AccountContext/Sources/FetchMediaUtils.swift
+++ b/submodules/AccountContext/Sources/FetchMediaUtils.swift
@@ -47,13 +47,19 @@ public func messageMediaFileCancelInteractiveFetch(context: AccountContext, mess
context.fetchManager.cancelInteractiveFetches(category: fetchCategoryForFile(file), location: .chat(messageId.peerId), locationKey: .messageId(messageId), resource: file.resource)
}
-public func messageMediaImageInteractiveFetched(context: AccountContext, message: Message, image: TelegramMediaImage, resource: MediaResource, storeToDownloadsPeerType: MediaAutoDownloadPeerType?) -> Signal {
- return messageMediaImageInteractiveFetched(fetchManager: context.fetchManager, messageId: message.id, messageReference: MessageReference(message), image: image, resource: resource, userInitiated: true, priority: .userInitiated, storeToDownloadsPeerType: storeToDownloadsPeerType)
+public func messageMediaImageInteractiveFetched(context: AccountContext, message: Message, image: TelegramMediaImage, resource: MediaResource, range: Range? = nil, storeToDownloadsPeerType: MediaAutoDownloadPeerType?) -> Signal {
+ return messageMediaImageInteractiveFetched(fetchManager: context.fetchManager, messageId: message.id, messageReference: MessageReference(message), image: image, resource: resource, range: range, userInitiated: true, priority: .userInitiated, storeToDownloadsPeerType: storeToDownloadsPeerType)
}
-public func messageMediaImageInteractiveFetched(fetchManager: FetchManager, messageId: MessageId, messageReference: MessageReference, image: TelegramMediaImage, resource: MediaResource, userInitiated: Bool, priority: FetchManagerPriority, storeToDownloadsPeerType: MediaAutoDownloadPeerType?) -> Signal {
+public func messageMediaImageInteractiveFetched(fetchManager: FetchManager, messageId: MessageId, messageReference: MessageReference, image: TelegramMediaImage, resource: MediaResource, range: Range? = nil, userInitiated: Bool, priority: FetchManagerPriority, storeToDownloadsPeerType: MediaAutoDownloadPeerType?) -> Signal {
let mediaReference = AnyMediaReference.message(message: messageReference, media: image)
- return fetchManager.interactivelyFetched(category: .image, location: .chat(messageId.peerId), locationKey: .messageId(messageId), mediaReference: mediaReference, resourceReference: mediaReference.resourceReference(resource), ranges: FetchCompleteRange, statsCategory: .image, elevatedPriority: false, userInitiated: userInitiated, priority: priority, storeToDownloadsPeerType: storeToDownloadsPeerType)
+ let ranges: IndexSet
+ if let range = range {
+ ranges = IndexSet(integersIn: range)
+ } else {
+ ranges = FetchCompleteRange
+ }
+ return fetchManager.interactivelyFetched(category: .image, location: .chat(messageId.peerId), locationKey: .messageId(messageId), mediaReference: mediaReference, resourceReference: mediaReference.resourceReference(resource), ranges: ranges, statsCategory: .image, elevatedPriority: false, userInitiated: userInitiated, priority: priority, storeToDownloadsPeerType: storeToDownloadsPeerType)
}
public func messageMediaImageCancelInteractiveFetch(context: AccountContext, messageId: MessageId, image: TelegramMediaImage, resource: MediaResource) {
diff --git a/submodules/AccountContext/Sources/PresentationCallManager.swift b/submodules/AccountContext/Sources/PresentationCallManager.swift
index f620a90761..b41359e3c9 100644
--- a/submodules/AccountContext/Sources/PresentationCallManager.swift
+++ b/submodules/AccountContext/Sources/PresentationCallManager.swift
@@ -8,7 +8,7 @@ import TelegramAudio
public enum RequestCallResult {
case requested
- case alreadyInProgress(PeerId)
+ case alreadyInProgress(PeerId?)
}
public struct CallAuxiliaryServer {
@@ -40,44 +40,75 @@ public struct PresentationCallState: Equatable {
case connecting(Data?)
case active(Double, Int32?, Data)
case reconnecting(Double, Int32?, Data)
- case terminating
+ case terminating(CallSessionTerminationReason?)
case terminated(CallId?, CallSessionTerminationReason?, Bool)
}
public enum VideoState: Equatable {
case notAvailable
- case possible
- case outgoingRequested
- case incomingRequested
+ case inactive
case active
+ case paused
}
public enum RemoteVideoState: Equatable {
case inactive
case active
+ case paused
+ }
+
+ public enum RemoteAudioState: Equatable {
+ case active
+ case muted
+ }
+
+ public enum RemoteBatteryLevel: Equatable {
+ case normal
+ case low
}
public var state: State
public var videoState: VideoState
public var remoteVideoState: RemoteVideoState
+ public var remoteAudioState: RemoteAudioState
+ public var remoteBatteryLevel: RemoteBatteryLevel
- public init(state: State, videoState: VideoState, remoteVideoState: RemoteVideoState) {
+ public init(state: State, videoState: VideoState, remoteVideoState: RemoteVideoState, remoteAudioState: RemoteAudioState, remoteBatteryLevel: RemoteBatteryLevel) {
self.state = state
self.videoState = videoState
self.remoteVideoState = remoteVideoState
+ self.remoteAudioState = remoteAudioState
+ self.remoteBatteryLevel = remoteBatteryLevel
}
}
public final class PresentationCallVideoView {
+ public enum Orientation {
+ case rotation0
+ case rotation90
+ case rotation180
+ case rotation270
+ }
+
public let view: UIView
- public let setOnFirstFrameReceived: ((() -> Void)?) -> Void
+ public let setOnFirstFrameReceived: (((Float) -> Void)?) -> Void
+
+ public let getOrientation: () -> Orientation
+ public let setOnOrientationUpdated: (((Orientation) -> Void)?) -> Void
+ public let setOnIsMirroredUpdated: (((Bool) -> Void)?) -> Void
public init(
view: UIView,
- setOnFirstFrameReceived: @escaping ((() -> Void)?) -> Void
+ setOnFirstFrameReceived: @escaping (((Float) -> Void)?) -> Void,
+ getOrientation: @escaping () -> Orientation,
+ setOnOrientationUpdated: @escaping (((Orientation) -> Void)?) -> Void,
+ setOnIsMirroredUpdated: @escaping (((Bool) -> Void)?) -> Void
) {
self.view = view
self.setOnFirstFrameReceived = setOnFirstFrameReceived
+ self.getOrientation = getOrientation
+ self.setOnOrientationUpdated = setOnOrientationUpdated
+ self.setOnIsMirroredUpdated = setOnIsMirroredUpdated
}
}
@@ -106,7 +137,7 @@ public protocol PresentationCall: class {
func toggleIsMuted()
func setIsMuted(_ value: Bool)
func requestVideo()
- func acceptVideo()
+ func disableVideo()
func setOutgoingVideoIsPaused(_ isPaused: Bool)
func switchVideoCamera()
func setCurrentAudioOutput(_ output: AudioSessionOutput)
@@ -119,5 +150,5 @@ public protocol PresentationCall: class {
public protocol PresentationCallManager: class {
var currentCallSignal: Signal { get }
- func requestCall(account: Account, peerId: PeerId, isVideo: Bool, endCurrentIfAny: Bool) -> RequestCallResult
+ func requestCall(context: AccountContext, peerId: PeerId, isVideo: Bool, endCurrentIfAny: Bool) -> RequestCallResult
}
diff --git a/submodules/AccountContext/Sources/VideoCallsConfiguration.swift b/submodules/AccountContext/Sources/VideoCallsConfiguration.swift
new file mode 100644
index 0000000000..2fd564d60d
--- /dev/null
+++ b/submodules/AccountContext/Sources/VideoCallsConfiguration.swift
@@ -0,0 +1,39 @@
+import SyncCore
+
+public struct VideoCallsConfiguration: Equatable {
+ public enum VideoCallsSupport {
+ case disabled
+ case full
+ case onlyVideo
+ }
+
+ public var videoCallsSupport: VideoCallsSupport
+
+ public init(appConfiguration: AppConfiguration) {
+ var videoCallsSupport: VideoCallsSupport = .full
+ if let data = appConfiguration.data, let value = data["video_calls_support"] as? String {
+ switch value {
+ case "disabled":
+ videoCallsSupport = .disabled
+ case "full":
+ videoCallsSupport = .full
+ case "only_video":
+ videoCallsSupport = .onlyVideo
+ default:
+ videoCallsSupport = .full
+ }
+ }
+ self.videoCallsSupport = videoCallsSupport
+ }
+}
+
+public extension VideoCallsConfiguration {
+ var areVideoCallsEnabled: Bool {
+ switch self.videoCallsSupport {
+ case .disabled:
+ return false
+ case .full, .onlyVideo:
+ return true
+ }
+ }
+}
diff --git a/submodules/AnimatedStickerNode/Sources/AnimatedStickerNode.swift b/submodules/AnimatedStickerNode/Sources/AnimatedStickerNode.swift
index 84445b572e..ebbf1292e8 100644
--- a/submodules/AnimatedStickerNode/Sources/AnimatedStickerNode.swift
+++ b/submodules/AnimatedStickerNode/Sources/AnimatedStickerNode.swift
@@ -5,8 +5,10 @@ import Display
import AsyncDisplayKit
import RLottieBinding
import GZip
+import YuvConversion
private let sharedQueue = Queue()
+private let sharedStoreQueue = Queue.concurrentDefaultQueue()
private class AnimatedStickerNodeDisplayEvents: ASDisplayNode {
private var value: Bool = false
@@ -46,12 +48,13 @@ private class AnimatedStickerNodeDisplayEvents: ASDisplayNode {
public enum AnimatedStickerMode {
case cached
- case direct
+ case direct(cachePathPrefix: String?)
}
public enum AnimatedStickerPlaybackPosition {
case start
case end
+ case timestamp(Double)
}
public enum AnimatedStickerPlaybackMode {
@@ -83,8 +86,9 @@ public final class AnimatedStickerFrame {
public protocol AnimatedStickerFrameSource: class {
var frameRate: Int { get }
var frameCount: Int { get }
+ var frameIndex: Int { get }
- func takeFrame() -> AnimatedStickerFrame?
+ func takeFrame(draw: Bool) -> AnimatedStickerFrame?
func skipToEnd()
}
@@ -109,7 +113,7 @@ public final class AnimatedStickerCachedFrameSource: AnimatedStickerFrameSource
let height: Int
public let frameRate: Int
public let frameCount: Int
- private var frameIndex: Int
+ public var frameIndex: Int
private let initialOffset: Int
private var offset: Int
var decodeBuffer: Data
@@ -179,7 +183,7 @@ public final class AnimatedStickerCachedFrameSource: AnimatedStickerFrameSource
assert(self.queue.isCurrent())
}
- public func takeFrame() -> AnimatedStickerFrame? {
+ public func takeFrame(draw: Bool) -> AnimatedStickerFrame? {
var frameData: Data?
var isLastFrame = false
@@ -210,27 +214,29 @@ public final class AnimatedStickerCachedFrameSource: AnimatedStickerFrameSource
self.offset += 4
- self.scratchBuffer.withUnsafeMutableBytes { (scratchBytes: UnsafeMutablePointer) -> Void in
- self.decodeBuffer.withUnsafeMutableBytes { (decodeBytes: UnsafeMutablePointer) -> Void in
- self.frameBuffer.withUnsafeMutableBytes { (frameBytes: UnsafeMutablePointer) -> Void in
- compression_decode_buffer(decodeBytes, decodeBufferLength, bytes.advanced(by: self.offset), Int(frameLength), UnsafeMutableRawPointer(scratchBytes), COMPRESSION_LZFSE)
-
- var lhs = UnsafeMutableRawPointer(frameBytes).assumingMemoryBound(to: UInt64.self)
- var rhs = UnsafeRawPointer(decodeBytes).assumingMemoryBound(to: UInt64.self)
- for _ in 0 ..< decodeBufferLength / 8 {
- lhs.pointee = lhs.pointee ^ rhs.pointee
- lhs = lhs.advanced(by: 1)
- rhs = rhs.advanced(by: 1)
+ if draw {
+ self.scratchBuffer.withUnsafeMutableBytes { (scratchBytes: UnsafeMutablePointer) -> Void in
+ self.decodeBuffer.withUnsafeMutableBytes { (decodeBytes: UnsafeMutablePointer) -> Void in
+ self.frameBuffer.withUnsafeMutableBytes { (frameBytes: UnsafeMutablePointer) -> Void in
+ compression_decode_buffer(decodeBytes, decodeBufferLength, bytes.advanced(by: self.offset), Int(frameLength), UnsafeMutableRawPointer(scratchBytes), COMPRESSION_LZFSE)
+
+ var lhs = UnsafeMutableRawPointer(frameBytes).assumingMemoryBound(to: UInt64.self)
+ var rhs = UnsafeRawPointer(decodeBytes).assumingMemoryBound(to: UInt64.self)
+ for _ in 0 ..< decodeBufferLength / 8 {
+ lhs.pointee = lhs.pointee ^ rhs.pointee
+ lhs = lhs.advanced(by: 1)
+ rhs = rhs.advanced(by: 1)
+ }
+ var lhsRest = UnsafeMutableRawPointer(frameBytes).assumingMemoryBound(to: UInt8.self).advanced(by: (decodeBufferLength / 8) * 8)
+ var rhsRest = UnsafeMutableRawPointer(decodeBytes).assumingMemoryBound(to: UInt8.self).advanced(by: (decodeBufferLength / 8) * 8)
+ for _ in (decodeBufferLength / 8) * 8 ..< decodeBufferLength {
+ lhsRest.pointee = rhsRest.pointee ^ lhsRest.pointee
+ lhsRest = lhsRest.advanced(by: 1)
+ rhsRest = rhsRest.advanced(by: 1)
+ }
+
+ frameData = Data(bytes: frameBytes, count: decodeBufferLength)
}
- var lhsRest = UnsafeMutableRawPointer(frameBytes).assumingMemoryBound(to: UInt8.self).advanced(by: (decodeBufferLength / 8) * 8)
- var rhsRest = UnsafeMutableRawPointer(decodeBytes).assumingMemoryBound(to: UInt8.self).advanced(by: (decodeBufferLength / 8) * 8)
- for _ in (decodeBufferLength / 8) * 8 ..< decodeBufferLength {
- lhsRest.pointee = rhsRest.pointee ^ lhsRest.pointee
- lhsRest = lhsRest.advanced(by: 1)
- rhsRest = rhsRest.advanced(by: 1)
- }
-
- frameData = Data(bytes: frameBytes, count: decodeBufferLength)
}
}
}
@@ -247,7 +253,7 @@ public final class AnimatedStickerCachedFrameSource: AnimatedStickerFrameSource
}
}
- if let frameData = frameData {
+ if let frameData = frameData, draw {
return AnimatedStickerFrame(data: frameData, type: .yuva, width: self.width, height: self.height, bytesPerRow: self.bytesPerRow, index: frameIndex, isLastFrame: isLastFrame)
} else {
return nil
@@ -263,18 +269,332 @@ public final class AnimatedStickerCachedFrameSource: AnimatedStickerFrameSource
}
}
+private func wrappedWrite(_ fd: Int32, _ data: UnsafeRawPointer, _ count: Int) -> Int {
+ return write(fd, data, count)
+}
+
+private func wrappedRead(_ fd: Int32, _ data: UnsafeMutableRawPointer, _ count: Int) -> Int {
+ return read(fd, data, count)
+}
+
+//TODO: separate ManagedFile into its own module
+private final class ManagedFileImpl {
+ enum Mode {
+ case read
+ case readwrite
+ case append
+ }
+
+ private let queue: Queue?
+ private let fd: Int32
+ private let mode: Mode
+
+ init?(queue: Queue?, path: String, mode: Mode) {
+ if let queue = queue {
+ assert(queue.isCurrent())
+ }
+ self.queue = queue
+ self.mode = mode
+ let fileMode: Int32
+ let accessMode: UInt16
+ switch mode {
+ case .read:
+ fileMode = O_RDONLY
+ accessMode = S_IRUSR
+ case .readwrite:
+ fileMode = O_RDWR | O_CREAT
+ accessMode = S_IRUSR | S_IWUSR
+ case .append:
+ fileMode = O_WRONLY | O_CREAT | O_APPEND
+ accessMode = S_IRUSR | S_IWUSR
+ }
+ let fd = open(path, fileMode, accessMode)
+ if fd >= 0 {
+ self.fd = fd
+ } else {
+ return nil
+ }
+ }
+
+ deinit {
+ if let queue = self.queue {
+ assert(queue.isCurrent())
+ }
+ close(self.fd)
+ }
+
+ public func write(_ data: UnsafeRawPointer, count: Int) -> Int {
+ if let queue = self.queue {
+ assert(queue.isCurrent())
+ }
+ return wrappedWrite(self.fd, data, count)
+ }
+
+ public func read(_ data: UnsafeMutableRawPointer, _ count: Int) -> Int {
+ if let queue = self.queue {
+ assert(queue.isCurrent())
+ }
+ return wrappedRead(self.fd, data, count)
+ }
+
+ public func readData(count: Int) -> Data {
+ if let queue = self.queue {
+ assert(queue.isCurrent())
+ }
+ var result = Data(count: count)
+ result.withUnsafeMutableBytes { (bytes: UnsafeMutablePointer) -> Void in
+ let readCount = self.read(bytes, count)
+ assert(readCount == count)
+ }
+ return result
+ }
+
+ public func seek(position: Int64) {
+ if let queue = self.queue {
+ assert(queue.isCurrent())
+ }
+ lseek(self.fd, position, SEEK_SET)
+ }
+
+ public func truncate(count: Int64) {
+ if let queue = self.queue {
+ assert(queue.isCurrent())
+ }
+ ftruncate(self.fd, count)
+ }
+
+ public func getSize() -> Int? {
+ if let queue = self.queue {
+ assert(queue.isCurrent())
+ }
+ var value = stat()
+ if fstat(self.fd, &value) == 0 {
+ return Int(value.st_size)
+ } else {
+ return nil
+ }
+ }
+
+ public func sync() {
+ if let queue = self.queue {
+ assert(queue.isCurrent())
+ }
+ fsync(self.fd)
+ }
+}
+
+private func compressFrame(width: Int, height: Int, rgbData: Data) -> Data? {
+ let bytesPerRow = rgbData.count / height
+
+ let yuvaPixelsPerAlphaRow = (Int(width) + 1) & (~1)
+ assert(yuvaPixelsPerAlphaRow % 2 == 0)
+
+ let yuvaLength = Int(width) * Int(height) * 2 + yuvaPixelsPerAlphaRow * Int(height) / 2
+ var yuvaFrameData = malloc(yuvaLength)!
+ defer {
+ free(yuvaFrameData)
+ }
+ memset(yuvaFrameData, 0, yuvaLength)
+
+ var compressedFrameData = Data(count: yuvaLength)
+ let compressedFrameDataLength = compressedFrameData.count
+
+ let scratchData = malloc(compression_encode_scratch_buffer_size(COMPRESSION_LZFSE))!
+ defer {
+ free(scratchData)
+ }
+
+ var rgbData = rgbData
+ rgbData.withUnsafeMutableBytes { (buffer: UnsafeMutableRawBufferPointer) -> Void in
+ if let baseAddress = buffer.baseAddress {
+ encodeRGBAToYUVA(yuvaFrameData.assumingMemoryBound(to: UInt8.self), baseAddress.assumingMemoryBound(to: UInt8.self), Int32(width), Int32(height), Int32(bytesPerRow))
+ }
+ }
+
+ var maybeResultSize: Int?
+
+ compressedFrameData.withUnsafeMutableBytes { (bytes: UnsafeMutablePointer) -> Void in
+ let length = compression_encode_buffer(bytes, compressedFrameDataLength, yuvaFrameData.assumingMemoryBound(to: UInt8.self), yuvaLength, scratchData, COMPRESSION_LZFSE)
+ maybeResultSize = length
+ }
+
+ guard let resultSize = maybeResultSize else {
+ return nil
+ }
+ compressedFrameData.count = resultSize
+ return compressedFrameData
+}
+
+private final class AnimatedStickerDirectFrameSourceCache {
+ private let queue: Queue
+ private let storeQueue: Queue
+ private let file: ManagedFileImpl
+ private let frameCount: Int
+ private let width: Int
+ private let height: Int
+
+ private var isStoringFrames = Set()
+
+ private var scratchBuffer: Data
+ private var decodeBuffer: Data
+
+ init?(queue: Queue, pathPrefix: String, width: Int, height: Int, frameCount: Int) {
+ self.queue = queue
+ self.storeQueue = sharedStoreQueue
+
+ self.frameCount = frameCount
+ self.width = width
+ self.height = height
+
+ let path = "\(pathPrefix)_\(width):\(height).stickerframecache"
+ var file = ManagedFileImpl(queue: queue, path: path, mode: .readwrite)
+ if let file = file {
+ self.file = file
+ } else {
+ let _ = try? FileManager.default.removeItem(atPath: path)
+ file = ManagedFileImpl(queue: queue, path: path, mode: .readwrite)
+ if let file = file {
+ self.file = file
+ } else {
+ return nil
+ }
+ }
+
+ self.scratchBuffer = Data(count: compression_decode_scratch_buffer_size(COMPRESSION_LZFSE))
+
+ let yuvaPixelsPerAlphaRow = (Int(width) + 1) & (~1)
+ let yuvaLength = Int(width) * Int(height) * 2 + yuvaPixelsPerAlphaRow * Int(height) / 2
+ self.decodeBuffer = Data(count: yuvaLength)
+
+ self.initializeFrameTable()
+ }
+
+ private func initializeFrameTable() {
+ if let size = self.file.getSize(), size >= self.frameCount * 4 * 2 {
+ } else {
+ self.file.truncate(count: 0)
+ for _ in 0 ..< self.frameCount {
+ var zero: Int32 = 0
+ let _ = self.file.write(&zero, count: 4)
+ let _ = self.file.write(&zero, count: 4)
+ }
+ }
+ }
+
+ private func readFrameRange(index: Int) -> Range? {
+ if index < 0 || index >= self.frameCount {
+ return nil
+ }
+
+ self.file.seek(position: Int64(index * 4 * 2))
+ var offset: Int32 = 0
+ var length: Int32 = 0
+ if self.file.read(&offset, 4) != 4 {
+ return nil
+ }
+ if self.file.read(&length, 4) != 4 {
+ return nil
+ }
+ if length == 0 {
+ return nil
+ }
+ if length < 0 || offset < 0 {
+ return nil
+ }
+ return (Int(offset) ..< Int(offset + length))
+ }
+
+ func storeUncompressedRgbFrame(index: Int, rgbData: Data) {
+ if index < 0 || index >= self.frameCount {
+ return
+ }
+ if self.isStoringFrames.contains(index) {
+ return
+ }
+ self.isStoringFrames.insert(index)
+
+ let width = self.width
+ let height = self.height
+
+ let queue = self.queue
+ self.storeQueue.async { [weak self] in
+ let compressedData = compressFrame(width: width, height: height, rgbData: rgbData)
+
+ queue.async {
+ guard let strongSelf = self else {
+ return
+ }
+ guard let currentSize = strongSelf.file.getSize() else {
+ return
+ }
+ guard let compressedData = compressedData else {
+ return
+ }
+
+ strongSelf.file.seek(position: Int64(index * 4 * 2))
+ var offset = Int32(currentSize)
+ var length = Int32(compressedData.count)
+ let _ = strongSelf.file.write(&offset, count: 4)
+ let _ = strongSelf.file.write(&length, count: 4)
+ strongSelf.file.seek(position: Int64(currentSize))
+ compressedData.withUnsafeBytes { (buffer: UnsafeRawBufferPointer) -> Void in
+ if let baseAddress = buffer.baseAddress {
+ let _ = strongSelf.file.write(baseAddress, count: Int(length))
+ }
+ }
+ }
+ }
+ }
+
+ func readUncompressedYuvFrame(index: Int) -> Data? {
+ if index < 0 || index >= self.frameCount {
+ return nil
+ }
+ guard let range = self.readFrameRange(index: index) else {
+ return nil
+ }
+ self.file.seek(position: Int64(range.lowerBound))
+ let length = range.upperBound - range.lowerBound
+ let compressedData = self.file.readData(count: length)
+ if compressedData.count != length {
+ return nil
+ }
+
+ var frameData: Data?
+
+ let decodeBufferLength = self.decodeBuffer.count
+
+ compressedData.withUnsafeBytes { (bytes: UnsafePointer) -> Void in
+ self.scratchBuffer.withUnsafeMutableBytes { (scratchBytes: UnsafeMutablePointer) -> Void in
+ self.decodeBuffer.withUnsafeMutableBytes { (decodeBytes: UnsafeMutablePointer) -> Void in
+ let resultLength = compression_decode_buffer(decodeBytes, decodeBufferLength, bytes, length, UnsafeMutableRawPointer(scratchBytes), COMPRESSION_LZFSE)
+
+ frameData = Data(bytes: decodeBytes, count: resultLength)
+ }
+ }
+ }
+
+ return frameData
+ }
+}
+
private final class AnimatedStickerDirectFrameSource: AnimatedStickerFrameSource {
private let queue: Queue
private let data: Data
private let width: Int
private let height: Int
+ private let cache: AnimatedStickerDirectFrameSourceCache?
private let bytesPerRow: Int
let frameCount: Int
let frameRate: Int
- private var currentFrame: Int
+ fileprivate var currentFrame: Int
private let animation: LottieInstance
- init?(queue: Queue, data: Data, width: Int, height: Int) {
+ var frameIndex: Int {
+ return self.currentFrame % self.frameCount
+ }
+
+ init?(queue: Queue, data: Data, width: Int, height: Int, cachePathPrefix: String?) {
self.queue = queue
self.data = data
self.width = width
@@ -286,23 +606,39 @@ private final class AnimatedStickerDirectFrameSource: AnimatedStickerFrameSource
return nil
}
self.animation = animation
- self.frameCount = Int(animation.frameCount)
+ let frameCount = Int(animation.frameCount)
+ self.frameCount = frameCount
self.frameRate = Int(animation.frameRate)
+
+ self.cache = cachePathPrefix.flatMap { cachePathPrefix in
+ AnimatedStickerDirectFrameSourceCache(queue: queue, pathPrefix: cachePathPrefix, width: width, height: height, frameCount: frameCount)
+ }
}
deinit {
assert(self.queue.isCurrent())
}
- func takeFrame() -> AnimatedStickerFrame? {
+ func takeFrame(draw: Bool) -> AnimatedStickerFrame? {
let frameIndex = self.currentFrame % self.frameCount
self.currentFrame += 1
- var frameData = Data(count: self.bytesPerRow * self.height)
- frameData.withUnsafeMutableBytes { (bytes: UnsafeMutablePointer) -> Void in
- memset(bytes, 0, self.bytesPerRow * self.height)
- self.animation.renderFrame(with: Int32(frameIndex), into: bytes, width: Int32(self.width), height: Int32(self.height), bytesPerRow: Int32(self.bytesPerRow))
+ if draw {
+ if let cache = self.cache, let yuvData = cache.readUncompressedYuvFrame(index: frameIndex) {
+ return AnimatedStickerFrame(data: yuvData, type: .yuva, width: self.width, height: self.height, bytesPerRow: 0, index: frameIndex, isLastFrame: frameIndex == self.frameCount - 1)
+ } else {
+ var frameData = Data(count: self.bytesPerRow * self.height)
+ frameData.withUnsafeMutableBytes { (bytes: UnsafeMutablePointer) -> Void in
+ memset(bytes, 0, self.bytesPerRow * self.height)
+ self.animation.renderFrame(with: Int32(frameIndex), into: bytes, width: Int32(self.width), height: Int32(self.height), bytesPerRow: Int32(self.bytesPerRow))
+ }
+ if let cache = self.cache {
+ cache.storeUncompressedRgbFrame(index: frameIndex, rgbData: frameData)
+ }
+ return AnimatedStickerFrame(data: frameData, type: .argb, width: self.width, height: self.height, bytesPerRow: self.bytesPerRow, index: frameIndex, isLastFrame: frameIndex == self.frameCount - 1)
+ }
+ } else {
+ return nil
}
- return AnimatedStickerFrame(data: frameData, type: .argb, width: self.width, height: self.height, bytesPerRow: self.bytesPerRow, index: frameIndex, isLastFrame: frameIndex == self.frameCount - 1)
}
func skipToEnd() {
@@ -326,9 +662,9 @@ public final class AnimatedStickerFrameQueue {
assert(self.queue.isCurrent())
}
- public func take() -> AnimatedStickerFrame? {
+ public func take(draw: Bool) -> AnimatedStickerFrame? {
if self.frames.isEmpty {
- if let frame = self.source.takeFrame() {
+ if let frame = self.source.takeFrame(draw: draw) {
self.frames.append(frame)
}
}
@@ -342,7 +678,7 @@ public final class AnimatedStickerFrameQueue {
public func generateFramesIfNeeded() {
if self.frames.isEmpty {
- if let frame = self.source.takeFrame() {
+ if let frame = self.source.takeFrame(draw: true) {
self.frames.append(frame)
}
}
@@ -397,7 +733,7 @@ public final class AnimatedStickerNode: ASDisplayNode {
private let timer = Atomic(value: nil)
private let frameSource = Atomic?>(value: nil)
- private var directData: (Data, String, Int, Int)?
+ private var directData: (Data, String, Int, Int, String?)?
private var cachedData: (Data, Bool)?
private var renderer: (AnimationRenderer & ASDisplayNode)?
@@ -467,13 +803,13 @@ public final class AnimatedStickerNode: ASDisplayNode {
}
self.playbackMode = playbackMode
switch mode {
- case .direct:
+ case let .direct(cachePathPrefix):
let f: (String) -> Void = { [weak self] path in
guard let strongSelf = self else {
return
}
if let directData = try? Data(contentsOf: URL(fileURLWithPath: path), options: [.mappedRead]) {
- strongSelf.directData = (directData, path, width, height)
+ strongSelf.directData = (directData, path, width, height, cachePathPrefix)
}
if case let .still(position) = playbackMode {
strongSelf.seekTo(position)
@@ -556,7 +892,7 @@ public final class AnimatedStickerNode: ASDisplayNode {
if maybeFrameSource == nil {
let notifyUpdated: (() -> Void)? = nil
if let directData = directData {
- maybeFrameSource = AnimatedStickerDirectFrameSource(queue: queue, data: directData.0, width: directData.2, height: directData.3)
+ maybeFrameSource = AnimatedStickerDirectFrameSource(queue: queue, data: directData.0, width: directData.2, height: directData.3, cachePathPrefix: directData.4)
} else if let (cachedData, cachedDataComplete) = cachedData {
if #available(iOS 9.0, *) {
maybeFrameSource = AnimatedStickerCachedFrameSource(queue: queue, data: cachedData, complete: cachedDataComplete, notifyUpdated: {
@@ -583,7 +919,7 @@ public final class AnimatedStickerNode: ASDisplayNode {
let timer = SwiftSignalKit.Timer(timeout: 1.0 / Double(frameRate), repeat: !firstFrame, completion: {
let maybeFrame = frameQueue.syncWith { frameQueue in
- return frameQueue.take()
+ return frameQueue.take(draw: true)
}
if let maybeFrame = maybeFrame, let frame = maybeFrame {
Queue.mainQueue().async {
@@ -628,7 +964,7 @@ public final class AnimatedStickerNode: ASDisplayNode {
var maybeFrameSource: AnimatedStickerFrameSource?
let notifyUpdated: (() -> Void)? = nil
if let directData = directData {
- maybeFrameSource = AnimatedStickerDirectFrameSource(queue: queue, data: directData.0, width: directData.2, height: directData.3)
+ maybeFrameSource = AnimatedStickerDirectFrameSource(queue: queue, data: directData.0, width: directData.2, height: directData.3, cachePathPrefix: directData.4)
} else if let (cachedData, cachedDataComplete) = cachedData {
if #available(iOS 9.0, *) {
maybeFrameSource = AnimatedStickerCachedFrameSource(queue: queue, data: cachedData, complete: cachedDataComplete, notifyUpdated: {
@@ -654,7 +990,7 @@ public final class AnimatedStickerNode: ASDisplayNode {
let timer = SwiftSignalKit.Timer(timeout: 1.0 / Double(frameRate), repeat: !firstFrame, completion: {
let maybeFrame = frameQueue.syncWith { frameQueue in
- return frameQueue.take()
+ return frameQueue.take(draw: true)
}
if let maybeFrame = maybeFrame, let frame = maybeFrame {
Queue.mainQueue().async {
@@ -710,19 +1046,25 @@ public final class AnimatedStickerNode: ASDisplayNode {
let directData = self.directData
let cachedData = self.cachedData
let queue = self.queue
+ let frameSourceHolder = self.frameSource
let timerHolder = self.timer
self.queue.async { [weak self] in
- var maybeFrameSource: AnimatedStickerFrameSource?
- if let directData = directData {
- maybeFrameSource = AnimatedStickerDirectFrameSource(queue: queue, data: directData.0, width: directData.2, height: directData.3)
- if position == .end {
- maybeFrameSource?.skipToEnd()
- }
- } else if let (cachedData, cachedDataComplete) = cachedData {
- if #available(iOS 9.0, *) {
- maybeFrameSource = AnimatedStickerCachedFrameSource(queue: queue, data: cachedData, complete: cachedDataComplete, notifyUpdated: {})
+ var maybeFrameSource: AnimatedStickerFrameSource? = frameSourceHolder.with { $0 }?.syncWith { $0 }?.value
+ if case .timestamp = position {
+ } else {
+ var maybeFrameSource: AnimatedStickerFrameSource?
+ if let directData = directData {
+ maybeFrameSource = AnimatedStickerDirectFrameSource(queue: queue, data: directData.0, width: directData.2, height: directData.3, cachePathPrefix: directData.4)
+ if case .end = position {
+ maybeFrameSource?.skipToEnd()
+ }
+ } else if let (cachedData, cachedDataComplete) = cachedData {
+ if #available(iOS 9.0, *) {
+ maybeFrameSource = AnimatedStickerCachedFrameSource(queue: queue, data: cachedData, complete: cachedDataComplete, notifyUpdated: {})
+ }
}
}
+
guard let frameSource = maybeFrameSource else {
return
}
@@ -732,9 +1074,31 @@ public final class AnimatedStickerNode: ASDisplayNode {
timerHolder.swap(nil)?.invalidate()
let duration: Double = frameSource.frameRate > 0 ? Double(frameSource.frameCount) / Double(frameSource.frameRate) : 0
-
- let maybeFrame = frameQueue.syncWith { frameQueue in
- return frameQueue.take()
+
+ var maybeFrame: AnimatedStickerFrame??
+ if case let .timestamp(timestamp) = position {
+ var stickerTimestamp = timestamp
+ while stickerTimestamp > duration {
+ stickerTimestamp -= duration
+ }
+ let targetFrame = Int(stickerTimestamp / duration * Double(frameSource.frameCount))
+ if targetFrame == frameSource.frameIndex {
+ return
+ }
+
+ var delta = targetFrame - frameSource.frameIndex
+ if delta < 0 {
+ delta = frameSource.frameCount + delta
+ }
+ for i in 0 ..< delta {
+ maybeFrame = frameQueue.syncWith { frameQueue in
+ return frameQueue.take(draw: i == delta - 1)
+ }
+ }
+ } else {
+ maybeFrame = frameQueue.syncWith { frameQueue in
+ return frameQueue.take(draw: true)
+ }
}
if let maybeFrame = maybeFrame, let frame = maybeFrame {
Queue.mainQueue().async {
diff --git a/submodules/AnimatedStickerNode/Sources/SoftwareAnimationRenderer.swift b/submodules/AnimatedStickerNode/Sources/SoftwareAnimationRenderer.swift
index 252cd89ae3..234698994d 100644
--- a/submodules/AnimatedStickerNode/Sources/SoftwareAnimationRenderer.swift
+++ b/submodules/AnimatedStickerNode/Sources/SoftwareAnimationRenderer.swift
@@ -8,8 +8,13 @@ import YuvConversion
final class SoftwareAnimationRenderer: ASDisplayNode, AnimationRenderer {
func render(queue: Queue, width: Int, height: Int, bytesPerRow: Int, data: Data, type: AnimationRendererFrameType, completion: @escaping () -> Void) {
queue.async { [weak self] in
- let calculatedBytesPerRow = (4 * Int(width) + 15) & (~15)
- assert(bytesPerRow == calculatedBytesPerRow)
+ switch type {
+ case .argb:
+ let calculatedBytesPerRow = (4 * Int(width) + 15) & (~15)
+ assert(bytesPerRow == calculatedBytesPerRow)
+ case .yuva:
+ break
+ }
let image = generateImagePixel(CGSize(width: CGFloat(width), height: CGFloat(height)), scale: 1.0, pixelGenerator: { _, pixelData, bytesPerRow in
switch type {
diff --git a/submodules/AuthTransferUI/Sources/AuthTransferConfirmationScreen.swift b/submodules/AuthTransferUI/Sources/AuthTransferConfirmationScreen.swift
index 4f966b5450..177eac4805 100644
--- a/submodules/AuthTransferUI/Sources/AuthTransferConfirmationScreen.swift
+++ b/submodules/AuthTransferUI/Sources/AuthTransferConfirmationScreen.swift
@@ -112,7 +112,7 @@ public final class AuthDataTransferSplashScreen: ViewController {
return
}
- DeviceAccess.authorizeAccess(to: .camera, presentationData: strongSelf.presentationData, present: { c, a in
+ DeviceAccess.authorizeAccess(to: .camera(.video), presentationData: strongSelf.presentationData, present: { c, a in
guard let strongSelf = self else {
return
}
diff --git a/submodules/CallListUI/Sources/CallListCallItem.swift b/submodules/CallListUI/Sources/CallListCallItem.swift
index a5ef89767c..3217d1d572 100644
--- a/submodules/CallListUI/Sources/CallListCallItem.swift
+++ b/submodules/CallListUI/Sources/CallListCallItem.swift
@@ -368,6 +368,7 @@ class CallListCallItemNode: ItemListRevealOptionsItemNode {
var hasMissed = false
var hasIncoming = false
var hasOutgoing = false
+ var isVideo = false
var hadDuration = false
var callDuration: Int32?
@@ -375,7 +376,8 @@ class CallListCallItemNode: ItemListRevealOptionsItemNode {
for message in item.messages {
inner: for media in message.media {
if let action = media as? TelegramMediaAction {
- if case let .phoneCall(_, discardReason, duration, _) = action.action {
+ if case let .phoneCall(_, discardReason, duration, video) = action.action {
+ isVideo = video
if message.flags.contains(.Incoming) {
hasIncoming = true
@@ -459,9 +461,12 @@ class CallListCallItemNode: ItemListRevealOptionsItemNode {
let nodeLayout = ListViewItemNodeLayout(contentSize: CGSize(width: params.width, height: titleLayout.size.height + titleSpacing + statusLayout.size.height + verticalInset * 2.0), insets: UIEdgeInsets(top: firstWithHeader ? 29.0 : 0.0, left: 0.0, bottom: 0.0, right: 0.0))
- let outgoingIcon = PresentationResourcesCallList.outgoingIcon(item.presentationData.theme)
+ let outgoingVoiceIcon = PresentationResourcesCallList.outgoingIcon(item.presentationData.theme)
+ let outgoingVideoIcon = PresentationResourcesCallList.outgoingVideoIcon(item.presentationData.theme)
let infoIcon = PresentationResourcesCallList.infoButton(item.presentationData.theme)
+ let outgoingIcon = isVideo ? outgoingVideoIcon : outgoingVoiceIcon
+
let contentSize = nodeLayout.contentSize
return (nodeLayout, { [weak self] synchronousLoads in
@@ -582,7 +587,7 @@ class CallListCallItemNode: ItemListRevealOptionsItemNode {
if strongSelf.typeIconNode.image !== outgoingIcon {
strongSelf.typeIconNode.image = outgoingIcon
}
- transition.updateFrameAdditive(node: strongSelf.typeIconNode, frame: CGRect(origin: CGPoint(x: revealOffset + leftInset - 81.0, y: floor((nodeLayout.contentSize.height - outgoingIcon.size.height) / 2.0)), size: outgoingIcon.size))
+ transition.updateFrameAdditive(node: strongSelf.typeIconNode, frame: CGRect(origin: CGPoint(x: revealOffset + leftInset - 79.0, y: floor((nodeLayout.contentSize.height - outgoingIcon.size.height) / 2.0)), size: outgoingIcon.size))
}
strongSelf.typeIconNode.isHidden = !hasOutgoing
diff --git a/submodules/CallListUI/Sources/CallListController.swift b/submodules/CallListUI/Sources/CallListController.swift
index 87da74d805..ee2abb052d 100644
--- a/submodules/CallListUI/Sources/CallListController.swift
+++ b/submodules/CallListUI/Sources/CallListController.swift
@@ -11,7 +11,6 @@ import ItemListUI
import PresentationDataUtils
import AccountContext
import AlertUI
-import PresentationDataUtils
import AppBundle
import LocalizedPeerData
@@ -201,18 +200,18 @@ public final class CallListController: ViewController {
}
@objc func callPressed() {
- self.beginCallImpl(isVideo: false)
+ self.beginCallImpl()
}
- private func beginCallImpl(isVideo: Bool) {
- let controller = self.context.sharedContext.makeContactSelectionController(ContactSelectionControllerParams(context: self.context, title: { $0.Calls_NewCall }))
+ private func beginCallImpl() {
+ let controller = self.context.sharedContext.makeContactSelectionController(ContactSelectionControllerParams(context: self.context, title: { $0.Calls_NewCall }, displayCallIcons: true))
controller.navigationPresentation = .modal
self.createActionDisposable.set((controller.result
|> take(1)
|> deliverOnMainQueue).start(next: { [weak controller, weak self] peer in
controller?.dismissSearch()
- if let strongSelf = self, let contactPeer = peer, case let .peer(peer, _, _) = contactPeer {
- strongSelf.call(peer.id, isVideo: isVideo, began: {
+ if let strongSelf = self, let (contactPeer, action) = peer, case let .peer(peer, _, _) = contactPeer {
+ strongSelf.call(peer.id, isVideo: action == .videoCall, began: {
if let strongSelf = self {
let _ = (strongSelf.context.sharedContext.hasOngoingCall.get()
|> filter { $0 }
@@ -277,7 +276,7 @@ public final class CallListController: ViewController {
return
}
- let callResult = strongSelf.context.sharedContext.callManager?.requestCall(account: strongSelf.context.account, peerId: peerId, isVideo: isVideo, endCurrentIfAny: false)
+ let callResult = strongSelf.context.sharedContext.callManager?.requestCall(context: strongSelf.context, peerId: peerId, isVideo: isVideo, endCurrentIfAny: false)
if let callResult = callResult {
if case let .alreadyInProgress(currentPeerId) = callResult {
if currentPeerId == peerId {
@@ -286,17 +285,22 @@ public final class CallListController: ViewController {
} else {
let presentationData = strongSelf.presentationData
let _ = (strongSelf.context.account.postbox.transaction { transaction -> (Peer?, Peer?) in
- return (transaction.getPeer(peerId), transaction.getPeer(currentPeerId))
- } |> deliverOnMainQueue).start(next: { [weak self] peer, current in
- if let strongSelf = self, let peer = peer, let current = current {
+ return (transaction.getPeer(peerId), currentPeerId.flatMap(transaction.getPeer))
+ } |> deliverOnMainQueue).start(next: { [weak self] peer, current in
+ if let strongSelf = self, let peer = peer {
+ if let current = current {
strongSelf.present(textAlertController(context: strongSelf.context, title: presentationData.strings.Call_CallInProgressTitle, text: presentationData.strings.Call_CallInProgressMessage(current.compactDisplayTitle, peer.compactDisplayTitle).0, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: {
if let strongSelf = self {
- let _ = strongSelf.context.sharedContext.callManager?.requestCall(account: strongSelf.context.account, peerId: peerId, isVideo: isVideo, endCurrentIfAny: true)
+ let _ = strongSelf.context.sharedContext.callManager?.requestCall(context: strongSelf.context, peerId: peerId, isVideo: isVideo, endCurrentIfAny: true)
began?()
}
})]), in: .window(.root))
+ } else {
+ strongSelf.present(textAlertController(context: strongSelf.context, title: presentationData.strings.Call_CallInProgressTitle, text: presentationData.strings.Call_ExternalCallInProgressMessage, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: {
+ })]), in: .window(.root))
}
- })
+ }
+ })
}
} else {
began?()
diff --git a/submodules/ChatListUI/Sources/ChatContextMenus.swift b/submodules/ChatListUI/Sources/ChatContextMenus.swift
index 0aaee436a0..7743bc1ad4 100644
--- a/submodules/ChatListUI/Sources/ChatContextMenus.swift
+++ b/submodules/ChatListUI/Sources/ChatContextMenus.swift
@@ -46,7 +46,7 @@ enum ChatContextMenuSource {
case search(ChatListSearchContextActionSource)
}
-func chatContextMenuItems(context: AccountContext, peerId: PeerId, promoInfo: ChatListNodeEntryPromoInfo?, source: ChatContextMenuSource, chatListController: ChatListControllerImpl?) -> Signal<[ContextMenuItem], NoError> {
+func chatContextMenuItems(context: AccountContext, peerId: PeerId, promoInfo: ChatListNodeEntryPromoInfo?, source: ChatContextMenuSource, chatListController: ChatListControllerImpl?, joined: Bool) -> Signal<[ContextMenuItem], NoError> {
let presentationData = context.sharedContext.currentPresentationData.with({ $0 })
let strings = presentationData.strings
return context.account.postbox.transaction { [weak chatListController] transaction -> [ContextMenuItem] in
@@ -233,7 +233,7 @@ func chatContextMenuItems(context: AccountContext, peerId: PeerId, promoInfo: Ch
updatedItems.append(.action(ContextMenuActionItem(text: strings.ChatList_Context_Back, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Back"), color: theme.contextMenu.primaryColor)
}, action: { c, _ in
- c.setItems(chatContextMenuItems(context: context, peerId: peerId, promoInfo: promoInfo, source: source, chatListController: chatListController))
+ c.setItems(chatContextMenuItems(context: context, peerId: peerId, promoInfo: promoInfo, source: source, chatListController: chatListController, joined: joined))
})))
return updatedItems
@@ -379,7 +379,7 @@ func chatContextMenuItems(context: AccountContext, peerId: PeerId, promoInfo: Ch
if case .chatList = source, groupAndIndex != nil {
items.append(.action(ContextMenuActionItem(text: strings.ChatList_Context_Delete, textColor: .destructive, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor) }, action: { _, f in
if let chatListController = chatListController {
- chatListController.deletePeerChat(peerId: peerId)
+ chatListController.deletePeerChat(peerId: peerId, joined: joined)
}
f(.default)
})))
diff --git a/submodules/ChatListUI/Sources/ChatListController.swift b/submodules/ChatListUI/Sources/ChatListController.swift
index a949a737a9..44e06dca1e 100644
--- a/submodules/ChatListUI/Sources/ChatListController.swift
+++ b/submodules/ChatListUI/Sources/ChatListController.swift
@@ -575,11 +575,11 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
strongSelf.hidePsa(peerId)
}
- self.chatListDisplayNode.containerNode.deletePeerChat = { [weak self] peerId in
+ self.chatListDisplayNode.containerNode.deletePeerChat = { [weak self] peerId, joined in
guard let strongSelf = self else {
return
}
- strongSelf.deletePeerChat(peerId: peerId)
+ strongSelf.deletePeerChat(peerId: peerId, joined: joined)
}
self.chatListDisplayNode.containerNode.peerSelected = { [weak self] peer, animated, promoInfo in
@@ -801,6 +801,16 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
gesture?.cancel()
return
}
+
+ var joined = false
+ if case let .peer(messages, _, _, _, _, _, _, _, _, _, _, _) = item.content, let message = messages.first {
+ for media in message.media {
+ if let action = media as? TelegramMediaAction, action.action == .peerJoined {
+ joined = true
+ }
+ }
+ }
+
switch item.content {
case let .groupReference(groupReference):
let chatListController = ChatListControllerImpl(context: strongSelf.context, groupId: groupReference.groupId, controlsHistoryPreload: false, hideNetworkActivityStatus: true, previewing: true, enableDebugActions: false)
@@ -810,7 +820,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
case let .peer(_, peer, _, _, _, _, _, _, promoInfo, _, _, _):
let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(peer.peerId), subject: nil, botStart: nil, mode: .standard(previewing: true))
chatController.canReadHistory.set(false)
- let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node, navigationController: strongSelf.navigationController as? NavigationController)), items: chatContextMenuItems(context: strongSelf.context, peerId: peer.peerId, promoInfo: promoInfo, source: .chatList(filter: strongSelf.chatListDisplayNode.containerNode.currentItemNode.chatListFilter), chatListController: strongSelf), reactionItems: [], gesture: gesture)
+ let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node, navigationController: strongSelf.navigationController as? NavigationController)), items: chatContextMenuItems(context: strongSelf.context, peerId: peer.peerId, promoInfo: promoInfo, source: .chatList(filter: strongSelf.chatListDisplayNode.containerNode.currentItemNode.chatListFilter), chatListController: strongSelf, joined: joined), reactionItems: [], gesture: gesture)
strongSelf.presentInGlobalOverlay(contextController)
}
}
@@ -823,7 +833,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(peer.id), subject: nil, botStart: nil, mode: .standard(previewing: true))
chatController.canReadHistory.set(false)
- let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node, navigationController: strongSelf.navigationController as? NavigationController)), items: chatContextMenuItems(context: strongSelf.context, peerId: peer.id, promoInfo: nil, source: .search(source), chatListController: strongSelf), reactionItems: [], gesture: gesture)
+ let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node, navigationController: strongSelf.navigationController as? NavigationController)), items: chatContextMenuItems(context: strongSelf.context, peerId: peer.id, promoInfo: nil, source: .search(source), chatListController: strongSelf, joined: false), reactionItems: [], gesture: gesture)
strongSelf.presentInGlobalOverlay(contextController)
}
@@ -2069,7 +2079,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
let _ = hideAccountPromoInfoChat(account: self.context.account, peerId: id).start()
}
- func deletePeerChat(peerId: PeerId) {
+ func deletePeerChat(peerId: PeerId, joined: Bool) {
let _ = (self.context.account.postbox.transaction { transaction -> RenderedPeer? in
guard let peer = transaction.getPeer(peerId) else {
return nil
@@ -2099,7 +2109,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
}
if let user = chatPeer as? TelegramUser, user.botInfo == nil, canRemoveGlobally {
- strongSelf.maybeAskForPeerChatRemoval(peer: peer, completion: { _ in }, removed: {})
+ strongSelf.maybeAskForPeerChatRemoval(peer: peer, joined: joined, completion: { _ in }, removed: {})
} else {
let actionSheet = ActionSheetController(presentationData: strongSelf.presentationData)
var items: [ActionSheetItem] = []
@@ -2189,16 +2199,24 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
if canRemoveGlobally {
let actionSheet = ActionSheetController(presentationData: strongSelf.presentationData)
var items: [ActionSheetItem] = []
-
+
items.append(DeleteChatPeerActionSheetItem(context: strongSelf.context, peer: mainPeer, chatPeer: chatPeer, action: .clearHistory, strings: strongSelf.presentationData.strings, nameDisplayOrder: strongSelf.presentationData.nameDisplayOrder))
- items.append(ActionSheetButtonItem(title: strongSelf.presentationData.strings.ChatList_DeleteForEveryone(mainPeer.compactDisplayTitle).0, color: .destructive, action: { [weak actionSheet] in
- beginClear(.forEveryone)
- actionSheet?.dismissAnimated()
- }))
- items.append(ActionSheetButtonItem(title: strongSelf.presentationData.strings.ChatList_DeleteForCurrentUser, color: .destructive, action: { [weak actionSheet] in
- beginClear(.forLocalPeer)
- actionSheet?.dismissAnimated()
- }))
+
+ if joined || mainPeer.isDeleted {
+ items.append(ActionSheetButtonItem(title: strongSelf.presentationData.strings.Common_Delete, color: .destructive, action: { [weak actionSheet] in
+ beginClear(.forEveryone)
+ actionSheet?.dismissAnimated()
+ }))
+ } else {
+ items.append(ActionSheetButtonItem(title: strongSelf.presentationData.strings.ChatList_DeleteForEveryone(mainPeer.compactDisplayTitle).0, color: .destructive, action: { [weak actionSheet] in
+ beginClear(.forEveryone)
+ actionSheet?.dismissAnimated()
+ }))
+ items.append(ActionSheetButtonItem(title: strongSelf.presentationData.strings.ChatList_DeleteForCurrentUser, color: .destructive, action: { [weak actionSheet] in
+ beginClear(.forLocalPeer)
+ actionSheet?.dismissAnimated()
+ }))
+ }
actionSheet.setItemGroups([
ActionSheetItemGroup(items: items),
@@ -2258,7 +2276,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
})
}
- public func maybeAskForPeerChatRemoval(peer: RenderedPeer, deleteGloballyIfPossible: Bool = false, completion: @escaping (Bool) -> Void, removed: @escaping () -> Void) {
+ public func maybeAskForPeerChatRemoval(peer: RenderedPeer, joined: Bool = false, deleteGloballyIfPossible: Bool = false, completion: @escaping (Bool) -> Void, removed: @escaping () -> Void) {
guard let chatPeer = peer.peers[peer.peerId], let mainPeer = peer.chatMainPeer else {
completion(false)
return
@@ -2279,31 +2297,41 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
var items: [ActionSheetItem] = []
items.append(DeleteChatPeerActionSheetItem(context: self.context, peer: mainPeer, chatPeer: chatPeer, action: .delete, strings: self.presentationData.strings, nameDisplayOrder: self.presentationData.nameDisplayOrder))
- items.append(ActionSheetButtonItem(title: self.presentationData.strings.ChatList_DeleteForEveryone(mainPeer.compactDisplayTitle).0, color: .destructive, action: { [weak self, weak actionSheet] in
- actionSheet?.dismissAnimated()
- guard let strongSelf = self else {
- return
- }
- strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: strongSelf.presentationData.strings.ChatList_DeleteForEveryoneConfirmationTitle, text: strongSelf.presentationData.strings.ChatList_DeleteForEveryoneConfirmationText, actions: [
- TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {
- completion(false)
- }),
- TextAlertAction(type: .destructiveAction, title: strongSelf.presentationData.strings.ChatList_DeleteForEveryoneConfirmationAction, action: {
- self?.schedulePeerChatRemoval(peer: peer, type: .forEveryone, deleteGloballyIfPossible: deleteGloballyIfPossible, completion: {
- removed()
- })
- completion(true)
- })
- ], parseMarkdown: true), in: .window(.root))
- }))
- items.append(ActionSheetButtonItem(title: self.presentationData.strings.ChatList_DeleteForCurrentUser, color: .destructive, action: { [weak self, weak actionSheet] in
- actionSheet?.dismissAnimated()
- self?.schedulePeerChatRemoval(peer: peer, type: .forLocalPeer, deleteGloballyIfPossible: deleteGloballyIfPossible, completion: {
- removed()
- })
- completion(true)
- }))
+ if joined || mainPeer.isDeleted {
+ items.append(ActionSheetButtonItem(title: self.presentationData.strings.Common_Delete, color: .destructive, action: { [weak self, weak actionSheet] in
+ actionSheet?.dismissAnimated()
+ self?.schedulePeerChatRemoval(peer: peer, type: .forEveryone, deleteGloballyIfPossible: deleteGloballyIfPossible, completion: {
+ removed()
+ })
+ completion(true)
+ }))
+ } else {
+ items.append(ActionSheetButtonItem(title: self.presentationData.strings.ChatList_DeleteForEveryone(mainPeer.compactDisplayTitle).0, color: .destructive, action: { [weak self, weak actionSheet] in
+ actionSheet?.dismissAnimated()
+ guard let strongSelf = self else {
+ return
+ }
+ strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: strongSelf.presentationData.strings.ChatList_DeleteForEveryoneConfirmationTitle, text: strongSelf.presentationData.strings.ChatList_DeleteForEveryoneConfirmationText, actions: [
+ TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {
+ completion(false)
+ }),
+ TextAlertAction(type: .destructiveAction, title: strongSelf.presentationData.strings.ChatList_DeleteForEveryoneConfirmationAction, action: {
+ self?.schedulePeerChatRemoval(peer: peer, type: .forEveryone, deleteGloballyIfPossible: deleteGloballyIfPossible, completion: {
+ removed()
+ })
+ completion(true)
+ })
+ ], parseMarkdown: true), in: .window(.root))
+ }))
+ items.append(ActionSheetButtonItem(title: self.presentationData.strings.ChatList_DeleteForCurrentUser, color: .destructive, action: { [weak self, weak actionSheet] in
+ actionSheet?.dismissAnimated()
+ self?.schedulePeerChatRemoval(peer: peer, type: .forLocalPeer, deleteGloballyIfPossible: deleteGloballyIfPossible, completion: {
+ removed()
+ })
+ completion(true)
+ }))
+ }
actionSheet.setItemGroups([
ActionSheetItemGroup(items: items),
ActionSheetItemGroup(items: [
diff --git a/submodules/ChatListUI/Sources/ChatListControllerNode.swift b/submodules/ChatListUI/Sources/ChatListControllerNode.swift
index 6f4eb396bf..d336726443 100644
--- a/submodules/ChatListUI/Sources/ChatListControllerNode.swift
+++ b/submodules/ChatListUI/Sources/ChatListControllerNode.swift
@@ -195,7 +195,7 @@ private final class ChatListShimmerNode: ASDisplayNode {
let timestamp1: Int32 = 100000
let peers = SimpleDictionary()
let interaction = ChatListNodeInteraction(activateSearch: {}, peerSelected: { _, _ in }, disabledPeerSelected: { _ in }, togglePeerSelected: { _ in }, additionalCategorySelected: { _ in
- }, messageSelected: { _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, deletePeer: { _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, hidePsa: { _ in }, activateChatPreview: { _, _, gesture in
+ }, messageSelected: { _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, deletePeer: { _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, hidePsa: { _ in }, activateChatPreview: { _, _, gesture in
gesture?.cancel()
}, present: { _ in })
@@ -478,8 +478,8 @@ final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDelegate {
itemNode.listNode.hidePsa = { [weak self] peerId in
self?.hidePsa?(peerId)
}
- itemNode.listNode.deletePeerChat = { [weak self] peerId in
- self?.deletePeerChat?(peerId)
+ itemNode.listNode.deletePeerChat = { [weak self] peerId, joined in
+ self?.deletePeerChat?(peerId, joined)
}
itemNode.listNode.peerSelected = { [weak self] peerId, a, b in
self?.peerSelected?(peerId, a, b)
@@ -527,7 +527,7 @@ final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDelegate {
var present: ((ViewController) -> Void)?
var toggleArchivedFolderHiddenByDefault: (() -> Void)?
var hidePsa: ((PeerId) -> Void)?
- var deletePeerChat: ((PeerId) -> Void)?
+ var deletePeerChat: ((PeerId, Bool) -> Void)?
var peerSelected: ((Peer, Bool, ChatListNodeEntryPromoInfo?) -> Void)?
var groupSelected: ((PeerGroupId) -> Void)?
var updatePeerGrouping: ((PeerId, Bool) -> Void)?
diff --git a/submodules/ChatListUI/Sources/ChatListEmptyNode.swift b/submodules/ChatListUI/Sources/ChatListEmptyNode.swift
index 46a9794511..8cb64b4e0f 100644
--- a/submodules/ChatListUI/Sources/ChatListEmptyNode.swift
+++ b/submodules/ChatListUI/Sources/ChatListEmptyNode.swift
@@ -68,7 +68,7 @@ final class ChatListEmptyNode: ASDisplayNode {
animationName = "ChatListEmpty"
}
if let path = getAppBundle().path(forResource: animationName, ofType: "tgs") {
- self.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(path: path), width: 248, height: 248, playbackMode: .once, mode: .direct)
+ self.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(path: path), width: 248, height: 248, playbackMode: .once, mode: .direct(cachePathPrefix: nil))
self.animationSize = CGSize(width: 124.0, height: 124.0)
self.animationNode.visibility = true
}
diff --git a/submodules/ChatListUI/Sources/ChatListFilterSettingsHeaderItem.swift b/submodules/ChatListUI/Sources/ChatListFilterSettingsHeaderItem.swift
index b80cceed46..d322c174fa 100644
--- a/submodules/ChatListUI/Sources/ChatListFilterSettingsHeaderItem.swift
+++ b/submodules/ChatListUI/Sources/ChatListFilterSettingsHeaderItem.swift
@@ -126,7 +126,7 @@ class ChatListFilterSettingsHeaderItemNode: ListViewItemNode {
animationName = "ChatListNewFolder"
}
if let path = getAppBundle().path(forResource: animationName, ofType: "tgs") {
- strongSelf.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(path: path), width: 192, height: 192, playbackMode: .once, mode: .direct)
+ strongSelf.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(path: path), width: 192, height: 192, playbackMode: .once, mode: .direct(cachePathPrefix: nil))
strongSelf.animationNode.visibility = true
}
}
diff --git a/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift b/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift
index 33af650006..96189597db 100644
--- a/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift
+++ b/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift
@@ -1028,7 +1028,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
}
}, setItemPinned: { _, _ in
}, setPeerMuted: { _, _ in
- }, deletePeer: { _ in
+ }, deletePeer: { _, _ in
}, updatePeerGrouping: { _, _ in
}, togglePeerMarkedUnread: { _, _ in
}, toggleArchivedFolderHiddenByDefault: {
diff --git a/submodules/ChatListUI/Sources/Node/ChatListItem.swift b/submodules/ChatListUI/Sources/Node/ChatListItem.swift
index 7edf5afe3b..f8f9c90175 100644
--- a/submodules/ChatListUI/Sources/Node/ChatListItem.swift
+++ b/submodules/ChatListUI/Sources/Node/ChatListItem.swift
@@ -1306,6 +1306,10 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
let (size, apply) = inputActivitiesLayout(CGSize(width: rawContentWidth - badgeSize, height: 40.0), item.presentationData, item.presentationData.theme.chatList.messageTextColor, item.index.messageIndex.id.peerId, inputActivities)
inputActivitiesSize = size
inputActivitiesApply = apply
+ } else {
+ let (size, apply) = inputActivitiesLayout(CGSize(width: rawContentWidth - badgeSize, height: 40.0), item.presentationData, item.presentationData.theme.chatList.messageTextColor, item.index.messageIndex.id.peerId, [])
+ inputActivitiesSize = size
+ inputActivitiesApply = apply
}
var online = false
@@ -1946,7 +1950,15 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
item.interaction.setPeerMuted(item.index.messageIndex.id.peerId, false)
close = false
case RevealOptionKey.delete.rawValue:
- item.interaction.deletePeer(item.index.messageIndex.id.peerId)
+ var joined = false
+ if case let .peer(messages, _, _, _, _, _, _, _, _, _, _, _) = item.content, let message = messages.first {
+ for media in message.media {
+ if let action = media as? TelegramMediaAction, action.action == .peerJoined {
+ joined = true
+ }
+ }
+ }
+ item.interaction.deletePeer(item.index.messageIndex.id.peerId, joined)
case RevealOptionKey.archive.rawValue:
item.interaction.updatePeerGrouping(item.index.messageIndex.id.peerId, true)
close = false
diff --git a/submodules/ChatListUI/Sources/Node/ChatListItemStrings.swift b/submodules/ChatListUI/Sources/Node/ChatListItemStrings.swift
index 6fdca48f9d..8cb7770fd2 100644
--- a/submodules/ChatListUI/Sources/Node/ChatListItemStrings.swift
+++ b/submodules/ChatListUI/Sources/Node/ChatListItemStrings.swift
@@ -201,9 +201,25 @@ public func chatListItemStrings(strings: PresentationStrings, nameDisplayOrder:
if let discardReason = discardReason {
switch discardReason {
case .busy, .disconnect:
- messageText = strings.Notification_CallCanceled
+ if isVideo {
+ messageText = strings.Notification_VideoCallCanceled
+ } else {
+ messageText = strings.Notification_CallCanceled
+ }
case .missed:
- messageText = incoming ? strings.Notification_CallMissed : strings.Notification_CallCanceled
+ if incoming {
+ if isVideo {
+ messageText = strings.Notification_VideoCallMissed
+ } else {
+ messageText = strings.Notification_CallMissed
+ }
+ } else {
+ if isVideo {
+ messageText = strings.Notification_VideoCallCanceled
+ } else {
+ messageText = strings.Notification_CallCanceled
+ }
+ }
case .hangup:
break
}
diff --git a/submodules/ChatListUI/Sources/Node/ChatListNode.swift b/submodules/ChatListUI/Sources/Node/ChatListNode.swift
index 326c5bcb5a..0506f6deb4 100644
--- a/submodules/ChatListUI/Sources/Node/ChatListNode.swift
+++ b/submodules/ChatListUI/Sources/Node/ChatListNode.swift
@@ -59,7 +59,7 @@ public final class ChatListNodeInteraction {
let setPeerIdWithRevealedOptions: (PeerId?, PeerId?) -> Void
let setItemPinned: (PinnedItemId, Bool) -> Void
let setPeerMuted: (PeerId, Bool) -> Void
- let deletePeer: (PeerId) -> Void
+ let deletePeer: (PeerId, Bool) -> Void
let updatePeerGrouping: (PeerId, Bool) -> Void
let togglePeerMarkedUnread: (PeerId, Bool) -> Void
let toggleArchivedFolderHiddenByDefault: () -> Void
@@ -70,7 +70,7 @@ public final class ChatListNodeInteraction {
public var searchTextHighightState: String?
var highlightedChatLocation: ChatListHighlightedLocation?
- public init(activateSearch: @escaping () -> Void, peerSelected: @escaping (Peer, ChatListNodeEntryPromoInfo?) -> Void, disabledPeerSelected: @escaping (Peer) -> Void, togglePeerSelected: @escaping (PeerId) -> Void, additionalCategorySelected: @escaping (Int) -> Void, messageSelected: @escaping (Peer, Message, ChatListNodeEntryPromoInfo?) -> Void, groupSelected: @escaping (PeerGroupId) -> Void, addContact: @escaping (String) -> Void, setPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void, setItemPinned: @escaping (PinnedItemId, Bool) -> Void, setPeerMuted: @escaping (PeerId, Bool) -> Void, deletePeer: @escaping (PeerId) -> Void, updatePeerGrouping: @escaping (PeerId, Bool) -> Void, togglePeerMarkedUnread: @escaping (PeerId, Bool) -> Void, toggleArchivedFolderHiddenByDefault: @escaping () -> Void, hidePsa: @escaping (PeerId) -> Void, activateChatPreview: @escaping (ChatListItem, ASDisplayNode, ContextGesture?) -> Void, present: @escaping (ViewController) -> Void) {
+ public init(activateSearch: @escaping () -> Void, peerSelected: @escaping (Peer, ChatListNodeEntryPromoInfo?) -> Void, disabledPeerSelected: @escaping (Peer) -> Void, togglePeerSelected: @escaping (PeerId) -> Void, additionalCategorySelected: @escaping (Int) -> Void, messageSelected: @escaping (Peer, Message, ChatListNodeEntryPromoInfo?) -> Void, groupSelected: @escaping (PeerGroupId) -> Void, addContact: @escaping (String) -> Void, setPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void, setItemPinned: @escaping (PinnedItemId, Bool) -> Void, setPeerMuted: @escaping (PeerId, Bool) -> Void, deletePeer: @escaping (PeerId, Bool) -> Void, updatePeerGrouping: @escaping (PeerId, Bool) -> Void, togglePeerMarkedUnread: @escaping (PeerId, Bool) -> Void, toggleArchivedFolderHiddenByDefault: @escaping () -> Void, hidePsa: @escaping (PeerId) -> Void, activateChatPreview: @escaping (ChatListItem, ASDisplayNode, ContextGesture?) -> Void, present: @escaping (ViewController) -> Void) {
self.activateSearch = activateSearch
self.peerSelected = peerSelected
self.disabledPeerSelected = disabledPeerSelected
@@ -430,7 +430,7 @@ public final class ChatListNode: ListView {
public var groupSelected: ((PeerGroupId) -> Void)?
public var addContact: ((String) -> Void)?
public var activateSearch: (() -> Void)?
- public var deletePeerChat: ((PeerId) -> Void)?
+ public var deletePeerChat: ((PeerId, Bool) -> Void)?
public var updatePeerGrouping: ((PeerId, Bool) -> Void)?
public var presentAlert: ((String) -> Void)?
public var present: ((ViewController) -> Void)?
@@ -628,8 +628,8 @@ public final class ChatListNode: ListView {
}
self?.setCurrentRemovingPeerId(nil)
})
- }, deletePeer: { [weak self] peerId in
- self?.deletePeerChat?(peerId)
+ }, deletePeer: { [weak self] peerId, joined in
+ self?.deletePeerChat?(peerId, joined)
}, updatePeerGrouping: { [weak self] peerId, group in
self?.updatePeerGrouping?(peerId, group)
}, togglePeerMarkedUnread: { [weak self, weak context] peerId, animated in
diff --git a/submodules/ContactListUI/Sources/ContactContextMenus.swift b/submodules/ContactListUI/Sources/ContactContextMenus.swift
index 279c60109b..ad8c469028 100644
--- a/submodules/ContactListUI/Sources/ContactContextMenus.swift
+++ b/submodules/ContactListUI/Sources/ContactContextMenus.swift
@@ -113,30 +113,47 @@ func contactContextMenuItems(context: AccountContext, peerId: PeerId, contactsCo
}
var canVideoCall = false
if canCall {
- if context.sharedContext.immediateExperimentalUISettings.videoCalls {
- canVideoCall = true
+ if let cachedUserData = transaction.getPeerCachedData(peerId: peerId) as? CachedUserData {
+ if cachedUserData.videoCallsAvailable {
+ canVideoCall = true
+ }
}
}
if canCall {
items.append(.action(ContextMenuActionItem(text: strings.ContactList_Context_Call, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Call"), color: theme.contextMenu.primaryColor) }, action: { _, f in
if let contactsController = contactsController {
- let callResult = context.sharedContext.callManager?.requestCall(account: context.account, peerId: peerId, isVideo: false, endCurrentIfAny: false)
+ let callResult = context.sharedContext.callManager?.requestCall(context: context, peerId: peerId, isVideo: false, endCurrentIfAny: false)
if let callResult = callResult, case let .alreadyInProgress(currentPeerId) = callResult {
if currentPeerId == peerId {
context.sharedContext.navigateToCurrentCall()
} else {
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
let _ = (context.account.postbox.transaction { transaction -> (Peer?, Peer?) in
+ return (transaction.getPeer(peerId), currentPeerId.flatMap(transaction.getPeer))
+ } |> deliverOnMainQueue).start(next: { [weak contactsController] peer, current in
+ if let contactsController = contactsController, let peer = peer {
+ if let current = current {
+ contactsController.present(textAlertController(context: context, title: presentationData.strings.Call_CallInProgressTitle, text: presentationData.strings.Call_CallInProgressMessage(current.compactDisplayTitle, peer.compactDisplayTitle).0, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: {
+ let _ = context.sharedContext.callManager?.requestCall(context: context, peerId: peerId, isVideo: false, endCurrentIfAny: true)
+ })]), in: .window(.root))
+ } else {
+ contactsController.present(textAlertController(context: context, title: presentationData.strings.Call_CallInProgressTitle, text: presentationData.strings.Call_ExternalCallInProgressMessage, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: {
+ })]), in: .window(.root))
+ }
+ }
+ })
+
+ /*let _ = (context.account.postbox.transaction { transaction -> (Peer?, Peer?) in
return (transaction.getPeer(peerId), transaction.getPeer(currentPeerId))
}
|> deliverOnMainQueue).start(next: { [weak contactsController] peer, current in
if let contactsController = contactsController, let peer = peer, let current = current {
contactsController.present(textAlertController(context: context, title: presentationData.strings.Call_CallInProgressTitle, text: presentationData.strings.Call_CallInProgressMessage(current.compactDisplayTitle, peer.compactDisplayTitle).0, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: {
- let _ = context.sharedContext.callManager?.requestCall(account: context.account, peerId: peerId, isVideo: false, endCurrentIfAny: true)
+ let _ = context.sharedContext.callManager?.requestCall(context: context, peerId: peerId, isVideo: false, endCurrentIfAny: true)
})]), in: .window(.root))
}
- })
+ })*/
}
}
}
@@ -144,25 +161,39 @@ func contactContextMenuItems(context: AccountContext, peerId: PeerId, contactsCo
})))
}
if canVideoCall {
- //TODO:localize
- items.append(.action(ContextMenuActionItem(text: "Video Call", icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Call"), color: theme.contextMenu.primaryColor) }, action: { _, f in
+ items.append(.action(ContextMenuActionItem(text: strings.ContactList_Context_VideoCall, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Call"), color: theme.contextMenu.primaryColor) }, action: { _, f in
if let contactsController = contactsController {
- let callResult = context.sharedContext.callManager?.requestCall(account: context.account, peerId: peerId, isVideo: true, endCurrentIfAny: false)
+ let callResult = context.sharedContext.callManager?.requestCall(context: context, peerId: peerId, isVideo: true, endCurrentIfAny: false)
if let callResult = callResult, case let .alreadyInProgress(currentPeerId) = callResult {
if currentPeerId == peerId {
context.sharedContext.navigateToCurrentCall()
} else {
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
let _ = (context.account.postbox.transaction { transaction -> (Peer?, Peer?) in
+ return (transaction.getPeer(peerId), currentPeerId.flatMap(transaction.getPeer))
+ } |> deliverOnMainQueue).start(next: { [weak contactsController] peer, current in
+ if let contactsController = contactsController, let peer = peer {
+ if let current = current {
+ contactsController.present(textAlertController(context: context, title: presentationData.strings.Call_CallInProgressTitle, text: presentationData.strings.Call_CallInProgressMessage(current.compactDisplayTitle, peer.compactDisplayTitle).0, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: {
+ let _ = context.sharedContext.callManager?.requestCall(context: context, peerId: peerId, isVideo: true, endCurrentIfAny: true)
+ })]), in: .window(.root))
+ } else {
+ contactsController.present(textAlertController(context: context, title: presentationData.strings.Call_CallInProgressTitle, text: presentationData.strings.Call_ExternalCallInProgressMessage, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: {
+ })]), in: .window(.root))
+ }
+ }
+ })
+
+ /*let _ = (context.account.postbox.transaction { transaction -> (Peer?, Peer?) in
return (transaction.getPeer(peerId), transaction.getPeer(currentPeerId))
}
|> deliverOnMainQueue).start(next: { [weak contactsController] peer, current in
if let contactsController = contactsController, let peer = peer, let current = current {
contactsController.present(textAlertController(context: context, title: presentationData.strings.Call_CallInProgressTitle, text: presentationData.strings.Call_CallInProgressMessage(current.compactDisplayTitle, peer.compactDisplayTitle).0, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: {
- let _ = context.sharedContext.callManager?.requestCall(account: context.account, peerId: peerId, isVideo: true, endCurrentIfAny: true)
+ let _ = context.sharedContext.callManager?.requestCall(context: context, peerId: peerId, isVideo: true, endCurrentIfAny: true)
})]), in: .window(.root))
}
- })
+ })*/
}
}
}
diff --git a/submodules/ContactListUI/Sources/ContactListNode.swift b/submodules/ContactListUI/Sources/ContactListNode.swift
index e68a12766a..fcbcb783e8 100644
--- a/submodules/ContactListUI/Sources/ContactListNode.swift
+++ b/submodules/ContactListUI/Sources/ContactListNode.swift
@@ -101,12 +101,12 @@ private final class ContactListNodeInteraction {
fileprivate let openSortMenu: () -> Void
fileprivate let authorize: () -> Void
fileprivate let suppressWarning: () -> Void
- fileprivate let openPeer: (ContactListPeer) -> Void
+ fileprivate let openPeer: (ContactListPeer, ContactListAction) -> Void
fileprivate let contextAction: ((Peer, ASDisplayNode, ContextGesture?) -> Void)?
let itemHighlighting = ContactItemHighlighting()
- init(activateSearch: @escaping () -> Void, openSortMenu: @escaping () -> Void, authorize: @escaping () -> Void, suppressWarning: @escaping () -> Void, openPeer: @escaping (ContactListPeer) -> Void, contextAction: ((Peer, ASDisplayNode, ContextGesture?) -> Void)?) {
+ init(activateSearch: @escaping () -> Void, openSortMenu: @escaping () -> Void, authorize: @escaping () -> Void, suppressWarning: @escaping () -> Void, openPeer: @escaping (ContactListPeer, ContactListAction) -> Void, contextAction: ((Peer, ASDisplayNode, ContextGesture?) -> Void)?) {
self.activateSearch = activateSearch
self.openSortMenu = openSortMenu
self.authorize = authorize
@@ -128,7 +128,7 @@ private enum ContactListNodeEntry: Comparable, Identifiable {
case permissionInfo(PresentationTheme, String, String, Bool)
case permissionEnable(PresentationTheme, String)
case option(Int, ContactListAdditionalOption, ListViewItemHeader?, PresentationTheme, PresentationStrings)
- case peer(Int, ContactListPeer, PeerPresence?, ListViewItemHeader?, ContactsPeerItemSelection, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, PresentationPersonNameOrder, PresentationPersonNameOrder, Bool)
+ case peer(Int, ContactListPeer, PeerPresence?, ListViewItemHeader?, ContactsPeerItemSelection, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, PresentationPersonNameOrder, PresentationPersonNameOrder, Bool, Bool)
var stableId: ContactListNodeEntryId {
switch self {
@@ -142,7 +142,7 @@ private enum ContactListNodeEntry: Comparable, Identifiable {
return .permission(action: true)
case let .option(index, _, _, _, _):
return .option(index: index)
- case let .peer(_, peer, _, _, _, _, _, _, _, _, _):
+ case let .peer(_, peer, _, _, _, _, _, _, _, _, _, _):
switch peer {
case let .peer(peer, _, _):
return .peerId(peer.id.toInt64())
@@ -176,7 +176,7 @@ private enum ContactListNodeEntry: Comparable, Identifiable {
})
case let .option(_, option, header, theme, _):
return ContactListActionItem(presentationData: ItemListPresentationData(presentationData), title: option.title, icon: option.icon, clearHighlightAutomatically: false, header: header, action: option.action)
- case let .peer(_, peer, presence, header, selection, theme, strings, dateTimeFormat, nameSortOrder, nameDisplayOrder, enabled):
+ case let .peer(_, peer, presence, header, selection, theme, strings, dateTimeFormat, nameSortOrder, nameDisplayOrder, displayCallIcons, enabled):
var status: ContactsPeerItemStatus
let itemPeer: ContactsPeerItemPeer
var isContextActionEnabled = false
@@ -230,8 +230,18 @@ private enum ContactListNodeEntry: Comparable, Identifiable {
}
}
}
- return ContactsPeerItem(presentationData: ItemListPresentationData(presentationData), sortOrder: nameSortOrder, displayOrder: nameDisplayOrder, context: context, peerMode: isSearch ? .generalSearch : .peer, peer: itemPeer, status: status, enabled: enabled, selection: selection, editing: ContactsPeerItemEditing(editable: false, editing: false, revealed: false), index: nil, header: header, action: { _ in
- interaction.openPeer(peer)
+
+ var additionalActions: [ContactsPeerItemAction] = []
+ if displayCallIcons {
+ additionalActions = [ContactsPeerItemAction(icon: .voiceCall, action: { _ in
+ interaction.openPeer(peer, .voiceCall)
+ }), ContactsPeerItemAction(icon: .videoCall, action: { _ in
+ interaction.openPeer(peer, .videoCall)
+ })]
+ }
+
+ return ContactsPeerItem(presentationData: ItemListPresentationData(presentationData), sortOrder: nameSortOrder, displayOrder: nameDisplayOrder, context: context, peerMode: isSearch ? .generalSearch : .peer, peer: itemPeer, status: status, enabled: enabled, selection: selection, editing: ContactsPeerItemEditing(editable: false, editing: false, revealed: false), additionalActions: additionalActions, index: nil, header: header, action: { _ in
+ interaction.openPeer(peer, .generic)
}, itemHighlighting: interaction.itemHighlighting, contextAction: itemContextAction)
}
}
@@ -268,9 +278,9 @@ private enum ContactListNodeEntry: Comparable, Identifiable {
} else {
return false
}
- case let .peer(lhsIndex, lhsPeer, lhsPresence, lhsHeader, lhsSelection, lhsTheme, lhsStrings, lhsTimeFormat, lhsSortOrder, lhsDisplayOrder, lhsEnabled):
+ case let .peer(lhsIndex, lhsPeer, lhsPresence, lhsHeader, lhsSelection, lhsTheme, lhsStrings, lhsTimeFormat, lhsSortOrder, lhsDisplayOrder, lhsDisplayCallIcons, lhsEnabled):
switch rhs {
- case let .peer(rhsIndex, rhsPeer, rhsPresence, rhsHeader, rhsSelection, rhsTheme, rhsStrings, rhsTimeFormat, rhsSortOrder, rhsDisplayOrder, rhsEnabled):
+ case let .peer(rhsIndex, rhsPeer, rhsPresence, rhsHeader, rhsSelection, rhsTheme, rhsStrings, rhsTimeFormat, rhsSortOrder, rhsDisplayOrder, rhsDisplayCallIcons, rhsEnabled):
if lhsIndex != rhsIndex {
return false
}
@@ -305,6 +315,9 @@ private enum ContactListNodeEntry: Comparable, Identifiable {
if lhsDisplayOrder != rhsDisplayOrder {
return false
}
+ if lhsDisplayCallIcons != rhsDisplayCallIcons {
+ return false
+ }
if lhsEnabled != rhsEnabled {
return false
}
@@ -349,11 +362,11 @@ private enum ContactListNodeEntry: Comparable, Identifiable {
case .peer:
return true
}
- case let .peer(lhsIndex, _, _, _, _, _, _, _, _, _, _):
+ case let .peer(lhsIndex, _, _, _, _, _, _, _, _, _, _, _):
switch rhs {
case .search, .sort, .permissionInfo, .permissionEnable, .option:
return false
- case let .peer(rhsIndex, _, _, _, _, _, _, _, _, _, _):
+ case let .peer(rhsIndex, _, _, _, _, _, _, _, _, _, _, _):
return lhsIndex < rhsIndex
}
}
@@ -426,7 +439,7 @@ private extension PeerIndexNameRepresentation {
}
}
-private func contactListNodeEntries(accountPeer: Peer?, peers: [ContactListPeer], presences: [PeerId: PeerPresence], presentation: ContactListPresentation, selectionState: ContactListNodeGroupSelectionState?, theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, sortOrder: PresentationPersonNameOrder, displayOrder: PresentationPersonNameOrder, disabledPeerIds:Set, authorizationStatus: AccessType, warningSuppressed: (Bool, Bool), displaySortOptions: Bool) -> [ContactListNodeEntry] {
+private func contactListNodeEntries(accountPeer: Peer?, peers: [ContactListPeer], presences: [PeerId: PeerPresence], presentation: ContactListPresentation, selectionState: ContactListNodeGroupSelectionState?, theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, sortOrder: PresentationPersonNameOrder, displayOrder: PresentationPersonNameOrder, disabledPeerIds:Set, authorizationStatus: AccessType, warningSuppressed: (Bool, Bool), displaySortOptions: Bool, displayCallIcons: Bool) -> [ContactListNodeEntry] {
var entries: [ContactListNodeEntry] = []
var commonHeader: ListViewItemHeader?
@@ -606,7 +619,7 @@ private func contactListNodeEntries(accountPeer: Peer?, peers: [ContactListPeer]
default:
enabled = true
}
- entries.append(.peer(i, orderedPeers[i], presence, header, selection, theme, strings, dateTimeFormat, sortOrder, displayOrder, enabled))
+ entries.append(.peer(i, orderedPeers[i], presence, header, selection, theme, strings, dateTimeFormat, sortOrder, displayOrder, displayCallIcons, enabled))
}
return entries
}
@@ -629,7 +642,7 @@ private func preparedContactListNodeTransition(context: AccountContext, presenta
case .search:
//indexSections.apend(CollectionIndexNode.searchIndex)
break
- case let .peer(_, _, _, header, _, _, _, _, _, _, _):
+ case let .peer(_, _, _, header, _, _, _, _, _, _, _, _):
if let header = header as? ContactListNameIndexHeader {
if !existingSections.contains(header.letter) {
existingSections.insert(header.letter)
@@ -771,7 +784,7 @@ public final class ContactListNode: ASDisplayNode {
public var activateSearch: (() -> Void)?
public var openSortMenu: (() -> Void)?
- public var openPeer: ((ContactListPeer) -> Void)?
+ public var openPeer: ((ContactListPeer, ContactListAction) -> Void)?
public var openPrivacyPolicy: (() -> Void)?
public var suppressPermissionWarning: (() -> Void)?
private let contextAction: ((Peer, ASDisplayNode, ContextGesture?) -> Void)?
@@ -786,7 +799,7 @@ public final class ContactListNode: ASDisplayNode {
private var authorizationNode: PermissionContentNode
private let displayPermissionPlaceholder: Bool
- public init(context: AccountContext, presentation: Signal, filters: [ContactListFilter] = [.excludeSelf], selectionState: ContactListNodeGroupSelectionState? = nil, displayPermissionPlaceholder: Bool = true, displaySortOptions: Bool = false, contextAction: ((Peer, ASDisplayNode, ContextGesture?) -> Void)? = nil, isSearch: Bool = false) {
+ public init(context: AccountContext, presentation: Signal, filters: [ContactListFilter] = [.excludeSelf], selectionState: ContactListNodeGroupSelectionState? = nil, displayPermissionPlaceholder: Bool = true, displaySortOptions: Bool = false, displayCallIcons: Bool = false, contextAction: ((Peer, ASDisplayNode, ContextGesture?) -> Void)? = nil, isSearch: Bool = false) {
self.context = context
self.filters = filters
self.displayPermissionPlaceholder = displayPermissionPlaceholder
@@ -856,8 +869,8 @@ public final class ContactListNode: ASDisplayNode {
authorizeImpl?()
}, suppressWarning: { [weak self] in
self?.suppressPermissionWarning?()
- }, openPeer: { [weak self] peer in
- self?.openPeer?(peer)
+ }, openPeer: { [weak self] peer, action in
+ self?.openPeer?(peer, action)
}, contextAction: contextAction)
self.indexNode.indexSelected = { [weak self] section in
@@ -885,7 +898,7 @@ public final class ContactListNode: ASDisplayNode {
strongSelf.listNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.PreferSynchronousDrawing, .PreferSynchronousResourceLoading], scrollToItem: ListViewScrollToItem(index: index, position: .top(-navigationBarSearchContentHeight), animated: false, curve: .Default(duration: nil), directionHint: .Down), additionalScrollDistance: 0.0, updateSizeAndInsets: updateSizeAndInsets, stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in })
break loop
}
- case let .peer(_, _, _, header, _, _, _, _, _, _, _):
+ case let .peer(_, _, _, header, _, _, _, _, _, _, _, _):
if let header = header as? ContactListNameIndexHeader {
if let scalar = UnicodeScalar(header.letter) {
let title = "\(Character(scalar))"
@@ -1113,7 +1126,7 @@ public final class ContactListNode: ASDisplayNode {
peers.append(.deviceContact(stableId, contact.0))
}
- let entries = contactListNodeEntries(accountPeer: nil, peers: peers, presences: localPeersAndStatuses.1, presentation: presentation, selectionState: selectionState, theme: presentationData.theme, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, sortOrder: presentationData.nameSortOrder, displayOrder: presentationData.nameDisplayOrder, disabledPeerIds: disabledPeerIds, authorizationStatus: .allowed, warningSuppressed: (true, true), displaySortOptions: false)
+ let entries = contactListNodeEntries(accountPeer: nil, peers: peers, presences: localPeersAndStatuses.1, presentation: presentation, selectionState: selectionState, theme: presentationData.theme, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, sortOrder: presentationData.nameSortOrder, displayOrder: presentationData.nameDisplayOrder, disabledPeerIds: disabledPeerIds, authorizationStatus: .allowed, warningSuppressed: (true, true), displaySortOptions: false, displayCallIcons: displayCallIcons)
let previous = previousEntries.swap(entries)
return .single(preparedContactListNodeTransition(context: context, presentationData: presentationData, from: previous ?? [], to: entries, interaction: interaction, firstTime: previous == nil, isEmpty: false, generateIndexSections: generateSections, animation: .none, isSearch: isSearch))
}
@@ -1191,7 +1204,7 @@ public final class ContactListNode: ASDisplayNode {
if (authorizationStatus == .notDetermined || authorizationStatus == .denied) && peers.isEmpty {
isEmpty = true
}
- let entries = contactListNodeEntries(accountPeer: view.accountPeer, peers: peers, presences: view.peerPresences, presentation: presentation, selectionState: selectionState, theme: presentationData.theme, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, sortOrder: presentationData.nameSortOrder, displayOrder: presentationData.nameDisplayOrder, disabledPeerIds: disabledPeerIds, authorizationStatus: authorizationStatus, warningSuppressed: warningSuppressed, displaySortOptions: displaySortOptions)
+ let entries = contactListNodeEntries(accountPeer: view.accountPeer, peers: peers, presences: view.peerPresences, presentation: presentation, selectionState: selectionState, theme: presentationData.theme, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, sortOrder: presentationData.nameSortOrder, displayOrder: presentationData.nameDisplayOrder, disabledPeerIds: disabledPeerIds, authorizationStatus: authorizationStatus, warningSuppressed: warningSuppressed, displaySortOptions: displaySortOptions, displayCallIcons: displayCallIcons)
let previous = previousEntries.swap(entries)
var hadPermissionInfo = false
diff --git a/submodules/ContactListUI/Sources/ContactsController.swift b/submodules/ContactListUI/Sources/ContactsController.swift
index 9fb455bd83..1f94a7f57f 100644
--- a/submodules/ContactListUI/Sources/ContactsController.swift
+++ b/submodules/ContactListUI/Sources/ContactsController.swift
@@ -275,7 +275,7 @@ public class ContactsController: ViewController {
self?.activateSearch()
}
- self.contactsNode.contactListNode.openPeer = { peer in
+ self.contactsNode.contactListNode.openPeer = { peer, _ in
openPeer(peer, false)
}
diff --git a/submodules/ContactsPeerItem/Sources/ContactsPeerItem.swift b/submodules/ContactsPeerItem/Sources/ContactsPeerItem.swift
index 59f22e7059..8623c54638 100644
--- a/submodules/ContactsPeerItem/Sources/ContactsPeerItem.swift
+++ b/submodules/ContactsPeerItem/Sources/ContactsPeerItem.swift
@@ -75,6 +75,18 @@ public struct ContactsPeerItemBadge {
public enum ContactsPeerItemActionIcon {
case none
case add
+ case voiceCall
+ case videoCall
+}
+
+public struct ContactsPeerItemAction {
+ public let icon: ContactsPeerItemActionIcon
+ public let action: ((ContactsPeerItemPeer) -> Void)?
+
+ public init(icon: ContactsPeerItemActionIcon, action: @escaping (ContactsPeerItemPeer) -> Void) {
+ self.icon = icon
+ self.action = action
+ }
}
public enum ContactsPeerItemPeer: Equatable {
@@ -120,6 +132,7 @@ public class ContactsPeerItem: ItemListItem, ListViewItemWithHeader {
let selection: ContactsPeerItemSelection
let editing: ContactsPeerItemEditing
let options: [ItemListPeerItemRevealOption]
+ let additionalActions: [ContactsPeerItemAction]
let actionIcon: ContactsPeerItemActionIcon
let action: (ContactsPeerItemPeer) -> Void
let disabledAction: ((ContactsPeerItemPeer) -> Void)?
@@ -134,7 +147,7 @@ public class ContactsPeerItem: ItemListItem, ListViewItemWithHeader {
public let header: ListViewItemHeader?
- public init(presentationData: ItemListPresentationData, style: ItemListStyle = .plain, sectionId: ItemListSectionId = 0, sortOrder: PresentationPersonNameOrder, displayOrder: PresentationPersonNameOrder, context: AccountContext, peerMode: ContactsPeerItemPeerMode, peer: ContactsPeerItemPeer, status: ContactsPeerItemStatus, badge: ContactsPeerItemBadge? = nil, enabled: Bool, selection: ContactsPeerItemSelection, editing: ContactsPeerItemEditing, options: [ItemListPeerItemRevealOption] = [], actionIcon: ContactsPeerItemActionIcon = .none, index: PeerNameIndex?, header: ListViewItemHeader?, action: @escaping (ContactsPeerItemPeer) -> Void, disabledAction: ((ContactsPeerItemPeer) -> Void)? = nil, setPeerIdWithRevealedOptions: ((PeerId?, PeerId?) -> Void)? = nil, deletePeer: ((PeerId) -> Void)? = nil, itemHighlighting: ContactItemHighlighting? = nil, contextAction: ((ASDisplayNode, ContextGesture?) -> Void)? = nil) {
+ public init(presentationData: ItemListPresentationData, style: ItemListStyle = .plain, sectionId: ItemListSectionId = 0, sortOrder: PresentationPersonNameOrder, displayOrder: PresentationPersonNameOrder, context: AccountContext, peerMode: ContactsPeerItemPeerMode, peer: ContactsPeerItemPeer, status: ContactsPeerItemStatus, badge: ContactsPeerItemBadge? = nil, enabled: Bool, selection: ContactsPeerItemSelection, editing: ContactsPeerItemEditing, options: [ItemListPeerItemRevealOption] = [], additionalActions: [ContactsPeerItemAction] = [], actionIcon: ContactsPeerItemActionIcon = .none, index: PeerNameIndex?, header: ListViewItemHeader?, action: @escaping (ContactsPeerItemPeer) -> Void, disabledAction: ((ContactsPeerItemPeer) -> Void)? = nil, setPeerIdWithRevealedOptions: ((PeerId?, PeerId?) -> Void)? = nil, deletePeer: ((PeerId) -> Void)? = nil, itemHighlighting: ContactItemHighlighting? = nil, contextAction: ((ASDisplayNode, ContextGesture?) -> Void)? = nil) {
self.presentationData = presentationData
self.style = style
self.sectionId = sectionId
@@ -149,6 +162,7 @@ public class ContactsPeerItem: ItemListItem, ListViewItemWithHeader {
self.selection = selection
self.editing = editing
self.options = options
+ self.additionalActions = additionalActions
self.actionIcon = actionIcon
self.action = action
self.disabledAction = disabledAction
@@ -303,7 +317,7 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode {
private var badgeBackgroundNode: ASImageNode?
private var badgeTextNode: TextNode?
private var selectionNode: CheckNode?
- private var actionIconNode: ASImageNode?
+ private var actionButtonNodes: [HighlightableButtonNode]?
private var isHighlighted: Bool = false
@@ -325,7 +339,7 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode {
public var item: ContactsPeerItem? {
return self.layoutParams?.0
}
-
+
required public init() {
self.backgroundNode = ASDisplayNode()
self.backgroundNode.isLayerBacked = true
@@ -489,12 +503,32 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode {
break
}
- let actionIconImage: UIImage?
- switch item.actionIcon {
- case .none:
- actionIconImage = nil
- case .add:
- actionIconImage = PresentationResourcesItemList.plusIconImage(item.presentationData.theme)
+ var actionButtons: [ActionButton]?
+ struct ActionButton {
+ let image: UIImage?
+ let action: ((ContactsPeerItemPeer) -> Void)?
+
+ init(theme: PresentationTheme, icon: ContactsPeerItemActionIcon, action: ((ContactsPeerItemPeer) -> Void)?) {
+ let image: UIImage?
+ switch icon {
+ case .none:
+ image = nil
+ case .add:
+ image = PresentationResourcesItemList.plusIconImage(theme)
+ case .voiceCall:
+ image = PresentationResourcesItemList.voiceCallIcon(theme)
+ case .videoCall:
+ image = PresentationResourcesItemList.videoCallIcon(theme)
+ }
+ self.image = image
+ self.action = action
+ }
+ }
+
+ if item.actionIcon != .none {
+ actionButtons = [ActionButton(theme: item.presentationData.theme, icon: item.actionIcon, action: nil)]
+ } else if !item.additionalActions.isEmpty {
+ actionButtons = item.additionalActions.map { ActionButton(theme: item.presentationData.theme, icon: $0.icon, action: $0.action) }
}
var titleAttributedString: NSAttributedString?
@@ -620,8 +654,13 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode {
if let verificationIconImage = verificationIconImage {
additionalTitleInset += 3.0 + verificationIconImage.size.width
}
- if let actionIconImage = actionIconImage {
- additionalTitleInset += 3.0 + actionIconImage.size.width
+ if let actionButtons = actionButtons {
+ additionalTitleInset += 3.0
+ for actionButton in actionButtons {
+ if let image = actionButton.image {
+ additionalTitleInset += image.size.width + 12.0
+ }
+ }
}
additionalTitleInset += badgeSize
@@ -784,23 +823,37 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode {
verificationIconNode.removeFromSupernode()
}
- if let actionIconImage = actionIconImage {
- if strongSelf.actionIconNode == nil {
- let actionIconNode = ASImageNode()
- actionIconNode.isLayerBacked = true
- actionIconNode.displayWithoutProcessing = true
- actionIconNode.displaysAsynchronously = false
- strongSelf.actionIconNode = actionIconNode
- strongSelf.containerNode.addSubnode(actionIconNode)
+ if let actionButtons = actionButtons {
+ if strongSelf.actionButtonNodes == nil {
+ var actionButtonNodes: [HighlightableButtonNode] = []
+ for action in actionButtons {
+ let actionButtonNode = HighlightableButtonNode()
+ actionButtonNode.isUserInteractionEnabled = action.action != nil
+ actionButtonNode.addTarget(strongSelf, action: #selector(strongSelf.actionButtonPressed(_:)), forControlEvents: .touchUpInside)
+ strongSelf.containerNode.addSubnode(actionButtonNode)
+
+ actionButtonNodes.append(actionButtonNode)
+ }
+ strongSelf.actionButtonNodes = actionButtonNodes
}
- if let actionIconNode = strongSelf.actionIconNode {
- actionIconNode.image = actionIconImage
-
- transition.updateFrame(node: actionIconNode, frame: CGRect(origin: CGPoint(x: revealOffset + params.width - params.rightInset - 12.0 - actionIconImage.size.width, y: floor((nodeLayout.contentSize.height - actionIconImage.size.height) / 2.0)), size: actionIconImage.size))
+ if let actionButtonNodes = strongSelf.actionButtonNodes {
+ var offset: CGFloat = 0.0
+ if actionButtons.count > 1 {
+ offset += 12.0
+ }
+ for (actionButtonNode, actionButton) in zip(actionButtonNodes, actionButtons).reversed() {
+ guard let actionButtonImage = actionButton.image else {
+ continue
+ }
+ actionButtonNode.setImage(actionButton.image, for: .normal)
+ transition.updateFrame(node: actionButtonNode, frame: CGRect(origin: CGPoint(x: revealOffset + params.width - params.rightInset - 12.0 - actionButtonImage.size.width - offset, y: floor((nodeLayout.contentSize.height - actionButtonImage.size.height) / 2.0)), size: actionButtonImage.size))
+
+ offset += actionButtonImage.size.width + 12.0
+ }
}
- } else if let actionIconNode = strongSelf.actionIconNode {
- strongSelf.actionIconNode = nil
- actionIconNode.removeFromSupernode()
+ } else if let actionButtonNodes = strongSelf.actionButtonNodes {
+ strongSelf.actionButtonNodes = nil
+ actionButtonNodes.forEach { $0.removeFromSupernode() }
}
let badgeBackgroundWidth: CGFloat
@@ -893,6 +946,13 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode {
}
}
+ @objc private func actionButtonPressed(_ sender: HighlightableButtonNode) {
+ guard let actionButtonNodes = self.actionButtonNodes, let index = actionButtonNodes.firstIndex(of: sender), let item = self.item, index < item.additionalActions.count else {
+ return
+ }
+ item.additionalActions[index].action?(item.peer)
+ }
+
override public func updateRevealOffset(offset: CGFloat, transition: ContainedViewLayoutTransition) {
super.updateRevealOffset(offset: offset, transition: transition)
diff --git a/submodules/DeviceAccess/Sources/DeviceAccess.swift b/submodules/DeviceAccess/Sources/DeviceAccess.swift
index b0d0bb4bf9..7c16e2247d 100644
--- a/submodules/DeviceAccess/Sources/DeviceAccess.swift
+++ b/submodules/DeviceAccess/Sources/DeviceAccess.swift
@@ -15,6 +15,12 @@ import TelegramPresentationData
import LegacyComponents
import AccountContext
+public enum DeviceAccessCameraSubject {
+ case video
+ case videoCall
+}
+
+
public enum DeviceAccessMicrophoneSubject {
case audio
case video
@@ -34,7 +40,7 @@ public enum DeviceAccessLocationSubject {
}
public enum DeviceAccessSubject {
- case camera
+ case camera(DeviceAccessCameraSubject)
case microphone(DeviceAccessMicrophoneSubject)
case mediaLibrary(DeviceAccessMediaLibrarySubject)
case location(DeviceAccessLocationSubject)
@@ -246,14 +252,20 @@ public final class DeviceAccess {
public static func authorizeAccess(to subject: DeviceAccessSubject, registerForNotifications: ((@escaping (Bool) -> Void) -> Void)? = nil, requestSiriAuthorization: ((@escaping (Bool) -> Void) -> Void)? = nil, locationManager: LocationManager? = nil, presentationData: PresentationData? = nil, present: @escaping (ViewController, Any?) -> Void = { _, _ in }, openSettings: @escaping () -> Void = { }, displayNotificationFromBackground: @escaping (String) -> Void = { _ in }, _ completion: @escaping (Bool) -> Void = { _ in }) {
switch subject {
- case .camera:
+ case let .camera(cameraSubject):
let status = PGCamera.cameraAuthorizationStatus()
if status == PGCameraAuthorizationStatusNotDetermined {
AVCaptureDevice.requestAccess(for: AVMediaType.video) { response in
Queue.mainQueue().async {
completion(response)
if !response, let presentationData = presentationData {
- let text = presentationData.strings.AccessDenied_Camera
+ let text: String
+ switch cameraSubject {
+ case .video:
+ text = presentationData.strings.AccessDenied_Camera
+ case .videoCall:
+ text = presentationData.strings.AccessDenied_VideoCallCamera
+ }
present(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: presentationData.strings.AccessDenied_Title, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_NotNow, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.AccessDenied_Settings, action: {
openSettings()
})]), nil)
diff --git a/submodules/Display/Source/CAAnimationUtils.swift b/submodules/Display/Source/CAAnimationUtils.swift
index 1d37fa419b..8244276576 100644
--- a/submodules/Display/Source/CAAnimationUtils.swift
+++ b/submodules/Display/Source/CAAnimationUtils.swift
@@ -220,8 +220,8 @@ public extension CALayer {
self.animate(from: NSNumber(value: Float(from)), to: NSNumber(value: Float(to)), keyPath: "opacity", timingFunction: timingFunction, duration: duration, delay: delay, mediaTimingFunction: mediaTimingFunction, removeOnCompletion: removeOnCompletion, completion: completion)
}
- func animateScale(from: CGFloat, to: CGFloat, duration: Double, delay: Double = 0.0, timingFunction: String = CAMediaTimingFunctionName.easeInEaseOut.rawValue, mediaTimingFunction: CAMediaTimingFunction? = nil, removeOnCompletion: Bool = true, completion: ((Bool) -> Void)? = nil) {
- self.animate(from: NSNumber(value: Float(from)), to: NSNumber(value: Float(to)), keyPath: "transform.scale", timingFunction: timingFunction, duration: duration, delay: delay, mediaTimingFunction: mediaTimingFunction, removeOnCompletion: removeOnCompletion, completion: completion)
+ func animateScale(from: CGFloat, to: CGFloat, duration: Double, delay: Double = 0.0, timingFunction: String = CAMediaTimingFunctionName.easeInEaseOut.rawValue, mediaTimingFunction: CAMediaTimingFunction? = nil, removeOnCompletion: Bool = true, additive: Bool = false, completion: ((Bool) -> Void)? = nil) {
+ self.animate(from: NSNumber(value: Float(from)), to: NSNumber(value: Float(to)), keyPath: "transform.scale", timingFunction: timingFunction, duration: duration, delay: delay, mediaTimingFunction: mediaTimingFunction, removeOnCompletion: removeOnCompletion, additive: additive, completion: completion)
}
func animateScaleY(from: CGFloat, to: CGFloat, duration: Double, delay: Double = 0.0, timingFunction: String = CAMediaTimingFunctionName.easeInEaseOut.rawValue, mediaTimingFunction: CAMediaTimingFunction? = nil, removeOnCompletion: Bool = true, completion: ((Bool) -> Void)? = nil) {
diff --git a/submodules/Display/Source/ContainedViewLayoutTransition.swift b/submodules/Display/Source/ContainedViewLayoutTransition.swift
index 3134ef5c54..053f6ad60b 100644
--- a/submodules/Display/Source/ContainedViewLayoutTransition.swift
+++ b/submodules/Display/Source/ContainedViewLayoutTransition.swift
@@ -576,7 +576,7 @@ public extension ContainedViewLayoutTransition {
}
}
- func animateTransformScale(node: ASDisplayNode, from fromScale: CGFloat, completion: ((Bool) -> Void)? = nil) {
+ func animateTransformScale(node: ASDisplayNode, from fromScale: CGFloat, additive: Bool = false, completion: ((Bool) -> Void)? = nil) {
let t = node.layer.transform
let currentScale = sqrt((t.m11 * t.m11) + (t.m12 * t.m12) + (t.m13 * t.m13))
if currentScale.isEqual(to: fromScale) {
@@ -592,7 +592,16 @@ public extension ContainedViewLayoutTransition {
completion(true)
}
case let .animated(duration, curve):
- node.layer.animateScale(from: fromScale, to: currentScale, duration: duration, timingFunction: curve.timingFunction, mediaTimingFunction: curve.mediaTimingFunction, completion: { result in
+ let calculatedFrom: CGFloat
+ let calculatedTo: CGFloat
+ if additive {
+ calculatedFrom = fromScale - currentScale
+ calculatedTo = 0.0
+ } else {
+ calculatedFrom = fromScale
+ calculatedTo = currentScale
+ }
+ node.layer.animateScale(from: calculatedFrom, to: calculatedTo, duration: duration, timingFunction: curve.timingFunction, mediaTimingFunction: curve.mediaTimingFunction, additive: additive, completion: { result in
if let completion = completion {
completion(result)
}
@@ -953,6 +962,73 @@ public extension ContainedViewLayoutTransition {
})
}
}
+
+ func updateTransformRotation(view: UIView, angle: CGFloat, beginWithCurrentState: Bool = false, completion: ((Bool) -> Void)? = nil) {
+ let t = view.layer.transform
+ let currentAngle = atan2(t.m12, t.m11)
+ if currentAngle.isEqual(to: angle) {
+ if let completion = completion {
+ completion(true)
+ }
+ return
+ }
+
+ switch self {
+ case .immediate:
+ view.layer.transform = CATransform3DMakeRotation(angle, 0.0, 0.0, 1.0)
+ if let completion = completion {
+ completion(true)
+ }
+ case let .animated(duration, curve):
+ let previousAngle: CGFloat
+ if beginWithCurrentState, let presentation = view.layer.presentation() {
+ let t = presentation.transform
+ previousAngle = atan2(t.m12, t.m11)
+ } else {
+ previousAngle = currentAngle
+ }
+ view.layer.transform = CATransform3DMakeRotation(angle, 0.0, 0.0, 1.0)
+ view.layer.animateRotation(from: previousAngle, to: angle, duration: duration, timingFunction: curve.timingFunction, mediaTimingFunction: curve.mediaTimingFunction, completion: { result in
+ if let completion = completion {
+ completion(result)
+ }
+ })
+ }
+ }
+
+ func updateTransformRotationAndScale(view: UIView, angle: CGFloat, scale: CGPoint, beginWithCurrentState: Bool = false, completion: ((Bool) -> Void)? = nil) {
+ let t = view.layer.transform
+ let currentAngle = atan2(t.m12, t.m11)
+ let currentScale = CGPoint(x: t.m11, y: t.m12)
+ if currentAngle.isEqual(to: angle) && currentScale == scale {
+ if let completion = completion {
+ completion(true)
+ }
+ return
+ }
+
+ switch self {
+ case .immediate:
+ view.layer.transform = CATransform3DRotate(CATransform3DMakeScale(scale.x, scale.y, 1.0), angle, 0.0, 0.0, 1.0)
+ if let completion = completion {
+ completion(true)
+ }
+ case let .animated(duration, curve):
+ let previousAngle: CGFloat
+ if beginWithCurrentState, let presentation = view.layer.presentation() {
+ let t = presentation.transform
+ previousAngle = atan2(t.m12, t.m11)
+ } else {
+ previousAngle = currentAngle
+ }
+ view.layer.transform = CATransform3DRotate(CATransform3DMakeScale(scale.x, scale.y, 1.0), angle, 0.0, 0.0, 1.0)
+ view.layer.animateRotation(from: previousAngle, to: angle, duration: duration, timingFunction: curve.timingFunction, mediaTimingFunction: curve.mediaTimingFunction, completion: { result in
+ if let completion = completion {
+ completion(result)
+ }
+ })
+ }
+ }
}
#if os(iOS)
diff --git a/submodules/Display/Source/GenerateImage.swift b/submodules/Display/Source/GenerateImage.swift
index fc8437bd5c..89abf47ece 100644
--- a/submodules/Display/Source/GenerateImage.swift
+++ b/submodules/Display/Source/GenerateImage.swift
@@ -383,6 +383,27 @@ public func generateGradientTintedImage(image: UIImage?, colors: [UIColor]) -> U
return tintedImage
}
+public func generateGradientImage(size: CGSize, colors: [UIColor], locations: [CGFloat]) -> UIImage? {
+ guard colors.count == locations.count else {
+ return nil
+ }
+ UIGraphicsBeginImageContextWithOptions(size, false, 0.0)
+ if let context = UIGraphicsGetCurrentContext() {
+ let gradientColors = colors.map { $0.cgColor } as CFArray
+ let colorSpace = CGColorSpaceCreateDeviceRGB()
+
+ var locations = locations
+ let gradient = CGGradient(colorsSpace: colorSpace, colors: gradientColors, locations: &locations)!
+
+ context.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: 0.0), end: CGPoint(x: 0.0, y: size.height), options: CGGradientDrawingOptions())
+ }
+
+ let image = UIGraphicsGetImageFromCurrentImageContext()!
+ UIGraphicsEndImageContext()
+
+ return image
+}
+
public func generateScaledImage(image: UIImage?, size: CGSize, opaque: Bool = true, scale: CGFloat? = nil) -> UIImage? {
guard let image = image else {
return nil
diff --git a/submodules/Display/Source/ListView.swift b/submodules/Display/Source/ListView.swift
index a548fd1bf5..faf1fefcfb 100644
--- a/submodules/Display/Source/ListView.swift
+++ b/submodules/Display/Source/ListView.swift
@@ -184,6 +184,8 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
public final var keepMinimalScrollHeightWithTopInset: CGFloat?
+ public final var itemNodeHitTest: ((CGPoint) -> Bool)?
+
public final var stackFromBottom: Bool = false
public final var stackFromBottomInsetItemFactor: CGFloat = 0.0
public final var limitHitTestToNodes: Bool = false
@@ -3876,67 +3878,73 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
}
self.touchesPosition = touchesPosition
- self.selectionTouchLocation = touches.first!.location(in: self.view)
- self.selectionTouchDelayTimer?.invalidate()
- self.selectionLongTapDelayTimer?.invalidate()
- self.selectionLongTapDelayTimer = nil
- let timer = Timer(timeInterval: 0.08, target: ListViewTimerProxy { [weak self] in
- if let strongSelf = self, strongSelf.selectionTouchLocation != nil {
- strongSelf.clearHighlightAnimated(false)
-
- if let index = strongSelf.itemIndexAtPoint(strongSelf.touchesPosition) {
- var canBeSelectedOrLongTapped = false
- for itemNode in strongSelf.itemNodes {
- if itemNode.index == index && (strongSelf.items[index].selectable && itemNode.canBeSelected) || itemNode.canBeLongTapped {
- canBeSelectedOrLongTapped = true
- }
- }
+ var processSelection = true
+ if let itemNodeHitTest = self.itemNodeHitTest, !itemNodeHitTest(touchesPosition) {
+ processSelection = false
+ }
+
+ if processSelection {
+ self.selectionTouchLocation = touches.first!.location(in: self.view)
+ self.selectionTouchDelayTimer?.invalidate()
+ self.selectionLongTapDelayTimer?.invalidate()
+ self.selectionLongTapDelayTimer = nil
+ let timer = Timer(timeInterval: 0.08, target: ListViewTimerProxy { [weak self] in
+ if let strongSelf = self, strongSelf.selectionTouchLocation != nil {
+ strongSelf.clearHighlightAnimated(false)
- if canBeSelectedOrLongTapped {
- strongSelf.highlightedItemIndex = index
+ if let index = strongSelf.itemIndexAtPoint(strongSelf.touchesPosition) {
+ var canBeSelectedOrLongTapped = false
for itemNode in strongSelf.itemNodes {
- if itemNode.index == index && itemNode.canBeSelected {
- if true {
- if !itemNode.isLayerBacked {
- strongSelf.reorderItemNodeToFront(itemNode)
- for (_, headerNode) in strongSelf.itemHeaderNodes {
- strongSelf.reorderHeaderNodeToFront(headerNode)
+ if itemNode.index == index && (strongSelf.items[index].selectable && itemNode.canBeSelected) || itemNode.canBeLongTapped {
+ canBeSelectedOrLongTapped = true
+ }
+ }
+
+ if canBeSelectedOrLongTapped {
+ strongSelf.highlightedItemIndex = index
+ for itemNode in strongSelf.itemNodes {
+ if itemNode.index == index && itemNode.canBeSelected {
+ if true {
+ if !itemNode.isLayerBacked {
+ strongSelf.reorderItemNodeToFront(itemNode)
+ for (_, headerNode) in strongSelf.itemHeaderNodes {
+ strongSelf.reorderHeaderNodeToFront(headerNode)
+ }
}
- }
- let itemNodeFrame = itemNode.frame
- let itemNodeBounds = itemNode.bounds
- if strongSelf.items[index].selectable {
- itemNode.setHighlighted(true, at: strongSelf.touchesPosition.offsetBy(dx: -itemNodeFrame.minX + itemNodeBounds.minX, dy: -itemNodeFrame.minY + itemNodeBounds.minY), animated: false)
- }
-
- if itemNode.canBeLongTapped {
- let timer = Timer(timeInterval: 0.3, target: ListViewTimerProxy {
- if let strongSelf = self, strongSelf.highlightedItemIndex == index {
- for itemNode in strongSelf.itemNodes {
- if itemNode.index == index && itemNode.canBeLongTapped {
- itemNode.longTapped()
- strongSelf.clearHighlightAnimated(true)
- strongSelf.selectionTouchLocation = nil
- break
+ let itemNodeFrame = itemNode.frame
+ let itemNodeBounds = itemNode.bounds
+ if strongSelf.items[index].selectable {
+ itemNode.setHighlighted(true, at: strongSelf.touchesPosition.offsetBy(dx: -itemNodeFrame.minX + itemNodeBounds.minX, dy: -itemNodeFrame.minY + itemNodeBounds.minY), animated: false)
+ }
+
+ if itemNode.canBeLongTapped {
+ let timer = Timer(timeInterval: 0.3, target: ListViewTimerProxy {
+ if let strongSelf = self, strongSelf.highlightedItemIndex == index {
+ for itemNode in strongSelf.itemNodes {
+ if itemNode.index == index && itemNode.canBeLongTapped {
+ itemNode.longTapped()
+ strongSelf.clearHighlightAnimated(true)
+ strongSelf.selectionTouchLocation = nil
+ break
+ }
}
}
- }
- }, selector: #selector(ListViewTimerProxy.timerEvent), userInfo: nil, repeats: false)
- strongSelf.selectionLongTapDelayTimer = timer
- RunLoop.main.add(timer, forMode: RunLoop.Mode.common)
+ }, selector: #selector(ListViewTimerProxy.timerEvent), userInfo: nil, repeats: false)
+ strongSelf.selectionLongTapDelayTimer = timer
+ RunLoop.main.add(timer, forMode: RunLoop.Mode.common)
+ }
}
+ break
}
- break
}
}
}
}
- }
- }, selector: #selector(ListViewTimerProxy.timerEvent), userInfo: nil, repeats: false)
- self.selectionTouchDelayTimer = timer
- RunLoop.main.add(timer, forMode: RunLoop.Mode.common)
-
+ }, selector: #selector(ListViewTimerProxy.timerEvent), userInfo: nil, repeats: false)
+ self.selectionTouchDelayTimer = timer
+ RunLoop.main.add(timer, forMode: RunLoop.Mode.common)
+ }
super.touchesBegan(touches, with: event)
self.updateScroller(transition: .immediate)
diff --git a/submodules/Display/Source/Navigation/NavigationController.swift b/submodules/Display/Source/Navigation/NavigationController.swift
index 26a12835c0..815b56521d 100644
--- a/submodules/Display/Source/Navigation/NavigationController.swift
+++ b/submodules/Display/Source/Navigation/NavigationController.swift
@@ -579,6 +579,7 @@ open class NavigationController: UINavigationController, ContainableController,
var previousModalContainer: NavigationModalContainer?
var visibleModalCount = 0
var topModalIsFlat = false
+ var isLandscape = layout.orientation == .landscape
var hasVisibleStandaloneModal = false
var topModalDismissProgress: CGFloat = 0.0
@@ -784,7 +785,7 @@ open class NavigationController: UINavigationController, ContainableController,
let visibleRootModalDismissProgress: CGFloat
var additionalModalFrameProgress: CGFloat
if visibleModalCount == 1 {
- effectiveRootModalDismissProgress = topModalIsFlat ? 1.0 : topModalDismissProgress
+ effectiveRootModalDismissProgress = (topModalIsFlat || isLandscape) ? 1.0 : topModalDismissProgress
visibleRootModalDismissProgress = effectiveRootModalDismissProgress
additionalModalFrameProgress = 0.0
} else if visibleModalCount >= 2 {
@@ -851,7 +852,7 @@ open class NavigationController: UINavigationController, ContainableController,
}
let maxScale: CGFloat
let maxOffset: CGFloat
- if topModalIsFlat {
+ if topModalIsFlat || isLandscape {
maxScale = 1.0
maxOffset = 0.0
} else if visibleModalCount <= 1 {
diff --git a/submodules/Display/Source/Navigation/NavigationModalContainer.swift b/submodules/Display/Source/Navigation/NavigationModalContainer.swift
index aa40c78ee3..247d077368 100644
--- a/submodules/Display/Source/Navigation/NavigationModalContainer.swift
+++ b/submodules/Display/Source/Navigation/NavigationModalContainer.swift
@@ -328,6 +328,7 @@ final class NavigationModalContainer: ASDisplayNode, UIScrollViewDelegate, UIGes
self.scrollNode.view.isScrollEnabled = !isStandaloneModal
+ let isLandscape = layout.orientation == .landscape
let containerLayout: ContainerViewLayout
let containerFrame: CGRect
let containerScale: CGFloat
@@ -336,7 +337,7 @@ final class NavigationModalContainer: ASDisplayNode, UIScrollViewDelegate, UIGes
self.panRecognizer?.isEnabled = true
self.dim.backgroundColor = UIColor(white: 0.0, alpha: 0.25)
self.container.clipsToBounds = true
- if isStandaloneModal {
+ if isStandaloneModal || isLandscape {
self.container.cornerRadius = 0.0
} else {
self.container.cornerRadius = 10.0
@@ -351,7 +352,7 @@ final class NavigationModalContainer: ASDisplayNode, UIScrollViewDelegate, UIGes
}
var topInset: CGFloat
- if isStandaloneModal {
+ if isStandaloneModal || isLandscape {
topInset = 0.0
containerLayout = layout
diff --git a/submodules/GalleryUI/Sources/GalleryPagerNode.swift b/submodules/GalleryUI/Sources/GalleryPagerNode.swift
index b51506c3e2..f6ee2f8308 100644
--- a/submodules/GalleryUI/Sources/GalleryPagerNode.swift
+++ b/submodules/GalleryUI/Sources/GalleryPagerNode.swift
@@ -343,6 +343,25 @@ public final class GalleryPagerNode: ASDisplayNode, UIScrollViewDelegate, UIGest
}
self.transaction(GalleryPagerTransaction(deleteItems: deleteItems, insertItems: insertItems, updateItems: updateItems, focusOnItem: centralItemIndex, synchronous: synchronous))
+
+ if self.updateOnReplacement {
+ self.items = items
+
+ for i in 0 ..< self.items.count {
+ if let itemNode = self.visibleItemNode(at: i) {
+ self.items[i].updateNode(node: itemNode, synchronous: synchronous)
+ }
+ }
+ for i in (0 ..< self.itemNodes.count).reversed() {
+ let node = self.itemNodes[i]
+ if node.index > self.items.count - 1 {
+ node.removeFromSupernode()
+ self.itemNodes.remove(at: i)
+ }
+ }
+
+ self.updateCentralIndexOffset(transition: .immediate)
+ }
}
public func transaction(_ transaction: GalleryPagerTransaction) {
@@ -353,7 +372,7 @@ public final class GalleryPagerNode: ASDisplayNode, UIScrollViewDelegate, UIGest
updatedItem.item.updateNode(node: itemNode, synchronous: transaction.synchronous)
}
}
-
+
if !transaction.deleteItems.isEmpty || !transaction.insertItems.isEmpty {
let deleteItems = transaction.deleteItems.sorted()
diff --git a/submodules/GalleryUI/Sources/GalleryThumbnailContainerNode.swift b/submodules/GalleryUI/Sources/GalleryThumbnailContainerNode.swift
index 9c54950f07..2c6202d330 100644
--- a/submodules/GalleryUI/Sources/GalleryThumbnailContainerNode.swift
+++ b/submodules/GalleryUI/Sources/GalleryThumbnailContainerNode.swift
@@ -10,7 +10,7 @@ private let maxWidth: CGFloat = 75.0
public protocol GalleryThumbnailItem {
func isEqual(to: GalleryThumbnailItem) -> Bool
- var image: (Signal<(TransformImageArguments) -> DrawingContext?, NoError>, CGSize) { get }
+ func image(synchronous: Bool) -> (Signal<(TransformImageArguments) -> DrawingContext?, NoError>, CGSize)
}
private final class GalleryThumbnailItemNode: ASDisplayNode {
@@ -19,19 +19,19 @@ private final class GalleryThumbnailItemNode: ASDisplayNode {
private let imageSize: CGSize
- init(item: GalleryThumbnailItem) {
+ init(item: GalleryThumbnailItem, synchronous: Bool) {
self.imageNode = TransformImageNode()
self.imageContainerNode = ASDisplayNode()
self.imageContainerNode.clipsToBounds = true
self.imageContainerNode.cornerRadius = 2.0
- let (signal, imageSize) = item.image
+ let (signal, imageSize) = item.image(synchronous: synchronous)
self.imageSize = imageSize
super.init()
self.imageContainerNode.addSubnode(self.imageNode)
self.addSubnode(self.imageContainerNode)
- self.imageNode.setSignal(signal)
+ self.imageNode.setSignal(signal, attemptSynchronously: synchronous)
}
func updateLayout(height: CGFloat, progress: CGFloat, transition: ContainedViewLayoutTransition) -> CGFloat {
@@ -57,6 +57,7 @@ public final class GalleryThumbnailContainerNode: ASDisplayNode, UIScrollViewDel
private var itemNodes: [GalleryThumbnailItemNode] = []
private var centralIndexAndProgress: (Int, CGFloat?)?
private var currentLayout: CGSize?
+ public var updateSynchronously: Bool = false
private var isPanning: Bool = false
@@ -96,7 +97,6 @@ public final class GalleryThumbnailContainerNode: ASDisplayNode, UIScrollViewDel
for i in 0 ..< self.items.count {
if !self.items[i].isEqual(to: items[i]) {
updated = true
- break
}
}
} else {
@@ -108,7 +108,7 @@ public final class GalleryThumbnailContainerNode: ASDisplayNode, UIScrollViewDel
if let index = self.items.firstIndex(where: { $0.isEqual(to: item) }) {
itemNodes.append(self.itemNodes[index])
} else {
- itemNodes.append(GalleryThumbnailItemNode(item: item))
+ itemNodes.append(GalleryThumbnailItemNode(item: item, synchronous: self.updateSynchronously))
}
}
diff --git a/submodules/GalleryUI/Sources/Items/ChatImageGalleryItem.swift b/submodules/GalleryUI/Sources/Items/ChatImageGalleryItem.swift
index 72b22f6c55..d582cc1883 100644
--- a/submodules/GalleryUI/Sources/Items/ChatImageGalleryItem.swift
+++ b/submodules/GalleryUI/Sources/Items/ChatImageGalleryItem.swift
@@ -60,7 +60,7 @@ final class ChatMediaGalleryThumbnailItem: GalleryThumbnailItem {
}
}
- var image: (Signal<(TransformImageArguments) -> DrawingContext?, NoError>, CGSize) {
+ func image(synchronous: Bool) -> (Signal<(TransformImageArguments) -> DrawingContext?, NoError>, CGSize) {
switch self.thumbnail {
case let .image(imageReference):
if let representation = largestImageRepresentation(imageReference.media.representations) {
diff --git a/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift b/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift
index 1b48905ccf..e406599fa7 100644
--- a/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift
+++ b/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift
@@ -626,7 +626,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
case .playing:
isPaused = false
playing = true
- case let .buffering(_, whilePlaying):
+ case let .buffering(_, whilePlaying, _):
initialBuffering = true
isPaused = !whilePlaying
var isStreaming = false
@@ -1121,7 +1121,34 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
let fromTransform: CATransform3D
let toTransform: CATransform3D
- if let interactiveMediaNode = node.0 as? GalleryItemTransitionNode, interactiveMediaNode.isAvailableForGalleryTransition(), videoNode.hasAttachedContext {
+ if let instantNode = node.0 as? GalleryItemTransitionNode, instantNode.isAvailableForInstantPageTransition(), videoNode.hasAttachedContext {
+ copyView.removeFromSuperview()
+
+ let previousFrame = videoNode.frame
+ let previousSuperview = videoNode.view.superview
+ addToTransitionSurface(videoNode.view)
+ videoNode.view.superview?.bringSubviewToFront(videoNode.view)
+
+ if let previousSuperview = previousSuperview {
+ videoNode.frame = previousSuperview.convert(previousFrame, to: videoNode.view.superview)
+ transformedSuperFrame = transformedSuperFrame.offsetBy(dx: videoNode.position.x - previousFrame.center.x, dy: videoNode.position.y - previousFrame.center.y)
+ }
+
+ let initialScale: CGFloat = 1.0
+ let targetScale = max(transformedFrame.size.width / videoNode.layer.bounds.size.width, transformedFrame.size.height / videoNode.layer.bounds.size.height)
+
+ videoNode.backgroundColor = .clear
+
+ let transformScale: CGFloat = initialScale * targetScale
+ fromTransform = CATransform3DScale(videoNode.layer.transform, initialScale, initialScale, 1.0)
+ toTransform = CATransform3DScale(videoNode.layer.transform, transformScale, transformScale, 1.0)
+
+ if videoNode.hasAttachedContext {
+ if self.isPaused || !self.keepSoundOnDismiss {
+ videoNode.continuePlayingWithoutSound()
+ }
+ }
+ } else if let interactiveMediaNode = node.0 as? GalleryItemTransitionNode, interactiveMediaNode.isAvailableForGalleryTransition(), videoNode.hasAttachedContext {
copyView.removeFromSuperview()
let previousFrame = videoNode.frame
@@ -1158,33 +1185,6 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
fromTransform = CATransform3DScale(videoNode.layer.transform, initialScale, initialScale, 1.0)
toTransform = CATransform3DScale(videoNode.layer.transform, transformScale, transformScale, 1.0)
- if videoNode.hasAttachedContext {
- if self.isPaused || !self.keepSoundOnDismiss {
- videoNode.continuePlayingWithoutSound()
- }
- }
- } else if let instantNode = node.0 as? GalleryItemTransitionNode, instantNode.isAvailableForInstantPageTransition(), videoNode.hasAttachedContext {
- copyView.removeFromSuperview()
-
- let previousFrame = videoNode.frame
- let previousSuperview = videoNode.view.superview
- addToTransitionSurface(videoNode.view)
- videoNode.view.superview?.bringSubviewToFront(videoNode.view)
-
- if let previousSuperview = previousSuperview {
- videoNode.frame = previousSuperview.convert(previousFrame, to: videoNode.view.superview)
- transformedSuperFrame = transformedSuperFrame.offsetBy(dx: videoNode.position.x - previousFrame.center.x, dy: videoNode.position.y - previousFrame.center.y)
- }
-
- let initialScale: CGFloat = 1.0
- let targetScale = max(transformedFrame.size.width / videoNode.layer.bounds.size.width, transformedFrame.size.height / videoNode.layer.bounds.size.height)
-
- videoNode.backgroundColor = .clear
-
- let transformScale: CGFloat = initialScale * targetScale
- fromTransform = CATransform3DScale(videoNode.layer.transform, initialScale, initialScale, 1.0)
- toTransform = CATransform3DScale(videoNode.layer.transform, transformScale, transformScale, 1.0)
-
if videoNode.hasAttachedContext {
if self.isPaused || !self.keepSoundOnDismiss {
videoNode.continuePlayingWithoutSound()
diff --git a/submodules/GameUI/Sources/GameControllerNode.swift b/submodules/GameUI/Sources/GameControllerNode.swift
index 91d89e57bf..668c683ccc 100644
--- a/submodules/GameUI/Sources/GameControllerNode.swift
+++ b/submodules/GameUI/Sources/GameControllerNode.swift
@@ -62,6 +62,16 @@ final class GameControllerNode: ViewControllerTracingNode {
}, name: "performAction")
configuration.userContentController = userController
+
+ configuration.allowsInlineMediaPlayback = true
+ if #available(iOSApplicationExtension 10.0, iOS 10.0, *) {
+ configuration.mediaTypesRequiringUserActionForPlayback = []
+ } else if #available(iOSApplicationExtension 9.0, iOS 9.0, *) {
+ configuration.requiresUserActionForMediaPlayback = false
+ } else {
+ configuration.mediaPlaybackRequiresUserAction = false
+ }
+
let webView = WKWebView(frame: CGRect(), configuration: configuration)
if #available(iOSApplicationExtension 9.0, iOS 9.0, *) {
webView.allowsLinkPreview = false
diff --git a/submodules/HashtagSearchUI/Sources/HashtagSearchController.swift b/submodules/HashtagSearchUI/Sources/HashtagSearchController.swift
index a2fcb6fda9..78442b6a65 100644
--- a/submodules/HashtagSearchUI/Sources/HashtagSearchController.swift
+++ b/submodules/HashtagSearchUI/Sources/HashtagSearchController.swift
@@ -66,7 +66,7 @@ public final class HashtagSearchController: TelegramBaseController {
}, setPeerIdWithRevealedOptions: { _, _ in
}, setItemPinned: { _, _ in
}, setPeerMuted: { _, _ in
- }, deletePeer: { _ in
+ }, deletePeer: { _, _ in
}, updatePeerGrouping: { _, _ in
}, togglePeerMarkedUnread: { _, _ in
}, toggleArchivedFolderHiddenByDefault: {
diff --git a/submodules/ImageCompression/BUCK b/submodules/ImageCompression/BUCK
index 04b38610bc..49136e92c4 100644
--- a/submodules/ImageCompression/BUCK
+++ b/submodules/ImageCompression/BUCK
@@ -5,6 +5,9 @@ static_library(
srcs = glob([
"Sources/**/*.swift",
]),
+ deps = [
+ "//submodules/MozjpegBinding:MozjpegBinding",
+ ],
frameworks = [
"$SDKROOT/System/Library/Frameworks/Foundation.framework",
"$SDKROOT/System/Library/Frameworks/UIKit.framework",
diff --git a/submodules/ImageCompression/BUILD b/submodules/ImageCompression/BUILD
index 5c95cf7b22..752daa198e 100644
--- a/submodules/ImageCompression/BUILD
+++ b/submodules/ImageCompression/BUILD
@@ -4,8 +4,11 @@ swift_library(
name = "ImageCompression",
module_name = "ImageCompression",
srcs = glob([
- "Sources/**/*.swift",
+ "Sources/**/*.swift",
]),
+ deps = [
+ "//submodules/MozjpegBinding:MozjpegBinding",
+ ],
visibility = [
"//visibility:public",
],
diff --git a/submodules/ImageCompression/Sources/ImageCompression.swift b/submodules/ImageCompression/Sources/ImageCompression.swift
index b4fd52325b..0d123acb2f 100644
--- a/submodules/ImageCompression/Sources/ImageCompression.swift
+++ b/submodules/ImageCompression/Sources/ImageCompression.swift
@@ -1,8 +1,19 @@
import Foundation
import AVFoundation
import UIKit
+import MozjpegBinding
+
+public func extractImageExtraScans(_ data: Data) -> [Int] {
+ return extractJPEGDataScans(data).map { item in
+ return item.intValue
+ }
+}
public func compressImageToJPEG(_ image: UIImage, quality: Float) -> Data? {
+ if let result = compressJPEGData(image) {
+ return result
+ }
+
let data = NSMutableData()
guard let destination = CGImageDestinationCreateWithData(data as CFMutableData, "public.jpeg" as CFString, 1, nil) else {
return nil
@@ -46,3 +57,7 @@ public func compressImage(_ image: UIImage, quality: Float) -> Data? {
return data as Data
}
+
+public func compressImageMiniThumbnail(_ image: UIImage) -> Data? {
+ return compressMiniThumbnail(image)
+}
diff --git a/submodules/InstantPageUI/Sources/InstantImageGalleryItem.swift b/submodules/InstantPageUI/Sources/InstantImageGalleryItem.swift
index 4c2c5bf418..1a743ad3c8 100644
--- a/submodules/InstantPageUI/Sources/InstantImageGalleryItem.swift
+++ b/submodules/InstantPageUI/Sources/InstantImageGalleryItem.swift
@@ -15,7 +15,7 @@ private struct InstantImageGalleryThumbnailItem: GalleryThumbnailItem {
let account: Account
let mediaReference: AnyMediaReference
- var image: (Signal<(TransformImageArguments) -> DrawingContext?, NoError>, CGSize) {
+ func image(synchronous: Bool) -> (Signal<(TransformImageArguments) -> DrawingContext?, NoError>, CGSize) {
if let imageReferene = mediaReference.concrete(TelegramMediaImage.self), let representation = largestImageRepresentation(imageReferene.media.representations) {
return (mediaGridMessagePhoto(account: self.account, photoReference: imageReferene), representation.dimensions.cgSize)
} else if let fileReference = mediaReference.concrete(TelegramMediaFile.self), let dimensions = fileReference.media.dimensions {
diff --git a/submodules/InstantPageUI/Sources/InstantPageGalleryController.swift b/submodules/InstantPageUI/Sources/InstantPageGalleryController.swift
index b135b65242..ea351f7630 100644
--- a/submodules/InstantPageUI/Sources/InstantPageGalleryController.swift
+++ b/submodules/InstantPageUI/Sources/InstantPageGalleryController.swift
@@ -118,7 +118,7 @@ public struct InstantPageGalleryEntry: Equatable {
var representations: [TelegramMediaImageRepresentation] = []
representations.append(contentsOf: file.previewRepresentations)
if let dimensions = file.dimensions {
- representations.append(TelegramMediaImageRepresentation(dimensions: dimensions, resource: file.resource))
+ representations.append(TelegramMediaImageRepresentation(dimensions: dimensions, resource: file.resource, progressiveSizes: []))
}
let image = TelegramMediaImage(imageId: MediaId(namespace: 0, id: 0), representations: representations, immediateThumbnailData: file.immediateThumbnailData, reference: nil, partialReference: nil, flags: [])
return InstantImageGalleryItem(context: context, presentationData: presentationData, itemId: self.index, imageReference: .webPage(webPage: WebpageReference(webPage), media: image), caption: caption, credit: credit, location: self.location, openUrl: openUrl, openUrlOptions: openUrlOptions)
diff --git a/submodules/ItemListStickerPackItem/Sources/ItemListStickerPackItem.swift b/submodules/ItemListStickerPackItem/Sources/ItemListStickerPackItem.swift
index 040bf8b63a..52baa70a90 100644
--- a/submodules/ItemListStickerPackItem/Sources/ItemListStickerPackItem.swift
+++ b/submodules/ItemListStickerPackItem/Sources/ItemListStickerPackItem.swift
@@ -375,7 +375,7 @@ class ItemListStickerPackItemNode: ItemListRevealOptionsItemNode {
thumbnailItem = .animated(item.file.resource)
resourceReference = MediaResourceReference.media(media: .standalone(media: item.file), resource: item.file.resource)
} else if let dimensions = item.file.dimensions, let resource = chatMessageStickerResource(file: item.file, small: true) as? TelegramMediaResource {
- thumbnailItem = .still(TelegramMediaImageRepresentation(dimensions: dimensions, resource: resource))
+ thumbnailItem = .still(TelegramMediaImageRepresentation(dimensions: dimensions, resource: resource, progressiveSizes: []))
resourceReference = MediaResourceReference.media(media: .standalone(media: item.file), resource: resource)
}
}
diff --git a/submodules/ItemListUI/Sources/ItemListControllerNode.swift b/submodules/ItemListUI/Sources/ItemListControllerNode.swift
index 21666e9305..59d3be2803 100644
--- a/submodules/ItemListUI/Sources/ItemListControllerNode.swift
+++ b/submodules/ItemListUI/Sources/ItemListControllerNode.swift
@@ -238,7 +238,9 @@ open class ItemListControllerNode: ASDisplayNode, UIScrollViewDelegate {
self.listNode = ListView()
self.leftOverlayNode = ASDisplayNode()
+ self.leftOverlayNode.isUserInteractionEnabled = false
self.rightOverlayNode = ASDisplayNode()
+ self.rightOverlayNode.isUserInteractionEnabled = false
super.init()
@@ -302,6 +304,14 @@ open class ItemListControllerNode: ASDisplayNode, UIScrollViewDelegate {
let _ = strongSelf.contentScrollingEnded?(strongSelf.listNode)
}
}
+
+ self.listNode.itemNodeHitTest = { [weak self] point in
+ if let strongSelf = self {
+ return point.x > strongSelf.leftOverlayNode.frame.maxX && point.x < strongSelf.rightOverlayNode.frame.minX
+ } else {
+ return true
+ }
+ }
let previousState = Atomic(value: nil)
self.transitionDisposable.set(((state
diff --git a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/LegacyComponents.h b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/LegacyComponents.h
index fb42bcd69d..18f24fd501 100644
--- a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/LegacyComponents.h
+++ b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/LegacyComponents.h
@@ -22,8 +22,6 @@
#import
#import
#import
-#import
-#import
#import
#import
#import
diff --git a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/PGCameraMomentSegment.h b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/PGCameraMomentSegment.h
deleted file mode 100644
index ab0699b79b..0000000000
--- a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/PGCameraMomentSegment.h
+++ /dev/null
@@ -1,13 +0,0 @@
-#import
-#import
-#import
-
-@interface PGCameraMomentSegment : NSObject
-
-@property (nonatomic, readonly) NSURL *fileURL;
-@property (nonatomic, readonly) AVAsset *asset;
-@property (nonatomic, readonly) NSTimeInterval duration;
-
-- (instancetype)initWithURL:(NSURL *)url duration:(NSTimeInterval)duration;
-
-@end
diff --git a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/PGCameraMomentSession.h b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/PGCameraMomentSession.h
deleted file mode 100644
index a7f8269308..0000000000
--- a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/PGCameraMomentSession.h
+++ /dev/null
@@ -1,28 +0,0 @@
-#import
-
-@class PGCamera;
-
-@interface PGCameraMomentSession : NSObject
-
-@property (nonatomic, copy) void (^beganCapture)(void);
-@property (nonatomic, copy) void (^finishedCapture)(void);
-@property (nonatomic, copy) bool (^captureIsAvailable)(void);
-@property (nonatomic, copy) void (^durationChanged)(NSTimeInterval);
-
-@property (nonatomic, readonly) bool isCapturing;
-@property (nonatomic, readonly) UIImage *previewImage;
-@property (nonatomic, readonly) bool hasSegments;
-
-@property (nonatomic, readonly) PGCameraMomentSegment *lastSegment;
-
-- (instancetype)initWithCamera:(PGCamera *)camera;
-
-- (void)captureSegment;
-- (void)commitSegment;
-
-- (void)addSegment:(PGCameraMomentSegment *)segment;
-- (void)removeSegment:(PGCameraMomentSegment *)segment;
-- (void)removeLastSegment;
-- (void)removeAllSegments;
-
-@end
diff --git a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGPhotoAvatarCropView.h b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGPhotoAvatarCropView.h
index 2510d6f309..d80f9a63ce 100644
--- a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGPhotoAvatarCropView.h
+++ b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGPhotoAvatarCropView.h
@@ -7,6 +7,8 @@
@property (nonatomic, strong) UIImage *image;
+@property (nonatomic, readonly) UIView *clipView;
+
@property (nonatomic, readonly) CGSize originalSize;
@property (nonatomic, assign) CGRect cropRect;
@property (nonatomic, assign) UIImageOrientation cropOrientation;
@@ -37,6 +39,7 @@
- (void)animateTransitionOut;
- (void)transitionInFinishedFromCamera:(bool)fromCamera;
+- (void)attachEntitiesView;
- (void)closeCurtains;
- (void)openCurtains;
diff --git a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGPhotoEditorController.h b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGPhotoEditorController.h
index 889afb55cc..a9f491c4cb 100644
--- a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGPhotoEditorController.h
+++ b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGPhotoEditorController.h
@@ -85,4 +85,7 @@ typedef enum {
+ (TGPhotoEditorTab)defaultTabsForAvatarIntent;
+- (NSTimeInterval)currentTime;
+- (void)setMinimalVideoDuration:(NSTimeInterval)duration;
+
@end
diff --git a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGPhotoEditorTabController.h b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGPhotoEditorTabController.h
index ee21c29ebe..0646c13aad 100644
--- a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGPhotoEditorTabController.h
+++ b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGPhotoEditorTabController.h
@@ -9,6 +9,8 @@
bool _dismissing;
UIView *_transitionView;
bool _noTransitionToSnapshot;
+
+ bool _animateScale;
}
@property (nonatomic, weak) id item;
@@ -36,6 +38,7 @@
@property (nonatomic, assign) TGPhotoEditorTab availableTabs;
+@property (nonatomic, assign) TGPhotoEditorTab switchingFromTab;
@property (nonatomic, assign) TGPhotoEditorTab switchingToTab;
- (void)transitionOutSwitching:(bool)switching completion:(void (^)(void))completion;
diff --git a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGPhotoEditorUtils.h b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGPhotoEditorUtils.h
index f5837b4dd0..948ccab2ff 100644
--- a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGPhotoEditorUtils.h
+++ b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGPhotoEditorUtils.h
@@ -14,9 +14,11 @@ CGFloat TGRadiansToDegrees(CGFloat radians);
UIImage *TGPhotoEditorCrop(UIImage *image, UIImage *paintingImage, UIImageOrientation orientation, CGFloat rotation, CGRect rect, bool mirrored, CGSize maxSize, CGSize originalSize, bool shouldResize);
UIImage *TGPhotoEditorVideoCrop(UIImage *image, UIImage *paintingImage, UIImageOrientation orientation, CGFloat rotation, CGRect rect, bool mirrored, CGSize maxSize, CGSize originalSize, bool shouldResize, bool useImageSize);
-UIImage *TGPhotoEditorVideoExtCrop(UIImage *inputImage, UIImage *paintingImage, UIImageOrientation orientation, CGFloat rotation, CGRect rect, bool mirrored, CGSize maxSize, CGSize originalSize, bool shouldResize, bool useImageSize, bool skipImageTransform);
+UIImage *TGPhotoEditorVideoExtCrop(UIImage *inputImage, UIImage *paintingImage, UIImageOrientation orientation, CGFloat rotation, CGRect rect, bool mirrored, CGSize maxSize, CGSize originalSize, bool shouldResize, bool useImageSize, bool skipImageTransform, bool fillPainting);
UIImage *TGPhotoEditorFitImage(UIImage *image, CGSize maxSize);
CGSize TGRotatedContentSize(CGSize contentSize, CGFloat rotation);
+
+UIImage *TGPhotoEditorPaintingCrop(UIImage *paintingImage, UIImageOrientation orientation, CGFloat rotation, CGRect rect, bool mirrored, CGSize maxSize, CGSize originalSize, bool shouldResize, bool useImageSize, bool skipImageTransform);
UIImageOrientation TGNextCWOrientationForOrientation(UIImageOrientation orientation);
UIImageOrientation TGNextCCWOrientationForOrientation(UIImageOrientation orientation);
diff --git a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGPhotoPaintStickersContext.h b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGPhotoPaintStickersContext.h
index 4c6e2e14ce..529d893be3 100644
--- a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGPhotoPaintStickersContext.h
+++ b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGPhotoPaintStickersContext.h
@@ -13,7 +13,13 @@
@protocol TGPhotoPaintStickerRenderView
+@property (nonatomic, copy) void(^started)(double);
+
- (void)setIsVisible:(bool)isVisible;
+- (void)seekTo:(double)timestamp;
+- (void)play;
+- (void)pause;
+- (void)resetToStart;
- (int64_t)documentId;
- (UIImage *)image;
diff --git a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGVideoEditAdjustments.h b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGVideoEditAdjustments.h
index 32dcbcc3a9..f8af7a8b09 100644
--- a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGVideoEditAdjustments.h
+++ b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGVideoEditAdjustments.h
@@ -13,6 +13,7 @@ typedef enum
TGMediaVideoConversionPresetCompressedVeryHigh,
TGMediaVideoConversionPresetAnimation,
TGMediaVideoConversionPresetVideoMessage,
+ TGMediaVideoConversionPresetProfileLow,
TGMediaVideoConversionPresetProfile,
TGMediaVideoConversionPresetProfileHigh,
TGMediaVideoConversionPresetProfileVeryHigh,
diff --git a/submodules/LegacyComponents/Sources/PGCameraMomentSegment.m b/submodules/LegacyComponents/Sources/PGCameraMomentSegment.m
deleted file mode 100644
index 50c9ed87a8..0000000000
--- a/submodules/LegacyComponents/Sources/PGCameraMomentSegment.m
+++ /dev/null
@@ -1,22 +0,0 @@
-#import "PGCameraMomentSegment.h"
-
-@interface PGCameraMomentSegment ()
-{
-
-}
-@end
-
-@implementation PGCameraMomentSegment
-
-- (instancetype)initWithURL:(NSURL *)url duration:(NSTimeInterval)duration
-{
- self = [super init];
- if (self != nil)
- {
- _fileURL = url;
- _duration = duration;
- }
- return self;
-}
-
-@end
diff --git a/submodules/LegacyComponents/Sources/PGCameraMomentSession.m b/submodules/LegacyComponents/Sources/PGCameraMomentSession.m
deleted file mode 100644
index d83cbfc354..0000000000
--- a/submodules/LegacyComponents/Sources/PGCameraMomentSession.m
+++ /dev/null
@@ -1,89 +0,0 @@
-#import "PGCameraMomentSession.h"
-#import "PGCamera.h"
-
-@interface PGCameraMomentSession ()
-{
- NSString *_uniqueIdentifier;
- NSURL *_segmentsDirectory;
-
- PGCamera *_camera;
- NSMutableArray *_segments;
-}
-@end
-
-@implementation PGCameraMomentSession
-
-- (instancetype)initWithCamera:(PGCamera *)camera
-{
- self = [super init];
- if (self != nil)
- {
- _camera = camera;
- _segments = [[NSMutableArray alloc] init];
-
- int64_t uniqueId = 0;
- arc4random_buf(&uniqueId, 8);
- _uniqueIdentifier = [NSString stringWithFormat:@"%x", (int)arc4random()];
- }
- return self;
-}
-
-- (NSURL *)segmentsDirectory
-{
- if (_segmentsDirectory == nil)
- _segmentsDirectory = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:_uniqueIdentifier]];
-
- return _segmentsDirectory;
-}
-
-- (void)captureSegment
-{
- if (self.isCapturing)
- return;
-
- _isCapturing = true;
-
- if (self.beganCapture != nil)
- self.beganCapture();
-
- [_camera startVideoRecordingForMoment:true completion:^(NSURL *resultUrl, __unused CGAffineTransform transform, __unused CGSize dimensions, NSTimeInterval duration, bool success)
- {
- if (!success)
- return;
-
- _isCapturing = false;
-
- if (self.finishedCapture != nil)
- self.finishedCapture();
-
- PGCameraMomentSegment *segment = [[PGCameraMomentSegment alloc] initWithURL:resultUrl duration:duration];
- [self addSegment:segment];
- }];
-}
-
-- (void)commitSegment
-{
- [_camera stopVideoRecording];
-}
-
-- (void)addSegment:(PGCameraMomentSegment *)segment
-{
- [_segments addObject:segment];
-}
-
-- (void)removeSegment:(PGCameraMomentSegment *)segment
-{
- [_segments removeObject:segment];
-}
-
-- (void)removeLastSegment
-{
- [_segments removeLastObject];
-}
-
-- (void)removeAllSegments
-{
- [_segments removeAllObjects];
-}
-
-@end
diff --git a/submodules/LegacyComponents/Sources/TGAttachmentCarouselItemView.m b/submodules/LegacyComponents/Sources/TGAttachmentCarouselItemView.m
index 37da1a71a1..3aa9bcda19 100644
--- a/submodules/LegacyComponents/Sources/TGAttachmentCarouselItemView.m
+++ b/submodules/LegacyComponents/Sources/TGAttachmentCarouselItemView.m
@@ -928,7 +928,8 @@ const NSUInteger TGAttachmentDisplayedAssetLimit = 500;
if (paintingImage == nil) {
paintingImage = adjustments.paintingData.image;
}
- UIImage *thumbnailImage = TGPhotoEditorVideoExtCrop(resultImage, paintingImage, adjustments.cropOrientation, adjustments.cropRotation, adjustments.cropRect, adjustments.cropMirrored, TGScaleToFill(asset.dimensions, CGSizeMake(800, 800)), adjustments.originalSize, true, true, true);
+ UIImage *croppedPaintingImage = TGPhotoEditorPaintingCrop(paintingImage, adjustments.cropOrientation, adjustments.cropRotation, adjustments.cropRect, adjustments.cropMirrored, resultImage.size, adjustments.originalSize, true, true, false);
+ UIImage *thumbnailImage = TGPhotoEditorVideoExtCrop(resultImage, croppedPaintingImage, adjustments.cropOrientation, adjustments.cropRotation, adjustments.cropRect, adjustments.cropMirrored, TGScaleToFill(asset.dimensions, CGSizeMake(800, 800)), adjustments.originalSize, true, true, true, true);
if (thumbnailImage != nil) {
previewImage = thumbnailImage;
}
diff --git a/submodules/LegacyComponents/Sources/TGCameraController.m b/submodules/LegacyComponents/Sources/TGCameraController.m
index 638153c61f..8482896730 100644
--- a/submodules/LegacyComponents/Sources/TGCameraController.m
+++ b/submodules/LegacyComponents/Sources/TGCameraController.m
@@ -1751,9 +1751,34 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus
TGDispatchOnMainThread(^
{
- if (strongSelf.finishedWithPhoto != nil)
- strongSelf.finishedWithPhoto(nil, resultImage, nil, nil, nil, nil);
-
+ if (editorValues.paintingData.hasAnimation) {
+ TGVideoEditAdjustments *adjustments = [TGVideoEditAdjustments editAdjustmentsWithPhotoEditorValues:(PGPhotoEditorValues *)editorValues preset:TGMediaVideoConversionPresetProfileVeryHigh];
+
+ NSString *filePath = [NSTemporaryDirectory() stringByAppendingPathComponent:[[NSString alloc] initWithFormat:@"gifvideo_%x.jpg", (int)arc4random()]];
+ NSData *data = UIImageJPEGRepresentation(resultImage, 0.8);
+ [data writeToFile:filePath atomically:true];
+
+ UIImage *previewImage = resultImage;
+ if ([adjustments cropAppliedForAvatar:false] || adjustments.hasPainting || adjustments.toolsApplied)
+ {
+ UIImage *paintingImage = adjustments.paintingData.stillImage;
+ if (paintingImage == nil) {
+ paintingImage = adjustments.paintingData.image;
+ }
+ UIImage *croppedPaintingImage = TGPhotoEditorPaintingCrop(paintingImage, adjustments.cropOrientation, adjustments.cropRotation, adjustments.cropRect, adjustments.cropMirrored, resultImage.size, adjustments.originalSize, true, true, false);
+ UIImage *thumbnailImage = TGPhotoEditorVideoExtCrop(resultImage, croppedPaintingImage, adjustments.cropOrientation, adjustments.cropRotation, adjustments.cropRect, adjustments.cropMirrored, TGScaleToFill(resultImage.size, CGSizeMake(800, 800)), adjustments.originalSize, true, true, true, true);
+ if (thumbnailImage != nil) {
+ previewImage = thumbnailImage;
+ }
+ }
+
+ if (strongSelf.finishedWithVideo != nil)
+ strongSelf.finishedWithVideo(nil, [NSURL fileURLWithPath:filePath], previewImage, 0, CGSizeZero, adjustments, nil, nil, nil, nil);
+ } else {
+ if (strongSelf.finishedWithPhoto != nil)
+ strongSelf.finishedWithPhoto(nil, resultImage, nil, nil, nil, nil);
+ }
+
if (strongSelf.shouldStoreCapturedAssets && [input isKindOfClass:[UIImage class]])
{
[strongSelf _savePhotoToCameraRollWithOriginalImage:image editedImage:[editorValues toolsApplied] ? resultImage : nil];
@@ -2608,7 +2633,7 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus
if (paintingImage == nil) {
paintingImage = adjustments.paintingData.image;
}
- UIImage *thumbnailImage = TGPhotoEditorVideoExtCrop(image, paintingImage, adjustments.cropOrientation, adjustments.cropRotation, adjustments.cropRect, adjustments.cropMirrored, TGScaleToFill(image.size, CGSizeMake(512, 512)), adjustments.originalSize, true, true, true);
+ UIImage *thumbnailImage = TGPhotoEditorVideoExtCrop(image, paintingImage, adjustments.cropOrientation, adjustments.cropRotation, adjustments.cropRect, adjustments.cropMirrored, TGScaleToFill(image.size, CGSizeMake(512, 512)), adjustments.originalSize, true, true, true, false);
if (thumbnailImage != nil) {
dict[@"previewImage"] = thumbnailImage;
}
diff --git a/submodules/LegacyComponents/Sources/TGLocationViewController.m b/submodules/LegacyComponents/Sources/TGLocationViewController.m
index 97489ab445..dd3ec6ea8c 100644
--- a/submodules/LegacyComponents/Sources/TGLocationViewController.m
+++ b/submodules/LegacyComponents/Sources/TGLocationViewController.m
@@ -71,6 +71,8 @@
bool _throttle;
TGLocationPinAnnotationView *_ownLiveLocationView;
__weak MKAnnotationView *_userLocationView;
+
+ UIImageView *_headingArrowView;
}
@end
@@ -162,6 +164,8 @@
[_liveLocationsDisposable dispose];
[_reloadDisposable dispose];
[_frequentUpdatesDisposable dispose];
+
+ [_locationManager stopUpdatingHeading];
}
- (void)tg_setRightBarButtonItem:(UIBarButtonItem *)barButtonItem action:(bool)action animated:(bool)animated {
@@ -438,6 +442,36 @@
{
[super loadView];
+ static UIImage *headingArrowImage = nil;
+ static dispatch_once_t onceToken;
+
+ dispatch_once(&onceToken, ^
+ {
+ UIGraphicsBeginImageContextWithOptions(CGSizeMake(28.0f, 28.0f), false, 0.0f);
+ CGContextRef context = UIGraphicsGetCurrentContext();
+
+ CGContextClearRect(context, CGRectMake(0, 0, 28, 28));
+
+ CGContextSetFillColorWithColor(context, UIColorRGB(0x3393fe).CGColor);
+
+ CGContextMoveToPoint(context, 14, 0);
+ CGContextAddLineToPoint(context, 19, 7);
+ CGContextAddLineToPoint(context, 9, 7);
+ CGContextClosePath(context);
+ CGContextFillPath(context);
+
+ CGContextSetBlendMode(context, kCGBlendModeClear);
+ CGContextFillEllipseInRect(context, CGRectMake(5.0, 5.0, 18.0, 18.0));
+
+ headingArrowImage = UIGraphicsGetImageFromCurrentImageContext();
+ UIGraphicsEndImageContext();
+ });
+
+ _headingArrowView = [[UIImageView alloc] init];
+ _headingArrowView.hidden = true;
+ _headingArrowView.frame = CGRectMake(0.0, 0.0, 28.0, 28.0);
+ _headingArrowView.image = headingArrowImage;
+
_tableView.scrollsToTop = false;
_mapView.tapEnabled = false;
@@ -495,6 +529,8 @@
{
[super viewWillAppear:animated];
+ [_locationManager startUpdatingHeading];
+
if (self.previewMode && !animated)
{
UIView *contentView = [[_mapView subviews] firstObject];
@@ -950,6 +986,9 @@
{
_userLocationView = view;
+ [_userLocationView addSubview:_headingArrowView];
+ _headingArrowView.center = CGPointMake(view.frame.size.width / 2.0, view.frame.size.height / 2.0);
+
if (_ownLiveLocationView != nil)
{
[_userLocationView addSubview:_ownLiveLocationView];
@@ -982,6 +1021,14 @@
return CLLocationCoordinate2DMake(_locationAttachment.latitude, _locationAttachment.longitude);
}
+- (void)locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)newHeading
+{
+ if (newHeading != nil) {
+ _headingArrowView.hidden = false;
+ _headingArrowView.transform = CGAffineTransformMakeRotation(newHeading.magneticHeading / 180.0 * M_PI);
+ }
+}
+
#pragma mark -
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
diff --git a/submodules/LegacyComponents/Sources/TGMediaAssetsController.m b/submodules/LegacyComponents/Sources/TGMediaAssetsController.m
index 7c295a7652..956d31fb69 100644
--- a/submodules/LegacyComponents/Sources/TGMediaAssetsController.m
+++ b/submodules/LegacyComponents/Sources/TGMediaAssetsController.m
@@ -920,7 +920,7 @@
if (paintingImage == nil) {
paintingImage = adjustments.paintingData.image;
}
- UIImage *thumbnailImage = TGPhotoEditorVideoExtCrop(image, paintingImage, adjustments.cropOrientation, adjustments.cropRotation, adjustments.cropRect, adjustments.cropMirrored, TGScaleToFill(asset.dimensions, CGSizeMake(512, 512)), adjustments.originalSize, true, true, true);
+ UIImage *thumbnailImage = TGPhotoEditorVideoExtCrop(image, paintingImage, adjustments.cropOrientation, adjustments.cropRotation, adjustments.cropRect, adjustments.cropMirrored, TGScaleToFill(asset.dimensions, CGSizeMake(512, 512)), adjustments.originalSize, true, true, true, false);
if (thumbnailImage != nil) {
dict[@"previewImage"] = thumbnailImage;
}
diff --git a/submodules/LegacyComponents/Sources/TGMediaAssetsPickerController.m b/submodules/LegacyComponents/Sources/TGMediaAssetsPickerController.m
index 28e2407007..61c68aec85 100644
--- a/submodules/LegacyComponents/Sources/TGMediaAssetsPickerController.m
+++ b/submodules/LegacyComponents/Sources/TGMediaAssetsPickerController.m
@@ -442,7 +442,8 @@
if (paintingImage == nil) {
paintingImage = adjustments.paintingData.image;
}
- UIImage *thumbnailImage = TGPhotoEditorVideoExtCrop(resultImage, paintingImage, adjustments.cropOrientation, adjustments.cropRotation, adjustments.cropRect, adjustments.cropMirrored, TGScaleToFill(asset.dimensions, CGSizeMake(800, 800)), adjustments.originalSize, true, true, true);
+ UIImage *croppedPaintingImage = TGPhotoEditorPaintingCrop(paintingImage, adjustments.cropOrientation, adjustments.cropRotation, adjustments.cropRect, adjustments.cropMirrored, resultImage.size, adjustments.originalSize, true, true, false);
+ UIImage *thumbnailImage = TGPhotoEditorVideoExtCrop(resultImage, croppedPaintingImage, adjustments.cropOrientation, adjustments.cropRotation, adjustments.cropRect, adjustments.cropMirrored, TGScaleToFill(asset.dimensions, CGSizeMake(800, 800)), adjustments.originalSize, true, true, true, true);
if (thumbnailImage != nil) {
previewImage = thumbnailImage;
}
diff --git a/submodules/LegacyComponents/Sources/TGMediaPickerCaptionInputPanel.m b/submodules/LegacyComponents/Sources/TGMediaPickerCaptionInputPanel.m
index f5a1ca7097..dbf7f8f746 100644
--- a/submodules/LegacyComponents/Sources/TGMediaPickerCaptionInputPanel.m
+++ b/submodules/LegacyComponents/Sources/TGMediaPickerCaptionInputPanel.m
@@ -86,7 +86,7 @@ static void setViewFrame(UIView *view, CGRect frame)
{
localizationPlaceholderText = TGLocalized(@"MediaPicker.AddCaption");
NSString *placeholderText = TGLocalized(@"MediaPicker.AddCaption");
- UIFont *placeholderFont = TGSystemFontOfSize(16);
+ UIFont *placeholderFont = TGSystemFontOfSize(17);
CGSize placeholderSize = [placeholderText sizeWithFont:placeholderFont];
placeholderSize.width += 2.0f;
placeholderSize.height += 2.0f;
@@ -121,7 +121,7 @@ static void setViewFrame(UIView *view, CGRect frame)
_placeholderLabel = [[UILabel alloc] init];
_placeholderLabel.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin;
_placeholderLabel.backgroundColor = [UIColor clearColor];
- _placeholderLabel.font = TGSystemFontOfSize(16);
+ _placeholderLabel.font = TGSystemFontOfSize(17);
_placeholderLabel.textColor = UIColorRGB(0x7f7f7f);
_placeholderLabel.text = TGLocalized(@"MediaPicker.AddCaption");
_placeholderLabel.userInteractionEnabled = true;
@@ -130,7 +130,7 @@ static void setViewFrame(UIView *view, CGRect frame)
_inputFieldOnelineLabel = [[UILabel alloc] init];
_inputFieldOnelineLabel.backgroundColor = [UIColor clearColor];
- _inputFieldOnelineLabel.font = TGSystemFontOfSize(16);
+ _inputFieldOnelineLabel.font = TGSystemFontOfSize(17);
_inputFieldOnelineLabel.hidden = true;
_inputFieldOnelineLabel.numberOfLines = 1;
_inputFieldOnelineLabel.textColor = [UIColor whiteColor];
@@ -169,7 +169,7 @@ static void setViewFrame(UIView *view, CGRect frame)
_inputField.textColor = [UIColor whiteColor];
_inputField.disableFormatting = !_allowEntities;
_inputField.placeholderView = _placeholderLabel;
- _inputField.font = TGSystemFontOfSize(16);
+ _inputField.font = TGSystemFontOfSize(17);
_inputField.accentColor = UIColorRGB(0x78b1f9);
_inputField.clipsToBounds = true;
_inputField.backgroundColor = nil;
@@ -188,7 +188,7 @@ static void setViewFrame(UIView *view, CGRect frame)
_inputField.internalTextView.scrollIndicatorInsets = UIEdgeInsetsMake(-inputFieldInternalEdgeInsets.top, 0, 5 - TGRetinaPixel, 0);
- [_inputField setAttributedText:[TGMediaPickerCaptionInputPanel attributedStringForText:_caption entities:_entities fontSize:16.0f] keepFormatting:true animated:false];
+ [_inputField setAttributedText:[TGMediaPickerCaptionInputPanel attributedStringForText:_caption entities:_entities fontSize:17.0f] keepFormatting:true animated:false];
[_inputFieldClippingContainer addSubview:_inputField];
}
@@ -439,7 +439,7 @@ static void setViewFrame(UIView *view, CGRect frame)
_fieldBackground.alpha = _placeholderLabel.hidden ? 1.0f : 0.0f;
}
- [self.inputField setAttributedText:[TGMediaPickerCaptionInputPanel attributedStringForText:_caption entities:_entities fontSize:16.0f] keepFormatting:true animated:false];
+ [self.inputField setAttributedText:[TGMediaPickerCaptionInputPanel attributedStringForText:_caption entities:_entities fontSize:17.0f] keepFormatting:true animated:false];
}
+ (NSAttributedString *)attributedStringForText:(NSString *)text entities:(NSArray *)entities fontSize:(CGFloat)fontSize {
@@ -777,14 +777,14 @@ static void setViewFrame(UIView *view, CGRect frame)
if (text == nil)
return nil;
- NSMutableAttributedString *string = [[NSMutableAttributedString alloc] initWithAttributedString:[TGMediaPickerCaptionInputPanel attributedStringForText:text entities:entities fontSize:16.0f]];
+ NSMutableAttributedString *string = [[NSMutableAttributedString alloc] initWithAttributedString:[TGMediaPickerCaptionInputPanel attributedStringForText:text entities:entities fontSize:17.0f]];
for (NSUInteger i = 0; i < string.length; i++)
{
unichar c = [text characterAtIndex:i];
if (c == '\t' || c == '\n')
{
- [string insertAttributedString:[[NSAttributedString alloc] initWithString:tokenString attributes:@{NSFontAttributeName:TGSystemFontOfSize(16.0f)}] atIndex:i];
+ [string insertAttributedString:[[NSAttributedString alloc] initWithString:tokenString attributes:@{NSFontAttributeName:TGSystemFontOfSize(17.0f)}] atIndex:i];
break;
}
}
diff --git a/submodules/LegacyComponents/Sources/TGMediaPickerGalleryInterfaceView.m b/submodules/LegacyComponents/Sources/TGMediaPickerGalleryInterfaceView.m
index b93165a11e..4cb93f57b1 100644
--- a/submodules/LegacyComponents/Sources/TGMediaPickerGalleryInterfaceView.m
+++ b/submodules/LegacyComponents/Sources/TGMediaPickerGalleryInterfaceView.m
@@ -167,13 +167,14 @@
if (recipientName.length > 0)
{
_arrowView = [[UIImageView alloc] initWithImage: TGComponentsImageNamed(@"PhotoPickerArrow")];
- _arrowView.alpha = 0.45f;
+ _arrowView.alpha = 0.6f;
[_wrapperView addSubview:_arrowView];
_recipientLabel = [[UILabel alloc] init];
+ _recipientLabel.alpha = 0.6;
_recipientLabel.backgroundColor = [UIColor clearColor];
_recipientLabel.font = TGBoldSystemFontOfSize(13.0f);
- _recipientLabel.textColor = UIColorRGBA(0xffffff, 0.45f);
+ _recipientLabel.textColor = UIColorRGB(0xffffff);
_recipientLabel.text = recipientName;
_recipientLabel.userInteractionEnabled = false;
[_recipientLabel sizeToFit];
@@ -510,13 +511,7 @@
UIEdgeInsets screenEdges = [self screenEdges];
__weak TGMediaPickerGalleryInterfaceView *weakSelf = self;
-
- if ([itemView.headerView isKindOfClass:[TGMediaPickerScrubberHeaderView class]])
- {
- TGMediaPickerScrubberHeaderView *headerView = (TGMediaPickerScrubberHeaderView *)itemView.headerView;
- [headerView.scrubberView setRecipientName:_recipientLabel.text];
- }
-
+
[self _layoutRecipientLabelForOrientation:[self interfaceOrientation] screenEdges:screenEdges hasHeaderView:(itemView.headerView != nil)];
if (_selectionContext != nil)
@@ -1039,8 +1034,8 @@
{
_checkButton.alpha = alpha;
_muteButton.alpha = alpha;
- _arrowView.alpha = alpha * 0.45f;
- _recipientLabel.alpha = alpha;
+ _arrowView.alpha = alpha * 0.6f;
+ _recipientLabel.alpha = alpha * 0.6;
} completion:^(BOOL finished)
{
if (finished)
@@ -1070,8 +1065,8 @@
_muteButton.alpha = alpha;
_muteButton.userInteractionEnabled = !hidden;
- _arrowView.alpha = alpha * 0.45f;
- _recipientLabel.alpha = alpha;
+ _arrowView.alpha = alpha * 0.6f;
+ _recipientLabel.alpha = alpha * 0.6;
}
if (hidden)
@@ -1095,7 +1090,7 @@
{
_checkButton.alpha = alpha;
_muteButton.alpha = alpha;
- _arrowView.alpha = alpha * 0.45f;
+ _arrowView.alpha = alpha * 0.6;
_recipientLabel.alpha = alpha;
_portraitToolbarView.alpha = alpha;
_landscapeToolbarView.alpha = alpha;
@@ -1132,7 +1127,7 @@
_muteButton.alpha = alpha;
_muteButton.userInteractionEnabled = !hidden;
- _arrowView.alpha = alpha * 0.45f;
+ _arrowView.alpha = alpha * 0.6;
_recipientLabel.alpha = alpha;
_portraitToolbarView.alpha = alpha;
@@ -1391,6 +1386,8 @@
screenEdges.left += _safeAreaInset.left;
screenEdges.right -= _safeAreaInset.right;
+ CGFloat panelInset = 0.0f;
+
switch (orientation)
{
case UIInterfaceOrientationLandscapeLeft:
@@ -1402,13 +1399,10 @@
break;
default:
- frame = CGRectMake(screenEdges.left + 5, screenEdges.top + 6, _muteButton.frame.size.width, _muteButton.frame.size.height);
+ frame = CGRectMake(screenEdges.left + 5, screenEdges.bottom - TGPhotoEditorToolbarSize - [_captionMixin.inputPanel baseHeight] - 45 - _safeAreaInset.bottom - panelInset - (hasHeaderView ? 64.0 : 0.0), _muteButton.frame.size.width, _muteButton.frame.size.height);
break;
}
- if (hasHeaderView)
- frame.origin.y += 64;
-
return frame;
}
@@ -1462,9 +1456,6 @@
break;
}
- if (hasHeaderView)
- frame.origin.y += 64;
-
return frame;
}
@@ -1491,7 +1482,7 @@
break;
default:
- frame = CGRectMake(screenEdges.right - 46 - _safeAreaInset.right - buttonInset, screenEdges.bottom - TGPhotoEditorToolbarSize - [_captionMixin.inputPanel baseHeight] - 45 - _safeAreaInset.bottom - panelInset, 44, 44);
+ frame = CGRectMake(screenEdges.right - 46 - _safeAreaInset.right - buttonInset, screenEdges.bottom - TGPhotoEditorToolbarSize - [_captionMixin.inputPanel baseHeight] - 45 - _safeAreaInset.bottom - panelInset - (hasHeaderView ? 64.0 : 0.0), 44, 44);
break;
}
@@ -1526,9 +1517,6 @@
_arrowView.frame = frame;
_recipientLabel.frame = CGRectMake(CGRectGetMaxX(_arrowView.frame) + 6.0f, _arrowView.frame.origin.y - 2.0f, recipientWidth, _recipientLabel.frame.size.height);
-
- _arrowView.hidden = hasHeaderView;
- _recipientLabel.hidden = hasHeaderView;
}
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)__unused duration
@@ -1670,15 +1658,14 @@
{
[UIView performWithoutAnimation:^
{
- _photoCounterButton.frame = CGRectMake(screenEdges.right - 56 - _safeAreaInset.right, screenEdges.bottom - TGPhotoEditorToolbarSize - [_captionMixin.inputPanel baseHeight] - 40 - _safeAreaInset.bottom, 64, 38);
+ _photoCounterButton.frame = CGRectMake(screenEdges.right - 56 - _safeAreaInset.right, screenEdges.bottom - TGPhotoEditorToolbarSize - [_captionMixin.inputPanel baseHeight] - 40 - _safeAreaInset.bottom - (hasHeaderView ? 64.0 : 0.0), 64, 38);
- _selectedPhotosView.frame = CGRectMake(screenEdges.left + 4, screenEdges.bottom - TGPhotoEditorToolbarSize - [_captionMixin.inputPanel baseHeight] - photosViewSize - 54 - _safeAreaInset.bottom, self.frame.size.width - 4 * 2 - _safeAreaInset.right, photosViewSize);
+ _selectedPhotosView.frame = CGRectMake(screenEdges.left + 4, screenEdges.bottom - TGPhotoEditorToolbarSize - [_captionMixin.inputPanel baseHeight] - photosViewSize - 54 - _safeAreaInset.bottom - (hasHeaderView ? 64.0 : 0.0), self.frame.size.width - 4 * 2 - _safeAreaInset.right, photosViewSize);
}];
_landscapeToolbarView.frame = CGRectMake(_landscapeToolbarView.frame.origin.x, screenEdges.top, TGPhotoEditorToolbarSize, self.frame.size.height);
- CGFloat topInset = _safeAreaInset.top > FLT_EPSILON ? _safeAreaInset.top - 14.0 : 0.0f;
- _headerWrapperView.frame = CGRectMake(screenEdges.left, screenEdges.top + topInset, self.frame.size.width, 64);
+ _headerWrapperView.frame = CGRectMake(screenEdges.left, _portraitToolbarView.frame.origin.y - 64.0 - [_captionMixin.inputPanel baseHeight], self.frame.size.width, 64.0);
}
break;
}
diff --git a/submodules/LegacyComponents/Sources/TGMediaPickerGalleryVideoItemView.m b/submodules/LegacyComponents/Sources/TGMediaPickerGalleryVideoItemView.m
index 4e8c6bcaea..84c7f381d7 100644
--- a/submodules/LegacyComponents/Sources/TGMediaPickerGalleryVideoItemView.m
+++ b/submodules/LegacyComponents/Sources/TGMediaPickerGalleryVideoItemView.m
@@ -71,6 +71,8 @@
bool _scrubbingPanelPresented;
bool _scrubbingPanelLocked;
bool _shouldResetScrubber;
+ NSArray *_cachedThumbnails;
+ UIImage *_immediateThumbnail;
UILabel *_fileInfoLabel;
@@ -215,13 +217,12 @@
_headerView = headerView;
_headerView.autoresizingMask = UIViewAutoresizingFlexibleWidth;
- _scrubberPanelView = [[UIView alloc] initWithFrame:CGRectMake(0, -64, _headerView.frame.size.width, 64)];
+ _scrubberPanelView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, _headerView.frame.size.width, 64)];
_scrubberPanelView.autoresizingMask = UIViewAutoresizingFlexibleWidth;
- _scrubberPanelView.hidden = true;
headerView.panelView = _scrubberPanelView;
[_headerView addSubview:_scrubberPanelView];
- UIView *scrubberBackgroundView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, -100.0f, _headerView.frame.size.width, 164.0f)];
+ UIView *scrubberBackgroundView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, _headerView.frame.size.width, 64.0f)];
scrubberBackgroundView.autoresizingMask = UIViewAutoresizingFlexibleWidth;
scrubberBackgroundView.backgroundColor = [TGPhotoEditorInterfaceAssets toolbarTransparentBackgroundColor];
[_scrubberPanelView addSubview:scrubberBackgroundView];
@@ -301,7 +302,6 @@
_appeared = false;
[self setScrubbingPanelApperanceLocked:false];
- [self setScrubbingPanelHidden:true animated:false];
[_positionTimer invalidate];
_positionTimer = nil;
@@ -386,9 +386,14 @@
- (void)setItem:(TGMediaPickerGalleryVideoItem *)item synchronously:(bool)synchronously
{
bool itemChanged = ![item isEqual:self.item];
+ bool itemIdChanged = item.uniqueId != self.item.uniqueId;
[super setItem:item synchronously:synchronously];
+ if (itemIdChanged) {
+ _immediateThumbnail = item.immediateThumbnailImage;
+ }
+
if (itemChanged) {
[self _playerCleanup];
@@ -618,17 +623,15 @@
void (^changeBlock)(void) = ^
{
- _scrubberPanelView.frame = CGRectMake(0.0f, -64.0f - _safeAreaInset.top, _scrubberPanelView.frame.size.width, _scrubberPanelView.frame.size.height);
+ _scrubberPanelView.alpha = 0.0f;
};
void (^completionBlock)(BOOL) = ^(BOOL finished)
{
- if (finished)
- _scrubberPanelView.hidden = true;
};
if (animated)
{
- [UIView animateWithDuration:0.3f delay:0.0f options:(7 << 16) animations:changeBlock completion:completionBlock];
+ [UIView animateWithDuration:0.2f delay:0.0f options:UIViewAnimationOptionCurveLinear animations:changeBlock completion:completionBlock];
}
else
{
@@ -642,18 +645,17 @@
return;
_scrubbingPanelPresented = true;
-
- _scrubberPanelView.hidden = false;
+
[_scrubberPanelView layoutSubviews];
[_scrubberView layoutSubviews];
void (^changeBlock)(void) = ^
{
- _scrubberPanelView.frame = CGRectMake(0.0f, 0.0f, _scrubberPanelView.frame.size.width, _scrubberPanelView.frame.size.height);
+ _scrubberPanelView.alpha = 1.0f;
};
if (animated)
- [UIView animateWithDuration:0.3f delay:0.0f options:(7 << 16) animations:changeBlock completion:nil];
+ [UIView animateWithDuration:0.2f delay:0.0f options:UIViewAnimationOptionCurveLinear animations:changeBlock completion:nil];
else
changeBlock();
}
@@ -708,7 +710,6 @@
{
[_scrubberView resetThumbnails];
- [self setScrubbingPanelHidden:true animated:false];
[_scrubberPanelView setNeedsLayout];
[_scrubberPanelView layoutIfNeeded];
@@ -722,11 +723,14 @@
if (_containerView == nil)
return;
- _containerView.frame = self.bounds;
+ if (self.bounds.size.width > self.bounds.size.height)
+ _containerView.frame = self.bounds;
+ else
+ _containerView.frame = CGRectMake(0, 0, self.bounds.size.width, self.bounds.size.height - 44.0);
[self _layoutPlayerView];
- _videoContentView.frame = (CGRect){CGPointZero, frame.size};
+ _videoContentView.frame = (CGRect){CGPointZero, _containerView.frame.size};
if (_tooltipContainerView != nil && frame.size.width > frame.size.height)
{
@@ -753,8 +757,6 @@
mirrored = adjustments.cropMirrored;
}
-// _scrubberView.maximumLength = adjustments.sendAsGif ? TGVideoEditMaximumGifDuration : 0.0;
-
[self _layoutPlayerViewWithCropRect:cropRect videoFrameSize:videoFrameSize orientation:orientation mirrored:mirrored];
}
@@ -1440,40 +1442,8 @@
trimEndValue = adjustments.trimEndValue;
}
}
-// NSTimeInterval trimDuration = trimEndValue - trimStartValue;
bool sendAsGif = !adjustments.sendAsGif;
-// if (sendAsGif && _scrubberView.allowsTrimming)
-// {
-// if (trimDuration > TGVideoEditMaximumGifDuration)
-// {
-// trimEndValue = trimStartValue + TGVideoEditMaximumGifDuration;
-//
-// if (_scrubberView.value > trimEndValue)
-// {
-// [self stop];
-// [_scrubberView setValue:_scrubberView.trimStartValue resetPosition:true];
-// [self _seekToPosition:_scrubberView.value manual:true];
-// }
-//
-// _scrubberView.trimStartValue = trimStartValue;
-// _scrubberView.trimEndValue = trimEndValue;
-// [_scrubberView setTrimApplied:true];
-// [self updatePlayerRange:trimEndValue];
-// }
-// }
-// else if (_shouldResetScrubber)
-// {
-// trimStartValue = 0.0;
-// trimEndValue = _videoDuration;
-//
-// _scrubberView.trimStartValue = trimStartValue;
-// _scrubberView.trimEndValue = trimEndValue;
-//
-// [_scrubberView setTrimApplied:false];
-// [self updatePlayerRange:trimEndValue];
-// }
-
TGVideoEditAdjustments *updatedAdjustments = [TGVideoEditAdjustments editAdjustmentsWithOriginalSize:_videoDimensions cropRect:cropRect cropOrientation:adjustments.cropOrientation cropRotation:adjustments.cropRotation cropLockedAspectRatio:adjustments.cropLockedAspectRatio cropMirrored:adjustments.cropMirrored trimStartValue:trimStartValue trimEndValue:trimEndValue toolValues:adjustments.toolValues paintingData:adjustments.paintingData sendAsGif:sendAsGif preset:adjustments.preset];
[self.item.editingContext setAdjustments:updatedAdjustments forItem:self.item.editableMediaItem];
@@ -1596,6 +1566,23 @@
return timestamps;
}
+- (SSignal *)_placeholderThumbnails:(NSArray *)timestamps {
+ NSMutableArray *thumbnails = [[NSMutableArray alloc] init];
+
+ UIImage *image = _immediateThumbnail;
+ if (image == nil)
+ return [SSignal complete];
+
+ UIImage *blurredImage = TGBlurredRectangularImage(image, true, image.size, image.size, NULL, nil);
+ for (__unused NSNumber *value in timestamps) {
+ if (thumbnails.count == 0)
+ [thumbnails addObject:image];
+ else
+ [thumbnails addObject:blurredImage];
+ }
+ return [SSignal single:thumbnails];
+}
+
- (void)videoScrubber:(TGMediaPickerGalleryVideoScrubber *)__unused videoScrubber requestThumbnailImagesForTimestamps:(NSArray *)timestamps size:(CGSize)size isSummaryThumbnails:(bool)isSummaryThumbnails
{
if (timestamps.count == 0)
@@ -1605,17 +1592,42 @@
TGMediaEditingContext *editingContext = self.item.editingContext;
id editableItem = self.editableMediaItem;
- SSignal *thumbnailsSignal = nil;
- if ([self.item.asset isKindOfClass:[TGMediaAsset class]] && ![self itemIsLivePhoto])
- thumbnailsSignal = [TGMediaAssetImageSignals videoThumbnailsForAsset:self.item.asset size:size timestamps:timestamps];
- else if (avAsset != nil)
- thumbnailsSignal = [avAsset mapToSignal:^SSignal *(AVAsset *avAsset) {
- return [TGMediaAssetImageSignals videoThumbnailsForAVAsset:avAsset size:size timestamps:timestamps];
- }];
+// SSignal *thumbnailsSignal = nil;
+// if ([self.item.asset isKindOfClass:[TGMediaAsset class]] && ![self itemIsLivePhoto])
+// thumbnailsSignal = [TGMediaAssetImageSignals videoThumbnailsForAsset:self.item.asset size:size timestamps:timestamps];
+// else if (avAsset != nil)
+// thumbnailsSignal = [avAsset mapToSignal:^SSignal *(AVAsset *avAsset) {
+// return [TGMediaAssetImageSignals videoThumbnailsForAVAsset:avAsset size:size timestamps:timestamps];
+// }];
+ __strong TGMediaPickerGalleryVideoItemView *weakSelf = self;
+ SSignal *thumbnailsSignal = nil;
+ if (_cachedThumbnails != nil) {
+ thumbnailsSignal = [SSignal single:_cachedThumbnails];
+ } else if ([self.item.asset isKindOfClass:[TGMediaAsset class]] && ![self itemIsLivePhoto]) {
+ thumbnailsSignal = [[self _placeholderThumbnails:timestamps] then:[[TGMediaAssetImageSignals videoThumbnailsForAsset:(TGMediaAsset *)self.item.asset size:size timestamps:timestamps] onNext:^(NSArray *images) {
+ __strong TGMediaPickerGalleryVideoItemView *strongSelf = weakSelf;
+ if (strongSelf == nil)
+ return;
+
+ if (strongSelf->_cachedThumbnails == nil)
+ strongSelf->_cachedThumbnails = images;
+ }]];
+ } else if ([self.item.asset isKindOfClass:[TGCameraCapturedVideo class]]) {
+ thumbnailsSignal = [[((TGCameraCapturedVideo *)self.item.asset).avAsset takeLast] mapToSignal:^SSignal *(AVAsset *avAsset) {
+ return [[self _placeholderThumbnails:timestamps] then:[[TGMediaAssetImageSignals videoThumbnailsForAVAsset:avAsset size:size timestamps:timestamps] onNext:^(NSArray *images) {
+ __strong TGMediaPickerGalleryVideoItemView *strongSelf = weakSelf;
+ if (strongSelf == nil)
+ return;
+
+ if (strongSelf->_cachedThumbnails == nil)
+ strongSelf->_cachedThumbnails = images;
+ }]];
+ }];
+ }
+
_requestingThumbnails = true;
- __weak TGMediaPickerGalleryVideoItemView *weakSelf = self;
[_thumbnailsDisposable setDisposable:[[[thumbnailsSignal map:^NSArray *(NSArray *images) {
id adjustments = [editingContext adjustmentsForItem:editableItem];
if (adjustments.toolsApplied) {
diff --git a/submodules/LegacyComponents/Sources/TGMediaPickerGalleryVideoScrubber.h b/submodules/LegacyComponents/Sources/TGMediaPickerGalleryVideoScrubber.h
index fcb8170187..07e3a8613b 100644
--- a/submodules/LegacyComponents/Sources/TGMediaPickerGalleryVideoScrubber.h
+++ b/submodules/LegacyComponents/Sources/TGMediaPickerGalleryVideoScrubber.h
@@ -19,6 +19,7 @@
- (void)setDotVideoView:(UIView *)dotVideoView;
- (void)setDotImage:(UIImage *)dotImage;
+@property (nonatomic, assign) NSTimeInterval minimumLength;
@property (nonatomic, assign) NSTimeInterval maximumLength;
@property (nonatomic, assign) bool disableZoom;
@@ -44,8 +45,6 @@
- (void)setThumbnailImage:(UIImage *)image forTimestamp:(NSTimeInterval)timestamp index:(NSInteger)index isSummaryThubmnail:(bool)isSummaryThumbnail;
-- (void)setRecipientName:(NSString *)recipientName;
-
- (CGPoint)scrubberPositionForPosition:(NSTimeInterval)position;
- (void)_updateScrubberAnimationsAndResetCurrentPosition:(bool)resetCurrentPosition;
diff --git a/submodules/LegacyComponents/Sources/TGMediaPickerGalleryVideoScrubber.m b/submodules/LegacyComponents/Sources/TGMediaPickerGalleryVideoScrubber.m
index 50dd34db80..c60928ba7e 100644
--- a/submodules/LegacyComponents/Sources/TGMediaPickerGalleryVideoScrubber.m
+++ b/submodules/LegacyComponents/Sources/TGMediaPickerGalleryVideoScrubber.m
@@ -81,9 +81,6 @@ typedef enum
NSInteger _zoomedPivotTimestampIndex;
NSArray *_zoomedTimestamps;
NSMutableArray *_zoomedThumbnailViews;
-
- UIImageView *_arrowView;
- UILabel *_recipientLabel;
}
@end
@@ -95,6 +92,7 @@ typedef enum
if (self != nil)
{
_allowsTrimming = true;
+ _minimumLength = TGVideoScrubberMinimumTrimDuration;
_currentTimeLabel = [[UILabel alloc] initWithFrame:CGRectMake(8, 4, 100, 15)];
_currentTimeLabel.font = TGSystemFontOfSize(12.0f);
@@ -237,7 +235,7 @@ typedef enum
NSTimeInterval duration = trimEndPosition - trimStartPosition;
- if (trimEndPosition - trimStartPosition < TGVideoScrubberMinimumTrimDuration)
+ if (trimEndPosition - trimStartPosition < self.minimumLength)
return;
if (strongSelf.maximumLength > DBL_EPSILON && duration > strongSelf.maximumLength)
@@ -300,7 +298,7 @@ typedef enum
NSTimeInterval duration = trimEndPosition - trimStartPosition;
- if (trimEndPosition - trimStartPosition < TGVideoScrubberMinimumTrimDuration)
+ if (trimEndPosition - trimStartPosition < self.minimumLength)
return;
if (strongSelf.maximumLength > DBL_EPSILON && duration > strongSelf.maximumLength)
@@ -416,33 +414,10 @@ typedef enum
_tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap:)];
_tapGestureRecognizer.enabled = false;
[_trimView addGestureRecognizer:_tapGestureRecognizer];
-
- _arrowView = [[UIImageView alloc] initWithImage:TGComponentsImageNamed(@"PhotoPickerArrow")];
- _arrowView.alpha = 0.45f;
- _arrowView.hidden = true;
- [self addSubview:_arrowView];
-
- _recipientLabel = [[UILabel alloc] init];
- _recipientLabel.backgroundColor = [UIColor clearColor];
- _recipientLabel.font = TGBoldSystemFontOfSize(13.0f);
- _recipientLabel.textColor = UIColorRGBA(0xffffff, 0.45f);
- _recipientLabel.hidden = true;
- [self addSubview:_recipientLabel];
}
return self;
}
-- (void)setRecipientName:(NSString *)recipientName
-{
- _recipientLabel.text = recipientName;
- _recipientLabel.hidden = recipientName.length == 0;
- _arrowView.hidden = _recipientLabel.hidden;
-
- [_recipientLabel sizeToFit];
-
- [self _layoutRecipientLabel];
-}
-
- (void)setHasDotPicker:(bool)hasDotPicker {
_hasDotPicker = hasDotPicker;
_tapGestureRecognizer.enabled = hasDotPicker;
@@ -1051,11 +1026,9 @@ typedef enum
- (void)_updateTimeLabels
{
- _currentTimeLabel.text = @"";
+ _currentTimeLabel.text = self.disableTimeDisplay ? @"" : [TGMediaPickerGalleryVideoScrubber _stringFromTotalSeconds:(NSInteger)self.value];
- NSString *text = [NSString stringWithFormat:@"%@ / %@", [TGMediaPickerGalleryVideoScrubber _stringFromTotalSeconds:(NSInteger)self.value], [TGMediaPickerGalleryVideoScrubber _stringFromTotalSeconds:(NSInteger)self.duration]];
-
- _inverseTimeLabel.text = self.disableTimeDisplay ? @"" : text;
+ _inverseTimeLabel.text = self.disableTimeDisplay ? @"" : [TGMediaPickerGalleryVideoScrubber _stringFromTotalSeconds:(NSInteger)self.duration];
}
#pragma mark - Scrubber Handle
@@ -1465,18 +1438,6 @@ typedef enum
}
}
-- (void)_layoutRecipientLabel
-{
- if (self.frame.size.width < FLT_EPSILON)
- return;
-
- CGFloat screenWidth = MAX(self.frame.size.width, self.frame.size.height);
- CGFloat recipientWidth = MIN(_recipientLabel.frame.size.width, screenWidth - 100.0f);
-
- _arrowView.frame = CGRectMake(14.0f, 6.0f, _arrowView.frame.size.width, _arrowView.frame.size.height);
- _recipientLabel.frame = CGRectMake(CGRectGetMaxX(_arrowView.frame) + 6.0f, _arrowView.frame.origin.y - 2.0f, recipientWidth, _recipientLabel.frame.size.height);
-}
-
- (void)setFrame:(CGRect)frame
{
if (isnan(frame.origin.x) || isnan(frame.origin.y) || isnan(frame.size.width) || isnan(frame.size.height))
@@ -1500,8 +1461,6 @@ typedef enum
_zoomedThumbnailWrapperView.frame = _summaryThumbnailWrapperView.frame;
[self _updateScrubberAnimationsAndResetCurrentPosition:true];
-
- [self _layoutRecipientLabel];
}
+ (NSString *)_stringFromTotalSeconds:(NSInteger)totalSeconds
diff --git a/submodules/LegacyComponents/Sources/TGMediaPickerPhotoCounterButton.m b/submodules/LegacyComponents/Sources/TGMediaPickerPhotoCounterButton.m
index ebc05148dc..8a94b4c4e2 100644
--- a/submodules/LegacyComponents/Sources/TGMediaPickerPhotoCounterButton.m
+++ b/submodules/LegacyComponents/Sources/TGMediaPickerPhotoCounterButton.m
@@ -48,7 +48,7 @@ const CGFloat TGPhotoCounterButtonMaskFade = 18;
{
UIGraphicsBeginImageContextWithOptions(CGSizeMake(38.0f, 38.0f), false, 0.0f);
CGContextRef context = UIGraphicsGetCurrentContext();
- CGContextSetFillColorWithColor(context, UIColorRGBA(0x000000, 0.7f).CGColor);
+ CGContextSetFillColorWithColor(context, UIColorRGBA(0x000000, 0.3f).CGColor);
CGContextFillEllipseInRect(context, CGRectMake(3.5f, 1.0f, 31.0f, 31.0f));
diff --git a/submodules/LegacyComponents/Sources/TGMediaVideoConverter.m b/submodules/LegacyComponents/Sources/TGMediaVideoConverter.m
index 070ea4892e..32340c5c77 100644
--- a/submodules/LegacyComponents/Sources/TGMediaVideoConverter.m
+++ b/submodules/LegacyComponents/Sources/TGMediaVideoConverter.m
@@ -125,7 +125,7 @@
CGSize dimensions = [avAsset tracksWithMediaType:AVMediaTypeVideo].firstObject.naturalSize;
TGMediaVideoConversionPreset preset = adjustments.sendAsGif ? TGMediaVideoConversionPresetAnimation : [self presetFromAdjustments:adjustments];
- if (!CGSizeEqualToSize(dimensions, CGSizeZero) && preset != TGMediaVideoConversionPresetAnimation && preset != TGMediaVideoConversionPresetVideoMessage && preset != TGMediaVideoConversionPresetProfile && preset != TGMediaVideoConversionPresetProfileHigh && preset != TGMediaVideoConversionPresetProfileVeryHigh && preset != TGMediaVideoConversionPresetPassthrough)
+ if (!CGSizeEqualToSize(dimensions, CGSizeZero) && preset != TGMediaVideoConversionPresetAnimation && preset != TGMediaVideoConversionPresetVideoMessage && preset != TGMediaVideoConversionPresetProfile && preset != TGMediaVideoConversionPresetProfileLow && preset != TGMediaVideoConversionPresetProfileHigh && preset != TGMediaVideoConversionPresetProfileVeryHigh && preset != TGMediaVideoConversionPresetPassthrough)
{
TGMediaVideoConversionPreset bestPreset = [self bestAvailablePresetForDimensions:dimensions];
if (preset > bestPreset)
@@ -240,7 +240,7 @@
return;
TGMediaVideoConversionPreset preset = TGMediaVideoConversionPresetAnimation;
- if (adjustments.preset == TGMediaVideoConversionPresetProfile || adjustments.preset == TGMediaVideoConversionPresetProfileHigh || adjustments.preset == TGMediaVideoConversionPresetProfileVeryHigh) {
+ if (adjustments.preset == TGMediaVideoConversionPresetProfile || adjustments.preset != TGMediaVideoConversionPresetProfileLow || adjustments.preset == TGMediaVideoConversionPresetProfileHigh || adjustments.preset == TGMediaVideoConversionPresetProfileVeryHigh) {
preset = adjustments.preset;
}
@@ -355,7 +355,7 @@
if (TGOrientationIsSideward(adjustments.cropOrientation, NULL))
outputDimensions = CGSizeMake(outputDimensions.height, outputDimensions.width);
- if ((preset == TGMediaVideoConversionPresetProfile || preset == TGMediaVideoConversionPresetProfileHigh || preset == TGMediaVideoConversionPresetProfileVeryHigh) && MIN(outputDimensions.width, outputDimensions.height) < 160.0) {
+ if ((preset == TGMediaVideoConversionPresetProfile || preset == TGMediaVideoConversionPresetProfileLow || preset == TGMediaVideoConversionPresetProfileHigh || preset == TGMediaVideoConversionPresetProfileVeryHigh) && MIN(outputDimensions.width, outputDimensions.height) < 160.0) {
outputDimensions = CGSizeMake(160.0, 160.0);
}
@@ -1255,6 +1255,9 @@ static CGFloat progressOfSampleBufferInTimeRange(CMSampleBufferRef sampleBuffer,
case TGMediaVideoConversionPresetVideoMessage:
return (CGSize){ 240.0f, 240.0f };
+ case TGMediaVideoConversionPresetProfileLow:
+ return (CGSize){ 720.0f, 720.0f };
+
case TGMediaVideoConversionPresetProfile:
case TGMediaVideoConversionPresetProfileHigh:
case TGMediaVideoConversionPresetProfileVeryHigh:
@@ -1267,7 +1270,7 @@ static CGFloat progressOfSampleBufferInTimeRange(CMSampleBufferRef sampleBuffer,
+ (bool)keepAudioForPreset:(TGMediaVideoConversionPreset)preset
{
- return preset != TGMediaVideoConversionPresetAnimation && preset != TGMediaVideoConversionPresetProfile && preset != TGMediaVideoConversionPresetProfileHigh && preset != TGMediaVideoConversionPresetProfileVeryHigh;
+ return preset != TGMediaVideoConversionPresetAnimation && preset != TGMediaVideoConversionPresetProfile && preset != TGMediaVideoConversionPresetProfileLow && preset != TGMediaVideoConversionPresetProfileHigh && preset != TGMediaVideoConversionPresetProfileVeryHigh;
}
+ (NSDictionary *)audioSettingsForPreset:(TGMediaVideoConversionPreset)preset
@@ -1346,11 +1349,14 @@ static CGFloat progressOfSampleBufferInTimeRange(CMSampleBufferRef sampleBuffer,
case TGMediaVideoConversionPresetProfile:
return 1500;
+ case TGMediaVideoConversionPresetProfileLow:
+ return 1100;
+
case TGMediaVideoConversionPresetProfileHigh:
return 2000;
case TGMediaVideoConversionPresetProfileVeryHigh:
- return 2500;
+ return 2400;
default:
return 900;
@@ -1381,6 +1387,7 @@ static CGFloat progressOfSampleBufferInTimeRange(CMSampleBufferRef sampleBuffer,
case TGMediaVideoConversionPresetAnimation:
case TGMediaVideoConversionPresetProfile:
+ case TGMediaVideoConversionPresetProfileLow:
case TGMediaVideoConversionPresetProfileHigh:
case TGMediaVideoConversionPresetProfileVeryHigh:
return 0;
@@ -1411,6 +1418,7 @@ static CGFloat progressOfSampleBufferInTimeRange(CMSampleBufferRef sampleBuffer,
case TGMediaVideoConversionPresetAnimation:
case TGMediaVideoConversionPresetProfile:
+ case TGMediaVideoConversionPresetProfileLow:
case TGMediaVideoConversionPresetProfileHigh:
case TGMediaVideoConversionPresetProfileVeryHigh:
return 0;
diff --git a/submodules/LegacyComponents/Sources/TGPhotoAvatarCropView.m b/submodules/LegacyComponents/Sources/TGPhotoAvatarCropView.m
index ddc7a2d393..26bc80caa5 100644
--- a/submodules/LegacyComponents/Sources/TGPhotoAvatarCropView.m
+++ b/submodules/LegacyComponents/Sources/TGPhotoAvatarCropView.m
@@ -146,10 +146,19 @@ const CGFloat TGPhotoAvatarCropViewCurtainMargin = 200;
UITapGestureRecognizer *tapRecognier = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap:)];
[_wrapperView addGestureRecognizer:tapRecognier];
+
+ _clipView = [[UIView alloc] init];
+ _clipView.clipsToBounds = true;
+ _clipView.userInteractionEnabled = false;
+ [self addSubview:_clipView];
}
return self;
}
+- (void)attachEntitiesView {
+ [_entitiesWrapperView addSubview:_fullEntitiesView];
+}
+
- (void)dealloc
{
_scrollView.delegate = nil;
@@ -644,6 +653,8 @@ const CGFloat TGPhotoAvatarCropViewCurtainMargin = 200;
{
[self _layoutOverlayViews];
+ _clipView.frame = self.bounds;
+
_flashView.frame = self.bounds;
if (_scrollView.superview == nil)
diff --git a/submodules/LegacyComponents/Sources/TGPhotoAvatarPreviewController.m b/submodules/LegacyComponents/Sources/TGPhotoAvatarPreviewController.m
index 76dbafa2f5..aa54d305f6 100644
--- a/submodules/LegacyComponents/Sources/TGPhotoAvatarPreviewController.m
+++ b/submodules/LegacyComponents/Sources/TGPhotoAvatarPreviewController.m
@@ -16,6 +16,7 @@
#import "TGMediaPickerGalleryVideoScrubber.h"
#import "TGModernGalleryVideoView.h"
+#import "TGPhotoEntitiesContainerView.h"
#import "TGPhotoPaintController.h"
@@ -257,6 +258,58 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
[self.view insertSubview:_transitionView belowSubview:_wrapperView];
}
+- (void)animateTransitionIn {
+ if (self.initialAppearance) {
+ [super animateTransitionIn];
+ return;
+ } else {
+ _animateScale = true;
+
+ [self transitEntities:_previewView];
+
+ [super animateTransitionIn];
+ }
+}
+
++ (CGRect)fittedCropRect:(CGRect)cropRect originalSize:(CGSize)originalSize fitSize:(CGSize)fitSize {
+ CGSize fittedOriginalSize = TGScaleToSize(originalSize, fitSize);
+ CGFloat scale = fittedOriginalSize.width / originalSize.width;
+
+ CGSize size = fittedOriginalSize;
+
+ return CGRectMake(-cropRect.origin.x * scale, -cropRect.origin.y * scale, size.width, size.height);
+}
+
+- (void)transitEntities:(UIView *)parentView {
+ UIView *containerView = [[UIView alloc] init];
+ [parentView addSubview:containerView];
+
+ containerView.frame = CGRectMake(0.0, 0.0, _fullEntitiesView.frame.size.width, _fullEntitiesView.frame.size.height);
+ [containerView addSubview:_fullEntitiesView];
+
+ CGFloat paintingScale = _fullEntitiesView.frame.size.width / _photoEditor.originalSize.width;
+ _fullEntitiesView.frame = CGRectMake(-_photoEditor.cropRect.origin.x * paintingScale, -_photoEditor.cropRect.origin.y * paintingScale, _fullEntitiesView.frame.size.width, _fullEntitiesView.frame.size.height);
+
+ CGFloat cropScale = 1.0;
+ if (_photoEditor.originalSize.width > _photoEditor.originalSize.height) {
+ cropScale = _photoEditor.originalSize.height / _photoEditor.cropRect.size.height;
+ } else {
+ cropScale = _photoEditor.originalSize.width / _photoEditor.cropRect.size.width;
+ }
+
+ UIImageOrientation imageOrientation = _photoEditor.cropOrientation;
+ if ([parentView isKindOfClass:[TGPhotoEditorPreviewView class]])
+ imageOrientation = UIImageOrientationUp;
+
+ CGAffineTransform rotationTransform = CGAffineTransformMakeRotation(TGRotationForOrientation(imageOrientation));
+ if ([parentView isKindOfClass:[TGPhotoEditorPreviewView class]] && _photoEditor.cropMirrored) {
+ rotationTransform = CGAffineTransformMakeScale(-1.0, 1.0);
+ }
+ CGFloat scale = parentView.frame.size.width / _fullEntitiesView.frame.size.width;
+ containerView.transform = CGAffineTransformScale(rotationTransform, scale * cropScale, scale * cropScale);
+ containerView.frame = CGRectMake(0.0, 0.0, parentView.frame.size.width, parentView.frame.size.height);
+}
+
- (void)transitionIn
{
if (_portraitToolsWrapperView.frame.size.height < FLT_EPSILON) {
@@ -373,13 +426,22 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
[_cropView closeCurtains];
+ [self transitEntities:_cropView.clipView];
+
+ CGAffineTransform initialTransform = _previewView.transform;
[UIView animateWithDuration:0.3f delay:0.0f options:UIViewAnimationOptionCurveEaseInOut | UIViewAnimationOptionLayoutSubviews animations:^
{
- _previewView.frame = targetFrame;
+ CGFloat scale = targetFrame.size.width / _previewView.frame.size.width;
+ _previewView.center = CGPointMake(CGRectGetMidX(targetFrame), CGRectGetMidY(targetFrame));
+ _previewView.transform = CGAffineTransformScale(initialTransform, scale, scale);
+
_cropView.center = CGPointMake(CGRectGetMidX(targetCropViewFrame), CGRectGetMidY(targetCropViewFrame));
_cropView.transform = CGAffineTransformMakeScale(targetCropViewScale, targetCropViewScale);
} completion:^(__unused BOOL finished)
- {
+ {
+ _fullEntitiesView.frame = CGRectMake(0, 0, _fullEntitiesView.frame.size.width, _fullEntitiesView.frame.size.height);
+ _previewView.transform = initialTransform;
+ _previewView.frame = targetFrame;
[_cropView removeFromSuperview];
_previewView.alpha = 1.0;
if (self.finishedTransitionOut != nil)
@@ -504,8 +566,13 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
{
_appeared = true;
+ if (!self.initialAppearance) {
+ [_fullEntitiesView.superview removeFromSuperview];
+ _fullEntitiesView.frame = CGRectMake(0, 0, _fullEntitiesView.frame.size.width, _fullEntitiesView.frame.size.height);
+ [_cropView attachEntitiesView];
+ }
+
if ([transitionView isKindOfClass:[TGPhotoEditorPreviewView class]]) {
-
} else {
[transitionView removeFromSuperview];
}
diff --git a/submodules/LegacyComponents/Sources/TGPhotoEditorController.m b/submodules/LegacyComponents/Sources/TGPhotoEditorController.m
index ed1d18f949..6a5e18795e 100644
--- a/submodules/LegacyComponents/Sources/TGPhotoEditorController.m
+++ b/submodules/LegacyComponents/Sources/TGPhotoEditorController.m
@@ -82,6 +82,7 @@
UIImage *_thumbnailImage;
CMTime _chaseTime;
+ bool _chaseStart;
bool _chasingTime;
bool _isPlaying;
AVPlayerItem *_playerItem;
@@ -297,8 +298,7 @@
break;
}
};
-
-
+
TGPhotoEditorBackButton backButton = TGPhotoEditorBackButtonCancel;
TGPhotoEditorDoneButton doneButton = TGPhotoEditorDoneButtonCheck;
_portraitToolbarView = [[TGPhotoToolbarView alloc] initWithBackButton:backButton doneButton:doneButton solidBackground:true];
@@ -368,6 +368,7 @@
if ([self presentedForAvatarCreation] && _item.isVideo) {
_scrubberView = [[TGMediaPickerGalleryVideoScrubber alloc] initWithFrame:CGRectMake(0.0f, 0.0, _portraitToolbarView.frame.size.width, 68.0f)];
+ _scrubberView.minimumLength = 3.0;
_scrubberView.layer.allowsGroupOpacity = true;
_scrubberView.hasDotPicker = true;
_scrubberView.dataSource = self;
@@ -481,6 +482,8 @@
if ([adjustments isKindOfClass:[TGMediaVideoEditAdjustments class]])
position = adjustments.trimStartValue;
+ PGPhotoEditor *photoEditor = _photoEditor;
+
CGSize screenSize = TGNativeScreenSize();
SSignal *signal = nil;
if ([_photoEditor hasDefaultCropping] && (NSInteger)screenSize.width == 320)
@@ -504,20 +507,18 @@
if (avatar) {
return image;
} else {
- return TGPhotoEditorCrop(image, nil, _photoEditor.cropOrientation, _photoEditor.cropRotation, _photoEditor.cropRect, _photoEditor.cropMirrored, TGPhotoEditorScreenImageMaxSize(), _photoEditor.originalSize, true);
+ return TGPhotoEditorCrop(image, nil, photoEditor.cropOrientation, photoEditor.cropRotation, photoEditor.cropRect, photoEditor.cropMirrored, TGPhotoEditorScreenImageMaxSize(), photoEditor.originalSize, true);
}
}];
}
}
__weak TGPhotoEditorController *weakSelf = self;
- PGPhotoEditor *photoEditor = _photoEditor;
-
[signal startWithNext:^(id next)
{
__strong TGPhotoEditorController *strongSelf = weakSelf;
- if (strongSelf == nil)
- return;
+ if (strongSelf == nil)
+ return;
if (strongSelf->_dismissed)
return;
@@ -566,7 +567,6 @@
if (strongSelf->_hadProgress && !progressVisible) {
[strongSelf->_progressView setPlay];
-
[strongSelf->_scrubberView reloadThumbnails];
}
});
@@ -622,7 +622,10 @@
- (NSTimeInterval)trimEndValue {
if (_scrubberView != nil) {
- return _scrubberView.trimEndValue;
+ if (_scrubberView.trimEndValue > 0.0)
+ return _scrubberView.trimEndValue;
+ else
+ return MIN(9.9, _scrubberView.duration);
} else {
return _photoEditor.trimEndValue;
}
@@ -669,6 +672,9 @@
if (strongSelf != nil && !strongSelf->_dismissed) {
[strongSelf->_player seekToTime:startTime];
[strongSelf->_scrubberView setValue:strongSelf.trimStartValue resetPosition:true];
+
+ [strongSelf->_fullEntitiesView seekTo:0.0];
+ [strongSelf->_fullEntitiesView play];
}
}];
}
@@ -698,6 +704,11 @@
[_player addObserver:self forKeyPath:@"rate" options:NSKeyValueObservingOptionNew context:nil];
_registeredKeypathObserver = true;
}
+
+ [_fullEntitiesView seekTo:0.0];
+ [_fullEntitiesView play];
+ } else {
+ [_fullEntitiesView play];
}
_isPlaying = true;
@@ -721,6 +732,8 @@
}
[_scrubberView setIsPlaying:false];
+ } else {
+ [_fullEntitiesView pause];
}
_isPlaying = false;
@@ -747,6 +760,14 @@
}
}
+- (NSTimeInterval)currentTime {
+ return CMTimeGetSeconds(_player.currentItem.currentTime) - [self trimStartValue];
+}
+
+- (void)setMinimalVideoDuration:(NSTimeInterval)duration {
+ _scrubberView.minimumLength = duration;
+}
+
- (void)seekVideo:(NSTimeInterval)position {
CMTime targetTime = CMTimeMakeWithSeconds(position, NSEC_PER_SEC);
@@ -765,6 +786,11 @@
CMTime currentChasingTime = _chaseTime;
[_player.currentItem seekToTime:currentChasingTime toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero completionHandler:^(BOOL finished) {
+ if (!_chaseStart) {
+ TGDispatchOnMainThread(^{
+ [_fullEntitiesView seekTo:CMTimeGetSeconds(currentChasingTime) - _scrubberView.trimStartValue];
+ });
+ }
if (CMTIME_COMPARE_INLINE(currentChasingTime, ==, _chaseTime)) {
_chasingTime = false;
_chaseTime = kCMTimeInvalid;
@@ -965,6 +991,7 @@
}];
};
+ SQueue *queue = _queue;
SSignal *(^imageRenderSignal)(UIImage *) = ^(UIImage *image)
{
return [[SSignal alloc] initWithGenerator:^id(SSubscriber *subscriber)
@@ -972,7 +999,7 @@
[photoEditor setImage:image forCropRect:photoEditor.cropRect cropRotation:photoEditor.cropRotation cropOrientation:photoEditor.cropOrientation cropMirrored:photoEditor.cropMirrored fullSize:true];
[photoEditor createResultImageWithCompletion:^(UIImage *result)
{
- [_queue dispatch:^{
+ [queue dispatch:^{
UIImage *final = result;
if (hasPainting)
{
@@ -995,7 +1022,7 @@
}] mapToSignal:^SSignal *(UIImage *image)
{
if (hasImageAdjustments)
- return [[[SSignal complete] delay:0.3 onQueue:_queue] then:imageRenderSignal(image)];
+ return [[[SSignal complete] delay:0.3 onQueue:queue] then:imageRenderSignal(image)];
else
return [SSignal single:image];
}];
@@ -1171,6 +1198,7 @@
UIView *snapshotView = nil;
TGPhotoEditorTabController *currentController = _currentTabController;
+ TGPhotoEditorTab switchingFromTab = TGPhotoEditorNoneTab;
if (currentController != nil)
{
if (![currentController isDismissAllowed])
@@ -1178,13 +1206,18 @@
[self savePaintingData];
+ bool resetTransform = false;
+ if ([self presentedForAvatarCreation] && tab == TGPhotoEditorCropTab && [currentController isKindOfClass:[TGPhotoPaintController class]]) {
+ resetTransform = true;
+ }
+
currentController.switchingToTab = tab;
[currentController transitionOutSwitching:true completion:^
{
[currentController removeFromParentViewController];
[currentController.view removeFromSuperview];
- if ([self presentedForAvatarCreation] && tab == TGPhotoEditorCropTab) {
+ if (resetTransform) {
_previewView.transform = CGAffineTransformIdentity;
}
}];
@@ -1200,6 +1233,9 @@
{
_backgroundView.alpha = 0.0f;
} completion:nil];
+ switchingFromTab = TGPhotoEditorCropTab;
+ } else if ([currentController isKindOfClass:[TGPhotoToolsController class]]) {
+ switchingFromTab = TGPhotoEditorToolsTab;
}
isInitialAppearance = false;
@@ -1281,7 +1317,7 @@
cropController.toolbarLandscapeSize = TGPhotoEditorToolbarSize;
cropController.controlVideoPlayback = ^(bool play) {
__strong TGPhotoEditorController *strongSelf = weakSelf;
- if (strongSelf == nil)
+ if (strongSelf == nil || strongSelf->_progressVisible)
return;
if (play) {
[strongSelf startVideoPlayback:false];
@@ -1297,7 +1333,7 @@
};
cropController.togglePlayback = ^{
__strong TGPhotoEditorController *strongSelf = weakSelf;
- if (strongSelf == nil || !strongSelf->_item.isVideo)
+ if (strongSelf == nil || !strongSelf->_item.isVideo || strongSelf->_progressVisible)
return;
if (strongSelf->_isPlaying) {
@@ -1613,6 +1649,7 @@
_currentTabController = controller;
_currentTabController.item = _item;
_currentTabController.intent = _intent;
+ _currentTabController.switchingFromTab = switchingFromTab;
_currentTabController.initialAppearance = isInitialAppearance;
if (![_currentTabController isKindOfClass:[TGPhotoPaintController class]])
@@ -1915,7 +1952,7 @@
{
videoStartValue = _dotPosition;
trimStartValue = self.trimStartValue;
- trimEndValue = self.trimEndValue;
+ trimEndValue = MIN(self.trimStartValue + 9.9, self.trimEndValue);
}
[self stopVideoPlayback:true];
@@ -1945,7 +1982,7 @@
generator.requestedTimeToleranceAfter = kCMTimeZero;
generator.requestedTimeToleranceBefore = kCMTimeZero;
- CGImageRef imageRef = [generator copyCGImageAtTime:CMTimeMakeWithSeconds(videoStartValue, NSEC_PER_SEC) actualTime:nil error:NULL];
+ CGImageRef imageRef = [generator copyCGImageAtTime:CMTimeMakeWithSeconds(MIN(videoStartValue, CMTimeGetSeconds(asset.duration) - 0.05), NSEC_PER_SEC) actualTime:nil error:NULL];
UIImage *image = [UIImage imageWithCGImage:imageRef];
CGImageRelease(imageRef);
@@ -1979,10 +2016,16 @@
NSTimeInterval duration = trimEndValue - trimStartValue;
TGMediaVideoConversionPreset preset;
- if (duration <= 2.5) {
- preset = TGMediaVideoConversionPresetProfileVeryHigh;
- } else if (duration <= 5.0) {
- preset = TGMediaVideoConversionPresetProfileHigh;
+ if (duration > 0.0) {
+ if (duration <= 2.0) {
+ preset = TGMediaVideoConversionPresetProfileVeryHigh;
+ } else if (duration <= 5.0) {
+ preset = TGMediaVideoConversionPresetProfileHigh;
+ } else if (duration <= 8.0) {
+ preset = TGMediaVideoConversionPresetProfile;
+ } else {
+ preset = TGMediaVideoConversionPresetProfileLow;
+ }
} else {
preset = TGMediaVideoConversionPresetProfile;
}
@@ -2725,7 +2768,7 @@
});
}
-- (void)videoScrubber:(TGMediaPickerGalleryVideoScrubber *)__unused videoScrubber valueDidChange:(NSTimeInterval)position
+- (void)videoScrubber:(TGMediaPickerGalleryVideoScrubber *)videoScrubber valueDidChange:(NSTimeInterval)position
{
[self seekVideo:position];
}
@@ -2762,6 +2805,8 @@
[self startVideoPlayback:true];
[self setPlayButtonHidden:true animated:false];
+
+ _chaseStart = false;
}
- (void)videoScrubber:(TGMediaPickerGalleryVideoScrubber *)videoScrubber editingStartValueDidChange:(NSTimeInterval)startValue
@@ -2770,6 +2815,12 @@
_resetDotPosition = true;
[self resetDotImage];
}
+
+ if (!_chaseStart) {
+ _chaseStart = true;
+ [_fullEntitiesView resetToStart];
+ }
+
[self seekVideo:startValue];
}
diff --git a/submodules/LegacyComponents/Sources/TGPhotoEditorInterfaceAssets.m b/submodules/LegacyComponents/Sources/TGPhotoEditorInterfaceAssets.m
index 8beac5ce0e..67f1ec0008 100644
--- a/submodules/LegacyComponents/Sources/TGPhotoEditorInterfaceAssets.m
+++ b/submodules/LegacyComponents/Sources/TGPhotoEditorInterfaceAssets.m
@@ -126,7 +126,7 @@
CGRect rect = CGRectMake(0, 0, 39.0f, 39.0f);
UIGraphicsBeginImageContextWithOptions(rect.size, false, 0);
CGContextRef context = UIGraphicsGetCurrentContext();
- CGContextSetFillColorWithColor(context, UIColorRGBA(0x000000, 0.6f).CGColor);
+ CGContextSetFillColorWithColor(context, UIColorRGBA(0x000000, 0.3f).CGColor);
CGContextFillEllipseInRect(context, CGRectInset(rect, 3, 3));
muteBackground = UIGraphicsGetImageFromCurrentImageContext();
diff --git a/submodules/LegacyComponents/Sources/TGPhotoEditorTabController.m b/submodules/LegacyComponents/Sources/TGPhotoEditorTabController.m
index 842172cf41..be182acce2 100644
--- a/submodules/LegacyComponents/Sources/TGPhotoEditorTabController.m
+++ b/submodules/LegacyComponents/Sources/TGPhotoEditorTabController.m
@@ -169,22 +169,34 @@ const CGFloat TGPhotoEditorToolbarSize = 49.0f;
_transitionInProgress = true;
+ CGAffineTransform initialTransform = _transitionView.transform;
[UIView animateWithDuration:0.3f delay:0.0f options:UIViewAnimationOptionCurveEaseInOut | UIViewAnimationOptionLayoutSubviews animations:^
{
- _transitionView.frame = _transitionTargetFrame;
+ if (_animateScale) {
+ CGFloat scale = _transitionTargetFrame.size.width / _transitionView.frame.size.width;
+ _transitionView.center = CGPointMake(CGRectGetMidX(_transitionTargetFrame), CGRectGetMidY(_transitionTargetFrame));
+ _transitionView.transform = CGAffineTransformScale(initialTransform, scale, scale);
+ } else {
+ _transitionView.frame = _transitionTargetFrame;
+ }
} completion:^(BOOL finished) {
_transitionInProgress = false;
- UIView *transitionView = _transitionView;
- _transitionView = nil;
-
- if (self.finishedTransitionIn != nil)
- {
- self.finishedTransitionIn();
- self.finishedTransitionIn = nil;
- }
-
- [self _finishedTransitionInWithView:transitionView];
+ UIView *transitionView = _transitionView;
+ _transitionView = nil;
+
+ if (_animateScale) {
+ _transitionView.transform = initialTransform;
+ _transitionView.frame = _transitionTargetFrame;
+ }
+
+ if (self.finishedTransitionIn != nil)
+ {
+ self.finishedTransitionIn();
+ self.finishedTransitionIn = nil;
+ }
+
+ [self _finishedTransitionInWithView:transitionView];
}];
}
diff --git a/submodules/LegacyComponents/Sources/TGPhotoEditorUtils.m b/submodules/LegacyComponents/Sources/TGPhotoEditorUtils.m
index 91dd667420..c2a2882fe3 100644
--- a/submodules/LegacyComponents/Sources/TGPhotoEditorUtils.m
+++ b/submodules/LegacyComponents/Sources/TGPhotoEditorUtils.m
@@ -218,10 +218,10 @@ UIImage *TGPhotoEditorCrop(UIImage *inputImage, UIImage *paintingImage, UIImageO
}
UIImage *TGPhotoEditorVideoCrop(UIImage *inputImage, UIImage *paintingImage, UIImageOrientation orientation, CGFloat rotation, CGRect rect, bool mirrored, CGSize maxSize, CGSize originalSize, bool shouldResize, bool useImageSize) {
- return TGPhotoEditorVideoExtCrop(inputImage, paintingImage, orientation, rotation, rect, mirrored, maxSize, originalSize, shouldResize, useImageSize, false);
+ return TGPhotoEditorVideoExtCrop(inputImage, paintingImage, orientation, rotation, rect, mirrored, maxSize, originalSize, shouldResize, useImageSize, false, false);
}
-UIImage *TGPhotoEditorVideoExtCrop(UIImage *inputImage, UIImage *paintingImage, UIImageOrientation orientation, CGFloat rotation, CGRect rect, bool mirrored, CGSize maxSize, CGSize originalSize, bool shouldResize, bool useImageSize, bool skipImageTransform)
+UIImage *TGPhotoEditorVideoExtCrop(UIImage *inputImage, UIImage *paintingImage, UIImageOrientation orientation, CGFloat rotation, CGRect rect, bool mirrored, CGSize maxSize, CGSize originalSize, bool shouldResize, bool useImageSize, bool skipImageTransform, bool fillPainting)
{
if (iosMajorVersion() < 7)
return TGPhotoEditorLegacyCrop(inputImage, paintingImage, orientation, rotation, rect, mirrored, maxSize, shouldResize);
@@ -251,6 +251,9 @@ UIImage *TGPhotoEditorVideoExtCrop(UIImage *inputImage, UIImage *paintingImage,
UIGraphicsBeginImageContextWithOptions(CGSizeMake(outputImageSize.width, outputImageSize.height), true, 1.0f);
CGContextRef context = UIGraphicsGetCurrentContext();
+
+ CGContextSaveGState(context);
+
CGContextSetFillColorWithColor(context, [UIColor blackColor].CGColor);
CGContextFillRect(context, CGRectMake(0, 0, outputImageSize.width, outputImageSize.height));
CGContextSetInterpolationQuality(context, kCGInterpolationHigh);
@@ -297,6 +300,97 @@ UIImage *TGPhotoEditorVideoExtCrop(UIImage *inputImage, UIImage *paintingImage,
[image drawAtPoint:CGPointMake(-image.size.width / 2, -image.size.height / 2)];
}
+ if (paintingImage != nil)
+ {
+ if (fillPainting) {
+ CGContextRestoreGState(context);
+ [paintingImage drawInRect:CGRectMake(0.0, 0.0, outputImageSize.width, outputImageSize.height)];
+ } else {
+ if (mirrored)
+ CGContextScaleCTM(context, -1.0f, 1.0f);
+
+ [paintingImage drawInRect:CGRectMake(-imageSize.width / 2, -imageSize.height / 2, imageSize.width, imageSize.height)];
+ }
+ }
+
+ UIImage *croppedImage = UIGraphicsGetImageFromCurrentImageContext();
+ UIGraphicsEndImageContext();
+
+ return croppedImage;
+}
+
+UIImage *TGPhotoEditorPaintingCrop(UIImage *paintingImage, UIImageOrientation orientation, CGFloat rotation, CGRect rect, bool mirrored, CGSize maxSize, CGSize originalSize, bool shouldResize, bool useImageSize, bool skipImageTransform)
+{
+ CGSize fittedOriginalSize = originalSize;
+ if (useImageSize)
+ {
+ CGFloat ratio = paintingImage.size.width / originalSize.width;
+ if (skipImageTransform) {
+
+ }
+ rect.origin.x = rect.origin.x * ratio;
+ rect.origin.y = rect.origin.y * ratio;
+ rect.size.width = rect.size.width * ratio;
+ rect.size.height = rect.size.height * ratio;
+
+ fittedOriginalSize = CGSizeMake(originalSize.width * ratio, originalSize.height * ratio);
+ }
+
+ CGSize fittedImageSize = shouldResize ? TGFitSize(rect.size, maxSize) : rect.size;
+
+ CGSize outputImageSize = fittedImageSize;
+ outputImageSize.width = CGFloor(outputImageSize.width);
+ outputImageSize.height = CGFloor(outputImageSize.height);
+ if (TGOrientationIsSideward(orientation, NULL))
+ outputImageSize = CGSizeMake(outputImageSize.height, outputImageSize.width);
+
+ UIGraphicsBeginImageContextWithOptions(CGSizeMake(outputImageSize.width, outputImageSize.height), false, 1.0f);
+ CGContextRef context = UIGraphicsGetCurrentContext();
+
+ UIImage *image = nil;
+ CGSize imageSize = paintingImage.size;
+ if (shouldResize)
+ {
+ CGSize referenceSize = useImageSize ? paintingImage.size : originalSize;
+ CGSize resizedSize = CGSizeMake(referenceSize.width * fittedImageSize.width / rect.size.width, referenceSize.height * fittedImageSize.height / rect.size.height);
+
+ UIGraphicsBeginImageContextWithOptions(resizedSize, false, 1.0f);
+ [image drawInRect:CGRectMake(0, 0, resizedSize.width, resizedSize.height) blendMode:kCGBlendModeCopy alpha:1.0f];
+ image = UIGraphicsGetImageFromCurrentImageContext();
+ UIGraphicsEndImageContext();
+
+ if (skipImageTransform) {
+ imageSize = CGSizeMake(image.size.width * fittedOriginalSize.width / rect.size.width, image.size.height * fittedOriginalSize.height / rect.size.height);
+ } else {
+ imageSize = image.size;
+ }
+ }
+ else
+ {
+ image = paintingImage;
+ imageSize = image.size;
+ }
+
+ if (skipImageTransform) {
+ [image drawInRect:CGRectMake(0.0, 0.0, outputImageSize.width, outputImageSize.height)];
+ }
+
+ CGSize scales = CGSizeMake(fittedImageSize.width / rect.size.width, fittedImageSize.height / rect.size.height);
+ CGSize rotatedContentSize = TGRotatedContentSize(paintingImage.size, rotation);
+ CGAffineTransform transform = CGAffineTransformIdentity;
+ transform = CGAffineTransformTranslate(transform, outputImageSize.width / 2, outputImageSize.height / 2);
+ transform = CGAffineTransformRotate(transform, TGRotationForOrientation(orientation));
+ transform = CGAffineTransformTranslate(transform, (rotatedContentSize.width / 2 - CGRectGetMidX(rect)) * scales.width, (rotatedContentSize.height / 2 - CGRectGetMidY(rect)) * scales.height);
+ transform = CGAffineTransformRotate(transform, rotation);
+ CGContextConcatCTM(context, transform);
+
+ if (mirrored)
+ CGContextScaleCTM(context, -1.0f, 1.0f);
+
+ if (!skipImageTransform) {
+ [image drawAtPoint:CGPointMake(-image.size.width / 2, -image.size.height / 2)];
+ }
+
if (paintingImage != nil)
{
if (mirrored)
diff --git a/submodules/LegacyComponents/Sources/TGPhotoEntitiesContainerView.h b/submodules/LegacyComponents/Sources/TGPhotoEntitiesContainerView.h
index 0ebde1c945..00e2f53e88 100644
--- a/submodules/LegacyComponents/Sources/TGPhotoEntitiesContainerView.h
+++ b/submodules/LegacyComponents/Sources/TGPhotoEntitiesContainerView.h
@@ -14,6 +14,10 @@
@property (nonatomic, copy) void (^entityRemoved)(TGPhotoPaintEntityView *);
- (void)updateVisibility:(bool)visible;
+- (void)seekTo:(double)timestamp;
+- (void)play;
+- (void)pause;
+- (void)resetToStart;
- (UIColor *)colorAtPoint:(CGPoint)point;
diff --git a/submodules/LegacyComponents/Sources/TGPhotoEntitiesContainerView.m b/submodules/LegacyComponents/Sources/TGPhotoEntitiesContainerView.m
index f4e9b85126..7e6e7bcb04 100644
--- a/submodules/LegacyComponents/Sources/TGPhotoEntitiesContainerView.m
+++ b/submodules/LegacyComponents/Sources/TGPhotoEntitiesContainerView.m
@@ -40,6 +40,55 @@
}
}
+- (void)seekTo:(double)timestamp {
+ for (TGPhotoPaintEntityView *view in self.subviews)
+ {
+ if (![view isKindOfClass:[TGPhotoPaintEntityView class]])
+ continue;
+
+ if ([view isKindOfClass:[TGPhotoStickerEntityView class]]) {
+ [(TGPhotoStickerEntityView *)view seekTo:timestamp];
+ }
+ }
+}
+
+- (void)play {
+ for (TGPhotoPaintEntityView *view in self.subviews)
+ {
+ if (![view isKindOfClass:[TGPhotoPaintEntityView class]])
+ continue;
+
+ if ([view isKindOfClass:[TGPhotoStickerEntityView class]]) {
+ [(TGPhotoStickerEntityView *)view play];
+ }
+ }
+}
+
+- (void)pause {
+ for (TGPhotoPaintEntityView *view in self.subviews)
+ {
+ if (![view isKindOfClass:[TGPhotoPaintEntityView class]])
+ continue;
+
+ if ([view isKindOfClass:[TGPhotoStickerEntityView class]]) {
+ [(TGPhotoStickerEntityView *)view pause];
+ }
+ }
+}
+
+
+- (void)resetToStart {
+ for (TGPhotoPaintEntityView *view in self.subviews)
+ {
+ if (![view isKindOfClass:[TGPhotoPaintEntityView class]])
+ continue;
+
+ if ([view isKindOfClass:[TGPhotoStickerEntityView class]]) {
+ [(TGPhotoStickerEntityView *)view resetToStart];
+ }
+ }
+}
+
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)__unused gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)__unused otherGestureRecognizer
{
return false;
diff --git a/submodules/LegacyComponents/Sources/TGPhotoPaintController.m b/submodules/LegacyComponents/Sources/TGPhotoPaintController.m
index e554aef9a2..06583aa272 100644
--- a/submodules/LegacyComponents/Sources/TGPhotoPaintController.m
+++ b/submodules/LegacyComponents/Sources/TGPhotoPaintController.m
@@ -99,6 +99,7 @@ const CGFloat TGPhotoPaintStickerKeyboardSize = 260.0f;
bool _appeared;
bool _skipEntitiesSetup;
+ bool _entitiesReady;
TGPhotoPaintFont *_selectedTextFont;
TGPhotoPaintTextEntityStyle _selectedTextStyle;
@@ -268,7 +269,9 @@ const CGFloat TGPhotoPaintStickerKeyboardSize = 260.0f;
[strongSelf updateSettingsButton];
};
- [_contentWrapperView addSubview:_entitiesContainerView];
+ if (!_skipEntitiesSetup) {
+ [_contentWrapperView addSubview:_entitiesContainerView];
+ }
_undoManager.entitiesContainer = _entitiesContainerView;
_dimView = [[UIView alloc] init];
@@ -1193,9 +1196,41 @@ const CGFloat TGPhotoPaintStickerKeyboardSize = 260.0f;
TGPhotoPaintStickerEntity *entity = [[TGPhotoPaintStickerEntity alloc] initWithDocument:document baseSize:[self _stickerBaseSizeForCurrentPainting] animated:animated];
[self _setStickerEntityPosition:entity];
+ bool hasStickers = false;
+ for (TGPhotoPaintEntityView *view in _entitiesContainerView.subviews) {
+ if ([view isKindOfClass:[TGPhotoStickerEntityView class]]) {
+ hasStickers = true;
+ break;
+ }
+ }
+
TGPhotoStickerEntityView *stickerView = (TGPhotoStickerEntityView *)[_entitiesContainerView createEntityViewWithEntity:entity];
[self _commonEntityViewSetup:stickerView];
+ __weak TGPhotoPaintController *weakSelf = self;
+ __weak TGPhotoStickerEntityView *weakStickerView = stickerView;
+ stickerView.started = ^(double duration) {
+ __strong TGPhotoPaintController *strongSelf = weakSelf;
+ if (strongSelf != nil) {
+ TGPhotoEditorController *editorController = (TGPhotoEditorController *)self.parentViewController;
+ if (![editorController isKindOfClass:[TGPhotoEditorController class]])
+ return;
+
+ if (!hasStickers) {
+ [editorController setMinimalVideoDuration:duration];
+ }
+
+ NSTimeInterval currentTime = editorController.currentTime;
+ __strong TGPhotoStickerEntityView *strongStickerView = weakStickerView;
+ if (strongStickerView != nil) {
+ if (!isnan(currentTime)) {
+ [strongStickerView seekTo:currentTime];
+ [strongStickerView play];
+ }
+ }
+ }
+ };
+
[self selectEntityView:stickerView];
_entitySelectionView.alpha = 0.0f;
@@ -1816,7 +1851,6 @@ const CGFloat TGPhotoPaintStickerKeyboardSize = 260.0f;
if (self.presentedForAvatarCreation) {
_canvasView.hidden = true;
- _entitiesContainerView.hidden = true;
}
}
@@ -1865,7 +1899,7 @@ const CGFloat TGPhotoPaintStickerKeyboardSize = 260.0f;
[self setupCanvas];
_entitiesContainerView.hidden = false;
-
+
TGPhotoEditorPreviewView *previewView = _previewView;
[previewView setPaintingHidden:true];
previewView.hidden = false;
@@ -1887,8 +1921,10 @@ const CGFloat TGPhotoPaintStickerKeyboardSize = 260.0f;
CGPoint boundsCenter = TGPaintCenterOfRect(_contentWrapperView.bounds);
_entitiesContainerView.center = TGPaintAddPoints(boundsCenter, offset);
- [_contentWrapperView addSubview:_entitiesContainerView];
-
+ if (!_skipEntitiesSetup || _entitiesReady) {
+ [_contentWrapperView addSubview:_entitiesContainerView];
+ }
+ _entitiesReady = true;
[self resetScrollView];
}
@@ -2322,6 +2358,13 @@ const CGFloat TGPhotoPaintStickerKeyboardSize = 260.0f;
previewView.frame = previewFrame;
+ if ([self presentedForAvatarCreation]) {
+ CGAffineTransform transform = CGAffineTransformMakeRotation(TGRotationForOrientation(photoEditor.cropOrientation));
+ if (photoEditor.cropMirrored)
+ transform = CGAffineTransformScale(transform, -1.0f, 1.0f);
+ previewView.transform = transform;
+ }
+
CGSize fittedOriginalSize = CGSizeMake(originalSize.width * ratio, originalSize.height * ratio);
CGSize rotatedSize = TGRotatedContentSize(fittedOriginalSize, rotation);
CGPoint centerPoint = CGPointMake(rotatedSize.width / 2.0f, rotatedSize.height / 2.0f);
diff --git a/submodules/LegacyComponents/Sources/TGPhotoStickerEntityView.h b/submodules/LegacyComponents/Sources/TGPhotoStickerEntityView.h
index 828588add6..e00badcf5b 100644
--- a/submodules/LegacyComponents/Sources/TGPhotoStickerEntityView.h
+++ b/submodules/LegacyComponents/Sources/TGPhotoStickerEntityView.h
@@ -9,6 +9,8 @@
@interface TGPhotoStickerEntityView : TGPhotoPaintEntityView
+@property (nonatomic, copy) void(^started)(double);
+
@property (nonatomic, readonly) TGPhotoPaintStickerEntity *entity;
@property (nonatomic, readonly) bool isMirrored;
@@ -17,6 +19,10 @@
- (UIImage *)image;
- (void)updateVisibility:(bool)visible;
+- (void)seekTo:(double)timestamp;
+- (void)play;
+- (void)pause;
+- (void)resetToStart;
- (CGRect)realBounds;
diff --git a/submodules/LegacyComponents/Sources/TGPhotoStickerEntityView.m b/submodules/LegacyComponents/Sources/TGPhotoStickerEntityView.m
index a89a23735d..9e2712308d 100644
--- a/submodules/LegacyComponents/Sources/TGPhotoStickerEntityView.m
+++ b/submodules/LegacyComponents/Sources/TGPhotoStickerEntityView.m
@@ -55,6 +55,13 @@ const CGFloat TGPhotoStickerSelectionViewHandleSide = 30.0f;
_mirrored = entity.isMirrored;
_stickerView = [context stickerViewForDocument:entity.document];
+
+ __weak TGPhotoStickerEntityView *weakSelf = self;
+ _stickerView.started = ^(double duration) {
+ __strong TGPhotoStickerEntityView *strongSelf = weakSelf;
+ if (strongSelf != nil && strongSelf.started != nil)
+ strongSelf.started(duration);
+ };
[self addSubview:_stickerView];
_document = entity.document;
@@ -178,6 +185,22 @@ const CGFloat TGPhotoStickerSelectionViewHandleSide = 30.0f;
[_stickerView setIsVisible:visible];
}
+- (void)seekTo:(double)timestamp {
+ [_stickerView seekTo:timestamp];
+}
+
+- (void)play {
+ [_stickerView play];
+}
+
+- (void)pause {
+ [_stickerView pause];
+}
+
+- (void)resetToStart {
+ [_stickerView resetToStart];
+}
+
@end
diff --git a/submodules/LegacyComponents/Sources/TGPhotoToolsController.m b/submodules/LegacyComponents/Sources/TGPhotoToolsController.m
index 1d610335e2..ab13e98dd0 100644
--- a/submodules/LegacyComponents/Sources/TGPhotoToolsController.m
+++ b/submodules/LegacyComponents/Sources/TGPhotoToolsController.m
@@ -35,6 +35,7 @@ const CGFloat TGPhotoEditorToolsLandscapePanelSize = TGPhotoEditorToolsPanelSize
bool _appeared;
bool _scheduledTransitionIn;
CGFloat _cellWidth;
+ int _entitiesReady;
NSArray *_allTools;
NSArray *_simpleTools;
@@ -107,26 +108,27 @@ const CGFloat TGPhotoEditorToolsLandscapePanelSize = TGPhotoEditorToolsPanelSize
}
- (void)layoutEntitiesView {
- CGSize fittedContentSize = [TGPhotoPaintController fittedContentSize:_photoEditor.cropRect orientation:_photoEditor.cropOrientation originalSize:_photoEditor.originalSize];
- CGRect fittedCropRect = [TGPhotoPaintController fittedCropRect:_photoEditor.cropRect originalSize:_photoEditor.originalSize keepOriginalSize:false];
- _entitiesWrapperView.frame = CGRectMake(0.0f, 0.0f, fittedContentSize.width, fittedContentSize.height);
+ if (_entitiesReady < 2 || _dismissing)
+ return;
- CGRect rect = [TGPhotoPaintController fittedCropRect:self.photoEditor.cropRect originalSize:self.photoEditor.originalSize keepOriginalSize:true];
- _entitiesView.frame = CGRectMake(0, 0, rect.size.width, rect.size.height);
- _entitiesView.transform = CGAffineTransformMakeRotation(_photoEditor.cropRotation);
+ _entitiesWrapperView.transform = CGAffineTransformIdentity;
+ _entitiesWrapperView.frame = CGRectMake(0.0, 0.0, _entitiesView.frame.size.width, _entitiesView.frame.size.height);
+ [_entitiesWrapperView addSubview:_entitiesView];
- CGSize fittedOriginalSize = TGScaleToSize(_photoEditor.originalSize, [TGPhotoPaintController maximumPaintingSize]);
- CGSize rotatedSize = TGRotatedContentSize(fittedOriginalSize, _photoEditor.cropRotation);
- CGPoint centerPoint = CGPointMake(rotatedSize.width / 2.0f, rotatedSize.height / 2.0f);
+ CGFloat paintingScale = _entitiesView.frame.size.width / _photoEditor.originalSize.width;
+ _entitiesView.frame = CGRectMake(-_photoEditor.cropRect.origin.x * paintingScale, -_photoEditor.cropRect.origin.y * paintingScale, _entitiesView.frame.size.width, _entitiesView.frame.size.height);
- CGFloat scale = fittedOriginalSize.width / _photoEditor.originalSize.width;
- CGPoint offset = TGPaintSubtractPoints(centerPoint, [TGPhotoPaintController fittedCropRect:_photoEditor.cropRect centerScale:scale]);
-
- CGPoint boundsCenter = TGPaintCenterOfRect(_entitiesWrapperView.bounds);
- _entitiesView.center = TGPaintAddPoints(boundsCenter, offset);
- if (_entitiesView.superview != _entitiesWrapperView) {
- [_entitiesWrapperView addSubview:_entitiesView];
+ CGFloat cropScale = 1.0;
+ if (_photoEditor.originalSize.width > _photoEditor.originalSize.height) {
+ cropScale = _photoEditor.originalSize.height / _photoEditor.cropRect.size.height;
+ } else {
+ cropScale = _photoEditor.originalSize.width / _photoEditor.cropRect.size.width;
}
+
+ CGFloat scale = _previewView.frame.size.width / _entitiesView.frame.size.width;
+ CGAffineTransform rotationTransform = CGAffineTransformMakeRotation(TGRotationForOrientation(_photoEditor.cropOrientation));
+ _entitiesWrapperView.transform = CGAffineTransformScale(rotationTransform, scale * cropScale, scale * cropScale);
+ _entitiesWrapperView.frame = [_previewView convertRect:_previewView.bounds toView:_entitiesWrapperView.superview];
}
- (void)loadView
@@ -507,6 +509,9 @@ const CGFloat TGPhotoEditorToolsLandscapePanelSize = TGPhotoEditorToolsPanelSize
TGPhotoEditorPreviewView *previewView = _previewView;
previewView.hidden = false;
[previewView performTransitionInIfNeeded];
+
+ _entitiesReady++;
+ [self layoutEntitiesView];
}
- (void)prepareForCustomTransitionOut
diff --git a/submodules/LegacyDataImport/Sources/LegacyChatImport.swift b/submodules/LegacyDataImport/Sources/LegacyChatImport.swift
index b644a17248..99c05c5600 100644
--- a/submodules/LegacyDataImport/Sources/LegacyChatImport.swift
+++ b/submodules/LegacyDataImport/Sources/LegacyChatImport.swift
@@ -405,7 +405,7 @@ private func loadLegacyMessages(account: TemporaryAccount, basePath: String, acc
if let resourcePath = resourcePath, let image = UIImage(contentsOfFile: resourcePath) {
dimensions = image.size
}
- representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(dimensions), resource: resource))
+ representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(dimensions), resource: resource, progressiveSizes: []))
}
}
@@ -414,7 +414,7 @@ private func loadLegacyMessages(account: TemporaryAccount, basePath: String, acc
if let image = UIImage(contentsOfFile: fullSizePath) {
let resource: TelegramMediaResource = LocalFileMediaResource(fileId: arc4random64())
copyLocalFiles.append((resource, fullSizePath))
- representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(image.size), resource: resource))
+ representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(image.size), resource: resource, progressiveSizes: []))
}
}
@@ -431,7 +431,7 @@ private func loadLegacyMessages(account: TemporaryAccount, basePath: String, acc
} else if imageUrl.hasPrefix("file://"), let path = URL(string: imageUrl)?.path {
copyLocalFiles.append((resource, path))
}
- representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(sizeValue.cgSizeValue), resource: resource))
+ representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(sizeValue.cgSizeValue), resource: resource, progressiveSizes: []))
}
}
@@ -488,7 +488,7 @@ private func loadLegacyMessages(account: TemporaryAccount, basePath: String, acc
resource = updatedResource
copyLocalFiles.append((resource, pathFromLegacyImageUrl(basePath: basePath, url: imageUrl)))
}
- representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(sizeValue.cgSizeValue), resource: resource))
+ representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(sizeValue.cgSizeValue), resource: resource, progressiveSizes: []))
}
}
diff --git a/submodules/LegacyDataImport/Sources/LegacyUserDataImport.swift b/submodules/LegacyDataImport/Sources/LegacyUserDataImport.swift
index 51fec45c1f..d3ba00070f 100644
--- a/submodules/LegacyDataImport/Sources/LegacyUserDataImport.swift
+++ b/submodules/LegacyDataImport/Sources/LegacyUserDataImport.swift
@@ -18,10 +18,10 @@ func loadLegacyUser(database: SqliteInterface, id: Int32) -> (TelegramUser, Tele
let photoBig = cursor.getString(at: 6)
var photo: [TelegramMediaImageRepresentation] = []
if let resource = resourceFromLegacyImageUrl(photoSmall) {
- photo.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 80, height: 80), resource: resource))
+ photo.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 80, height: 80), resource: resource, progressiveSizes: []))
}
if let resource = resourceFromLegacyImageUrl(photoBig) {
- photo.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 600, height: 600), resource: resource))
+ photo.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 600, height: 600), resource: resource, progressiveSizes: []))
}
let user = TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: cursor.getInt32(at: 0)), accessHash: accessHash == 0 ? nil : .personal(accessHash), firstName: firstName.isEmpty ? nil : firstName, lastName: lastName.isEmpty ? nil : lastName, username: username.isEmpty ? nil : username, phone: phone.isEmpty ? nil : phone, photo: photo, botInfo: nil, restrictionInfo: nil, flags: [])
diff --git a/submodules/LegacyMediaPickerUI/Sources/LegacyAttachmentMenu.swift b/submodules/LegacyMediaPickerUI/Sources/LegacyAttachmentMenu.swift
index 5fbefd9734..6b091e3ec4 100644
--- a/submodules/LegacyMediaPickerUI/Sources/LegacyAttachmentMenu.swift
+++ b/submodules/LegacyMediaPickerUI/Sources/LegacyAttachmentMenu.swift
@@ -127,7 +127,7 @@ public func legacyAttachmentMenu(context: AccountContext, peer: Peer, editMediaO
return
}
- DeviceAccess.authorizeAccess(to: .camera, presentationData: context.sharedContext.currentPresentationData.with { $0 }, present: context.sharedContext.presentGlobalController, openSettings: context.sharedContext.applicationBindings.openSettings, { value in
+ DeviceAccess.authorizeAccess(to: .camera(.video), presentationData: context.sharedContext.currentPresentationData.with { $0 }, present: context.sharedContext.presentGlobalController, openSettings: context.sharedContext.applicationBindings.openSettings, { value in
if value {
openCamera(cameraView, controller)
}
diff --git a/submodules/LegacyMediaPickerUI/Sources/LegacyMediaPickers.swift b/submodules/LegacyMediaPickerUI/Sources/LegacyMediaPickers.swift
index a1c2ed2062..f6eb986ea0 100644
--- a/submodules/LegacyMediaPickerUI/Sources/LegacyMediaPickers.swift
+++ b/submodules/LegacyMediaPickerUI/Sources/LegacyMediaPickers.swift
@@ -96,7 +96,7 @@ public func legacyAssetPicker(context: AccountContext, presentationData: Present
})
} else {
subscriber.putNext({ context in
- let controller = TGMediaAssetsController(context: context, assetGroup: nil, intent: intent, recipientName: peer?.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder), saveEditedPhotos: !isSecretChat && saveEditedPhotos, allowGrouping: allowGrouping, selectionLimit: Int32(selectionLimit))
+ let controller = TGMediaAssetsController(context: context, assetGroup: nil, intent: intent, recipientName: peer?.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder), saveEditedPhotos: !isSecretChat && saveEditedPhotos, allowGrouping: allowGrouping, inhibitSelection: editingMedia, selectionLimit: Int32(selectionLimit))
return controller!
})
subscriber.putCompletion()
@@ -265,7 +265,7 @@ public func legacyEnqueueGifMessage(account: Account, data: Data) -> Signal 0 && timer <= 60 {
- attributes.append(AutoremoveTimeoutMessageAttribute(timeout: Int32(timer), countdownBeginTime: nil))
- }
- messages.append(.message(text: caption ?? "", attributes: attributes, mediaReference: .standalone(media: media), replyToMessageId: nil, localGroupingKey: item.groupedId))
- }
- }
- #endif
let resource = LocalFileReferenceMediaResource(localFilePath: tempFilePath, randomId: randomId)
- representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(scaledSize), resource: resource))
+ representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(scaledSize), resource: resource, progressiveSizes: []))
var imageFlags: TelegramMediaImageFlags = []
@@ -375,7 +358,7 @@ public func legacyAssetPickerEnqueueMessages(account: Account, signals: [Any]) -
let size = CGSize(width: CGFloat(asset.pixelWidth), height: CGFloat(asset.pixelHeight))
let scaledSize = size.aspectFittedOrSmaller(CGSize(width: 1280.0, height: 1280.0))
let resource = PhotoLibraryMediaResource(localIdentifier: asset.localIdentifier, uniqueId: arc4random64())
- representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(scaledSize), resource: resource))
+ representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(scaledSize), resource: resource, progressiveSizes: []))
let media = TelegramMediaImage(imageId: MediaId(namespace: Namespaces.Media.LocalImage, id: randomId), representations: representations, immediateThumbnailData: nil, reference: nil, partialReference: nil, flags: [])
var attributes: [MessageAttribute] = []
@@ -442,7 +425,7 @@ public func legacyAssetPickerEnqueueMessages(account: Account, signals: [Any]) -
let thumbnailImage = TGScaleImageToPixelSize(thumbnail, thumbnailSize)!
if let thumbnailData = thumbnailImage.jpegData(compressionQuality: 0.4) {
account.postbox.mediaBox.storeResourceData(resource.id, data: thumbnailData)
- previewRepresentations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(thumbnailSize), resource: resource))
+ previewRepresentations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(thumbnailSize), resource: resource, progressiveSizes: []))
}
}
diff --git a/submodules/LegacyMediaPickerUI/Sources/LegacyPaintStickerView.swift b/submodules/LegacyMediaPickerUI/Sources/LegacyPaintStickerView.swift
index c6c9d72dac..36bdadbdb2 100644
--- a/submodules/LegacyMediaPickerUI/Sources/LegacyPaintStickerView.swift
+++ b/submodules/LegacyMediaPickerUI/Sources/LegacyPaintStickerView.swift
@@ -10,6 +10,8 @@ import StickerResources
import LegacyComponents
class LegacyPaintStickerView: UIView, TGPhotoPaintStickerRenderView {
+ var started: ((Double) -> Void)?
+
private let context: AccountContext
private let file: TelegramMediaFile
private var currentSize: CGSize?
@@ -63,8 +65,16 @@ class LegacyPaintStickerView: UIView, TGPhotoPaintStickerRenderView {
if self.animationNode == nil {
let animationNode = AnimatedStickerNode()
self.animationNode = animationNode
- animationNode.started = { [weak self] in
+ animationNode.started = { [weak self, weak animationNode] in
self?.imageNode.isHidden = true
+
+ if let animationNode = animationNode {
+ let _ = (animationNode.status
+ |> take(1)
+ |> deliverOnMainQueue).start(next: { [weak self] status in
+ self?.started?(status.duration)
+ })
+ }
}
self.addSubnode(animationNode)
}
@@ -107,7 +117,7 @@ class LegacyPaintStickerView: UIView, TGPhotoPaintStickerRenderView {
let dimensions = self.file.dimensions ?? PixelDimensions(width: 512, height: 512)
let fittedDimensions = dimensions.cgSize.aspectFitted(CGSize(width: 384.0, height: 384.0))
let source = AnimatedStickerResourceSource(account: self.context.account, resource: self.file.resource)
- self.animationNode?.setup(source: source, width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .direct)
+ self.animationNode?.setup(source: source, width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .direct(cachePathPrefix: nil))
self.cachedDisposable.set((source.cachedDataPath(width: 384, height: 384)
|> deliverOn(Queue.concurrentDefaultQueue())).start())
@@ -115,6 +125,30 @@ class LegacyPaintStickerView: UIView, TGPhotoPaintStickerRenderView {
}
}
+ func seek(to timestamp: Double) {
+ self.isVisible = false
+ self.isPlaying = false
+ self.animationNode?.seekTo(.timestamp(timestamp))
+ }
+
+ func play() {
+ self.isVisible = true
+ self.isPlaying = true
+ self.animationNode?.play()
+ }
+
+ func pause() {
+ self.isVisible = false
+ self.isPlaying = false
+ self.animationNode?.pause()
+ }
+
+ func resetToStart() {
+ self.isVisible = false
+ self.isPlaying = false
+ self.animationNode?.seekTo(.timestamp(0.0))
+ }
+
override func layoutSubviews() {
super.layoutSubviews()
diff --git a/submodules/LegacyMediaPickerUI/Sources/LegacyPaintStickersContext.swift b/submodules/LegacyMediaPickerUI/Sources/LegacyPaintStickersContext.swift
index 0d8c690267..dbaebce170 100644
--- a/submodules/LegacyMediaPickerUI/Sources/LegacyPaintStickersContext.swift
+++ b/submodules/LegacyMediaPickerUI/Sources/LegacyPaintStickersContext.swift
@@ -188,8 +188,8 @@ private class LegacyPaintStickerEntity: LegacyPaintEntity {
let maybeFrame = frameQueue.syncWith { frameQueue -> AnimatedStickerFrame? in
var frame: AnimatedStickerFrame?
- for _ in 0 ..< delta {
- frame = frameQueue.take()
+ for i in 0 ..< delta {
+ frame = frameQueue.take(draw: i == delta - 1)
}
return frame
}
diff --git a/submodules/LegacyUI/Sources/LegacyComponentsStickers.swift b/submodules/LegacyUI/Sources/LegacyComponentsStickers.swift
index 429d7ae8a8..ddcdbe4018 100644
--- a/submodules/LegacyUI/Sources/LegacyComponentsStickers.swift
+++ b/submodules/LegacyUI/Sources/LegacyComponentsStickers.swift
@@ -198,7 +198,7 @@ final class LegacyStickerImageDataSource: TGImageDataSource {
var previewRepresentations: [TelegramMediaImageRepresentation] = []
if let legacyThumbnailUri = args["legacyThumbnailUri"] as? String, let data = Data(base64Encoded: legacyThumbnailUri, options: []) {
if let resource = PostboxDecoder(buffer: MemoryBuffer(data: data)).decodeRootObject() as? TelegramMediaResource {
- previewRepresentations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 140, height: 140), resource: resource))
+ previewRepresentations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 140, height: 140), resource: resource, progressiveSizes: []))
}
}
diff --git a/submodules/LocalMediaResources/Sources/FetchPhotoLibraryImageResource.swift b/submodules/LocalMediaResources/Sources/FetchPhotoLibraryImageResource.swift
index 90c0cbb583..bf04a0038e 100644
--- a/submodules/LocalMediaResources/Sources/FetchPhotoLibraryImageResource.swift
+++ b/submodules/LocalMediaResources/Sources/FetchPhotoLibraryImageResource.swift
@@ -52,19 +52,7 @@ public func fetchPhotoLibraryResource(localIdentifier: String) -> Signal Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal?, (ListViewItemApply) -> Void)) -> Void) {
@@ -94,11 +100,20 @@ private class LocationAttributionItemNode: ListViewItemNode {
strongSelf.layoutParams = params
if let _ = updatedTheme {
- strongSelf.imageNode.image = generateTintedImage(image: UIImage(bundleImageName: "Location/FoursquareAttribution"), color: item.presentationData.theme.list.itemSecondaryTextColor)
+ switch item.attribution {
+ case .foursquare:
+ strongSelf.imageNode.image = generateTintedImage(image: UIImage(bundleImageName: "Location/FoursquareAttribution"), color: item.presentationData.theme.list.itemSecondaryTextColor)
+ case .google:
+ if item.presentationData.theme.overallDarkAppearance {
+ strongSelf.imageNode.image = generateTintedImage(image: UIImage(bundleImageName: "Location/GoogleAttribution"), color: item.presentationData.theme.list.itemSecondaryTextColor)
+ } else {
+ strongSelf.imageNode.image = UIImage(bundleImageName: "Location/GoogleAttribution")
+ }
+ }
}
if let image = strongSelf.imageNode.image {
- strongSelf.imageNode.frame = CGRect(x: floor((params.width - image.size.width) / 2.0), y: 0.0, width: image.size.width, height: image.size.height)
+ strongSelf.imageNode.frame = CGRect(x: floor((params.width - image.size.width) / 2.0), y: floor((contentSize.height - image.size.height) / 2.0), width: image.size.width, height: image.size.height)
}
}
})
diff --git a/submodules/LocationUI/Sources/LocationMapNode.swift b/submodules/LocationUI/Sources/LocationMapNode.swift
index 0576883bb4..c330c2017e 100644
--- a/submodules/LocationUI/Sources/LocationMapNode.swift
+++ b/submodules/LocationUI/Sources/LocationMapNode.swift
@@ -68,11 +68,30 @@ private class LocationMapView: MKMapView, UIGestureRecognizerDelegate {
}
}
+private func generateHeadingArrowImage() -> UIImage? {
+ return generateImage(CGSize(width: 28.0, height: 28.0)) { size, context in
+ let bounds = CGRect(origin: CGPoint(), size: size)
+ context.clear(bounds)
+
+ context.setFillColor(UIColor(rgb: 0x3393fe).cgColor)
+
+ context.move(to: CGPoint(x: 14.0, y: 0.0))
+ context.addLine(to: CGPoint(x: 19.0, y: 7.0))
+ context.addLine(to: CGPoint(x: 9.0, y: 7.0))
+ context.closePath()
+ context.fillPath()
+
+ context.setBlendMode(.clear)
+ context.fillEllipse(in: bounds.insetBy(dx: 5.0, dy: 5.0))
+ }
+}
+
final class LocationMapNode: ASDisplayNode, MKMapViewDelegate {
private let locationPromise = Promise(nil)
private let pickerAnnotationContainerView: PickerAnnotationContainerView
private weak var userLocationAnnotationView: MKAnnotationView?
+ private var headingArrowView: UIImageView?
private let pinDisposable = MetaDisposable()
@@ -103,6 +122,10 @@ final class LocationMapNode: ASDisplayNode, MKMapViewDelegate {
override func didLoad() {
super.didLoad()
+ self.headingArrowView = UIImageView()
+ self.headingArrowView?.frame = CGRect(origin: CGPoint(), size: CGSize(width: 28.0, height: 28.0))
+ self.headingArrowView?.image = generateHeadingArrowImage()
+
self.mapView?.interactiveTransitionGestureRecognizerTest = { p in
if p.x > 44.0 {
return true
@@ -232,6 +255,10 @@ final class LocationMapNode: ASDisplayNode, MKMapViewDelegate {
for view in views {
if view.annotation is MKUserLocation {
self.userLocationAnnotationView = view
+ if let headingArrowView = self.headingArrowView {
+ view.addSubview(headingArrowView)
+ headingArrowView.center = CGPoint(x: view.frame.width / 2.0, y: view.frame.height / 2.0)
+ }
if let annotationView = self.customUserLocationAnnotationView {
view.addSubview(annotationView)
}
@@ -347,6 +374,18 @@ final class LocationMapNode: ASDisplayNode, MKMapViewDelegate {
}
}
+ var userHeading: CGFloat? = nil {
+ didSet {
+ if let heading = self.userHeading {
+ self.headingArrowView?.isHidden = false
+ self.headingArrowView?.transform = CGAffineTransform(rotationAngle: CGFloat(heading / 180.0 * CGFloat.pi))
+ } else {
+ self.headingArrowView?.isHidden = true
+ self.headingArrowView?.transform = CGAffineTransform.identity
+ }
+ }
+ }
+
var annotations: [LocationPinAnnotation] = [] {
didSet {
guard let mapView = self.mapView else {
diff --git a/submodules/LocationUI/Sources/LocationPickerController.swift b/submodules/LocationUI/Sources/LocationPickerController.swift
index 1a150f9c18..3602c194f3 100644
--- a/submodules/LocationUI/Sources/LocationPickerController.swift
+++ b/submodules/LocationUI/Sources/LocationPickerController.swift
@@ -289,7 +289,7 @@ public final class LocationPickerController: ViewController {
return
}
- self.displayNode = LocationPickerControllerNode(context: self.context, presentationData: self.presentationData, mode: self.mode, interaction: interaction)
+ self.displayNode = LocationPickerControllerNode(context: self.context, presentationData: self.presentationData, mode: self.mode, interaction: interaction, locationManager: self.locationManager)
self.displayNodeDidLoad()
self.permissionDisposable = (DeviceAccess.authorizationStatus(subject: .location(.send))
diff --git a/submodules/LocationUI/Sources/LocationPickerControllerNode.swift b/submodules/LocationUI/Sources/LocationPickerControllerNode.swift
index 03b0ebcdb3..4aee790229 100644
--- a/submodules/LocationUI/Sources/LocationPickerControllerNode.swift
+++ b/submodules/LocationUI/Sources/LocationPickerControllerNode.swift
@@ -17,6 +17,7 @@ import AppBundle
import CoreLocation
import Geocoding
import PhoneNumberFormat
+import DeviceAccess
private struct LocationPickerTransaction {
let deletions: [ListViewDeleteItem]
@@ -40,7 +41,7 @@ private enum LocationPickerEntry: Comparable, Identifiable {
case liveLocation(PresentationTheme, String, String, CLLocationCoordinate2D?)
case header(PresentationTheme, String)
case venue(PresentationTheme, TelegramMediaMap, Int)
- case attribution(PresentationTheme)
+ case attribution(PresentationTheme, LocationAttribution)
var stableId: LocationPickerEntryId {
switch self {
@@ -83,8 +84,8 @@ private enum LocationPickerEntry: Comparable, Identifiable {
} else {
return false
}
- case let .attribution(lhsTheme):
- if case let .attribution(rhsTheme) = rhs, lhsTheme === rhsTheme {
+ case let .attribution(lhsTheme, lhsAttribution):
+ if case let .attribution(rhsTheme, rhsAttribution) = rhs, lhsTheme === rhsTheme, lhsAttribution == rhsAttribution {
return true
} else {
return false
@@ -131,7 +132,7 @@ private enum LocationPickerEntry: Comparable, Identifiable {
func item(account: Account, presentationData: PresentationData, interaction: LocationPickerInteraction?) -> ListViewItem {
switch self {
- case let .location(theme, title, subtitle, venue, coordinate):
+ case let .location(_, title, subtitle, venue, coordinate):
let icon: LocationActionListItemIcon
if let venue = venue {
icon = .venue(venue)
@@ -147,23 +148,23 @@ private enum LocationPickerEntry: Comparable, Identifiable {
}, highlighted: { highlighted in
interaction?.updateSendActionHighlight(highlighted)
})
- case let .liveLocation(theme, title, subtitle, coordinate):
+ case let .liveLocation(_, title, subtitle, coordinate):
return LocationActionListItem(presentationData: ItemListPresentationData(presentationData), account: account, title: title, subtitle: subtitle, icon: .liveLocation, action: {
if let coordinate = coordinate {
interaction?.sendLiveLocation(coordinate)
}
})
- case let .header(theme, title):
+ case let .header(_, title):
return LocationSectionHeaderItem(presentationData: ItemListPresentationData(presentationData), title: title)
- case let .venue(theme, venue, _):
+ case let .venue(_, venue, _):
let venueType = venue.venue?.type ?? ""
return ItemListVenueItem(presentationData: ItemListPresentationData(presentationData), account: account, venue: venue, style: .plain, action: {
interaction?.sendVenue(venue)
}, infoAction: ["home", "work"].contains(venueType) ? {
interaction?.openHomeWorkInfo()
} : nil)
- case let .attribution(theme):
- return LocationAttributionItem(presentationData: ItemListPresentationData(presentationData))
+ case let .attribution(_, attribution):
+ return LocationAttributionItem(presentationData: ItemListPresentationData(presentationData), attribution: attribution)
}
}
}
@@ -240,12 +241,13 @@ struct LocationPickerState {
}
}
-final class LocationPickerControllerNode: ViewControllerTracingNode {
+final class LocationPickerControllerNode: ViewControllerTracingNode, CLLocationManagerDelegate {
private let context: AccountContext
private var presentationData: PresentationData
private let presentationDataPromise: Promise
private let mode: LocationPickerMode
private let interaction: LocationPickerInteraction
+ private let locationManager: LocationManager
private let listNode: ListView
private let emptyResultsTextNode: ImmediateTextNode
@@ -269,12 +271,13 @@ final class LocationPickerControllerNode: ViewControllerTracingNode {
private var validLayout: (layout: ContainerViewLayout, navigationHeight: CGFloat)?
private var listOffset: CGFloat?
- init(context: AccountContext, presentationData: PresentationData, mode: LocationPickerMode, interaction: LocationPickerInteraction) {
+ init(context: AccountContext, presentationData: PresentationData, mode: LocationPickerMode, interaction: LocationPickerInteraction, locationManager: LocationManager) {
self.context = context
self.presentationData = presentationData
self.presentationDataPromise = Promise(presentationData)
self.mode = mode
self.interaction = interaction
+ self.locationManager = locationManager
self.state = LocationPickerState()
self.statePromise = Promise(self.state)
@@ -496,15 +499,21 @@ final class LocationPickerControllerNode: ViewControllerTracingNode {
entries.append(.header(presentationData.theme, presentationData.strings.Map_ChooseAPlace.uppercased()))
- var displayedVenues = foundVenues != nil || state.searchingVenuesAround ? foundVenues : venues
+ let displayedVenues = foundVenues != nil || state.searchingVenuesAround ? foundVenues : venues
if let venues = displayedVenues {
var index: Int = 0
+ var attribution: LocationAttribution?
for venue in venues {
+ if venue.venue?.provider == "foursquare" {
+ attribution = .foursquare
+ } else if venue.venue?.provider == "gplaces" {
+ attribution = .google
+ }
entries.append(.venue(presentationData.theme, venue, index))
index += 1
}
- if !venues.isEmpty {
- entries.append(.attribution(presentationData.theme))
+ if let attribution = attribution {
+ entries.append(.attribution(presentationData.theme, attribution))
}
}
let previousEntries = previousEntries.swap(entries)
@@ -533,7 +542,7 @@ final class LocationPickerControllerNode: ViewControllerTracingNode {
switch previousState.selectedLocation {
case .none, .venue:
updateMap = true
- case let .location(previousCoordinate, address):
+ case let .location(previousCoordinate, _):
if previousCoordinate != coordinate {
updateMap = true
}
@@ -574,7 +583,7 @@ final class LocationPickerControllerNode: ViewControllerTracingNode {
if let (layout, navigationBarHeight) = strongSelf.validLayout {
var updateLayout = false
- var transition: ContainedViewLayoutTransition = .animated(duration: 0.45, curve: .spring)
+ let transition: ContainedViewLayoutTransition = .animated(duration: 0.45, curve: .spring)
if previousState.displayingMapModeOptions != state.displayingMapModeOptions {
updateLayout = true
@@ -685,11 +694,20 @@ final class LocationPickerControllerNode: ViewControllerTracingNode {
strongSelf.goToUserLocation()
}
}
+
+ self.locationManager.manager.startUpdatingHeading()
+ self.locationManager.manager.delegate = self
}
deinit {
self.disposable?.dispose()
self.geocodingDisposable.dispose()
+
+ self.locationManager.manager.stopUpdatingHeading()
+ }
+
+ func locationManager(_ manager: CLLocationManager, didUpdateHeading newHeading: CLHeading) {
+ self.headerNode.mapNode.userHeading = CGFloat(newHeading.magneticHeading)
}
func updatePresentationData(_ presentationData: PresentationData) {
@@ -721,7 +739,7 @@ final class LocationPickerControllerNode: ViewControllerTracingNode {
}
private func dequeueTransition() {
- guard let layout = self.validLayout, let transition = self.enqueuedTransitions.first else {
+ guard let _ = self.validLayout, let transition = self.enqueuedTransitions.first else {
return
}
self.enqueuedTransitions.remove(at: 0)
diff --git a/submodules/LocationUI/Sources/LocationUtils.swift b/submodules/LocationUI/Sources/LocationUtils.swift
index 9aa2f05a6e..901d96af3c 100644
--- a/submodules/LocationUI/Sources/LocationUtils.swift
+++ b/submodules/LocationUI/Sources/LocationUtils.swift
@@ -33,36 +33,40 @@ public func ==(lhs: CLLocationCoordinate2D, rhs: CLLocationCoordinate2D) -> Bool
}
public func nearbyVenues(account: Account, latitude: Double, longitude: Double, query: String? = nil) -> Signal<[TelegramMediaMap], NoError> {
- return resolvePeerByName(account: account, name: "foursquare")
- |> take(1)
- |> mapToSignal { peerId -> Signal in
- guard let peerId = peerId else {
- return .single(nil)
- }
- return requestChatContextResults(account: account, botId: peerId, peerId: account.peerId, query: query ?? "", location: .single((latitude, longitude)), offset: "")
- |> map { results -> ChatContextResultCollection? in
- return results?.results
- }
- |> `catch` { error -> Signal in
- return .single(nil)
- }
- }
- |> map { contextResult -> [TelegramMediaMap] in
- guard let contextResult = contextResult else {
- return []
- }
- var list: [TelegramMediaMap] = []
- for result in contextResult.results {
- switch result.message {
- case let .mapLocation(mapMedia, _):
- if let _ = mapMedia.venue {
- list.append(mapMedia)
- }
- default:
- break
+ return account.postbox.transaction { transaction -> SearchBotsConfiguration in
+ return currentSearchBotsConfiguration(transaction: transaction)
+ } |> mapToSignal { searchBotsConfiguration in
+ return resolvePeerByName(account: account, name: searchBotsConfiguration.venueBotUsername ?? "foursquare")
+ |> take(1)
+ |> mapToSignal { peerId -> Signal in
+ guard let peerId = peerId else {
+ return .single(nil)
+ }
+ return requestChatContextResults(account: account, botId: peerId, peerId: account.peerId, query: query ?? "", location: .single((latitude, longitude)), offset: "")
+ |> map { results -> ChatContextResultCollection? in
+ return results?.results
+ }
+ |> `catch` { error -> Signal in
+ return .single(nil)
}
}
- return list
+ |> map { contextResult -> [TelegramMediaMap] in
+ guard let contextResult = contextResult else {
+ return []
+ }
+ var list: [TelegramMediaMap] = []
+ for result in contextResult.results {
+ switch result.message {
+ case let .mapLocation(mapMedia, _):
+ if let _ = mapMedia.venue {
+ list.append(mapMedia)
+ }
+ default:
+ break
+ }
+ }
+ return list
+ }
}
}
@@ -86,7 +90,7 @@ func stringForDistance(strings: PresentationStrings, distance: CLLocationDistanc
func stringForEstimatedDuration(strings: PresentationStrings, eta: Double) -> String? {
if eta > 0.0 && eta < 60.0 * 60.0 * 10.0 {
- var eta = max(eta, 60.0)
+ let eta = max(eta, 60.0)
let minutes = Int32(eta / 60.0) % 60
let hours = Int32(eta / 3600.0)
diff --git a/submodules/MediaPlayer/Sources/MediaPlayer.swift b/submodules/MediaPlayer/Sources/MediaPlayer.swift
index 4c53584893..cadb46bfa5 100644
--- a/submodules/MediaPlayer/Sources/MediaPlayer.swift
+++ b/submodules/MediaPlayer/Sources/MediaPlayer.swift
@@ -292,12 +292,12 @@ private final class MediaPlayerContext {
duration = max(duration, CMTimeGetSeconds(audioTrackFrameBuffer.duration))
}
loadedDuration = duration
- let status = MediaPlayerStatus(generationTimestamp: CACurrentMediaTime(), duration: duration, dimensions: CGSize(), timestamp: min(max(timestamp, 0.0), duration), baseRate: self.baseRate, seekId: self.seekId, status: .buffering(initial: false, whilePlaying: action == .play), soundEnabled: self.enableSound)
+ let status = MediaPlayerStatus(generationTimestamp: CACurrentMediaTime(), duration: duration, dimensions: CGSize(), timestamp: min(max(timestamp, 0.0), duration), baseRate: self.baseRate, seekId: self.seekId, status: .buffering(initial: false, whilePlaying: action == .play, progress: 0.0), soundEnabled: self.enableSound)
self.playerStatus.set(.single(status))
let _ = self.playerStatusValue.swap(status)
} else {
let duration = seekState?.duration ?? 0.0
- let status = MediaPlayerStatus(generationTimestamp: CACurrentMediaTime(), duration: duration, dimensions: CGSize(), timestamp: min(max(timestamp, 0.0), duration), baseRate: self.baseRate, seekId: self.seekId, status: .buffering(initial: false, whilePlaying: action == .play), soundEnabled: self.enableSound)
+ let status = MediaPlayerStatus(generationTimestamp: CACurrentMediaTime(), duration: duration, dimensions: CGSize(), timestamp: min(max(timestamp, 0.0), duration), baseRate: self.baseRate, seekId: self.seekId, status: .buffering(initial: false, whilePlaying: action == .play, progress: 0.0), soundEnabled: self.enableSound)
self.playerStatus.set(.single(status))
let _ = self.playerStatusValue.swap(status)
}
@@ -778,7 +778,7 @@ private final class MediaPlayerContext {
}
var rate: Double
- var buffering = false
+ var bufferingProgress: Float?
if let worstStatus = worstStatus, case let .full(fullUntil) = worstStatus, fullUntil.isFinite {
if case .playing = self.state {
@@ -811,8 +811,18 @@ private final class MediaPlayerContext {
rate = 0.0
}
}
+ } else if case let .buffering(progress) = worstStatus {
+ bufferingProgress = Float(progress)
+ rate = 0.0
+ //print("bufferingProgress = \(progress)")
+
+ let tickTimer = SwiftSignalKit.Timer(timeout: 0.3, repeat: false, completion: { [weak self] in
+ self?.tick()
+ }, queue: self.queue)
+ self.tickTimer = tickTimer
+ tickTimer.start()
} else {
- buffering = true
+ bufferingProgress = 0.0
rate = 0.0
}
@@ -854,12 +864,12 @@ private final class MediaPlayerContext {
var statusTimestamp = CACurrentMediaTime()
let playbackStatus: MediaPlayerPlaybackStatus
- if buffering {
+ if let bufferingProgress = bufferingProgress {
var whilePlaying = false
if case .playing = self.state {
whilePlaying = true
}
- playbackStatus = .buffering(initial: false, whilePlaying: whilePlaying)
+ playbackStatus = .buffering(initial: false, whilePlaying: whilePlaying, progress: Float(bufferingProgress))
} else if !rate.isZero {
if reportRate.isZero {
//playbackStatus = .buffering(initial: false, whilePlaying: true)
@@ -910,7 +920,7 @@ private final class MediaPlayerContext {
public enum MediaPlayerPlaybackStatus: Equatable {
case playing
case paused
- case buffering(initial: Bool, whilePlaying: Bool)
+ case buffering(initial: Bool, whilePlaying: Bool, progress: Float)
public static func ==(lhs: MediaPlayerPlaybackStatus, rhs: MediaPlayerPlaybackStatus) -> Bool {
switch lhs {
@@ -926,8 +936,8 @@ public enum MediaPlayerPlaybackStatus: Equatable {
} else {
return false
}
- case let .buffering(initial, whilePlaying):
- if case .buffering(initial, whilePlaying) = rhs {
+ case let .buffering(initial, whilePlaying, progress):
+ if case .buffering(initial, whilePlaying, progress) = rhs {
return true
} else {
return false
diff --git a/submodules/MediaPlayer/Sources/MediaPlayerScrubbingNode.swift b/submodules/MediaPlayer/Sources/MediaPlayerScrubbingNode.swift
index d398f5ef36..bd3a68be34 100644
--- a/submodules/MediaPlayer/Sources/MediaPlayerScrubbingNode.swift
+++ b/submodules/MediaPlayer/Sources/MediaPlayerScrubbingNode.swift
@@ -781,7 +781,7 @@ public final class MediaPlayerScrubbingNode: ASDisplayNode {
default:
break
}
- if case .buffering(true, _) = statusValue.status {
+ if case .buffering(true, _, _) = statusValue.status {
//initialBuffering = true
} else if Double(0.0).isLess(than: statusValue.duration) {
if let scrubbingTimestampValue = self.scrubbingTimestampValue {
diff --git a/submodules/MediaPlayer/Sources/MediaTrackFrameBuffer.swift b/submodules/MediaPlayer/Sources/MediaTrackFrameBuffer.swift
index 598ae2be68..49b3764921 100644
--- a/submodules/MediaPlayer/Sources/MediaTrackFrameBuffer.swift
+++ b/submodules/MediaPlayer/Sources/MediaTrackFrameBuffer.swift
@@ -3,7 +3,7 @@ import SwiftSignalKit
import CoreMedia
public enum MediaTrackFrameBufferStatus {
- case buffering
+ case buffering(progress: Double)
case full(until: Double)
case finished(at: Double)
}
@@ -37,6 +37,7 @@ public final class MediaTrackFrameBuffer {
private var frames: [MediaTrackDecodableFrame] = []
private var endOfStream = false
private var bufferedUntilTime: CMTime?
+ private var isWaitingForLowWaterDuration: Bool = false
init(frameSource: MediaFrameSource, decoder: MediaTrackFrameDecoder, type: MediaTrackFrameType, duration: CMTime, rotationAngle: Double, aspect: Double, stallDuration: Double = 1.0, lowWaterDuration: Double = 2.0, highWaterDuration: Double = 3.0) {
self.frameSource = frameSource
@@ -97,6 +98,7 @@ public final class MediaTrackFrameBuffer {
private func endOfStreamReached() {
self.endOfStream = true
+ self.isWaitingForLowWaterDuration = false
self.statusUpdated()
}
@@ -123,17 +125,24 @@ public final class MediaTrackFrameBuffer {
if traceEvents {
print("buffered duration: \(bufferedDuration), requesting until \(timestamp) + \(self.highWaterDuration - bufferedDuration)")
}
- self.frameSource.generateFrames(until: timestamp + self.highWaterDuration)
+ let delayIncrement = 0.3
+ var generateUntil = timestamp + delayIncrement
+ while generateUntil < timestamp + self.highWaterDuration {
+ self.frameSource.generateFrames(until: min(timestamp + self.highWaterDuration, generateUntil))
+ generateUntil += delayIncrement
+ }
- if bufferedDuration > self.stallDuration {
+ if bufferedDuration > self.stallDuration && !self.isWaitingForLowWaterDuration {
if traceEvents {
print("buffered1 duration: \(bufferedDuration), wait until \(timestamp) + \(self.highWaterDuration - bufferedDuration)")
}
return .full(until: timestamp + self.highWaterDuration)
} else {
- return .buffering
+ self.isWaitingForLowWaterDuration = true
+ return .buffering(progress: max(0.0, bufferedDuration / self.lowWaterDuration))
}
} else {
+ self.isWaitingForLowWaterDuration = false
if traceEvents {
print("buffered2 duration: \(bufferedDuration), wait until \(timestamp) + \(bufferedDuration - self.lowWaterDuration)")
}
diff --git a/submodules/MozjpegBinding/BUCK b/submodules/MozjpegBinding/BUCK
new file mode 100644
index 0000000000..64759fab2b
--- /dev/null
+++ b/submodules/MozjpegBinding/BUCK
@@ -0,0 +1,20 @@
+load("//Config:buck_rule_macros.bzl", "static_library")
+
+static_library(
+ name = "MozjpegBinding",
+ srcs = glob([
+ "Sources/*.m",
+ ]),
+ headers = glob([
+ "Sources/*.h",
+ ]),
+ exported_headers = glob([
+ "Public/**/*.h",
+ ]),
+ deps = [
+ "//third-party/mozjpeg:mozjpeg",
+ ],
+ frameworks = [
+ "$SDKROOT/System/Library/Frameworks/Foundation.framework",
+ ],
+)
diff --git a/submodules/MozjpegBinding/BUILD b/submodules/MozjpegBinding/BUILD
new file mode 100644
index 0000000000..55ef3065ee
--- /dev/null
+++ b/submodules/MozjpegBinding/BUILD
@@ -0,0 +1,22 @@
+
+objc_library(
+ name = "MozjpegBinding",
+ module_name = "MozjpegBinding",
+ enable_modules = True,
+ srcs = glob([
+ "Sources/**/*.m",
+ "Sources/**/*.h",
+ ]),
+ hdrs = glob([
+ "Public/**/*.h",
+ ]),
+ includes = [
+ "Public",
+ ],
+ deps = [
+ "//third-party/mozjpeg:mozjpeg",
+ ],
+ visibility = [
+ "//visibility:public",
+ ]
+)
diff --git a/submodules/MozjpegBinding/Public/MozjpegBinding/MozjpegBinding.h b/submodules/MozjpegBinding/Public/MozjpegBinding/MozjpegBinding.h
new file mode 100644
index 0000000000..3e7ec4696f
--- /dev/null
+++ b/submodules/MozjpegBinding/Public/MozjpegBinding/MozjpegBinding.h
@@ -0,0 +1,5 @@
+#import
+
+NSData * _Nullable compressJPEGData(UIImage * _Nonnull sourceImage);
+NSArray * _Nonnull extractJPEGDataScans(NSData * _Nonnull data);
+NSData * _Nullable compressMiniThumbnail(UIImage * _Nonnull image);
diff --git a/submodules/MozjpegBinding/Sources/MozjpegBinding.m b/submodules/MozjpegBinding/Sources/MozjpegBinding.m
new file mode 100644
index 0000000000..e3952205cc
--- /dev/null
+++ b/submodules/MozjpegBinding/Sources/MozjpegBinding.m
@@ -0,0 +1,256 @@
+#import
+
+#import
+#import
+
+static NSData *getHeaderPattern() {
+ static NSData *value = nil;
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ value = [[NSData alloc] initWithBase64EncodedString:@"/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDACgcHiMeGSgjISMtKygwPGRBPDc3PHtYXUlkkYCZlo+AjIqgtObDoKrarYqMyP/L2u71////m8H////6/+b9//j/2wBDASstLTw1PHZBQXb4pYyl+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj/wAARCAAAAAADASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwA=" options:0];
+ });
+ return value;
+}
+
+static NSData *getFooterPattern() {
+ static NSData *value = nil;
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ value = [[NSData alloc] initWithBase64EncodedString:@"/9k=" options:0];
+ });
+ return value;
+}
+
+NSArray * _Nonnull extractJPEGDataScans(NSData * _Nonnull data) {
+ NSMutableArray *result = [[NSMutableArray alloc] init];
+
+ const uint8_t *dataBytes = data.bytes;
+ int offset = 0;
+ while (offset < data.length) {
+ bool found = false;
+ for (int i = offset + 2; i < data.length - 1; i++) {
+ if (dataBytes[i] == 0xffU && dataBytes[i + 1] == 0xdaU) {
+ if (offset != 0) {
+ [result addObject:@(i)];
+ }
+ offset = i;
+ found = true;
+ }
+ }
+ if (!found) {
+ break;
+ }
+ }
+
+#if DEBUG
+ static NSString *sessionPrefix = nil;
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ sessionPrefix = [NSString stringWithFormat:@"%u", arc4random()];
+ });
+
+ NSString *randomId = [NSString stringWithFormat:@"%u", arc4random()];
+ NSString *dirPath = [[NSTemporaryDirectory() stringByAppendingPathComponent:sessionPrefix] stringByAppendingPathComponent:randomId];
+ [[NSFileManager defaultManager] createDirectoryAtPath:dirPath withIntermediateDirectories:true attributes:nil error:nil];
+ for (int i = 0; i < result.count + 1; i++) {
+ NSString *filePath = [dirPath stringByAppendingPathComponent:[NSString stringWithFormat:@"%d.jpg", i]];
+ if (i == result.count) {
+ [data writeToFile:filePath atomically:true];
+ } else {
+ [[data subdataWithRange:NSMakeRange(0, [result[i] intValue])] writeToFile:filePath atomically:true];
+ }
+ }
+ NSLog(@"Path: %@", dirPath);
+#endif
+
+ return result;
+}
+
+NSData * _Nullable compressJPEGData(UIImage * _Nonnull sourceImage) {
+ int width = (int)(sourceImage.size.width * sourceImage.scale);
+ int height = (int)(sourceImage.size.height * sourceImage.scale);
+
+ int targetBytesPerRow = ((4 * (int)width) + 15) & (~15);
+ uint8_t *targetMemory = malloc((int)(targetBytesPerRow * height));
+
+ CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
+ CGBitmapInfo bitmapInfo = kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host;
+
+ CGContextRef targetContext = CGBitmapContextCreate(targetMemory, width, height, 8, targetBytesPerRow, colorSpace, bitmapInfo);
+
+ UIGraphicsPushContext(targetContext);
+
+ CGColorSpaceRelease(colorSpace);
+
+ CGContextDrawImage(targetContext, CGRectMake(0, 0, width, height), sourceImage.CGImage);
+
+ UIGraphicsPopContext();
+
+ int bufferBytesPerRow = ((3 * (int)width) + 15) & (~15);
+ uint8_t *buffer = malloc(bufferBytesPerRow * height);
+
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+ uint32_t *color = ((uint32_t *)&targetMemory[y * targetBytesPerRow + x * 4]);
+
+ uint32_t r = ((*color >> 16) & 0xff);
+ uint32_t g = ((*color >> 8) & 0xff);
+ uint32_t b = (*color & 0xff);
+
+ buffer[y * bufferBytesPerRow + x * 3 + 0] = r;
+ buffer[y * bufferBytesPerRow + x * 3 + 1] = g;
+ buffer[y * bufferBytesPerRow + x * 3 + 2] = b;
+ }
+ }
+
+ CGContextRelease(targetContext);
+
+ free(targetMemory);
+
+ struct jpeg_compress_struct cinfo;
+ struct jpeg_error_mgr jerr;
+ cinfo.err = jpeg_std_error(&jerr);
+ jpeg_create_compress(&cinfo);
+
+ uint8_t *outBuffer = NULL;
+ unsigned long outSize = 0;
+ jpeg_mem_dest(&cinfo, &outBuffer, &outSize);
+
+ cinfo.image_width = (uint32_t)width;
+ cinfo.image_height = (uint32_t)height;
+ cinfo.input_components = 3;
+ cinfo.in_color_space = JCS_RGB;
+ jpeg_c_set_int_param(&cinfo, JINT_COMPRESS_PROFILE, JCP_FASTEST);
+ jpeg_set_defaults(&cinfo);
+ cinfo.arith_code = FALSE;
+ cinfo.dct_method = JDCT_ISLOW;
+ cinfo.optimize_coding = TRUE;
+ jpeg_set_quality(&cinfo, 78, 1);
+ jpeg_simple_progression(&cinfo);
+ jpeg_start_compress(&cinfo, 1);
+
+ JSAMPROW rowPointer[1];
+ while (cinfo.next_scanline < cinfo.image_height) {
+ rowPointer[0] = (JSAMPROW)(buffer + cinfo.next_scanline * bufferBytesPerRow);
+ jpeg_write_scanlines(&cinfo, rowPointer, 1);
+ }
+
+ jpeg_finish_compress(&cinfo);
+
+ NSData *result = [[NSData alloc] initWithBytes:outBuffer length:outSize];
+
+ jpeg_destroy_compress(&cinfo);
+
+ free(buffer);
+
+ return result;
+}
+
+NSData * _Nullable compressMiniThumbnail(UIImage * _Nonnull image) {
+ CGSize size = CGSizeMake(40.0f, 40.0f);
+ CGSize fittedSize = image.size;
+ if (fittedSize.width > size.width) {
+ fittedSize = CGSizeMake(size.width, (int)((fittedSize.height * size.width / MAX(fittedSize.width, 1.0f))));
+ }
+ if (fittedSize.height > size.height) {
+ fittedSize = CGSizeMake((int)((fittedSize.width * size.height / MAX(fittedSize.height, 1.0f))), size.height);
+ }
+
+ int width = (int)fittedSize.width;
+ int height = (int)fittedSize.height;
+
+ int targetBytesPerRow = ((4 * (int)width) + 15) & (~15);
+ uint8_t *targetMemory = malloc((int)(targetBytesPerRow * height));
+
+ CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
+ CGBitmapInfo bitmapInfo = kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host;
+
+ CGContextRef targetContext = CGBitmapContextCreate(targetMemory, width, height, 8, targetBytesPerRow, colorSpace, bitmapInfo);
+
+ UIGraphicsPushContext(targetContext);
+
+ CGColorSpaceRelease(colorSpace);
+
+ CGContextDrawImage(targetContext, CGRectMake(0, 0, width, height), image.CGImage);
+
+ UIGraphicsPopContext();
+
+ int bufferBytesPerRow = ((3 * (int)width) + 15) & (~15);
+ uint8_t *buffer = malloc(bufferBytesPerRow * height);
+
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+ uint32_t *color = ((uint32_t *)&targetMemory[y * targetBytesPerRow + x * 4]);
+
+ uint32_t r = ((*color >> 16) & 0xff);
+ uint32_t g = ((*color >> 8) & 0xff);
+ uint32_t b = (*color & 0xff);
+
+ buffer[y * bufferBytesPerRow + x * 3 + 0] = r;
+ buffer[y * bufferBytesPerRow + x * 3 + 1] = g;
+ buffer[y * bufferBytesPerRow + x * 3 + 2] = b;
+ }
+ }
+
+ CGContextRelease(targetContext);
+
+ free(targetMemory);
+
+ struct jpeg_compress_struct cinfo;
+ struct jpeg_error_mgr jerr;
+ cinfo.err = jpeg_std_error(&jerr);
+ jpeg_create_compress(&cinfo);
+
+ uint8_t *outBuffer = NULL;
+ unsigned long outSize = 0;
+ jpeg_mem_dest(&cinfo, &outBuffer, &outSize);
+
+ cinfo.image_width = (uint32_t)width;
+ cinfo.image_height = (uint32_t)height;
+ cinfo.input_components = 3;
+ cinfo.in_color_space = JCS_RGB;
+ jpeg_c_set_int_param(&cinfo, JINT_COMPRESS_PROFILE, JCP_FASTEST);
+ jpeg_set_defaults(&cinfo);
+ cinfo.arith_code = FALSE;
+ cinfo.dct_method = JDCT_ISLOW;
+ cinfo.optimize_coding = FALSE;
+ jpeg_set_quality(&cinfo, 20, 1);
+ jpeg_start_compress(&cinfo, 1);
+
+ JSAMPROW rowPointer[1];
+ while (cinfo.next_scanline < cinfo.image_height) {
+ rowPointer[0] = (JSAMPROW)(buffer + cinfo.next_scanline * bufferBytesPerRow);
+ jpeg_write_scanlines(&cinfo, rowPointer, 1);
+ }
+
+ jpeg_finish_compress(&cinfo);
+
+ NSMutableData *serializedData = nil;
+
+ NSData *headerPattern = getHeaderPattern();
+ NSData *footerPattern = getFooterPattern();
+ if (outBuffer[164] == height && outBuffer[166] == width && headerPattern != nil && footerPattern != nil) {
+ outBuffer[164] = 0;
+ outBuffer[166] = 0;
+
+ if (memcmp(headerPattern.bytes, outBuffer, headerPattern.length) == 0) {
+ if (memcmp(footerPattern.bytes, outBuffer + outSize - footerPattern.length, footerPattern.length) == 0) {
+ serializedData = [[NSMutableData alloc] init];
+ uint8_t version = 1;
+ [serializedData appendBytes:&version length:1];
+ uint8_t outWidth = (uint8_t)width;
+ uint8_t outHeight = (uint8_t)height;
+ [serializedData appendBytes:&outHeight length:1];
+ [serializedData appendBytes:&outWidth length:1];
+ unsigned long contentSize = outSize - headerPattern.length - footerPattern.length;
+ [serializedData appendBytes:outBuffer + headerPattern.length length:contentSize];
+ }
+ }
+ }
+
+ jpeg_destroy_compress(&cinfo);
+
+ free(buffer);
+
+ return serializedData;
+}
diff --git a/submodules/MtProtoKit/Sources/MTApiEnvironment.m b/submodules/MtProtoKit/Sources/MTApiEnvironment.m
index 996f8fa14d..ea79285863 100644
--- a/submodules/MtProtoKit/Sources/MTApiEnvironment.m
+++ b/submodules/MtProtoKit/Sources/MTApiEnvironment.m
@@ -29,6 +29,7 @@
#define IPHONE_8Plus_NAMESTRING @"iPhone 8 Plus"
#define IPHONE_X_NAMESTRING @"iPhone X"
#define IPHONE_SE_NAMESTRING @"iPhone SE"
+#define IPHONE_SE2_NAMESTRING @"iPhone SE (2nd gen)"
#define IPHONE_XS_NAMESTRING @"iPhone XS"
#define IPHONE_XSMAX_NAMESTRING @"iPhone XS Max"
#define IPHONE_XR_NAMESTRING @"iPhone XR"
@@ -96,7 +97,7 @@ typedef enum {
UIDevice5SiPhone,
UIDevice6iPhone,
UIDevice6PlusiPhone,
- UIDevice6siPhone,
+ UIDevice6SiPhone,
UIDevice6SPlusiPhone,
UIDevice7iPhone,
UIDevice7PlusiPhone,
@@ -104,6 +105,7 @@ typedef enum {
UIDevice8PlusiPhone,
UIDeviceXiPhone,
UIDeviceSEPhone,
+ UIDeviceSE2Phone,
UIDeviceXSiPhone,
UIDeviceXSMaxiPhone,
UIDeviceXRiPhone,
@@ -587,7 +589,7 @@ NSString *suffix = @"";
case UIDevice5SiPhone: return IPHONE_5S_NAMESTRING;
case UIDevice6iPhone: return IPHONE_6_NAMESTRING;
case UIDevice6PlusiPhone: return IPHONE_6Plus_NAMESTRING;
- case UIDevice6siPhone: return IPHONE_6S_NAMESTRING;
+ case UIDevice6SiPhone: return IPHONE_6S_NAMESTRING;
case UIDevice6SPlusiPhone: return IPHONE_6SPlus_NAMESTRING;
case UIDevice7iPhone: return IPHONE_7_NAMESTRING;
case UIDevice7PlusiPhone: return IPHONE_7Plus_NAMESTRING;
@@ -595,6 +597,7 @@ NSString *suffix = @"";
case UIDevice8PlusiPhone: return IPHONE_8Plus_NAMESTRING;
case UIDeviceXiPhone: return IPHONE_X_NAMESTRING;
case UIDeviceSEPhone: return IPHONE_SE_NAMESTRING;
+ case UIDeviceSE2Phone: return IPHONE_SE2_NAMESTRING;
case UIDeviceXSiPhone: return IPHONE_XS_NAMESTRING;
case UIDeviceXSMaxiPhone: return IPHONE_XSMAX_NAMESTRING;
case UIDeviceXRiPhone: return IPHONE_XR_NAMESTRING;
@@ -675,7 +678,7 @@ NSString *suffix = @"";
if ([platform isEqualToString:@"iPhone7,1"]) return UIDevice6PlusiPhone;
if ([platform isEqualToString:@"iPhone7,2"]) return UIDevice6iPhone;
- if ([platform isEqualToString:@"iPhone8,1"]) return UIDevice6siPhone;
+ if ([platform isEqualToString:@"iPhone8,1"]) return UIDevice6SiPhone;
if ([platform isEqualToString:@"iPhone8,2"]) return UIDevice6SPlusiPhone;
if ([platform isEqualToString:@"iPhone9,1"]) return UIDevice7iPhone;
if ([platform isEqualToString:@"iPhone9,3"]) return UIDevice7iPhone;
@@ -696,6 +699,7 @@ NSString *suffix = @"";
if ([platform isEqualToString:@"iPhone12,1"]) return UIDevice11iPhone;
if ([platform isEqualToString:@"iPhone12,3"]) return UIDevice11ProiPhone;
if ([platform isEqualToString:@"iPhone12,5"]) return UIDevice11ProMaxiPhone;
+ if ([platform isEqualToString:@"iPhone12,8"]) return UIDeviceSE2Phone;
if ([platform isEqualToString:@"iPhone8,4"]) return UIDeviceSEPhone;
diff --git a/submodules/OpenInExternalAppUI/Sources/OpenInOptions.swift b/submodules/OpenInExternalAppUI/Sources/OpenInOptions.swift
index 8d77aeeec4..e855eae738 100644
--- a/submodules/OpenInExternalAppUI/Sources/OpenInOptions.swift
+++ b/submodules/OpenInExternalAppUI/Sources/OpenInOptions.swift
@@ -181,7 +181,11 @@ private func allOpenInOptions(context: AccountContext, item: OpenInItem) -> [Ope
if withDirections {
return .openUrl(url: "comgooglemaps-x-callback://?daddr=\(coordinates)&directionsmode=driving&x-success=telegram://?resume=true&x-source=Telegram")
} else {
- return .openUrl(url: "comgooglemaps-x-callback://?center=\(coordinates)&q=\(coordinates)&x-success=telegram://?resume=true&x-source=Telegram")
+ if let venue = location.venue, let venueId = venue.id, let provider = venue.provider, provider == "gplaces" {
+ return .openUrl(url: "https://www.google.com/maps/search/?api=1&query=\(venue.address ?? "")&query_place_id=\(venueId)")
+ } else {
+ return .openUrl(url: "comgooglemaps-x-callback://?center=\(coordinates)&q=\(coordinates)&x-success=telegram://?resume=true&x-source=Telegram")
+ }
}
}))
diff --git a/submodules/PasswordSetupUI/Sources/TwoFactorAuthDataInputScreen.swift b/submodules/PasswordSetupUI/Sources/TwoFactorAuthDataInputScreen.swift
index eadc36ff4a..a4f3dcc72a 100644
--- a/submodules/PasswordSetupUI/Sources/TwoFactorAuthDataInputScreen.swift
+++ b/submodules/PasswordSetupUI/Sources/TwoFactorAuthDataInputScreen.swift
@@ -700,14 +700,14 @@ private final class TwoFactorDataInputScreenNode: ViewControllerTracingNode, UIS
case .emailConfirmation:
if let path = getAppBundle().path(forResource: "TwoFactorSetupMail", ofType: "tgs") {
let animatedStickerNode = AnimatedStickerNode()
- animatedStickerNode.setup(source: AnimatedStickerNodeLocalFileSource(path: path), width: 272, height: 272, playbackMode: .once, mode: .direct)
+ animatedStickerNode.setup(source: AnimatedStickerNodeLocalFileSource(path: path), width: 272, height: 272, playbackMode: .once, mode: .direct(cachePathPrefix: nil))
animatedStickerNode.visibility = true
self.animatedStickerNode = animatedStickerNode
}
case .passwordHint:
if let path = getAppBundle().path(forResource: "TwoFactorSetupHint", ofType: "tgs") {
let animatedStickerNode = AnimatedStickerNode()
- animatedStickerNode.setup(source: AnimatedStickerNodeLocalFileSource(path: path), width: 272, height: 272, playbackMode: .once, mode: .direct)
+ animatedStickerNode.setup(source: AnimatedStickerNodeLocalFileSource(path: path), width: 272, height: 272, playbackMode: .once, mode: .direct(cachePathPrefix: nil))
animatedStickerNode.visibility = true
self.animatedStickerNode = animatedStickerNode
}
diff --git a/submodules/PasswordSetupUI/Sources/TwoFactorAuthSplashScreen.swift b/submodules/PasswordSetupUI/Sources/TwoFactorAuthSplashScreen.swift
index 48428f078e..af6154e147 100644
--- a/submodules/PasswordSetupUI/Sources/TwoFactorAuthSplashScreen.swift
+++ b/submodules/PasswordSetupUI/Sources/TwoFactorAuthSplashScreen.swift
@@ -112,7 +112,7 @@ private final class TwoFactorAuthSplashScreenNode: ViewControllerTracingNode {
buttonText = self.presentationData.strings.TwoFactorSetup_Intro_Action
if let path = getAppBundle().path(forResource: "TwoFactorSetupIntro", ofType: "tgs") {
- self.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(path: path), width: 248, height: 248, playbackMode: .once, mode: .direct)
+ self.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(path: path), width: 248, height: 248, playbackMode: .once, mode: .direct(cachePathPrefix: nil))
self.animationSize = CGSize(width: 124.0, height: 124.0)
self.animationNode.visibility = true
}
@@ -122,7 +122,7 @@ private final class TwoFactorAuthSplashScreenNode: ViewControllerTracingNode {
buttonText = self.presentationData.strings.TwoFactorSetup_Done_Action
if let path = getAppBundle().path(forResource: "TwoFactorSetupDone", ofType: "tgs") {
- self.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(path: path), width: 248, height: 248, mode: .direct)
+ self.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(path: path), width: 248, height: 248, mode: .direct(cachePathPrefix: nil))
self.animationSize = CGSize(width: 124.0, height: 124.0)
self.animationNode.visibility = true
}
diff --git a/submodules/PeerAvatarGalleryUI/Sources/AvatarGalleryController.swift b/submodules/PeerAvatarGalleryUI/Sources/AvatarGalleryController.swift
index a652a5c105..f1b913406f 100644
--- a/submodules/PeerAvatarGalleryUI/Sources/AvatarGalleryController.swift
+++ b/submodules/PeerAvatarGalleryUI/Sources/AvatarGalleryController.swift
@@ -24,7 +24,7 @@ public enum AvatarGalleryEntryId: Hashable {
public enum AvatarGalleryEntry: Equatable {
case topImage([ImageRepresentationWithReference], [VideoRepresentationWithReference], Peer?, GalleryItemIndexData?, Data?, String?)
- case image(MediaId, TelegramMediaImageReference?, [ImageRepresentationWithReference], [VideoRepresentationWithReference], Peer?, Int32, GalleryItemIndexData?, MessageId?, Data?, String?)
+ case image(MediaId, TelegramMediaImageReference?, [ImageRepresentationWithReference], [VideoRepresentationWithReference], Peer?, Int32?, GalleryItemIndexData?, MessageId?, Data?, String?)
public var id: AvatarGalleryEntryId {
switch self {
@@ -136,7 +136,7 @@ public func initialAvatarGalleryEntries(account: Account, peer: Peer) -> Signal<
initialEntries.append(.topImage(peer.profileImageRepresentations.map({ ImageRepresentationWithReference(representation: $0, reference: MediaResourceReference.avatar(peer: peerReference, resource: $0.resource)) }), [], peer, nil, nil, nil))
}
- if peer is TelegramChannel || peer is TelegramGroup {
+ if peer is TelegramChannel || peer is TelegramGroup, let peerReference = PeerReference(peer) {
return account.postbox.transaction { transaction in
return transaction.getPeerCachedData(peerId: peer.id)
} |> map { cachedData in
@@ -148,10 +148,14 @@ public func initialAvatarGalleryEntries(account: Account, peer: Peer) -> Signal<
initialPhoto = photo
}
- if let photo = initialPhoto, !photo.videoRepresentations.isEmpty, let peerReference = PeerReference(peer) {
- return [.topImage(photo.representations.map({ ImageRepresentationWithReference(representation: $0, reference: MediaResourceReference.avatar(peer: peerReference, resource: $0.resource)) }), photo.videoRepresentations.map({ VideoRepresentationWithReference(representation: $0, reference: MediaResourceReference.avatar(peer: peerReference, resource: $0.resource)) }), peer, nil, nil, nil)]
+ if let photo = initialPhoto {
+ var representations = photo.representations.map({ ImageRepresentationWithReference(representation: $0, reference: MediaResourceReference.avatar(peer: peerReference, resource: $0.resource)) })
+ if photo.immediateThumbnailData == nil, let firstEntry = initialEntries.first, let firstRepresentation = firstEntry.representations.first {
+ representations.insert(firstRepresentation, at: 0)
+ }
+ return [.image(photo.imageId, photo.reference, representations, photo.videoRepresentations.map({ VideoRepresentationWithReference(representation: $0, reference: MediaResourceReference.avatarList(peer: peerReference, resource: $0.resource)) }), peer, nil, nil, nil, photo.immediateThumbnailData, nil)]
} else {
- return initialEntries
+ return []
}
}
} else {
@@ -171,6 +175,95 @@ public func fetchedAvatarGalleryEntries(account: Account, peer: Peer) -> Signal<
result = initialEntries
} else if let peerReference = PeerReference(peer) {
var index: Int32 = 0
+ if [Namespaces.Peer.CloudGroup, Namespaces.Peer.CloudChannel].contains(peer.id.namespace) {
+ var initialMediaIds = Set()
+ for entry in initialEntries {
+ if case let .image(image) = entry {
+ initialMediaIds.insert(image.0)
+ }
+ }
+
+ var photosCount = photos.count
+ for i in 0 ..< photos.count {
+ let photo = photos[i]
+ if i == 0 && !initialMediaIds.contains(photo.image.imageId) {
+ photosCount += 1
+ for entry in initialEntries {
+ let indexData = GalleryItemIndexData(position: index, totalCount: Int32(photosCount))
+ if case let .image(image) = entry {
+ result.append(.image(image.0, image.1, image.2, image.3, image.4, nil, indexData, nil, image.8, nil))
+ index += 1
+ }
+ }
+ }
+
+ let indexData = GalleryItemIndexData(position: index, totalCount: Int32(photosCount))
+ result.append(.image(photo.image.imageId, photo.image.reference, photo.image.representations.map({ ImageRepresentationWithReference(representation: $0, reference: MediaResourceReference.avatarList(peer: peerReference, resource: $0.resource)) }), photo.image.videoRepresentations.map({ VideoRepresentationWithReference(representation: $0, reference: MediaResourceReference.avatarList(peer: peerReference, resource: $0.resource)) }), peer, photo.date, indexData, photo.messageId, photo.image.immediateThumbnailData, nil))
+ index += 1
+ }
+ } else {
+ for photo in photos {
+ let indexData = GalleryItemIndexData(position: index, totalCount: Int32(photos.count))
+ if result.isEmpty, let first = initialEntries.first {
+ result.append(.image(photo.image.imageId, photo.image.reference, first.representations, photo.image.videoRepresentations.map({ VideoRepresentationWithReference(representation: $0, reference: MediaResourceReference.avatarList(peer: peerReference, resource: $0.resource)) }), peer, photo.date, indexData, photo.messageId, photo.image.immediateThumbnailData, nil))
+ } else {
+ result.append(.image(photo.image.imageId, photo.image.reference, photo.image.representations.map({ ImageRepresentationWithReference(representation: $0, reference: MediaResourceReference.avatarList(peer: peerReference, resource: $0.resource)) }), photo.image.videoRepresentations.map({ VideoRepresentationWithReference(representation: $0, reference: MediaResourceReference.avatarList(peer: peerReference, resource: $0.resource)) }), peer, photo.date, indexData, photo.messageId, photo.image.immediateThumbnailData, nil))
+ }
+ index += 1
+ }
+ }
+ }
+ return result
+ }
+ )
+ }
+}
+
+public func fetchedAvatarGalleryEntries(account: Account, peer: Peer, firstEntry: AvatarGalleryEntry) -> Signal<(Bool, [AvatarGalleryEntry]), NoError> {
+ let initialEntries = [firstEntry]
+ return Signal<(Bool, [AvatarGalleryEntry]), NoError>.single((false, initialEntries))
+ |> then(
+ requestPeerPhotos(postbox: account.postbox, network: account.network, peerId: peer.id)
+ |> map { photos -> (Bool, [AvatarGalleryEntry]) in
+ var result: [AvatarGalleryEntry] = []
+ let initialEntries = [firstEntry]
+ if photos.isEmpty {
+ result = initialEntries
+ } else if let peerReference = PeerReference(peer) {
+ var index: Int32 = 0
+
+ if [Namespaces.Peer.CloudGroup, Namespaces.Peer.CloudChannel].contains(peer.id.namespace) {
+ var initialMediaIds = Set()
+ for entry in initialEntries {
+ if case let .image(image) = entry {
+ initialMediaIds.insert(image.0)
+ }
+ }
+
+ var photosCount = photos.count
+ for i in 0 ..< photos.count {
+ let photo = photos[i]
+ var representations = photo.image.representations.map({ ImageRepresentationWithReference(representation: $0, reference: MediaResourceReference.avatarList(peer: peerReference, resource: $0.resource)) })
+ if i == 0 {
+ if !initialMediaIds.contains(photo.image.imageId) {
+ photosCount += 1
+ for entry in initialEntries {
+ let indexData = GalleryItemIndexData(position: index, totalCount: Int32(photosCount))
+ if case let .image(image) = entry {
+ result.append(.image(image.0, image.1, image.2, image.3, image.4, nil, indexData, nil, image.8, nil))
+ index += 1
+ }
+ }
+ } else if photo.image.immediateThumbnailData == nil, let firstEntry = initialEntries.first, let firstRepresentation = firstEntry.representations.first {
+ representations.insert(firstRepresentation, at: 0)
+ }
+ }
+
+ let indexData = GalleryItemIndexData(position: index, totalCount: Int32(photosCount))
+ result.append(.image(photo.image.imageId, photo.image.reference, representations, photo.image.videoRepresentations.map({ VideoRepresentationWithReference(representation: $0, reference: MediaResourceReference.avatarList(peer: peerReference, resource: $0.resource)) }), peer, photo.date, indexData, photo.messageId, photo.image.immediateThumbnailData, nil))
+ index += 1
+ }
+ } else {
for photo in photos {
let indexData = GalleryItemIndexData(position: index, totalCount: Int32(photos.count))
if result.isEmpty, let first = initialEntries.first {
@@ -181,35 +274,8 @@ public func fetchedAvatarGalleryEntries(account: Account, peer: Peer) -> Signal<
index += 1
}
}
- return result
}
- )
- }
-}
-
-public func fetchedAvatarGalleryEntries(account: Account, peer: Peer, firstEntry: AvatarGalleryEntry) -> Signal<[AvatarGalleryEntry], NoError> {
- let initialEntries = [firstEntry]
- return Signal<[AvatarGalleryEntry], NoError>.single(initialEntries)
- |> then(
- requestPeerPhotos(postbox: account.postbox, network: account.network, peerId: peer.id)
- |> map { photos -> [AvatarGalleryEntry] in
- var result: [AvatarGalleryEntry] = []
- let initialEntries = [firstEntry]
- if photos.isEmpty {
- result = initialEntries
- } else if let peerReference = PeerReference(peer) {
- var index: Int32 = 0
- for photo in photos {
- let indexData = GalleryItemIndexData(position: index, totalCount: Int32(photos.count))
- if result.isEmpty, let first = initialEntries.first {
- result.append(.image(photo.image.imageId, photo.image.reference, first.representations, photo.image.videoRepresentations.map({ VideoRepresentationWithReference(representation: $0, reference: MediaResourceReference.avatarList(peer: peerReference, resource: $0.resource)) }), peer, photo.date, indexData, photo.messageId, photo.image.immediateThumbnailData, nil))
- } else {
- result.append(.image(photo.image.imageId, photo.image.reference, photo.image.representations.map({ ImageRepresentationWithReference(representation: $0, reference: MediaResourceReference.avatarList(peer: peerReference, resource: $0.resource)) }), photo.image.videoRepresentations.map({ VideoRepresentationWithReference(representation: $0, reference: MediaResourceReference.avatarList(peer: peerReference, resource: $0.resource)) }), peer, photo.date, indexData, photo.messageId, photo.image.immediateThumbnailData, nil))
- }
- index += 1
- }
- }
- return result
+ return (true, result)
}
)
}
@@ -599,6 +665,19 @@ public class AvatarGalleryController: ViewController, StandalonePresentableContr
return canDelete
}
+ private func replaceEntries(_ entries: [AvatarGalleryEntry]) {
+ self.galleryNode.currentThumbnailContainerNode?.updateSynchronously = true
+ self.galleryNode.pager.replaceItems(entries.map({ entry in PeerAvatarImageGalleryItem(context: self.context, peer: self.peer, presentationData: presentationData, entry: entry, sourceCorners: self.sourceCorners, delete: self.canDelete ? { [weak self] in
+ self?.deleteEntry(entry)
+ } : nil, setMain: { [weak self] in
+ self?.setMainEntry(entry)
+ }, edit: { [weak self] in
+ self?.editEntry(entry)
+ }) }), centralItemIndex: 0, synchronous: true)
+ self.entries = entries
+ self.galleryNode.currentThumbnailContainerNode?.updateSynchronously = false
+ }
+
private func setMainEntry(_ rawEntry: AvatarGalleryEntry) {
var entry = rawEntry
if case .topImage = entry, !self.entries.isEmpty {
@@ -611,9 +690,33 @@ public class AvatarGalleryController: ViewController, StandalonePresentableContr
} else {
}
case let .image(_, reference, _, _, _, _, _, _, _, _):
- if self.peer.id == self.context.account.peerId {
+ if self.peer.id == self.context.account.peerId, let peerReference = PeerReference(self.peer) {
if let reference = reference {
- let _ = updatePeerPhotoExisting(network: self.context.account.network, reference: reference).start()
+ let _ = (updatePeerPhotoExisting(network: self.context.account.network, reference: reference)
+ |> deliverOnMainQueue).start(next: { [weak self] photo in
+ if let strongSelf = self, let photo = photo, let firstEntry = strongSelf.entries.first, case let .image(image) = firstEntry {
+ let updatedEntry = AvatarGalleryEntry.image(photo.imageId, photo.reference, photo.representations.map({ ImageRepresentationWithReference(representation: $0, reference: MediaResourceReference.avatar(peer: peerReference, resource: $0.resource)) }), photo.videoRepresentations.map({ VideoRepresentationWithReference(representation: $0, reference: MediaResourceReference.avatarList(peer: peerReference, resource: $0.resource)) }), strongSelf.peer, image.5, image.6, image.7, photo.immediateThumbnailData, image.9)
+
+ for (lhs, rhs) in zip(firstEntry.representations, updatedEntry.representations) {
+ if lhs.representation.dimensions == rhs.representation.dimensions {
+ strongSelf.context.account.postbox.mediaBox.copyResourceData(from: lhs.representation.resource.id, to: rhs.representation.resource.id, synchronous: true)
+ }
+ }
+ for (lhs, rhs) in zip(firstEntry.videoRepresentations, updatedEntry.videoRepresentations) {
+ if lhs.representation.dimensions == rhs.representation.dimensions {
+ strongSelf.context.account.postbox.mediaBox.copyResourceData(from: lhs.representation.resource.id, to: rhs.representation.resource.id, synchronous: true)
+ }
+ }
+
+ var entries = strongSelf.entries
+ entries.remove(at: 0)
+ entries.insert(updatedEntry, at: 0)
+ strongSelf.replaceEntries(normalizeEntries(entries))
+ if let firstEntry = strongSelf.entries.first {
+ strongSelf._hiddenMedia.set(.single(firstEntry))
+ }
+ }
+ })
}
if let index = self.entries.firstIndex(of: entry) {
@@ -627,100 +730,13 @@ public class AvatarGalleryController: ViewController, StandalonePresentableContr
entries.insert(previousFirstEntry, at: index)
}
- entries = normalizeEntries(entries)
- self.galleryNode.pager.replaceItems(entries.map({ entry in PeerAvatarImageGalleryItem(context: self.context, peer: self.peer, presentationData: presentationData, entry: entry, sourceCorners: self.sourceCorners, delete: self.canDelete ? { [weak self] in
- self?.deleteEntry(entry)
- } : nil, setMain: { [weak self] in
- self?.setMainEntry(entry)
- }, edit: { [weak self] in
- self?.editEntry(entry)
- }) }), centralItemIndex: 0, synchronous: true)
- self.entries = entries
-
+ self.replaceEntries(normalizeEntries(entries))
if let firstEntry = self.entries.first {
self._hiddenMedia.set(.single(firstEntry))
}
}
- } else {
-// if let messageId = messageId {
-// let _ = deleteMessagesInteractively(account: self.context.account, messageIds: [messageId], type: .forEveryone).start()
-// }
-//
-// if entry == self.entries.first {
-// let _ = updatePeerPhoto(postbox: self.context.account.postbox, network: self.context.account.network, stateManager: self.context.account.stateManager, accountPeerId: self.context.account.peerId, peerId: self.peer.id, photo: nil, mapResourceToAvatarSizes: { _, _ in .single([:]) }).start()
-// self.dismiss(forceAway: true)
-// } else {
-// if let index = self.entries.firstIndex(of: entry) {
-// self.entries.remove(at: index)
-// self.galleryNode.pager.transaction(GalleryPagerTransaction(deleteItems: [index], insertItems: [], updateItems: [], focusOnItem: index - 1))
-// }
-// }
- }
- }
- }
-
- private func openEntryEdit(_ rawEntry: AvatarGalleryEntry) {
- let mediaReference: AnyMediaReference
- if let video = rawEntry.videoRepresentations.last?.representation {
- mediaReference = .standalone(media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: video.resource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: video.dimensions, flags: [])]))
- } else {
- let media = TelegramMediaImage(imageId: MediaId(namespace: 0, id: 0), representations: rawEntry.representations.map({ $0.representation }), immediateThumbnailData: nil, reference: nil, partialReference: nil, flags: [])
- mediaReference = .standalone(media: media)
- }
-
- var dismissStatus: (() -> Void)?
- let statusController = OverlayStatusController(theme: self.presentationData.theme, type: .loading(cancelled: {
- dismissStatus?()
- }))
- dismissStatus = { [weak self, weak statusController] in
- self?.editDisposable.set(nil)
- statusController?.dismiss()
- }
- self.present(statusController, in: .window(.root))
-
- self.editDisposable.set((fetchMediaData(context: self.context, postbox: self.context.account.postbox, mediaReference: mediaReference)
- |> deliverOnMainQueue).start(next: { [weak self] state, isImage in
- guard let strongSelf = self else {
- return
- }
- switch state {
- case .progress:
- break
- case let .data(data):
- dismissStatus?()
-
- let image: UIImage?
- let video: URL?
- if isImage {
- if let fileData = try? Data(contentsOf: URL(fileURLWithPath: data.path)) {
- image = UIImage(data: fileData)
- } else {
- image = nil
- }
- video = nil
- } else {
- image = nil
- video = URL(fileURLWithPath: data.path)
- }
-
- let avatarPhotoEditCompletion = strongSelf.avatarPhotoEditCompletion
- let avatarVideoEditCompletion = strongSelf.avatarVideoEditCompletion
-
- presentLegacyAvatarEditor(theme: strongSelf.presentationData.theme, image: image, video: video, present: { [weak self] c, a in
- if let strongSelf = self {
- strongSelf.present(c, in: .window(.root), with: a, blockInteraction: true)
- }
- }, imageCompletion: { image in
- avatarPhotoEditCompletion?(image)
- }, videoCompletion: { image, url, adjustments in
- avatarVideoEditCompletion?(image, url, adjustments)
- })
-
- Queue.mainQueue().after(0.4) {
- strongSelf.dismiss(forceAway: true)
}
- }
- }))
+ }
}
private func editEntry(_ rawEntry: AvatarGalleryEntry) {
@@ -750,11 +766,6 @@ public class AvatarGalleryController: ViewController, StandalonePresentableContr
}))
}
-// items.append(ActionSheetButtonItem(title: self.presentationData.strings.ProfilePhoto_OpenInEditor, color: .accent, action: { [weak self] in
-// dismissAction()
-// self?.openEntryEdit(rawEntry)
-// }))
-
let deleteTitle: String
if let _ = rawEntry.videoRepresentations.last {
deleteTitle = self.presentationData.strings.Settings_RemoveVideo
@@ -786,6 +797,7 @@ public class AvatarGalleryController: ViewController, StandalonePresentableContr
var focusOnItem: Int?
var updatedEntries = self.entries
var replaceItems = false
+ var dismiss = false
switch entry {
case .topImage:
@@ -793,7 +805,7 @@ public class AvatarGalleryController: ViewController, StandalonePresentableContr
} else {
if entry == self.entries.first {
let _ = updatePeerPhoto(postbox: self.context.account.postbox, network: self.context.account.network, stateManager: self.context.account.stateManager, accountPeerId: self.context.account.peerId, peerId: self.peer.id, photo: nil, mapResourceToAvatarSizes: { _, _ in .single([:]) }).start()
- self.dismiss(forceAway: true)
+ dismiss = true
} else {
if let index = self.entries.firstIndex(of: entry) {
self.entries.remove(at: index)
@@ -807,7 +819,7 @@ public class AvatarGalleryController: ViewController, StandalonePresentableContr
let _ = removeAccountPhoto(network: self.context.account.network, reference: reference).start()
}
if entry == self.entries.first {
- self.dismiss(forceAway: true)
+ dismiss = true
} else {
if let index = self.entries.firstIndex(of: entry) {
replaceItems = true
@@ -822,7 +834,7 @@ public class AvatarGalleryController: ViewController, StandalonePresentableContr
if entry == self.entries.first {
let _ = updatePeerPhoto(postbox: self.context.account.postbox, network: self.context.account.network, stateManager: self.context.account.stateManager, accountPeerId: self.context.account.peerId, peerId: self.peer.id, photo: nil, mapResourceToAvatarSizes: { _, _ in .single([:]) }).start()
- self.dismiss(forceAway: true)
+ dismiss = true
} else {
if let index = self.entries.firstIndex(of: entry) {
replaceItems = true
@@ -844,6 +856,16 @@ public class AvatarGalleryController: ViewController, StandalonePresentableContr
}) }), centralItemIndex: focusOnItem, synchronous: true)
self.entries = updatedEntries
}
+ if dismiss {
+ self._hiddenMedia.set(.single(nil))
+ Queue.mainQueue().after(0.2) {
+ self.dismiss(forceAway: true)
+ }
+ } else {
+ if let firstEntry = self.entries.first {
+ self._hiddenMedia.set(.single(firstEntry))
+ }
+ }
}
let actionSheet = ActionSheetController(presentationData: presentationData)
let items: [ActionSheetItem] = [
diff --git a/submodules/PeerAvatarGalleryUI/Sources/AvatarGalleryItemFooterContentNode.swift b/submodules/PeerAvatarGalleryUI/Sources/AvatarGalleryItemFooterContentNode.swift
index 75dbe97de5..280f1bca0a 100644
--- a/submodules/PeerAvatarGalleryUI/Sources/AvatarGalleryItemFooterContentNode.swift
+++ b/submodules/PeerAvatarGalleryUI/Sources/AvatarGalleryItemFooterContentNode.swift
@@ -109,8 +109,10 @@ final class AvatarGalleryItemFooterContentNode: GalleryFooterContentNode {
switch entry {
case let .image(_, _, _, videoRepresentations, peer, date, _, _, _, _):
nameText = peer?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? ""
- dateText = humanReadableStringForTimestamp(strings: self.strings, dateTimeFormat: self.dateTimeFormat, timestamp: date)
-
+ if let date = date {
+ dateText = humanReadableStringForTimestamp(strings: self.strings, dateTimeFormat: self.dateTimeFormat, timestamp: date)
+ }
+
if (!videoRepresentations.isEmpty) {
typeText = self.strings.ProfilePhoto_MainVideo
buttonText = self.strings.ProfilePhoto_SetMainVideo
diff --git a/submodules/PeerAvatarGalleryUI/Sources/PeerAvatarImageGalleryItem.swift b/submodules/PeerAvatarGalleryUI/Sources/PeerAvatarImageGalleryItem.swift
index e890c9db7a..2345f79d4d 100644
--- a/submodules/PeerAvatarGalleryUI/Sources/PeerAvatarImageGalleryItem.swift
+++ b/submodules/PeerAvatarGalleryUI/Sources/PeerAvatarImageGalleryItem.swift
@@ -25,9 +25,9 @@ private struct PeerAvatarImageGalleryThumbnailItem: GalleryThumbnailItem {
self.content = content
}
- var image: (Signal<(TransformImageArguments) -> DrawingContext?, NoError>, CGSize) {
+ func image(synchronous: Bool) -> (Signal<(TransformImageArguments) -> DrawingContext?, NoError>, CGSize) {
if let representation = largestImageRepresentation(self.content.map({ $0.representation })) {
- return (avatarGalleryThumbnailPhoto(account: self.account, representations: self.content), representation.dimensions.cgSize)
+ return (avatarGalleryThumbnailPhoto(account: self.account, representations: self.content, synchronousLoad: synchronous), representation.dimensions.cgSize)
} else {
return (.single({ _ in return nil }), CGSize(width: 128.0, height: 128.0))
}
@@ -43,8 +43,6 @@ private struct PeerAvatarImageGalleryThumbnailItem: GalleryThumbnailItem {
}
class PeerAvatarImageGalleryItem: GalleryItem {
-
-
var id: AnyHashable {
return self.entry.id
}
@@ -268,7 +266,7 @@ final class PeerAvatarImageGalleryItemNode: ZoomableContentGalleryItemNode {
if video != previousVideoRepresentations?.last {
let mediaManager = self.context.sharedContext.mediaManager
let videoFileReference = FileMediaReference.avatarList(peer: peerReference, media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: video.representation.resource, previewRepresentations: representations.map { $0.representation }, videoThumbnails: [], immediateThumbnailData: entry.immediateThumbnailData, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: video.representation.dimensions, flags: [])]))
- let videoContent = NativeVideoContent(id: .profileVideo(id, category), fileReference: videoFileReference, streamVideo: isMediaStreamable(resource: video.representation.resource) ? .conservative : .none, loopVideo: true, enableSound: false, fetchAutomatically: true, onlyFullSizeThumbnail: true, continuePlayingWithoutSoundOnLostAudioSession: false, placeholderColor: .clear)
+ let videoContent = NativeVideoContent(id: .profileVideo(id, category), fileReference: videoFileReference, streamVideo: isMediaStreamable(resource: video.representation.resource) ? .conservative : .none, loopVideo: true, enableSound: false, fetchAutomatically: true, onlyFullSizeThumbnail: true, useLargeThumbnail: true, continuePlayingWithoutSoundOnLostAudioSession: false, placeholderColor: .clear)
let videoNode = UniversalVideoNode(postbox: self.context.account.postbox, audioSession: mediaManager.audioSession, manager: mediaManager.universalVideoManager, decoration: GalleryVideoDecoration(), content: videoContent, priority: .overlay)
videoNode.isUserInteractionEnabled = false
videoNode.isHidden = true
diff --git a/submodules/PeerInfoUI/Sources/ChannelInfoController.swift b/submodules/PeerInfoUI/Sources/ChannelInfoController.swift
index 685be43fc3..80d960096e 100644
--- a/submodules/PeerInfoUI/Sources/ChannelInfoController.swift
+++ b/submodules/PeerInfoUI/Sources/ChannelInfoController.swift
@@ -734,7 +734,7 @@ public func channelInfoController(context: AccountContext, peerId: PeerId) -> Vi
if let data = image.jpegData(compressionQuality: 0.6) {
let resource = LocalFileMediaResource(fileId: arc4random64())
context.account.postbox.mediaBox.storeResourceData(resource.id, data: data)
- let representation = TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 640, height: 640), resource: resource)
+ let representation = TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 640, height: 640), resource: resource, progressiveSizes: [])
updateState {
$0.withUpdatedUpdatingAvatar(.image(representation, true))
}
@@ -1076,8 +1076,8 @@ public func channelInfoController(context: AccountContext, peerId: PeerId) -> Vi
}
for childController in tabController.controllers {
if let chatListController = childController as? ChatListController {
- chatListController.maybeAskForPeerChatRemoval(peer: RenderedPeer(peer: peer), deleteGloballyIfPossible: deleteGloballyIfPossible, completion: { [weak navigationController] deleted in
- if deleted {
+ chatListController.maybeAskForPeerChatRemoval(peer: RenderedPeer(peer: peer), joined: false, deleteGloballyIfPossible: deleteGloballyIfPossible, completion: { [weak navigationController] removed in
+ if removed {
navigationController?.popToRoot(animated: true)
}
}, removed: {
diff --git a/submodules/PeerInfoUI/Sources/DeviceContactInfoController.swift b/submodules/PeerInfoUI/Sources/DeviceContactInfoController.swift
index 1e07cd3a9e..fa0f13c85b 100644
--- a/submodules/PeerInfoUI/Sources/DeviceContactInfoController.swift
+++ b/submodules/PeerInfoUI/Sources/DeviceContactInfoController.swift
@@ -877,21 +877,26 @@ public func deviceContactInfoController(context: AccountContext, subject: Device
ActionSheetItemGroup(items: [
ActionSheetButtonItem(title: presentationData.strings.UserInfo_TelegramCall, action: {
dismissAction()
- let callResult = context.sharedContext.callManager?.requestCall(account: context.account, peerId: user.id, isVideo: false, endCurrentIfAny: false)
+ let callResult = context.sharedContext.callManager?.requestCall(context: context, peerId: user.id, isVideo: false, endCurrentIfAny: false)
if let callResult = callResult, case let .alreadyInProgress(currentPeerId) = callResult {
if currentPeerId == user.id {
context.sharedContext.navigateToCurrentCall()
} else {
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
let _ = (context.account.postbox.transaction { transaction -> (Peer?, Peer?) in
- return (transaction.getPeer(user.id), transaction.getPeer(currentPeerId))
- } |> deliverOnMainQueue).start(next: { peer, current in
- if let peer = peer, let current = current {
+ return (transaction.getPeer(user.id), currentPeerId.flatMap(transaction.getPeer))
+ } |> deliverOnMainQueue).start(next: { peer, current in
+ if let peer = peer {
+ if let current = current {
presentControllerImpl?(textAlertController(context: context, title: presentationData.strings.Call_CallInProgressTitle, text: presentationData.strings.Call_CallInProgressMessage(current.compactDisplayTitle, peer.compactDisplayTitle).0, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: {
- let _ = context.sharedContext.callManager?.requestCall(account: context.account, peerId: peer.id, isVideo: false, endCurrentIfAny: true)
+ let _ = context.sharedContext.callManager?.requestCall(context: context, peerId: user.id, isVideo: false, endCurrentIfAny: true)
+ })]), nil)
+ } else {
+ presentControllerImpl?(textAlertController(context: context, title: presentationData.strings.Call_CallInProgressTitle, text: presentationData.strings.Call_ExternalCallInProgressMessage, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: {
})]), nil)
}
- })
+ }
+ })
}
}
}),
@@ -1336,7 +1341,7 @@ private func addContactToExisting(context: AccountContext, parentController: Vie
(parentController.navigationController as? NavigationController)?.pushViewController(contactsController)
let _ = (contactsController.result
|> deliverOnMainQueue).start(next: { peer in
- if let peer = peer {
+ if let (peer, _) = peer {
let dataSignal: Signal<(Peer?, DeviceContactStableId?), NoError>
switch peer {
case let .peer(contact, _, _):
diff --git a/submodules/PeerInfoUI/Sources/GroupInfoController.swift b/submodules/PeerInfoUI/Sources/GroupInfoController.swift
index 7b25fb885f..a9b1029dfd 100644
--- a/submodules/PeerInfoUI/Sources/GroupInfoController.swift
+++ b/submodules/PeerInfoUI/Sources/GroupInfoController.swift
@@ -1460,7 +1460,7 @@ public func groupInfoController(context: AccountContext, peerId originalPeerId:
if let data = image.jpegData(compressionQuality: 0.6) {
let resource = LocalFileMediaResource(fileId: arc4random64())
context.account.postbox.mediaBox.storeResourceData(resource.id, data: data)
- let representation = TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 640, height: 640), resource: resource)
+ let representation = TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 640, height: 640), resource: resource, progressiveSizes: [])
updateState {
$0.withUpdatedUpdatingAvatar(.image(representation, true))
}
@@ -1896,7 +1896,7 @@ public func groupInfoController(context: AccountContext, peerId originalPeerId:
if let contactsController = contactsController as? ContactSelectionController {
selectAddMemberDisposable.set((contactsController.result
|> deliverOnMainQueue).start(next: { [weak contactsController] memberPeer in
- guard let memberPeer = memberPeer else {
+ guard let (memberPeer, _) = memberPeer else {
return
}
@@ -2439,7 +2439,7 @@ public func groupInfoController(context: AccountContext, peerId originalPeerId:
}
for childController in tabController.controllers {
if let chatListController = childController as? ChatListController {
- chatListController.maybeAskForPeerChatRemoval(peer: RenderedPeer(peer: peer), deleteGloballyIfPossible: deleteGloballyIfPossible, completion: { [weak navigationController] removed in
+ chatListController.maybeAskForPeerChatRemoval(peer: RenderedPeer(peer: peer), joined: false, deleteGloballyIfPossible: deleteGloballyIfPossible, completion: { [weak navigationController] removed in
if removed {
navigationController?.popToRoot(animated: true)
}
diff --git a/submodules/PeerInfoUI/Sources/ItemListCallListItem.swift b/submodules/PeerInfoUI/Sources/ItemListCallListItem.swift
index efa01b0b08..49d6835f9b 100644
--- a/submodules/PeerInfoUI/Sources/ItemListCallListItem.swift
+++ b/submodules/PeerInfoUI/Sources/ItemListCallListItem.swift
@@ -68,14 +68,30 @@ private func stringForCallType(message: Message, strings: PresentationStrings) -
switch media {
case let action as TelegramMediaAction:
switch action.action {
- case let .phoneCall(_, discardReason, _, _):
+ case let .phoneCall(_, discardReason, _, isVideo):
let incoming = message.flags.contains(.Incoming)
if let discardReason = discardReason {
switch discardReason {
case .busy, .disconnect:
- string = strings.Notification_CallCanceled
+ if isVideo {
+ string = strings.Notification_VideoCallCanceled
+ } else {
+ string = strings.Notification_CallCanceled
+ }
case .missed:
- string = incoming ? strings.Notification_CallMissed : strings.Notification_CallCanceled
+ if incoming {
+ if isVideo {
+ string = strings.Notification_VideoCallMissed
+ } else {
+ string = strings.Notification_CallMissed
+ }
+ } else {
+ if isVideo {
+ string = strings.Notification_VideoCallCanceled
+ } else {
+ string = strings.Notification_CallCanceled
+ }
+ }
case .hangup:
break
}
@@ -83,9 +99,17 @@ private func stringForCallType(message: Message, strings: PresentationStrings) -
if string.isEmpty {
if incoming {
- string = strings.Notification_CallIncoming
+ if isVideo {
+ string = strings.Notification_VideoCallIncoming
+ } else {
+ string = strings.Notification_CallIncoming
+ }
} else {
- string = strings.Notification_CallOutgoing
+ if isVideo {
+ string = strings.Notification_VideoCallOutgoing
+ } else {
+ string = strings.Notification_CallOutgoing
+ }
}
}
default:
diff --git a/submodules/PeerInfoUI/Sources/UserInfoController.swift b/submodules/PeerInfoUI/Sources/UserInfoController.swift
index 072f34c20b..5cf6a75cbc 100644
--- a/submodules/PeerInfoUI/Sources/UserInfoController.swift
+++ b/submodules/PeerInfoUI/Sources/UserInfoController.swift
@@ -597,7 +597,7 @@ private func userInfoEntries(account: Account, presentationData: PresentationDat
var callsAvailable = true
if let cachedUserData = cachedPeerData as? CachedUserData {
- callsAvailable = cachedUserData.callsAvailable
+ callsAvailable = cachedUserData.voiceCallsAvailable
}
entries.append(UserInfoEntry.info(presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, peer: user, presence: view.peerPresences[user.id], cachedData: cachedPeerData, state: ItemListAvatarAndNameInfoItemState(editingName: editingName, updatingName: nil), displayCall: user.botInfo == nil && callsAvailable))
@@ -873,18 +873,18 @@ public func userInfoController(context: AccountContext, peerId: PeerId, mode: Pe
return
}
- let callResult = context.sharedContext.callManager?.requestCall(account: context.account, peerId: peer.id, isVideo: isVideo, endCurrentIfAny: false)
+ let callResult = context.sharedContext.callManager?.requestCall(context: context, peerId: peer.id, isVideo: isVideo, endCurrentIfAny: false)
if let callResult = callResult, case let .alreadyInProgress(currentPeerId) = callResult {
if currentPeerId == peer.id {
context.sharedContext.navigateToCurrentCall()
} else {
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
let _ = (context.account.postbox.transaction { transaction -> (Peer?, Peer?) in
- return (transaction.getPeer(peer.id), transaction.getPeer(currentPeerId))
+ return (transaction.getPeer(peer.id), currentPeerId.flatMap(transaction.getPeer))
} |> deliverOnMainQueue).start(next: { peer, current in
if let peer = peer, let current = current {
presentControllerImpl?(textAlertController(context: context, title: presentationData.strings.Call_CallInProgressTitle, text: presentationData.strings.Call_CallInProgressMessage(current.compactDisplayTitle, peer.compactDisplayTitle).0, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: {
- let _ = context.sharedContext.callManager?.requestCall(account: context.account, peerId: peer.id, isVideo: isVideo, endCurrentIfAny: true)
+ let _ = context.sharedContext.callManager?.requestCall(context: context, peerId: peer.id, isVideo: isVideo, endCurrentIfAny: true)
})]), nil)
}
})
diff --git a/submodules/PeersNearbyUI/Sources/PeersNearbyHeaderItem.swift b/submodules/PeersNearbyUI/Sources/PeersNearbyHeaderItem.swift
index ed5a84dc13..223449a8ff 100644
--- a/submodules/PeersNearbyUI/Sources/PeersNearbyHeaderItem.swift
+++ b/submodules/PeersNearbyUI/Sources/PeersNearbyHeaderItem.swift
@@ -73,7 +73,7 @@ class PeersNearbyHeaderItemNode: ListViewItemNode {
self.animationNode = AnimatedStickerNode()
if let path = getAppBundle().path(forResource: "Compass", ofType: "tgs") {
- self.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(path: path), width: 192, height: 192, playbackMode: .once, mode: .direct)
+ self.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(path: path), width: 192, height: 192, playbackMode: .once, mode: .direct(cachePathPrefix: nil))
self.animationNode.visibility = true
}
diff --git a/submodules/PhotoResources/Sources/PhotoResources.swift b/submodules/PhotoResources/Sources/PhotoResources.swift
index 779ea83794..b8e3aa46fd 100644
--- a/submodules/PhotoResources/Sources/PhotoResources.swift
+++ b/submodules/PhotoResources/Sources/PhotoResources.swift
@@ -24,10 +24,110 @@ private enum ResourceFileData {
}
public func largestRepresentationForPhoto(_ photo: TelegramMediaImage) -> TelegramMediaImageRepresentation? {
+ if let progressiveRepresentation = progressiveImageRepresentation(photo.representations) {
+ return progressiveRepresentation
+ }
return photo.representationForDisplayAtSize(PixelDimensions(width: 1280, height: 1280))
}
+private let progressiveRangeMap: [(Int, [Int])] = [
+ (100, [0]),
+ (400, [2]),
+ (600, [4, 5]),
+ (Int(Int32.max), [4, 5, 8, 9])
+]
+
+public func representationFetchRangeForDisplayAtSize(representation: TelegramMediaImageRepresentation, dimension: Int) -> Range? {
+ if representation.progressiveSizes.count > 1 {
+ var largestByteSize = Int(representation.progressiveSizes[0])
+ for (maxDimension, byteSizes) in progressiveRangeMap {
+ largestByteSize = Int(representation.progressiveSizes[byteSizes.last!])
+ if maxDimension >= dimension {
+ break
+ }
+ }
+ return 0 ..< largestByteSize
+ }
+ return nil
+}
+
public func chatMessagePhotoDatas(postbox: Postbox, photoReference: ImageMediaReference, fullRepresentationSize: CGSize = CGSize(width: 1280.0, height: 1280.0), autoFetchFullSize: Bool = false, tryAdditionalRepresentations: Bool = false, synchronousLoad: Bool = false, useMiniThumbnailIfAvailable: Bool = false) -> Signal, NoError> {
+ if let progressiveRepresentation = progressiveImageRepresentation(photoReference.media.representations), progressiveRepresentation.progressiveSizes.count == 10 {
+ enum SizeSource {
+ case miniThumbnail(data: Data)
+ case image(size: Int)
+ }
+
+ var sources: [SizeSource] = []
+ if let miniThumbnail = photoReference.media.immediateThumbnailData.flatMap(decodeTinyThumbnail) {
+ sources.append(.miniThumbnail(data: miniThumbnail))
+ }
+ let thumbnailByteSize = Int(progressiveRepresentation.progressiveSizes[0])
+ var largestByteSize = Int(progressiveRepresentation.progressiveSizes[0])
+ for (maxDimension, byteSizes) in progressiveRangeMap {
+ if Int(fullRepresentationSize.width) > 100 && maxDimension <= 100 {
+ continue
+ }
+ sources.append(contentsOf: byteSizes.map { sizeIndex -> SizeSource in
+ return .image(size: Int(progressiveRepresentation.progressiveSizes[sizeIndex]))
+ })
+ largestByteSize = Int(progressiveRepresentation.progressiveSizes[byteSizes.last!])
+ if maxDimension >= Int(fullRepresentationSize.width) {
+ break
+ }
+ }
+
+ return Signal { subscriber in
+ let signals: [Signal<(SizeSource, Data?), NoError>] = sources.map { source -> Signal<(SizeSource, Data?), NoError> in
+ switch source {
+ case let .miniThumbnail(data):
+ return .single((source, data))
+ case let .image(size):
+ return postbox.mediaBox.resourceData(progressiveRepresentation.resource, size: Int(progressiveRepresentation.progressiveSizes.last!), in: 0 ..< size, mode: .incremental, notifyAboutIncomplete: true, attemptSynchronously: synchronousLoad)
+ |> map { (data, _) -> (SizeSource, Data?) in
+ return (source, data)
+ }
+ }
+ }
+
+ let dataDisposable = combineLatest(signals).start(next: { results in
+ var foundData = false
+ loop: for i in (0 ..< results.count).reversed() {
+ let isLastSize = i == results.count - 1
+ switch results[i].0 {
+ case .image:
+ if let data = results[i].1, data.count != 0 {
+ subscriber.putNext(Tuple4(nil, data, .full, isLastSize))
+ foundData = true
+ if isLastSize {
+ subscriber.putCompletion()
+ }
+ break loop
+ }
+ case let .miniThumbnail(thumbnailData):
+ subscriber.putNext(Tuple4(thumbnailData, nil, .blurred, false))
+ foundData = true
+ break loop
+ }
+ }
+ if !foundData {
+ subscriber.putNext(Tuple4(nil, nil, .blurred, false))
+ }
+ })
+ var fetchDisposable: Disposable?
+ if autoFetchFullSize {
+ fetchDisposable = fetchedMediaResource(mediaBox: postbox.mediaBox, reference: photoReference.resourceReference(progressiveRepresentation.resource), range: (0 ..< largestByteSize, .default), statsCategory: .image).start()
+ } else if useMiniThumbnailIfAvailable {
+ fetchDisposable = fetchedMediaResource(mediaBox: postbox.mediaBox, reference: photoReference.resourceReference(progressiveRepresentation.resource), range: (0 ..< thumbnailByteSize, .default), statsCategory: .image).start()
+ }
+
+ return ActionDisposable {
+ dataDisposable.dispose()
+ fetchDisposable?.dispose()
+ }
+ }
+ }
+
if let smallestRepresentation = smallestImageRepresentation(photoReference.media.representations), let largestRepresentation = photoReference.media.representationForDisplayAtSize(PixelDimensions(width: Int32(fullRepresentationSize.width), height: Int32(fullRepresentationSize.height))), let fullRepresentation = largestImageRepresentation(photoReference.media.representations) {
let maybeFullSize = postbox.mediaBox.resourceData(largestRepresentation.resource, option: .complete(waitUntilFetchStatus: false), attemptSynchronously: synchronousLoad)
let maybeLargestSize = postbox.mediaBox.resourceData(fullRepresentation.resource, option: .complete(waitUntilFetchStatus: false), attemptSynchronously: synchronousLoad)
@@ -326,14 +426,14 @@ private func chatMessageImageFileThumbnailDatas(account: Account, fileReference:
return signal
}
-private func chatMessageVideoDatas(postbox: Postbox, fileReference: FileMediaReference, thumbnailSize: Bool = false, onlyFullSize: Bool = false, synchronousLoad: Bool = false, autoFetchFullSizeThumbnail: Bool = false) -> Signal?, Bool>, NoError> {
+private func chatMessageVideoDatas(postbox: Postbox, fileReference: FileMediaReference, thumbnailSize: Bool = false, onlyFullSize: Bool = false, useLargeThumbnail: Bool = false, synchronousLoad: Bool = false, autoFetchFullSizeThumbnail: Bool = false) -> Signal?, Bool>, NoError> {
let fullSizeResource = fileReference.media.resource
var reducedSizeResource: MediaResource?
if let videoThumbnail = fileReference.media.videoThumbnails.first {
reducedSizeResource = videoThumbnail.resource
}
- let thumbnailRepresentation = smallestImageRepresentation(fileReference.media.previewRepresentations)
+ let thumbnailRepresentation = useLargeThumbnail ? largestImageRepresentation(fileReference.media.previewRepresentations) : smallestImageRepresentation(fileReference.media.previewRepresentations)
let thumbnailResource = thumbnailRepresentation?.resource
let maybeFullSize = postbox.mediaBox.cachedResourceRepresentation(fullSizeResource, representation: thumbnailSize ? CachedScaledVideoFirstFrameRepresentation(size: CGSize(width: 160.0, height: 160.0)) : CachedVideoFirstFrameRepresentation(), complete: false, fetch: false, attemptSynchronously: synchronousLoad)
@@ -1000,9 +1100,9 @@ public func chatSecretPhoto(account: Account, photoReference: ImageMediaReferenc
}
}
-private func avatarGalleryThumbnailDatas(postbox: Postbox, representations: [ImageRepresentationWithReference], fullRepresentationSize: CGSize = CGSize(width: 1280.0, height: 1280.0), autoFetchFullSize: Bool = false) -> Signal, NoError> {
+private func avatarGalleryThumbnailDatas(postbox: Postbox, representations: [ImageRepresentationWithReference], fullRepresentationSize: CGSize = CGSize(width: 1280.0, height: 1280.0), autoFetchFullSize: Bool = false, synchronousLoad: Bool) -> Signal, NoError> {
if let smallestRepresentation = smallestImageRepresentation(representations.map({ $0.representation })), let largestRepresentation = imageRepresentationLargerThan(representations.map({ $0.representation }), size: PixelDimensions(width: Int32(fullRepresentationSize.width), height: Int32(fullRepresentationSize.height))), let smallestIndex = representations.firstIndex(where: { $0.representation == smallestRepresentation }), let largestIndex = representations.firstIndex(where: { $0.representation == largestRepresentation }) {
- let maybeFullSize = postbox.mediaBox.resourceData(largestRepresentation.resource)
+ let maybeFullSize = postbox.mediaBox.resourceData(largestRepresentation.resource, attemptSynchronously: synchronousLoad)
let signal = maybeFullSize
|> take(1)
@@ -1070,8 +1170,8 @@ private func avatarGalleryThumbnailDatas(postbox: Postbox, representations: [Ima
}
}
-public func avatarGalleryThumbnailPhoto(account: Account, representations: [ImageRepresentationWithReference]) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> {
- let signal = avatarGalleryThumbnailDatas(postbox: account.postbox, representations: representations, fullRepresentationSize: CGSize(width: 127.0, height: 127.0), autoFetchFullSize: true)
+public func avatarGalleryThumbnailPhoto(account: Account, representations: [ImageRepresentationWithReference], synchronousLoad: Bool) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> {
+ let signal = avatarGalleryThumbnailDatas(postbox: account.postbox, representations: representations, fullRepresentationSize: CGSize(width: 127.0, height: 127.0), autoFetchFullSize: true, synchronousLoad: synchronousLoad)
return signal
|> map { value in
let thumbnailData = value._0
@@ -1079,7 +1179,6 @@ public func avatarGalleryThumbnailPhoto(account: Account, representations: [Imag
let fullSizeComplete = value._2
return { arguments in
- assertNotOnMainThread()
let context = DrawingContext(size: arguments.drawingSize, clear: true)
let drawingRect = arguments.drawingRect
@@ -1159,7 +1258,9 @@ public func mediaGridMessagePhoto(account: Account, photoReference: ImageMediaRe
let useMiniThumbnailIfAvailable: Bool = fullRepresentationSize.width < 40.0
var updatedFullRepresentationSize = fullRepresentationSize
if useMiniThumbnailIfAvailable, let largest = largestImageRepresentation(photoReference.media.representations) {
- updatedFullRepresentationSize = largest.dimensions.cgSize
+ if progressiveImageRepresentation(photoReference.media.representations) == nil {
+ updatedFullRepresentationSize = largest.dimensions.cgSize
+ }
}
let signal = chatMessagePhotoDatas(postbox: account.postbox, photoReference: photoReference, fullRepresentationSize: updatedFullRepresentationSize, autoFetchFullSize: true, tryAdditionalRepresentations: useMiniThumbnailIfAvailable, synchronousLoad: synchronousLoad, useMiniThumbnailIfAvailable: useMiniThumbnailIfAvailable)
@@ -1320,14 +1421,14 @@ public func gifPaneVideoThumbnail(account: Account, videoReference: FileMediaRef
}
}
-public func mediaGridMessageVideo(postbox: Postbox, videoReference: FileMediaReference, onlyFullSize: Bool = false, synchronousLoad: Bool = false, autoFetchFullSizeThumbnail: Bool = false, overlayColor: UIColor? = nil, nilForEmptyResult: Bool = false, useMiniThumbnailIfAvailable: Bool = false) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> {
- return internalMediaGridMessageVideo(postbox: postbox, videoReference: videoReference, onlyFullSize: onlyFullSize, synchronousLoad: synchronousLoad, autoFetchFullSizeThumbnail: autoFetchFullSizeThumbnail, overlayColor: overlayColor, nilForEmptyResult: nilForEmptyResult, useMiniThumbnailIfAvailable: useMiniThumbnailIfAvailable)
+public func mediaGridMessageVideo(postbox: Postbox, videoReference: FileMediaReference, onlyFullSize: Bool = false, useLargeThumbnail: Bool = false, synchronousLoad: Bool = false, autoFetchFullSizeThumbnail: Bool = false, overlayColor: UIColor? = nil, nilForEmptyResult: Bool = false, useMiniThumbnailIfAvailable: Bool = false) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> {
+ return internalMediaGridMessageVideo(postbox: postbox, videoReference: videoReference, onlyFullSize: onlyFullSize, useLargeThumbnail: useLargeThumbnail, synchronousLoad: synchronousLoad, autoFetchFullSizeThumbnail: autoFetchFullSizeThumbnail, overlayColor: overlayColor, nilForEmptyResult: nilForEmptyResult, useMiniThumbnailIfAvailable: useMiniThumbnailIfAvailable)
|> map {
return $0.1
}
}
-public func internalMediaGridMessageVideo(postbox: Postbox, videoReference: FileMediaReference, imageReference: ImageMediaReference? = nil, onlyFullSize: Bool = false, synchronousLoad: Bool = false, autoFetchFullSizeThumbnail: Bool = false, overlayColor: UIColor? = nil, nilForEmptyResult: Bool = false, useMiniThumbnailIfAvailable: Bool = false) -> Signal<(() -> CGSize?, (TransformImageArguments) -> DrawingContext?), NoError> {
+public func internalMediaGridMessageVideo(postbox: Postbox, videoReference: FileMediaReference, imageReference: ImageMediaReference? = nil, onlyFullSize: Bool = false, useLargeThumbnail: Bool = false, synchronousLoad: Bool = false, autoFetchFullSizeThumbnail: Bool = false, overlayColor: UIColor? = nil, nilForEmptyResult: Bool = false, useMiniThumbnailIfAvailable: Bool = false) -> Signal<(() -> CGSize?, (TransformImageArguments) -> DrawingContext?), NoError> {
let signal: Signal?, Bool>, NoError>
if let imageReference = imageReference {
signal = chatMessagePhotoDatas(postbox: postbox, photoReference: imageReference, tryAdditionalRepresentations: true, synchronousLoad: synchronousLoad)
@@ -1338,7 +1439,7 @@ public func internalMediaGridMessageVideo(postbox: Postbox, videoReference: File
return Tuple(thumbnailData, fullSizeData.flatMap({ Tuple($0, "") }), fullSizeComplete)
}
} else {
- signal = chatMessageVideoDatas(postbox: postbox, fileReference: videoReference, onlyFullSize: onlyFullSize, synchronousLoad: synchronousLoad, autoFetchFullSizeThumbnail: autoFetchFullSizeThumbnail)
+ signal = chatMessageVideoDatas(postbox: postbox, fileReference: videoReference, onlyFullSize: onlyFullSize, useLargeThumbnail: useLargeThumbnail, synchronousLoad: synchronousLoad, autoFetchFullSizeThumbnail: autoFetchFullSizeThumbnail)
}
return signal
@@ -1542,9 +1643,31 @@ public func internalMediaGridMessageVideo(postbox: Postbox, videoReference: File
}
}
-public func chatMessagePhotoStatus(context: AccountContext, messageId: MessageId, photoReference: ImageMediaReference) -> Signal {
+public func chatMessagePhotoStatus(context: AccountContext, messageId: MessageId, photoReference: ImageMediaReference, displayAtSize: Int = 1000) -> Signal {
if let largestRepresentation = largestRepresentationForPhoto(photoReference.media) {
- return context.fetchManager.fetchStatus(category: .image, location: .chat(messageId.peerId), locationKey: .messageId(messageId), resource: largestRepresentation.resource)
+ if let range = representationFetchRangeForDisplayAtSize(representation: largestRepresentation, dimension: displayAtSize) {
+ return combineLatest(
+ context.fetchManager.fetchStatus(category: .image, location: .chat(messageId.peerId), locationKey: .messageId(messageId), resource: largestRepresentation.resource),
+ context.account.postbox.mediaBox.resourceRangesStatus(largestRepresentation.resource)
+ )
+ |> map { status, rangeStatus -> MediaResourceStatus in
+ if rangeStatus.contains(integersIn: range) {
+ return .Local
+ }
+
+ switch status {
+ case .Local:
+ return .Local
+ case .Remote:
+ return .Remote
+ case let .Fetching(isActive, progress):
+ return .Fetching(isActive: isActive, progress: max(progress, 0.0))
+ }
+ }
+ |> distinctUntilChanged
+ } else {
+ return context.fetchManager.fetchStatus(category: .image, location: .chat(messageId.peerId), locationKey: .messageId(messageId), resource: largestRepresentation.resource)
+ }
} else {
return .never()
}
@@ -1561,15 +1684,19 @@ public func standaloneChatMessagePhotoInteractiveFetched(account: Account, photo
}
}
-public func chatMessagePhotoInteractiveFetched(context: AccountContext, photoReference: ImageMediaReference, storeToDownloadsPeerType: MediaAutoDownloadPeerType?) -> Signal {
+public func chatMessagePhotoInteractiveFetched(context: AccountContext, photoReference: ImageMediaReference, displayAtSize: Int = 1000, storeToDownloadsPeerType: MediaAutoDownloadPeerType?) -> Signal {
if let largestRepresentation = largestRepresentationForPhoto(photoReference.media) {
- return fetchedMediaResource(mediaBox: context.account.postbox.mediaBox, reference: photoReference.resourceReference(largestRepresentation.resource), statsCategory: .image, reportResultStatus: true)
+ var fetchRange: (Range, MediaBoxFetchPriority)?
+ if let range = representationFetchRangeForDisplayAtSize(representation: largestRepresentation, dimension: displayAtSize) {
+ fetchRange = (range, .default)
+ }
+
+ return fetchedMediaResource(mediaBox: context.account.postbox.mediaBox, reference: photoReference.resourceReference(largestRepresentation.resource), range: fetchRange, statsCategory: .image, reportResultStatus: true)
|> mapToSignal { type -> Signal in
if case .remote = type, let peerType = storeToDownloadsPeerType {
return storeDownloadedMedia(storeManager: context.downloadedMediaStoreManager, media: photoReference.abstract, peerType: peerType)
|> castError(FetchResourceError.self)
|> mapToSignal { _ -> Signal in
- return .complete()
}
|> then(.single(type))
}
@@ -2218,16 +2345,14 @@ public func chatAvatarGalleryPhoto(account: Account, representations: [ImageRepr
blurredThumbnailImage = UIImage(cgImage: thumbnailImage)
} else {
let thumbnailSize = CGSize(width: thumbnailImage.width, height: thumbnailImage.height)
-
- let initialThumbnailContextFittingSize = fittedSize.fitted(CGSize(width: 100.0, height: 100.0))
+ let initialThumbnailContextFittingSize = fittedSize.fitted(CGSize(width: 90.0, height: 90.0))
let thumbnailContextSize = thumbnailSize.aspectFitted(initialThumbnailContextFittingSize)
- let thumbnailContext = DrawingContext(size: thumbnailContextSize, scale: 1.0, clear: false)
+ let thumbnailContext = DrawingContext(size: thumbnailContextSize, scale: 1.0)
thumbnailContext.withFlippedContext { c in
- c.interpolationQuality = .none
c.draw(thumbnailImage, in: CGRect(origin: CGPoint(), size: thumbnailContextSize))
}
- imageFastBlur(Int32(thumbnailContextSize.width), Int32(thumbnailContextSize.height), Int32(thumbnailContext.bytesPerRow), thumbnailContext.bytes)
+ telegramFastBlurMore(Int32(thumbnailContextSize.width), Int32(thumbnailContextSize.height), Int32(thumbnailContext.bytesPerRow), thumbnailContext.bytes)
var thumbnailContextFittingSize = CGSize(width: floor(arguments.drawingSize.width * 0.5), height: floor(arguments.drawingSize.width * 0.5))
if thumbnailContextFittingSize.width < 150.0 || thumbnailContextFittingSize.height < 150.0 {
@@ -2236,7 +2361,7 @@ public func chatAvatarGalleryPhoto(account: Account, representations: [ImageRepr
if thumbnailContextFittingSize.width > thumbnailContextSize.width {
let additionalContextSize = thumbnailContextFittingSize
- let additionalBlurContext = DrawingContext(size: additionalContextSize, scale: 1.0, clear: false)
+ let additionalBlurContext = DrawingContext(size: additionalContextSize, scale: 1.0)
additionalBlurContext.withFlippedContext { c in
c.interpolationQuality = .default
if let image = thumbnailContext.generateImage()?.cgImage {
@@ -2261,7 +2386,7 @@ public func chatAvatarGalleryPhoto(account: Account, representations: [ImageRepr
c.setBlendMode(.copy)
if let blurredThumbnailImage = blurredThumbnailImage, let cgImage = blurredThumbnailImage.cgImage {
- c.interpolationQuality = .medium
+ c.interpolationQuality = .default
drawImage(context: c, image: cgImage, orientation: imageOrientation, in: fittedRect)
c.setBlendMode(.normal)
}
diff --git a/submodules/Postbox/Sources/MediaBox.swift b/submodules/Postbox/Sources/MediaBox.swift
index 014f45c1fd..f7239a91f0 100644
--- a/submodules/Postbox/Sources/MediaBox.swift
+++ b/submodules/Postbox/Sources/MediaBox.swift
@@ -239,6 +239,11 @@ public final class MediaBox {
return "\(self.basePath)/\(cacheString)/\(fileNameForId(id)):\(representation.uniqueId)"
}
+ public func shortLivedResourceCachePathPrefix(_ id: MediaResourceId) -> String {
+ let cacheString = "short-cache"
+ return "\(self.basePath)/\(cacheString)/\(fileNameForId(id))"
+ }
+
public func storeResourceData(_ id: MediaResourceId, data: Data, synchronous: Bool = false) {
let begin = {
let paths = self.storePathsForId(id)
@@ -593,10 +598,40 @@ public final class MediaBox {
}
}
- public func resourceData(_ resource: MediaResource, size: Int, in range: Range, mode: ResourceDataRangeMode = .complete) -> Signal<(Data, Bool), NoError> {
+ public func resourceData(_ resource: MediaResource, size: Int, in range: Range, mode: ResourceDataRangeMode = .complete, notifyAboutIncomplete: Bool = false, attemptSynchronously: Bool = false) -> Signal<(Data, Bool), NoError> {
return Signal { subscriber in
let disposable = MetaDisposable()
+ if attemptSynchronously {
+ let paths = self.storePathsForId(resource.id)
+
+ if let completeSize = fileSize(paths.complete) {
+ self.timeBasedCleanup.touch(paths: [
+ paths.complete
+ ])
+
+ if let file = ManagedFile(queue: nil, path: paths.complete, mode: .read) {
+ let clippedLowerBound = min(completeSize, max(0, range.lowerBound))
+ let clippedUpperBound = min(completeSize, max(0, range.upperBound))
+ if clippedLowerBound < clippedUpperBound {
+ file.seek(position: Int64(clippedLowerBound))
+ let data = file.readData(count: clippedUpperBound)
+ subscriber.putNext((data, true))
+ } else {
+ subscriber.putNext((Data(), isComplete: true))
+ }
+ subscriber.putCompletion()
+ return EmptyDisposable
+ } else {
+ if let data = MediaBoxPartialFile.extractPartialData(path: paths.partial, metaPath: paths.partial + ".meta", range: Int32(range.lowerBound) ..< Int32(range.upperBound)) {
+ subscriber.putNext((data, true))
+ subscriber.putCompletion()
+ return EmptyDisposable
+ }
+ }
+ }
+ }
+
self.dataQueue.async {
guard let (fileContext, releaseContext) = self.fileContext(for: resource) else {
subscriber.putCompletion()
@@ -608,24 +643,25 @@ public final class MediaBox {
let dataDisposable = fileContext.data(range: range, waitUntilAfterInitialFetch: false, next: { result in
if let file = ManagedFile(queue: self.dataQueue, path: result.path, mode: .read), let fileSize = file.getSize() {
if result.complete {
- if result.offset + result.size <= fileSize {
- if fileSize >= result.offset + result.size {
- file.seek(position: Int64(result.offset))
- let resultData = file.readData(count: result.size)
- subscriber.putNext((resultData, true))
- subscriber.putCompletion()
- } else {
- assertionFailure("data.count >= result.offset + result.size")
- }
+ let clippedLowerBound = min(result.offset, fileSize)
+ let clippedUpperBound = min(result.offset + result.size, fileSize)
+ if clippedUpperBound == clippedLowerBound {
+ subscriber.putNext((Data(), true))
+ subscriber.putCompletion()
+ } else if clippedUpperBound <= fileSize {
+ file.seek(position: Int64(clippedLowerBound))
+ let resultData = file.readData(count: clippedUpperBound - clippedLowerBound)
+ subscriber.putNext((resultData, true))
+ subscriber.putCompletion()
} else {
assertionFailure()
}
} else {
switch mode {
- case .complete:
- break
- case .incremental:
- break
+ case .complete, .incremental:
+ if notifyAboutIncomplete {
+ subscriber.putNext((Data(), false))
+ }
case .partial:
subscriber.putNext((Data(), false))
}
@@ -1105,4 +1141,22 @@ public final class MediaBox {
return EmptyDisposable
}
}
+
+
+
+ public func allFileContexts() -> Signal<[(partial: String, complete: String)], NoError> {
+ return Signal { subscriber in
+ self.dataQueue.async {
+ var result: [(partial: String, complete: String)] = []
+ for (id, _) in self.fileContexts {
+ let paths = self.storePathsForId(id.id)
+ result.append((partial: paths.partial, complete: paths.complete))
+ }
+ subscriber.putNext(result)
+ subscriber.putCompletion()
+ }
+ return EmptyDisposable
+ }
+ }
+
}
diff --git a/submodules/Postbox/Sources/MediaBoxFile.swift b/submodules/Postbox/Sources/MediaBoxFile.swift
index 2a013197ad..a7bd7cce60 100644
--- a/submodules/Postbox/Sources/MediaBoxFile.swift
+++ b/submodules/Postbox/Sources/MediaBoxFile.swift
@@ -216,6 +216,23 @@ final class MediaBoxPartialFile {
self.currentFetch?.1.dispose()
}
+ static func extractPartialData(path: String, metaPath: String, range: Range) -> Data? {
+ guard let metadataFd = ManagedFile(queue: nil, path: metaPath, mode: .read) else {
+ return nil
+ }
+ guard let fd = ManagedFile(queue: nil, path: path, mode: .read) else {
+ return nil
+ }
+ guard let fileMap = MediaBoxFileMap(fd: metadataFd) else {
+ return nil
+ }
+ guard let clippedRange = fileMap.contains(range) else {
+ return nil
+ }
+ fd.seek(position: Int64(clippedRange.lowerBound))
+ return fd.readData(count: Int(clippedRange.upperBound - clippedRange.lowerBound))
+ }
+
var storedSize: Int32 {
assert(self.queue.isCurrent())
return self.fileMap.sum
@@ -582,7 +599,7 @@ final class MediaBoxPartialFile {
private func immediateStatus(size: Int32?) -> MediaResourceStatus {
let status: MediaResourceStatus
- if self.fullRangeRequests.isEmpty {
+ if self.fullRangeRequests.isEmpty && self.currentFetch == nil {
if let truncationSize = self.fileMap.truncationSize, self.fileMap.sum == truncationSize {
status = .Local
} else {
@@ -633,6 +650,7 @@ final class MediaBoxPartialFile {
if intervals.isEmpty {
if let (_, disposable) = self.currentFetch {
self.currentFetch = nil
+ self.updateStatuses()
disposable.dispose()
}
} else {
@@ -642,6 +660,7 @@ final class MediaBoxPartialFile {
let promise = Promise<[(Range, MediaBoxFetchPriority)]>()
let disposable = MetaDisposable()
self.currentFetch = (promise, disposable)
+ self.updateStatuses()
disposable.set((fetch(promise.get())
|> deliverOn(self.queue)).start(next: { [weak self] data in
if let strongSelf = self {
diff --git a/submodules/Postbox/Sources/MessageHistoryView.swift b/submodules/Postbox/Sources/MessageHistoryView.swift
index 615ef4082c..87c2e0db6a 100644
--- a/submodules/Postbox/Sources/MessageHistoryView.swift
+++ b/submodules/Postbox/Sources/MessageHistoryView.swift
@@ -272,6 +272,8 @@ final class MutableMessageHistoryView {
fileprivate(set) var sampledState: HistoryViewSample
+ fileprivate var isAddedToChatList: Bool
+
init(postbox: Postbox, orderStatistics: MessageHistoryViewOrderStatistics, clipHoles: Bool, peerIds: MessageHistoryViewPeerIds, anchor inputAnchor: HistoryViewInputAnchor, combinedReadStates: MessageHistoryViewReadState?, transientReadStates: MessageHistoryViewReadState?, tag: MessageTags?, namespaces: MessageIdNamespaces, count: Int, topTaggedMessages: [MessageId.Namespace: MessageHistoryTopTaggedMessage?], additionalDatas: [AdditionalMessageHistoryViewDataEntry], getMessageCountInRange: (MessageIndex, MessageIndex) -> Int32) {
self.anchor = inputAnchor
@@ -286,6 +288,15 @@ final class MutableMessageHistoryView {
self.topTaggedMessages = topTaggedMessages
self.additionalDatas = additionalDatas
+ let mainPeerId: PeerId
+ switch peerIds {
+ case let .associated(peerId, _):
+ mainPeerId = peerId
+ case let .single(peerId):
+ mainPeerId = peerId
+ }
+ self.isAddedToChatList = postbox.chatListTable.getPeerChatListIndex(peerId: mainPeerId) != nil
+
self.state = HistoryViewState(postbox: postbox, inputAnchor: inputAnchor, tag: tag, namespaces: namespaces, statistics: self.orderStatistics, halfLimit: count + 1, locations: peerIds)
if case let .loading(loadingState) = self.state {
let sampledState = loadingState.checkAndSample(postbox: postbox)
@@ -351,6 +362,17 @@ final class MutableMessageHistoryView {
var operations: [[MessageHistoryOperation]] = []
var peerIdsSet = Set()
+ if !transaction.chatListOperations.isEmpty {
+ let mainPeerId: PeerId
+ switch peerIds {
+ case let .associated(peerId, _):
+ mainPeerId = peerId
+ case let .single(peerId):
+ mainPeerId = peerId
+ }
+ self.isAddedToChatList = postbox.chatListTable.getPeerChatListIndex(peerId: mainPeerId) != nil
+ }
+
switch self.peerIds {
case let .single(peerId):
peerIdsSet.insert(peerId)
@@ -718,10 +740,12 @@ public final class MessageHistoryView {
public let topTaggedMessages: [Message]
public let additionalData: [AdditionalMessageHistoryViewDataEntry]
public let isLoading: Bool
+ public let isAddedToChatList: Bool
init(_ mutableView: MutableMessageHistoryView) {
self.tagMask = mutableView.tag
self.namespaces = mutableView.namespaces
+ self.isAddedToChatList = mutableView.isAddedToChatList
var entries: [MessageHistoryEntry]
switch mutableView.sampledState {
case .loading:
diff --git a/submodules/Postbox/Sources/Postbox.swift b/submodules/Postbox/Sources/Postbox.swift
index 0b453a89b8..9aa8835a17 100644
--- a/submodules/Postbox/Sources/Postbox.swift
+++ b/submodules/Postbox/Sources/Postbox.swift
@@ -945,6 +945,10 @@ public final class Transaction {
self.postbox?.scanMessages(peerId: peerId, namespace: namespace, tag: tag, f)
}
+ public func scanMessageAttributes(peerId: PeerId, namespace: MessageId.Namespace, _ f: (MessageId, [MessageAttribute]) -> Bool) {
+ self.postbox?.scanMessageAttributes(peerId: peerId, namespace: namespace, f)
+ }
+
public func invalidateMessageHistoryTagsSummary(peerId: PeerId, namespace: MessageId.Namespace, tagMask: MessageTags) {
assert(!self.disposed)
self.postbox?.invalidateMessageHistoryTagsSummary(peerId: peerId, namespace: namespace, tagMask: tagMask)
@@ -3170,6 +3174,24 @@ public final class Postbox {
}
}
+ fileprivate func scanMessageAttributes(peerId: PeerId, namespace: MessageId.Namespace, _ f: (MessageId, [MessageAttribute]) -> Bool) {
+ var index = MessageIndex.upperBound(peerId: peerId, namespace: namespace)
+ while true {
+ let messages = self.messageHistoryTable.fetch(peerId: peerId, namespace: namespace, tag: nil, from: index, includeFrom: false, to: MessageIndex.lowerBound(peerId: peerId, namespace: namespace), limit: 32)
+ for message in messages {
+ let attributes = MessageHistoryTable.renderMessageAttributes(message)
+ if !f(message.id, attributes) {
+ break
+ }
+ }
+ if let last = messages.last {
+ index = last.index
+ } else {
+ break
+ }
+ }
+ }
+
fileprivate func invalidateMessageHistoryTagsSummary(peerId: PeerId, namespace: MessageId.Namespace, tagMask: MessageTags) {
self.invalidatedMessageHistoryTagsSummaryTable.insert(InvalidatedMessageHistoryTagsSummaryKey(peerId: peerId, namespace: namespace, tagMask: tagMask), operations: &self.currentInvalidateMessageTagSummaries)
}
diff --git a/submodules/Postbox/Sources/TimeBasedCleanup.swift b/submodules/Postbox/Sources/TimeBasedCleanup.swift
index a3f40b25d1..f6e1f57123 100644
--- a/submodules/Postbox/Sources/TimeBasedCleanup.swift
+++ b/submodules/Postbox/Sources/TimeBasedCleanup.swift
@@ -102,14 +102,14 @@ private final class TimeBasedCleanupImpl {
})
}
- var checkFiles:[GeneralFile] = []
+ var checkFiles: [GeneralFile] = []
- var totalLimitSize: Int = 0
+ var totalLimitSize: UInt64 = 0
for path in generalPaths {
scanFiles(at: path, olderThan: oldestGeneralTimestamp, anyway: { file, size, timestamp in
checkFiles.append(GeneralFile(file: file, size: size, timestamp: timestamp))
- totalLimitSize += size
+ totalLimitSize += UInt64(size)
}, unlink: { file in
removedGeneralCount += 1
unlink(file)
@@ -120,7 +120,11 @@ private final class TimeBasedCleanupImpl {
if totalLimitSize > bytesLimit {
unlink(item.file)
removedGeneralLimitCount += 1
- totalLimitSize -= item.size
+ if totalLimitSize > UInt64(item.size) {
+ totalLimitSize -= UInt64(item.size)
+ } else {
+ totalLimitSize = 0
+ }
} else {
break clear
}
diff --git a/submodules/PresentationDataUtils/Sources/AlertTheme.swift b/submodules/PresentationDataUtils/Sources/AlertTheme.swift
index cc5f0907fc..ada3ded9b5 100644
--- a/submodules/PresentationDataUtils/Sources/AlertTheme.swift
+++ b/submodules/PresentationDataUtils/Sources/AlertTheme.swift
@@ -8,6 +8,10 @@ public func textAlertController(context: AccountContext, title: String?, text: S
return textAlertController(alertContext: AlertControllerContext(theme: AlertControllerTheme(presentationData: context.sharedContext.currentPresentationData.with { $0 }), themeSignal: context.sharedContext.presentationData |> map { presentationData in AlertControllerTheme(presentationData: presentationData) }), title: title, text: text, actions: actions, actionLayout: actionLayout, allowInputInset: allowInputInset, dismissOnOutsideTap: dismissOnOutsideTap)
}
+public func textAlertController(sharedContext: SharedAccountContext, title: String?, text: String, actions: [TextAlertAction], actionLayout: TextAlertContentActionLayout = .horizontal, allowInputInset: Bool = true, dismissOnOutsideTap: Bool = true) -> AlertController {
+ return textAlertController(alertContext: AlertControllerContext(theme: AlertControllerTheme(presentationData: sharedContext.currentPresentationData.with { $0 }), themeSignal: sharedContext.presentationData |> map { presentationData in AlertControllerTheme(presentationData: presentationData) }), title: title, text: text, actions: actions, actionLayout: actionLayout, allowInputInset: allowInputInset, dismissOnOutsideTap: dismissOnOutsideTap)
+}
+
public func richTextAlertController(context: AccountContext, title: NSAttributedString?, text: NSAttributedString, actions: [TextAlertAction], actionLayout: TextAlertContentActionLayout = .horizontal, allowInputInset: Bool = true, dismissAutomatically: Bool = true) -> AlertController {
return richTextAlertController(alertContext: AlertControllerContext(theme: AlertControllerTheme(presentationData: context.sharedContext.currentPresentationData.with { $0 }), themeSignal: context.sharedContext.presentationData |> map { presentationData in AlertControllerTheme(presentationData: presentationData) }), title: title, text: text, actions: actions, actionLayout: actionLayout, allowInputInset: allowInputInset, dismissAutomatically: dismissAutomatically)
}
diff --git a/submodules/SemanticStatusNode/Sources/SemanticStatusNode.swift b/submodules/SemanticStatusNode/Sources/SemanticStatusNode.swift
index ea921fd018..c075d9c70a 100644
--- a/submodules/SemanticStatusNode/Sources/SemanticStatusNode.swift
+++ b/submodules/SemanticStatusNode/Sources/SemanticStatusNode.swift
@@ -4,11 +4,21 @@ import AsyncDisplayKit
import Display
public enum SemanticStatusNodeState: Equatable {
+ public struct ProgressAppearance: Equatable {
+ public var inset: CGFloat
+ public var lineWidth: CGFloat
+
+ public init(inset: CGFloat, lineWidth: CGFloat) {
+ self.inset = inset
+ self.lineWidth = lineWidth
+ }
+ }
+
case none
case download
case play
case pause
- case progress(value: CGFloat?, cancelEnabled: Bool)
+ case progress(value: CGFloat?, cancelEnabled: Bool, appearance: ProgressAppearance?)
case customIcon(UIImage)
}
@@ -224,12 +234,14 @@ private final class SemanticStatusNodeProgressContext: SemanticStatusNodeStateCo
let transitionFraction: CGFloat
let value: CGFloat?
let displayCancel: Bool
+ let appearance: SemanticStatusNodeState.ProgressAppearance?
let timestamp: Double
- init(transitionFraction: CGFloat, value: CGFloat?, displayCancel: Bool, timestamp: Double) {
+ init(transitionFraction: CGFloat, value: CGFloat?, displayCancel: Bool, appearance: SemanticStatusNodeState.ProgressAppearance?, timestamp: Double) {
self.transitionFraction = transitionFraction
self.value = value
self.displayCancel = displayCancel
+ self.appearance = appearance
self.timestamp = timestamp
super.init()
@@ -252,22 +264,49 @@ private final class SemanticStatusNodeProgressContext: SemanticStatusNodeStateCo
context.setStrokeColor(foregroundColor.withAlphaComponent(foregroundColor.alpha * self.transitionFraction).cgColor)
}
- var progress = self.value ?? 0.1
- var startAngle = -CGFloat.pi / 2.0
- var endAngle = CGFloat(progress) * 2.0 * CGFloat.pi + startAngle
-
- if progress > 1.0 {
- progress = 2.0 - progress
- let tmp = startAngle
- startAngle = endAngle
- endAngle = tmp
+ var progress: CGFloat
+ var startAngle: CGFloat
+ var endAngle: CGFloat
+ if let value = self.value {
+ progress = value
+ startAngle = -CGFloat.pi / 2.0
+ endAngle = CGFloat(progress) * 2.0 * CGFloat.pi + startAngle
+
+ if progress > 1.0 {
+ progress = 2.0 - progress
+ let tmp = startAngle
+ startAngle = endAngle
+ endAngle = tmp
+ }
+ progress = min(1.0, progress)
+ } else {
+ progress = CGFloat(1.0 + self.timestamp.remainder(dividingBy: 2.0))
+
+ startAngle = -CGFloat.pi / 2.0
+ endAngle = CGFloat(progress) * 2.0 * CGFloat.pi + startAngle
+
+ if progress > 1.0 {
+ progress = 2.0 - progress
+ let tmp = startAngle
+ startAngle = endAngle
+ endAngle = tmp
+ }
+ progress = min(1.0, progress)
}
- progress = min(1.0, progress)
- let lineWidth: CGFloat = max(1.6, 2.25 * factor)
+ let lineWidth: CGFloat
+ if let appearance = self.appearance {
+ lineWidth = appearance.lineWidth
+ } else {
+ lineWidth = max(1.6, 2.25 * factor)
+ }
let pathDiameter: CGFloat
- pathDiameter = diameter - lineWidth - 2.5 * 2.0
+ if let appearance = self.appearance {
+ pathDiameter = diameter - lineWidth - appearance.inset * 2.0
+ } else {
+ pathDiameter = diameter - lineWidth - 2.5 * 2.0
+ }
var angle = self.timestamp.truncatingRemainder(dividingBy: Double.pi * 2.0)
angle *= 4.0
@@ -317,15 +356,17 @@ private final class SemanticStatusNodeProgressContext: SemanticStatusNodeStateCo
var value: CGFloat?
let displayCancel: Bool
+ let appearance: SemanticStatusNodeState.ProgressAppearance?
var transition: SemanticStatusNodeProgressTransition?
var isAnimating: Bool {
return true
}
- init(value: CGFloat?, displayCancel: Bool) {
+ init(value: CGFloat?, displayCancel: Bool, appearance: SemanticStatusNodeState.ProgressAppearance?) {
self.value = value
self.displayCancel = displayCancel
+ self.appearance = appearance
}
func drawingState(transitionFraction: CGFloat) -> SemanticStatusNodeStateDrawingState {
@@ -341,7 +382,7 @@ private final class SemanticStatusNodeProgressContext: SemanticStatusNodeStateCo
} else {
resolvedValue = nil
}
- return DrawingState(transitionFraction: transitionFraction, value: resolvedValue, displayCancel: self.displayCancel, timestamp: timestamp)
+ return DrawingState(transitionFraction: transitionFraction, value: resolvedValue, displayCancel: self.displayCancel, appearance: self.appearance, timestamp: timestamp)
}
func updateValue(value: CGFloat?) {
@@ -386,12 +427,12 @@ private extension SemanticStatusNodeState {
} else {
return SemanticStatusNodeIconContext(icon: icon)
}
- case let .progress(value, cancelEnabled):
+ case let .progress(value, cancelEnabled, appearance):
if let current = current as? SemanticStatusNodeProgressContext, current.displayCancel == cancelEnabled {
current.updateValue(value: value)
return current
} else {
- return SemanticStatusNodeProgressContext(value: value, displayCancel: cancelEnabled)
+ return SemanticStatusNodeProgressContext(value: value, displayCancel: cancelEnabled, appearance: appearance)
}
}
}
@@ -410,12 +451,14 @@ private final class SemanticStatusNodeTransitionDrawingState {
private final class SemanticStatusNodeDrawingState: NSObject {
let background: UIColor
let foreground: UIColor
+ let hollow: Bool
let transitionState: SemanticStatusNodeTransitionDrawingState?
let drawingState: SemanticStatusNodeStateDrawingState
- init(background: UIColor, foreground: UIColor, transitionState: SemanticStatusNodeTransitionDrawingState?, drawingState: SemanticStatusNodeStateDrawingState) {
+ init(background: UIColor, foreground: UIColor, hollow: Bool, transitionState: SemanticStatusNodeTransitionDrawingState?, drawingState: SemanticStatusNodeStateDrawingState) {
self.background = background
self.foreground = foreground
+ self.hollow = hollow
self.transitionState = transitionState
self.drawingState = drawingState
@@ -454,6 +497,8 @@ public final class SemanticStatusNode: ASControlNode {
}
}
+ private let hollow: Bool
+
private var animator: ConstantDisplayLinkAnimator?
private var hasState: Bool = false
@@ -461,9 +506,10 @@ public final class SemanticStatusNode: ASControlNode {
private var transtionContext: SemanticStatusNodeTransitionContext?
private var stateContext: SemanticStatusNodeStateContext
- public init(backgroundNodeColor: UIColor, foregroundNodeColor: UIColor) {
+ public init(backgroundNodeColor: UIColor, foregroundNodeColor: UIColor, hollow: Bool = false) {
self.backgroundNodeColor = backgroundNodeColor
self.foregroundNodeColor = foregroundNodeColor
+ self.hollow = hollow
self.state = .none
self.stateContext = self.state.context(current: nil)
@@ -543,7 +589,7 @@ public final class SemanticStatusNode: ASControlNode {
transitionState = SemanticStatusNodeTransitionDrawingState(transition: t, drawingState: transitionContext.previousStateContext.drawingState(transitionFraction: 1.0 - t))
}
- return SemanticStatusNodeDrawingState(background: self.backgroundNodeColor, foreground: self.foregroundNodeColor, transitionState: transitionState, drawingState: self.stateContext.drawingState(transitionFraction: transitionFraction))
+ return SemanticStatusNodeDrawingState(background: self.backgroundNodeColor, foreground: self.foregroundNodeColor, hollow: self.hollow, transitionState: transitionState, drawingState: self.stateContext.drawingState(transitionFraction: transitionFraction))
}
@objc override public class func draw(_ bounds: CGRect, withParameters parameters: Any?, isCancelled: () -> Bool, isRasterizing: Bool) {
@@ -565,5 +611,10 @@ public final class SemanticStatusNode: ASControlNode {
transitionState.drawingState.draw(context: context, size: bounds.size, foregroundColor: parameters.foreground)
}
parameters.drawingState.draw(context: context, size: bounds.size, foregroundColor: parameters.foreground)
+
+ if parameters.hollow {
+ context.setBlendMode(.clear)
+ context.fillEllipse(in: bounds.insetBy(dx: 8.0, dy: 8.0))
+ }
}
}
diff --git a/submodules/SettingsUI/Sources/Data and Storage/MaximumCacheSizePickerItem.swift b/submodules/SettingsUI/Sources/Data and Storage/MaximumCacheSizePickerItem.swift
new file mode 100644
index 0000000000..bb511ddb3a
--- /dev/null
+++ b/submodules/SettingsUI/Sources/Data and Storage/MaximumCacheSizePickerItem.swift
@@ -0,0 +1,330 @@
+import Foundation
+import UIKit
+import Display
+import AsyncDisplayKit
+import SwiftSignalKit
+import TelegramCore
+import SyncCore
+import TelegramUIPreferences
+import TelegramPresentationData
+import LegacyComponents
+import ItemListUI
+import PresentationDataUtils
+
+private func totalDiskSpace() -> Int64 {
+ do {
+ let systemAttributes = try FileManager.default.attributesOfFileSystem(forPath: NSHomeDirectory() as String)
+ return (systemAttributes[FileAttributeKey.systemSize] as? NSNumber)?.int64Value ?? 0
+ } catch {
+ return 0
+ }
+}
+
+private func stringForCacheSize(strings: PresentationStrings, size: Int32) -> String {
+ if size > 100 {
+ return strings.Cache_NoLimit
+ } else {
+ return dataSizeString(Int64(size) * 1024 * 1024 * 1024)
+ }
+}
+
+private let maximumCacheSizeValues: [Int32] = {
+ let diskSpace = totalDiskSpace()
+ if diskSpace > 100 * 1024 * 1024 * 1024 {
+ return [5, 20, 50, Int32.max]
+ } else if diskSpace > 50 * 1024 * 1024 * 1024 {
+ return [5, 16, 32, Int32.max]
+ } else if diskSpace > 24 * 1024 * 1024 * 1024 {
+ return [2, 8, 16, Int32.max]
+ } else {
+ return [1, 4, 8, Int32.max]
+ }
+}()
+
+final class MaximumCacheSizePickerItem: ListViewItem, ItemListItem {
+ let theme: PresentationTheme
+ let strings: PresentationStrings
+ let value: Int32
+ let sectionId: ItemListSectionId
+ let updated: (Int32) -> Void
+
+ init(theme: PresentationTheme, strings: PresentationStrings, value: Int32, sectionId: ItemListSectionId, updated: @escaping (Int32) -> Void) {
+ self.theme = theme
+ self.strings = strings
+ self.value = value
+ self.sectionId = sectionId
+ self.updated = updated
+ }
+
+ 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 = MaximumCacheSizePickerItemNode()
+ let (layout, apply) = node.asyncLayout()(self, params, itemListNeighbors(item: self, topItem: previousItem as? ItemListItem, bottomItem: nextItem as? ItemListItem))
+
+ node.contentSize = layout.contentSize
+ node.insets = layout.insets
+
+ Queue.mainQueue().async {
+ completion(node, {
+ return (nil, { _ in apply() })
+ })
+ }
+ }
+ }
+
+ func updateNode(async: @escaping (@escaping () -> Void) -> Void, node: @escaping () -> ListViewItemNode, params: ListViewItemLayoutParams, previousItem: ListViewItem?, nextItem: ListViewItem?, animation: ListViewItemUpdateAnimation, completion: @escaping (ListViewItemNodeLayout, @escaping (ListViewItemApply) -> Void) -> Void) {
+ Queue.mainQueue().async {
+ if let nodeValue = node() as? MaximumCacheSizePickerItemNode {
+ let makeLayout = nodeValue.asyncLayout()
+
+ async {
+ let (layout, apply) = makeLayout(self, params, itemListNeighbors(item: self, topItem: previousItem as? ItemListItem, bottomItem: nextItem as? ItemListItem))
+ Queue.mainQueue().async {
+ completion(layout, { _ in
+ apply()
+ })
+ }
+ }
+ }
+ }
+ }
+}
+
+private func generateKnobImage() -> UIImage? {
+ return generateImage(CGSize(width: 40.0, height: 40.0), rotatedContext: { size, context in
+ context.clear(CGRect(origin: CGPoint(), size: size))
+ context.setShadow(offset: CGSize(width: 0.0, height: -2.0), blur: 3.5, color: UIColor(white: 0.0, alpha: 0.35).cgColor)
+ context.setFillColor(UIColor.white.cgColor)
+ context.fillEllipse(in: CGRect(origin: CGPoint(x: 6.0, y: 6.0), size: CGSize(width: 28.0, height: 28.0)))
+ })
+}
+
+private final class MaximumCacheSizePickerItemNode: ListViewItemNode {
+ private let backgroundNode: ASDisplayNode
+ private let topStripeNode: ASDisplayNode
+ private let bottomStripeNode: ASDisplayNode
+ private let maskNode: ASImageNode
+
+ private let textNodes: [TextNode]
+ private var sliderView: TGPhotoEditorSliderView?
+
+ private var item: MaximumCacheSizePickerItem?
+ private var layoutParams: ListViewItemLayoutParams?
+
+ init() {
+ self.backgroundNode = ASDisplayNode()
+ self.backgroundNode.isLayerBacked = true
+
+ self.topStripeNode = ASDisplayNode()
+ self.topStripeNode.isLayerBacked = true
+
+ self.bottomStripeNode = ASDisplayNode()
+ self.bottomStripeNode.isLayerBacked = true
+
+ self.maskNode = ASImageNode()
+
+ var textNodes: [TextNode] = []
+ for i in 0 ..< 4 {
+ let textNode = TextNode()
+ textNode.isUserInteractionEnabled = false
+ textNode.displaysAsynchronously = false
+ textNodes.append(textNode)
+ }
+ self.textNodes = textNodes
+
+ super.init(layerBacked: false, dynamicBounce: false)
+
+ for textNode in textNodes {
+ self.addSubnode(textNode)
+ }
+ }
+
+ func updateSliderView() {
+ if let sliderView = self.sliderView, let item = self.item {
+ sliderView.maximumValue = 3.0
+ sliderView.positionsCount = 4
+
+ let value = maximumCacheSizeValues.firstIndex(where: { $0 == item.value }) ?? 0
+ sliderView.value = CGFloat(value)
+ }
+ }
+
+ override func didLoad() {
+ super.didLoad()
+
+ let sliderView = TGPhotoEditorSliderView()
+ sliderView.enablePanHandling = true
+ sliderView.trackCornerRadius = 1.0
+ sliderView.lineSize = 2.0
+ sliderView.dotSize = 5.0
+ sliderView.minimumValue = 0.0
+ sliderView.maximumValue = 3.0
+ sliderView.startValue = 0.0
+ sliderView.disablesInteractiveTransitionGestureRecognizer = true
+ sliderView.positionsCount = 4
+ sliderView.useLinesForPositions = true
+ if let item = self.item, let params = self.layoutParams {
+ let value = maximumCacheSizeValues.firstIndex(where: { $0 == item.value }) ?? 0
+
+ sliderView.value = CGFloat(value)
+ sliderView.backgroundColor = item.theme.list.itemBlocksBackgroundColor
+ sliderView.backColor = item.theme.list.disclosureArrowColor
+ sliderView.startColor = item.theme.list.disclosureArrowColor
+ sliderView.trackColor = item.theme.list.itemAccentColor
+ sliderView.knobImage = generateKnobImage()
+
+ sliderView.frame = CGRect(origin: CGPoint(x: params.leftInset + 15.0, y: 37.0), size: CGSize(width: params.width - params.leftInset - params.rightInset - 15.0 * 2.0, height: 44.0))
+ sliderView.hitTestEdgeInsets = UIEdgeInsets(top: -sliderView.frame.minX, left: 0.0, bottom: 0.0, right: -sliderView.frame.minX)
+ }
+ self.view.addSubview(sliderView)
+ sliderView.addTarget(self, action: #selector(self.sliderValueChanged), for: .valueChanged)
+ self.sliderView = sliderView
+
+ self.updateSliderView()
+ }
+
+ func asyncLayout() -> (_ item: MaximumCacheSizePickerItem, _ params: ListViewItemLayoutParams, _ neighbors: ItemListNeighbors) -> (ListViewItemNodeLayout, () -> Void) {
+ let currentItem = self.item
+ var makeTextLayouts: [(TextNodeLayoutArguments) -> (TextNodeLayout, () -> TextNode)] = []
+ for textNode in self.textNodes {
+ makeTextLayouts.append(TextNode.asyncLayout(textNode))
+ }
+
+ return { item, params, neighbors in
+ var themeUpdated = false
+ if currentItem?.theme !== item.theme {
+ themeUpdated = true
+ }
+
+ let contentSize: CGSize
+ let insets: UIEdgeInsets
+ let separatorHeight = UIScreenPixel
+
+ var textLayouts: [TextNodeLayout] = []
+ var textApplies: [() -> TextNode] = []
+
+ for i in 0 ..< makeTextLayouts.count {
+ let makeTextLayout = makeTextLayouts[i]
+ let (textLayout, textApply) = makeTextLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: stringForCacheSize(strings: item.strings, size: maximumCacheSizeValues[i]), font: Font.regular(13.0), textColor: item.theme.list.itemSecondaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width, height: CGFloat.greatestFiniteMagnitude), alignment: .center, lineSpacing: 0.0, cutout: nil, insets: UIEdgeInsets()))
+ textLayouts.append(textLayout)
+ textApplies.append(textApply)
+ }
+
+ contentSize = CGSize(width: params.width, height: 88.0)
+ insets = itemListNeighborsGroupedInsets(neighbors)
+
+ let layout = ListViewItemNodeLayout(contentSize: contentSize, insets: insets)
+ let layoutSize = layout.size
+
+ return (layout, { [weak self] in
+ if let strongSelf = self {
+ strongSelf.item = item
+ strongSelf.layoutParams = params
+
+ strongSelf.backgroundNode.backgroundColor = item.theme.list.itemBlocksBackgroundColor
+ strongSelf.topStripeNode.backgroundColor = item.theme.list.itemBlocksSeparatorColor
+ strongSelf.bottomStripeNode.backgroundColor = item.theme.list.itemBlocksSeparatorColor
+
+ if strongSelf.backgroundNode.supernode == nil {
+ strongSelf.insertSubnode(strongSelf.backgroundNode, at: 0)
+ }
+ if strongSelf.topStripeNode.supernode == nil {
+ strongSelf.insertSubnode(strongSelf.topStripeNode, at: 1)
+ }
+ if strongSelf.bottomStripeNode.supernode == nil {
+ strongSelf.insertSubnode(strongSelf.bottomStripeNode, at: 2)
+ }
+ if strongSelf.maskNode.supernode == nil {
+ strongSelf.insertSubnode(strongSelf.maskNode, at: 3)
+ }
+
+ let hasCorners = itemListHasRoundedBlockLayout(params)
+ var hasTopCorners = false
+ var hasBottomCorners = false
+ switch neighbors.top {
+ case .sameSection(false):
+ strongSelf.topStripeNode.isHidden = true
+ default:
+ hasTopCorners = true
+ strongSelf.topStripeNode.isHidden = hasCorners
+ }
+ let bottomStripeInset: CGFloat
+ let bottomStripeOffset: CGFloat
+ switch neighbors.bottom {
+ case .sameSection(false):
+ bottomStripeInset = 0.0
+ bottomStripeOffset = -separatorHeight
+ default:
+ bottomStripeInset = 0.0
+ bottomStripeOffset = 0.0
+ hasBottomCorners = true
+ strongSelf.bottomStripeNode.isHidden = hasCorners
+ }
+
+ strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.theme, top: hasTopCorners, bottom: hasBottomCorners) : nil
+
+ strongSelf.backgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: contentSize.height + min(insets.top, separatorHeight) + min(insets.bottom, separatorHeight)))
+ strongSelf.maskNode.frame = strongSelf.backgroundNode.frame.insetBy(dx: params.leftInset, dy: 0.0)
+ strongSelf.topStripeNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: layoutSize.width, height: separatorHeight))
+ strongSelf.bottomStripeNode.frame = CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height + bottomStripeOffset), size: CGSize(width: layoutSize.width - bottomStripeInset, height: separatorHeight))
+
+ for apply in textApplies {
+ let _ = apply()
+ }
+
+ var textNodes: [(TextNode, CGSize)] = []
+ for (node, size) in zip(strongSelf.textNodes, textLayouts.map { $0.size }) {
+ textNodes.append((node, size))
+ }
+
+ let delta = (params.width - params.leftInset - params.rightInset - 18.0 * 2.0) / CGFloat(textNodes.count - 1)
+ for i in 0 ..< textNodes.count {
+ let (textNode, textSize) = textNodes[i]
+
+ var position = params.leftInset + 18.0 + delta * CGFloat(i)
+ if i == textNodes.count - 1 {
+ position -= textSize.width
+ } else if i > 0 {
+ position -= textSize.width / 2.0
+ }
+
+ textNode.frame = CGRect(origin: CGPoint(x: position, y: 15.0), size: textSize)
+ }
+
+ if let sliderView = strongSelf.sliderView {
+ if themeUpdated {
+ sliderView.backgroundColor = item.theme.list.itemBlocksBackgroundColor
+ sliderView.backColor = item.theme.list.disclosureArrowColor
+ sliderView.trackColor = item.theme.list.itemAccentColor
+ sliderView.knobImage = generateKnobImage()
+ }
+
+ sliderView.frame = CGRect(origin: CGPoint(x: params.leftInset + 15.0, y: 37.0), size: CGSize(width: params.width - params.leftInset - params.rightInset - 15.0 * 2.0, height: 44.0))
+ sliderView.hitTestEdgeInsets = UIEdgeInsets(top: -sliderView.frame.minX, left: 0.0, bottom: 0.0, right: -sliderView.frame.minX)
+
+ strongSelf.updateSliderView()
+ }
+ }
+ })
+ }
+ }
+
+ override func animateInsertion(_ currentTimestamp: Double, duration: Double, short: Bool) {
+ self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.4)
+ }
+
+ override func animateRemoved(_ currentTimestamp: Double, duration: Double) {
+ self.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false)
+ }
+
+ @objc private func sliderValueChanged() {
+ guard let sliderView = self.sliderView else {
+ return
+ }
+
+ let position = Int(sliderView.value)
+ let value = maximumCacheSizeValues[position]
+ self.item?.updated(value)
+ }
+}
+
diff --git a/submodules/SettingsUI/Sources/Data and Storage/StorageUsageController.swift b/submodules/SettingsUI/Sources/Data and Storage/StorageUsageController.swift
index 62bc22c0ee..9b9882a2b2 100644
--- a/submodules/SettingsUI/Sources/Data and Storage/StorageUsageController.swift
+++ b/submodules/SettingsUI/Sources/Data and Storage/StorageUsageController.swift
@@ -37,14 +37,16 @@ private func freeDiskSpace() -> Int64 {
private final class StorageUsageControllerArguments {
let context: AccountContext
let updateKeepMediaTimeout: (Int32) -> Void
+ let updateMaximumCacheSize: (Int32) -> Void
let openClearAll: () -> Void
let openPeerMedia: (PeerId) -> Void
let clearPeerMedia: (PeerId) -> Void
let setPeerIdWithRevealedOptions: (PeerId?, PeerId?) -> Void
- init(context: AccountContext, updateKeepMediaTimeout: @escaping (Int32) -> Void, openClearAll: @escaping () -> Void, openPeerMedia: @escaping (PeerId) -> Void, clearPeerMedia: @escaping (PeerId) -> Void, setPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void) {
+ init(context: AccountContext, updateKeepMediaTimeout: @escaping (Int32) -> Void, updateMaximumCacheSize: @escaping (Int32) -> Void, openClearAll: @escaping () -> Void, openPeerMedia: @escaping (PeerId) -> Void, clearPeerMedia: @escaping (PeerId) -> Void, setPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void) {
self.context = context
self.updateKeepMediaTimeout = updateKeepMediaTimeout
+ self.updateMaximumCacheSize = updateMaximumCacheSize
self.openClearAll = openClearAll
self.openPeerMedia = openPeerMedia
self.clearPeerMedia = clearPeerMedia
@@ -54,6 +56,7 @@ private final class StorageUsageControllerArguments {
private enum StorageUsageSection: Int32 {
case keepMedia
+ case maximumSize
case storage
case peers
}
@@ -63,6 +66,10 @@ private enum StorageUsageEntry: ItemListNodeEntry {
case keepMedia(PresentationTheme, PresentationStrings, Int32)
case keepMediaInfo(PresentationTheme, String)
+ case maximumSizeHeader(PresentationTheme, String)
+ case maximumSize(PresentationTheme, PresentationStrings, Int32)
+ case maximumSizeInfo(PresentationTheme, String)
+
case storageHeader(PresentationTheme, String)
case storageUsage(PresentationTheme, PresentationDateTimeFormat, [StorageUsageCategory])
case collecting(PresentationTheme, String)
@@ -75,6 +82,8 @@ private enum StorageUsageEntry: ItemListNodeEntry {
switch self {
case .keepMediaHeader, .keepMedia, .keepMediaInfo:
return StorageUsageSection.keepMedia.rawValue
+ case .maximumSizeHeader, .maximumSize, .maximumSizeInfo:
+ return StorageUsageSection.maximumSize.rawValue
case .storageHeader, .storageUsage, .collecting, .clearAll:
return StorageUsageSection.storage.rawValue
case .peersHeader, .peer:
@@ -90,18 +99,24 @@ private enum StorageUsageEntry: ItemListNodeEntry {
return 1
case .keepMediaInfo:
return 2
- case .storageHeader:
+ case .maximumSizeHeader:
return 3
- case .storageUsage:
+ case .maximumSize:
return 4
- case .collecting:
+ case .maximumSizeInfo:
return 5
- case .clearAll:
+ case .storageHeader:
return 6
- case .peersHeader:
+ case .storageUsage:
return 7
+ case .collecting:
+ return 8
+ case .clearAll:
+ return 9
+ case .peersHeader:
+ return 10
case let .peer(index, _, _, _, _, _, _, _, _):
- return 8 + index
+ return 11 + index
}
}
@@ -125,6 +140,24 @@ private enum StorageUsageEntry: ItemListNodeEntry {
} else {
return false
}
+ case let .maximumSizeHeader(lhsTheme, lhsText):
+ if case let .maximumSizeHeader(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
+ return true
+ } else {
+ return false
+ }
+ case let .maximumSize(lhsTheme, lhsStrings, lhsValue):
+ if case let .maximumSize(rhsTheme, rhsStrings, rhsValue) = rhs, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsValue == rhsValue {
+ return true
+ } else {
+ return false
+ }
+ case let .maximumSizeInfo(lhsTheme, lhsText):
+ if case let .maximumSizeInfo(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
+ return true
+ } else {
+ return false
+ }
case let .storageHeader(lhsTheme, lhsText):
if case let .storageHeader(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
return true
@@ -202,25 +235,33 @@ private enum StorageUsageEntry: ItemListNodeEntry {
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
case let .keepMedia(theme, strings, value):
return KeepMediaDurationPickerItem(theme: theme, strings: strings, value: value, sectionId: self.section, updated: { updatedValue in
- arguments.updateKeepMediaTimeout(updatedValue)
- })
- case let .keepMediaInfo(theme, text):
+ arguments.updateKeepMediaTimeout(updatedValue)
+ })
+ case let .keepMediaInfo(_, text):
return ItemListTextItem(presentationData: presentationData, text: .markdown(text), sectionId: self.section)
- case let .storageHeader(theme, text):
+ case let .maximumSizeHeader(_, text):
+ return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
+ case let .maximumSize(theme, strings, value):
+ return MaximumCacheSizePickerItem(theme: theme, strings: strings, value: value, sectionId: self.section, updated: { updatedValue in
+ arguments.updateMaximumCacheSize(updatedValue)
+ })
+ case let .maximumSizeInfo(_, text):
+ return ItemListTextItem(presentationData: presentationData, text: .markdown(text), sectionId: self.section)
+ case let .storageHeader(_, text):
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
case let .storageUsage(theme, dateTimeFormat, categories):
return StorageUsageItem(theme: theme, dateTimeFormat: dateTimeFormat, categories: categories, sectionId: self.section)
case let .collecting(theme, text):
return CalculatingCacheSizeItem(theme: theme, title: text, sectionId: self.section, style: .blocks)
- case let .clearAll(theme, text, enabled):
+ case let .clearAll(_, text, enabled):
return ItemListActionItem(presentationData: presentationData, title: text, kind: enabled ? .generic : .disabled, alignment: .natural, sectionId: self.section, style: .blocks, action: {
if enabled {
arguments.openClearAll()
}
})
- case let .peersHeader(theme, text):
+ case let .peersHeader(_, text):
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
- case let .peer(_, theme, strings, dateTimeFormat, nameDisplayOrder, peer, chatPeer, value, revealed):
+ case let .peer(_, _, strings, dateTimeFormat, nameDisplayOrder, peer, chatPeer, value, revealed):
var options: [ItemListPeerItemRevealOption] = [ItemListPeerItemRevealOption(type: .destructive, title: strings.ClearCache_Clear, action: {
arguments.clearPeerMedia(peer.id)
})]
@@ -248,7 +289,11 @@ private func storageUsageControllerEntries(presentationData: PresentationData, c
entries.append(.keepMediaHeader(presentationData.theme, presentationData.strings.Cache_KeepMedia.uppercased()))
entries.append(.keepMedia(presentationData.theme, presentationData.strings, cacheSettings.defaultCacheStorageTimeout))
- entries.append(.keepMediaInfo(presentationData.theme, presentationData.strings.Cache_Help))
+ entries.append(.keepMediaInfo(presentationData.theme, presentationData.strings.Cache_KeepMediaHelp))
+
+ entries.append(.maximumSizeHeader(presentationData.theme, presentationData.strings.Cache_MaximumCacheSize.uppercased()))
+ entries.append(.maximumSize(presentationData.theme, presentationData.strings, cacheSettings.defaultCacheStorageLimitGigabytes))
+ entries.append(.maximumSizeInfo(presentationData.theme, presentationData.strings.Cache_MaximumCacheSizeHelp))
var addedHeader = false
@@ -396,6 +441,10 @@ public func storageUsageController(context: AccountContext, cacheUsagePromise: P
let _ = updateCacheStorageSettingsInteractively(accountManager: context.sharedContext.accountManager, { current in
return current.withUpdatedDefaultCacheStorageTimeout(value)
}).start()
+ }, updateMaximumCacheSize: { value in
+ let _ = updateCacheStorageSettingsInteractively(accountManager: context.sharedContext.accountManager, { current in
+ return current.withUpdatedDefaultCacheStorageLimitGigabytes(value)
+ }).start()
}, openClearAll: {
let _ = (statsPromise.get()
|> take(1)
diff --git a/submodules/SettingsUI/Sources/DebugController.swift b/submodules/SettingsUI/Sources/DebugController.swift
index d06d8a8810..4209a92e08 100644
--- a/submodules/SettingsUI/Sources/DebugController.swift
+++ b/submodules/SettingsUI/Sources/DebugController.swift
@@ -72,8 +72,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
case alternativeFolderTabs(Bool)
case playerEmbedding(Bool)
case playlistPlayback(Bool)
- case videoCalls(Bool)
- case videoCallsInfo(PresentationTheme, String)
+ case preferredVideoCodec(Int, String, String?, Bool)
case hostInfo(PresentationTheme, String)
case versionInfo(PresentationTheme)
@@ -89,14 +88,14 @@ private enum DebugControllerEntry: ItemListNodeEntry {
return DebugControllerSection.experiments.rawValue
case .clearTips, .reimport, .resetData, .resetDatabase, .resetHoles, .reindexUnread, .resetBiometricsData, .optimizeDatabase, .photoPreview, .knockoutWallpaper, .alternativeFolderTabs, .playerEmbedding, .playlistPlayback:
return DebugControllerSection.experiments.rawValue
- case .videoCalls, .videoCallsInfo:
+ case .preferredVideoCodec:
return DebugControllerSection.videoExperiments.rawValue
case .hostInfo, .versionInfo:
return DebugControllerSection.info.rawValue
}
}
- var stableId: Int32 {
+ var stableId: Int {
switch self {
case .sendLogs:
return 0
@@ -148,14 +147,12 @@ private enum DebugControllerEntry: ItemListNodeEntry {
return 24
case .playlistPlayback:
return 25
- case .videoCalls:
- return 26
- case .videoCallsInfo:
- return 27
+ case let .preferredVideoCodec(index, _, _, _):
+ return 26 + index
case .hostInfo:
- return 28
+ return 100
case .versionInfo:
- return 29
+ return 101
}
}
@@ -573,18 +570,16 @@ private enum DebugControllerEntry: ItemListNodeEntry {
})
}).start()
})
- case let .videoCalls(value):
- return ItemListSwitchItem(presentationData: presentationData, title: "Experimental Feature", value: value, sectionId: self.section, style: .blocks, updated: { value in
+ case let .preferredVideoCodec(_, title, value, isSelected):
+ return ItemListCheckboxItem(presentationData: presentationData, title: title, style: .right, checked: isSelected, zeroSeparatorInsets: false, sectionId: self.section, action: {
let _ = arguments.sharedContext.accountManager.transaction ({ transaction in
transaction.updateSharedData(ApplicationSpecificSharedDataKeys.experimentalUISettings, { settings in
var settings = settings as? ExperimentalUISettings ?? ExperimentalUISettings.defaultSettings
- settings.videoCalls = value
+ settings.preferredVideoCodec = value
return settings
})
}).start()
})
- case let .videoCallsInfo(_, text):
- return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section)
case let .hostInfo(theme, string):
return ItemListTextItem(presentationData: presentationData, text: .plain(string), sectionId: self.section)
case let .versionInfo(theme):
@@ -630,8 +625,18 @@ private func debugControllerEntries(presentationData: PresentationData, loggingS
entries.append(.alternativeFolderTabs(experimentalSettings.foldersTabAtBottom))
entries.append(.playerEmbedding(experimentalSettings.playerEmbedding))
entries.append(.playlistPlayback(experimentalSettings.playlistPlayback))
- entries.append(.videoCalls(experimentalSettings.videoCalls))
- entries.append(.videoCallsInfo(presentationData.theme, "Enables experimental transmission of electromagnetic radiation synchronized with pressure waves. Needs to be enabled on both sides."))
+
+ let codecs: [(String, String?)] = [
+ ("No Preference", nil),
+ ("H265", "H265"),
+ ("H264", "H264"),
+ ("VP8", "VP8"),
+ ("VP9", "VP9")
+ ]
+
+ for i in 0 ..< codecs.count {
+ entries.append(.preferredVideoCodec(i, codecs[i].0, codecs[i].1, experimentalSettings.preferredVideoCodec == codecs[i].1))
+ }
if let backupHostOverride = networkSettings?.backupHostOverride {
entries.append(.hostInfo(presentationData.theme, "Host: \(backupHostOverride)"))
diff --git a/submodules/SettingsUI/Sources/EditSettingsController.swift b/submodules/SettingsUI/Sources/EditSettingsController.swift
deleted file mode 100644
index 8ebbfa7eec..0000000000
--- a/submodules/SettingsUI/Sources/EditSettingsController.swift
+++ /dev/null
@@ -1,773 +0,0 @@
-import Foundation
-import UIKit
-import AsyncDisplayKit
-import Display
-import SwiftSignalKit
-import Postbox
-import TelegramCore
-import SyncCore
-import LegacyComponents
-import TelegramPresentationData
-import ItemListUI
-import PresentationDataUtils
-import AccountContext
-import GalleryUI
-import LegacyUI
-import ItemListAvatarAndNameInfoItem
-import WebSearchUI
-import PeerAvatarGalleryUI
-import MapResourceToAvatarSizes
-import PhoneNumberFormat
-import LegacyMediaPickerUI
-import LocalMediaResources
-
-private struct EditSettingsItemArguments {
- let context: AccountContext
- let accountManager: AccountManager
- let avatarAndNameInfoContext: ItemListAvatarAndNameInfoItemContext
-
- let avatarTapAction: () -> Void
- let setProfilePhoto: () -> Void
-
- let pushController: (ViewController) -> Void
- let presentController: (ViewController) -> Void
- let updateEditingName: (ItemListAvatarAndNameInfoItemName) -> Void
- let updateBioText: (String, String) -> Void
- let saveEditingState: () -> Void
- let addAccount: () -> Void
- let logout: () -> Void
-}
-
-private enum SettingsSection: Int32 {
- case info
- case bio
- case personalData
- case addAccount
- case logOut
-}
-
-public enum EditSettingsEntryTag: ItemListItemTag {
- case bio
-
- public func isEqual(to other: ItemListItemTag) -> Bool {
- if let other = other as? EditSettingsEntryTag, self == other {
- return true
- } else {
- return false
- }
- }
-}
-
-
-private enum SettingsEntry: ItemListNodeEntry {
- case userInfo(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, Peer?, CachedPeerData?, ItemListAvatarAndNameInfoItemState, ItemListAvatarAndNameInfoItemUpdatingAvatar?)
- case setProfilePhoto(PresentationTheme, String)
- case userInfoNotice(PresentationTheme, String)
-
- case bioText(PresentationTheme, String, String)
- case bioInfo(PresentationTheme, String)
-
- case phoneNumber(PresentationTheme, String, String)
- case username(PresentationTheme, String, String)
-
- case addAccount(PresentationTheme, String)
- case logOut(PresentationTheme, String)
-
- var section: ItemListSectionId {
- switch self {
- case .userInfo, .setProfilePhoto, .userInfoNotice:
- return SettingsSection.info.rawValue
- case .bioText, .bioInfo:
- return SettingsSection.bio.rawValue
- case .phoneNumber, .username:
- return SettingsSection.personalData.rawValue
- case .addAccount:
- return SettingsSection.addAccount.rawValue
- case .logOut:
- return SettingsSection.logOut.rawValue
- }
- }
-
- var stableId: Int32 {
- switch self {
- case .userInfo:
- return 0
- case .setProfilePhoto:
- return 1
- case .userInfoNotice:
- return 2
- case .bioText:
- return 3
- case .bioInfo:
- return 4
- case .phoneNumber:
- return 5
- case .username:
- return 6
- case .addAccount:
- return 7
- case .logOut:
- return 8
- }
- }
-
- static func ==(lhs: SettingsEntry, rhs: SettingsEntry) -> Bool {
- switch lhs {
- case let .userInfo(lhsTheme, lhsStrings, lhsDateTimeFormat, lhsPeer, lhsCachedData, lhsEditingState, lhsUpdatingImage):
- if case let .userInfo(rhsTheme, rhsStrings, rhsDateTimeFormat, rhsPeer, rhsCachedData, rhsEditingState, rhsUpdatingImage) = rhs {
- if lhsTheme !== rhsTheme {
- return false
- }
- if lhsStrings !== rhsStrings {
- return false
- }
- if lhsDateTimeFormat != rhsDateTimeFormat {
- return false
- }
- if let lhsPeer = lhsPeer, let rhsPeer = rhsPeer {
- if !lhsPeer.isEqual(rhsPeer) {
- return false
- }
- } else if (lhsPeer != nil) != (rhsPeer != nil) {
- return false
- }
- if let lhsCachedData = lhsCachedData, let rhsCachedData = rhsCachedData {
- if !lhsCachedData.isEqual(to: rhsCachedData) {
- return false
- }
- } else if (lhsCachedData != nil) != (rhsCachedData != nil) {
- return false
- }
- if lhsEditingState != rhsEditingState {
- return false
- }
- if lhsUpdatingImage != rhsUpdatingImage {
- return false
- }
- return true
- } else {
- return false
- }
- case let .setProfilePhoto(lhsTheme, lhsText):
- if case let .setProfilePhoto(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
- return true
- } else {
- return false
- }
- case let .userInfoNotice(lhsTheme, lhsText):
- if case let .userInfoNotice(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
- return true
- } else {
- return false
- }
- case let .bioText(lhsTheme, lhsCurrentText, lhsText):
- if case let .bioText(rhsTheme, rhsCurrentText, rhsText) = rhs, lhsTheme === rhsTheme, lhsCurrentText == rhsCurrentText, lhsText == rhsText {
- return true
- } else {
- return false
- }
- case let .bioInfo(lhsTheme, lhsText):
- if case let .bioInfo(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
- return true
- } else {
- return false
- }
- case let .phoneNumber(lhsTheme, lhsText, lhsNumber):
- if case let .phoneNumber(rhsTheme, rhsText, rhsNumber) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsNumber == rhsNumber {
- return true
- } else {
- return false
- }
- case let .username(lhsTheme, lhsText, lhsAddress):
- if case let .username(rhsTheme, rhsText, rhsAddress) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsAddress == rhsAddress {
- return true
- } else {
- return false
- }
- case let .addAccount(lhsTheme, lhsText):
- if case let .addAccount(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
- return true
- } else {
- return false
- }
- case let .logOut(lhsTheme, lhsText):
- if case let .logOut(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
- return true
- } else {
- return false
- }
- }
- }
-
- static func <(lhs: SettingsEntry, rhs: SettingsEntry) -> Bool {
- return lhs.stableId < rhs.stableId
- }
-
- func item(presentationData: ItemListPresentationData, arguments: Any) -> ListViewItem {
- let arguments = arguments as! EditSettingsItemArguments
- switch self {
- case let .userInfo(_, _, dateTimeFormat, peer, cachedData, state, updatingImage):
- return ItemListAvatarAndNameInfoItem(accountContext: arguments.context, presentationData: presentationData, dateTimeFormat: dateTimeFormat, mode: .editSettings, peer: peer, presence: TelegramUserPresence(status: .present(until: Int32.max), lastActivity: 0), cachedData: cachedData, state: state, sectionId: ItemListSectionId(self.section), style: .blocks(withTopInset: false, withExtendedBottomInset: false), editingNameUpdated: { editingName in
- arguments.updateEditingName(editingName)
- }, avatarTapped: {
- arguments.avatarTapAction()
- }, context: arguments.avatarAndNameInfoContext, updatingImage: updatingImage)
- case let .setProfilePhoto(_, text):
- return ItemListActionItem(presentationData: presentationData, title: text, kind: .generic, alignment: .natural, sectionId: ItemListSectionId(self.section), style: .blocks, action: {
- arguments.setProfilePhoto()
- })
- case let .userInfoNotice(_, text):
- return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section)
- case let .bioText(_, currentText, placeholder):
- return ItemListMultilineInputItem(presentationData: presentationData, text: currentText, placeholder: placeholder, maxLength: ItemListMultilineInputItemTextLimit(value: 70, display: true), sectionId: self.section, style: .blocks, textUpdated: { updatedText in
- arguments.updateBioText(currentText, updatedText)
- }, tag: EditSettingsEntryTag.bio)
- case let .bioInfo(_, text):
- return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section)
- case let .phoneNumber(_, text, number):
- return ItemListDisclosureItem(presentationData: presentationData, title: text, label: number, sectionId: ItemListSectionId(self.section), style: .blocks, action: {
- arguments.pushController(ChangePhoneNumberIntroController(context: arguments.context, phoneNumber: number))
- })
- case let .username(_, text, address):
- return ItemListDisclosureItem(presentationData: presentationData, title: text, label: address, sectionId: ItemListSectionId(self.section), style: .blocks, action: {
- arguments.pushController(usernameSetupController(context: arguments.context))
- })
- case let .addAccount(_, text):
- return ItemListActionItem(presentationData: presentationData, title: text, kind: .generic, alignment: .center, sectionId: ItemListSectionId(self.section), style: .blocks, action: {
- arguments.addAccount()
- })
- case let .logOut(_, text):
- return ItemListActionItem(presentationData: presentationData, title: text, kind: .destructive, alignment: .center, sectionId: ItemListSectionId(self.section), style: .blocks, action: {
- arguments.logout()
- })
- }
- }
-}
-
-private struct EditSettingsState: Equatable {
- let updatingAvatar: ItemListAvatarAndNameInfoItemUpdatingAvatar?
- let editingName: ItemListAvatarAndNameInfoItemName
- let updatingName: ItemListAvatarAndNameInfoItemName?
- let editingBioText: String
- let updatingBioText: Bool
-
- init(updatingAvatar: ItemListAvatarAndNameInfoItemUpdatingAvatar? = nil, editingName: ItemListAvatarAndNameInfoItemName, updatingName: ItemListAvatarAndNameInfoItemName? = nil, editingBioText: String, updatingBioText: Bool = false) {
- self.updatingAvatar = updatingAvatar
- self.editingName = editingName
- self.updatingName = updatingName
- self.editingBioText = editingBioText
- self.updatingBioText = updatingBioText
- }
-
- func withUpdatedUpdatingAvatar(_ updatingAvatar: ItemListAvatarAndNameInfoItemUpdatingAvatar?) -> EditSettingsState {
- return EditSettingsState(updatingAvatar: updatingAvatar, editingName: self.editingName, updatingName: self.updatingName, editingBioText: self.editingBioText, updatingBioText: self.updatingBioText)
- }
-
- func withUpdatedEditingName(_ editingName: ItemListAvatarAndNameInfoItemName) -> EditSettingsState {
- return EditSettingsState(updatingAvatar: self.updatingAvatar, editingName: editingName, updatingName: self.updatingName, editingBioText: self.editingBioText, updatingBioText: self.updatingBioText)
- }
-
- func withUpdatedUpdatingName(_ updatingName: ItemListAvatarAndNameInfoItemName?) -> EditSettingsState {
- return EditSettingsState(updatingAvatar: self.updatingAvatar, editingName: self.editingName, updatingName: updatingName, editingBioText: self.editingBioText, updatingBioText: self.updatingBioText)
- }
-
- func withUpdatedEditingBioText(_ editingBioText: String) -> EditSettingsState {
- return EditSettingsState(updatingAvatar: self.updatingAvatar, editingName: self.editingName, updatingName: self.updatingName, editingBioText: editingBioText, updatingBioText: self.updatingBioText)
- }
-
- func withUpdatedUpdatingBioText(_ updatingBioText: Bool) -> EditSettingsState {
- return EditSettingsState(updatingAvatar: self.updatingAvatar, editingName: self.editingName, updatingName: self.updatingName, editingBioText: self.editingBioText, updatingBioText: updatingBioText)
- }
-
- static func ==(lhs: EditSettingsState, rhs: EditSettingsState) -> Bool {
- if lhs.updatingAvatar != rhs.updatingAvatar {
- return false
- }
- if lhs.editingName != rhs.editingName {
- return false
- }
- if lhs.updatingName != rhs.updatingName {
- return false
- }
- if lhs.editingBioText != rhs.editingBioText {
- return false
- }
- if lhs.updatingBioText != rhs.updatingBioText {
- return false
- }
- return true
- }
-}
-
-private func editSettingsEntries(presentationData: PresentationData, state: EditSettingsState, view: PeerView, canAddAccounts: Bool) -> [SettingsEntry] {
- var entries: [SettingsEntry] = []
-
- if let peer = peerViewMainPeer(view) as? TelegramUser {
- let userInfoState = ItemListAvatarAndNameInfoItemState(editingName: state.editingName, updatingName: state.updatingName)
- entries.append(.userInfo(presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, peer, view.cachedData, userInfoState, state.updatingAvatar))
- entries.append(.setProfilePhoto(presentationData.theme, peer.photo.isEmpty ? presentationData.strings.Settings_SetProfilePhotoOrVideo : presentationData.strings.Settings_SetNewProfilePhotoOrVideo))
- entries.append(.userInfoNotice(presentationData.theme, presentationData.strings.EditProfile_NameAndPhotoHelp))
-
- entries.append(.bioText(presentationData.theme, state.editingBioText, presentationData.strings.UserInfo_About_Placeholder))
- entries.append(.bioInfo(presentationData.theme, presentationData.strings.Settings_About_Help))
-
- if let phone = peer.phone {
- entries.append(.phoneNumber(presentationData.theme, presentationData.strings.Settings_PhoneNumber, formatPhoneNumber(phone)))
- }
- entries.append(.username(presentationData.theme, presentationData.strings.Settings_Username, peer.addressName == nil ? "" : ("@" + peer.addressName!)))
-
- if canAddAccounts {
- entries.append(.addAccount(presentationData.theme, presentationData.strings.Settings_AddAccount))
- }
- entries.append(.logOut(presentationData.theme, presentationData.strings.Settings_Logout))
- }
-
- return entries
-}
-
-func editSettingsController(context: AccountContext, currentName: ItemListAvatarAndNameInfoItemName, currentBioText: String, accountManager: AccountManager, canAddAccounts: Bool, focusOnItemTag: EditSettingsEntryTag? = nil) -> ViewController {
- let initialState = EditSettingsState(editingName: currentName, editingBioText: currentBioText)
- let statePromise = ValuePromise(initialState, ignoreRepeated: true)
- let stateValue = Atomic(value: initialState)
- let updateState: ((EditSettingsState) -> EditSettingsState) -> Void = { f in
- statePromise.set(stateValue.modify { f($0) })
- }
-
- var pushControllerImpl: ((ViewController) -> Void)?
- var presentControllerImpl: ((ViewController, Any?) -> Void)?
- var dismissImpl: (() -> Void)?
- var errorImpl: (() -> Void)?
-
- let actionsDisposable = DisposableSet()
-
- let updateAvatarDisposable = MetaDisposable()
- //actionsDisposable.add(updateAvatarDisposable)
-
- let updatePeerNameDisposable = MetaDisposable()
- actionsDisposable.add(updatePeerNameDisposable)
-
- let supportPeerDisposable = MetaDisposable()
- actionsDisposable.add(supportPeerDisposable)
-
- let hiddenAvatarRepresentationDisposable = MetaDisposable()
- actionsDisposable.add(hiddenAvatarRepresentationDisposable)
-
- let currentAvatarMixin = Atomic(value: nil)
- let cachedAvatarEntries = Atomic?>(value: nil)
-
- var avatarGalleryTransitionArguments: ((AvatarGalleryEntry) -> GalleryTransitionArguments?)?
- let avatarAndNameInfoContext = ItemListAvatarAndNameInfoItemContext()
- var updateHiddenAvatarImpl: (() -> Void)?
- var changeProfilePhotoImpl: (() -> Void)?
-
- var getNavigationController: (() -> NavigationController?)?
-
- let arguments = EditSettingsItemArguments(context: context, accountManager: accountManager, avatarAndNameInfoContext: avatarAndNameInfoContext, avatarTapAction: {
- var updating = false
- updateState {
- updating = $0.updatingAvatar != nil
- return $0
- }
-
- if updating {
- return
- }
-
- changeProfilePhotoImpl?()
- }, setProfilePhoto: {
- changeProfilePhotoImpl?()
- }, pushController: { controller in
- pushControllerImpl?(controller)
- }, presentController: { controller in
- presentControllerImpl?(controller, nil)
- }, updateEditingName: { editingName in
- updateState { state in
- return state.withUpdatedEditingName(editingName)
- }
- }, updateBioText: { currentText, text in
- updateState { state in
- return state.withUpdatedEditingBioText(text)
- }
- }, saveEditingState: {
- var updateName: ItemListAvatarAndNameInfoItemName?
- var updateBio: String?
- var failed = false
- updateState { state in
- if state.editingName != currentName {
- updateName = state.editingName
- }
- if state.editingBioText != currentBioText {
- updateBio = state.editingBioText
- }
-
- if (updateBio?.count ?? 0) > 70 {
- failed = true
- return state
- }
-
- if updateName != nil || updateBio != nil {
- return state.withUpdatedUpdatingName(state.editingName).withUpdatedUpdatingBioText(true)
- } else {
- return state
- }
- }
-
- guard !failed else {
- errorImpl?()
- return
- }
-
- var updateNameSignal: Signal = .complete()
- if let updateName = updateName, case let .personName(firstName, lastName, _) = updateName {
- updateNameSignal = updateAccountPeerName(account: context.account, firstName: firstName, lastName: lastName)
- }
- var updateBioSignal: Signal = .complete()
- if let updateBio = updateBio {
- updateBioSignal = updateAbout(account: context.account, about: updateBio)
- |> `catch` { _ -> Signal in
- return .complete()
- }
- }
- updatePeerNameDisposable.set((combineLatest(updateNameSignal, updateBioSignal) |> deliverOnMainQueue).start(completed: {
- dismissImpl?()
- }))
- }, addAccount: {
- let isTestingEnvironment = context.account.testingEnvironment
- context.sharedContext.beginNewAuth(testingEnvironment: isTestingEnvironment)
- }, logout: {
- let _ = (context.account.postbox.transaction { transaction -> String in
- return (transaction.getPeer(context.account.peerId) as? TelegramUser)?.phone ?? ""
- }
- |> deliverOnMainQueue).start(next: { phoneNumber in
- if let navigationController = getNavigationController?() {
- presentControllerImpl?(logoutOptionsController(context: context, navigationController: navigationController, canAddAccounts: canAddAccounts, phoneNumber: phoneNumber), ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
- }
- })
- })
-
- let peerView = context.account.viewTracker.peerView(context.account.peerId)
-
- let signal = combineLatest(context.sharedContext.presentationData, statePromise.get(), peerView)
- |> map { presentationData, state, view -> (ItemListControllerState, (ItemListNodeState, Any)) in
- let rightNavigationButton: ItemListNavigationButton
- if state.updatingName != nil || state.updatingBioText {
- rightNavigationButton = ItemListNavigationButton(content: .none, style: .activity, enabled: true, action: {})
- } else {
- rightNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Done), style: .bold, enabled: true, action: {
- arguments.saveEditingState()
- })
- }
-
- let peer = peerViewMainPeer(view)
- if let peer = peer {
- let _ = cachedAvatarEntries.modify { value in
- if value != nil {
- return value
- } else {
- let promise = Promise<[AvatarGalleryEntry]>()
- promise.set(fetchedAvatarGalleryEntries(account: context.account, peer: peer))
- return promise
- }
- }
- }
-
- let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(presentationData.strings.EditProfile_Title), leftNavigationButton: nil, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back))
- let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: editSettingsEntries(presentationData: presentationData, state: state, view: view, canAddAccounts: canAddAccounts), style: .blocks, ensureVisibleItemTag: focusOnItemTag)
-
- return (controllerState, (listState, arguments))
- } |> afterDisposed {
- actionsDisposable.dispose()
- }
-
- let controller = ItemListController(context: context, state: signal, tabBarItem: nil)
- pushControllerImpl = { [weak controller] value in
- (controller?.navigationController as? NavigationController)?.pushViewController(value)
- }
- presentControllerImpl = { [weak controller] value, arguments in
- controller?.present(value, in: .window(.root), with: arguments)
- }
- dismissImpl = { [weak controller] in
- let _ = (controller?.navigationController as? NavigationController)?.popViewController(animated: true)
- }
- avatarGalleryTransitionArguments = { [weak controller] entry in
- if let controller = controller {
- var result: ((ASDisplayNode, CGRect, () -> (UIView?, UIView?)), CGRect)?
- controller.forEachItemNode { itemNode in
- if let itemNode = itemNode as? ItemListAvatarAndNameInfoItemNode {
- result = itemNode.avatarTransitionNode()
- }
- }
- if let (node, _) = result {
- return GalleryTransitionArguments(transitionNode: node, addToTransitionSurface: { _ in
- })
- }
- }
- return nil
- }
- updateHiddenAvatarImpl = { [weak controller] in
- if let controller = controller {
- controller.forEachItemNode { itemNode in
- if let itemNode = itemNode as? ItemListAvatarAndNameInfoItemNode {
- itemNode.updateAvatarHidden()
- }
- }
- }
- }
- changeProfilePhotoImpl = { [weak controller] in
- let avatarEntries = (cachedAvatarEntries.with({ $0 })?.get()) ?? .single([])
- let _ = (combineLatest(context.account.postbox.transaction { transaction -> (Peer?, SearchBotsConfiguration) in
- return (transaction.getPeer(context.account.peerId), currentSearchBotsConfiguration(transaction: transaction))
- }, avatarEntries |> take(1)) |> deliverOnMainQueue).start(next: { peerAndSearchBotsConfiguration, avatarEntries in
- let peer = peerAndSearchBotsConfiguration.0
- let searchBotsConfiguration = peerAndSearchBotsConfiguration.1
-
- let mainIsVideo: Bool
- if let main = avatarEntries.first, case let .image(image) = main {
- mainIsVideo = !image.3.isEmpty
- } else {
- mainIsVideo = false
- }
-
- controller?.view.endEditing(true)
-
- let presentationData = context.sharedContext.currentPresentationData.with { $0 }
-
- let legacyController = LegacyController(presentation: .custom, theme: presentationData.theme)
- legacyController.statusBar.statusBarStyle = .Ignore
-
- let emptyController = LegacyEmptyController(context: legacyController.context)!
- let navigationController = makeLegacyNavigationController(rootController: emptyController)
- navigationController.setNavigationBarHidden(true, animated: false)
- navigationController.navigationBar.transform = CGAffineTransform(translationX: -1000.0, y: 0.0)
-
- legacyController.bind(controller: navigationController)
-
- presentControllerImpl?(legacyController, nil)
-
- var hasPhotos = false
- if let peer = peer, !peer.profileImageRepresentations.isEmpty {
- hasPhotos = true
- }
-
- let completedProfilePhotoImpl: (UIImage) -> Void = { image in
- if let data = image.jpegData(compressionQuality: 0.6) {
- let resource = LocalFileMediaResource(fileId: arc4random64())
- context.account.postbox.mediaBox.storeResourceData(resource.id, data: data)
- let representation = TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 640, height: 640), resource: resource)
- updateState {
- $0.withUpdatedUpdatingAvatar(.image(representation, true))
- }
- updateAvatarDisposable.set((updateAccountPhoto(account: context.account, resource: resource, videoResource: nil, videoStartTimestamp: nil, mapResourceToAvatarSizes: { resource, representations in
- return mapResourceToAvatarSizes(postbox: context.account.postbox, resource: resource, representations: representations)
- }) |> deliverOnMainQueue).start(next: { result in
- switch result {
- case .complete:
- updateState {
- $0.withUpdatedUpdatingAvatar(nil)
- }
-
- if let peer = peer {
- let _ = cachedAvatarEntries.modify { value in
- let promise = Promise<[AvatarGalleryEntry]>()
- promise.set(fetchedAvatarGalleryEntries(account: context.account, peer: peer))
- return promise
- }
- }
- case .progress:
- break
- }
- }))
- }
- }
-
- let completedProfileVideoImpl: (UIImage, URL, TGVideoEditAdjustments?) -> Void = { image, url, adjustments in
- if let data = image.jpegData(compressionQuality: 0.6) {
- let photoResource = LocalFileMediaResource(fileId: arc4random64())
- context.account.postbox.mediaBox.storeResourceData(photoResource.id, data: data)
- let representation = TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 640, height: 640), resource: photoResource)
- updateState {
- $0.withUpdatedUpdatingAvatar(.image(representation, true))
- }
-
- var videoStartTimestamp: Double? = nil
- if let adjustments = adjustments, adjustments.videoStartValue > 0.0 {
- videoStartTimestamp = adjustments.videoStartValue - adjustments.trimStartValue
- }
-
- let signal = Signal { subscriber in
- var filteredPath = url.path
- if filteredPath.hasPrefix("file://") {
- filteredPath = String(filteredPath[filteredPath.index(filteredPath.startIndex, offsetBy: "file://".count)])
- }
-
- let avAsset = AVURLAsset(url: URL(fileURLWithPath: filteredPath))
- let entityRenderer: LegacyPaintEntityRenderer? = adjustments.flatMap { adjustments in
- if let paintingData = adjustments.paintingData, paintingData.hasAnimation {
- return LegacyPaintEntityRenderer(account: context.account, adjustments: adjustments)
- } else {
- return nil
- }
- }
- let uploadInterface = LegacyLiveUploadInterface(account: context.account)
- let signal = TGMediaVideoConverter.convert(avAsset, adjustments: adjustments, watcher: uploadInterface, entityRenderer: entityRenderer)!
-
- let signalDisposable = signal.start(next: { next in
- if let result = next as? TGMediaVideoConversionResult {
- if let image = result.coverImage, let data = image.jpegData(compressionQuality: 0.7) {
- context.account.postbox.mediaBox.storeResourceData(photoResource.id, data: data)
- }
-
- var value = stat()
- if stat(result.fileURL.path, &value) == 0 {
- if let data = try? Data(contentsOf: result.fileURL) {
- let resource: TelegramMediaResource
- if let liveUploadData = result.liveUploadData as? LegacyLiveUploadInterfaceResult {
- resource = LocalFileMediaResource(fileId: liveUploadData.id)
- } else {
- resource = LocalFileMediaResource(fileId: arc4random64())
- }
- context.account.postbox.mediaBox.storeResourceData(resource.id, data: data, synchronous: true)
- subscriber.putNext(resource)
- }
- }
- subscriber.putCompletion()
- }
- }, error: { _ in
- }, completed: nil)
-
- let disposable = ActionDisposable {
- signalDisposable?.dispose()
- }
-
- return ActionDisposable {
- disposable.dispose()
- }
- }
-
- updateAvatarDisposable.set((signal
- |> mapToSignal { videoResource in
- return updateAccountPhoto(account: context.account, resource: photoResource, videoResource: videoResource, videoStartTimestamp: videoStartTimestamp, mapResourceToAvatarSizes: { resource, representations in
- return mapResourceToAvatarSizes(postbox: context.account.postbox, resource: resource, representations: representations)
- })
- } |> deliverOnMainQueue).start(next: { result in
- switch result {
- case .complete:
- updateState {
- $0.withUpdatedUpdatingAvatar(nil)
- }
-
- if let peer = peer {
- let _ = cachedAvatarEntries.modify { value in
- let promise = Promise<[AvatarGalleryEntry]>()
- promise.set(fetchedAvatarGalleryEntries(account: context.account, peer: peer))
- return promise
- }
- }
- case .progress:
- break
- }
- }))
- }
- }
-
- let mixin = TGMediaAvatarMenuMixin(context: legacyController.context, parentController: emptyController, hasSearchButton: true, hasDeleteButton: hasPhotos, hasViewButton: hasPhotos, personalPhoto: true, isVideo: mainIsVideo, saveEditedPhotos: false, saveCapturedMedia: false, signup: false)!
- let _ = currentAvatarMixin.swap(mixin)
- mixin.requestSearchController = { assetsController in
- let controller = WebSearchController(context: context, peer: peer, configuration: searchBotsConfiguration, mode: .avatar(initialQuery: nil, completion: { result in
- assetsController?.dismiss()
- completedProfilePhotoImpl(result)
- }))
- presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
- }
- mixin.didFinishWithImage = { image in
- if let image = image {
- completedProfilePhotoImpl(image)
- }
- }
- mixin.didFinishWithVideo = { image, asset, adjustments in
- if let image = image {
-// completedProfileVideoImpl(image, url, adjustments)
- }
- }
- mixin.didFinishWithDelete = {
- let _ = currentAvatarMixin.swap(nil)
- updateState {
- if let profileImage = peer?.smallProfileImage {
- return $0.withUpdatedUpdatingAvatar(.image(profileImage, false))
- } else {
- return $0.withUpdatedUpdatingAvatar(ItemListAvatarAndNameInfoItemUpdatingAvatar.none)
- }
- }
- updateAvatarDisposable.set((updateAccountPhoto(account: context.account, resource: nil, videoResource: nil, videoStartTimestamp: nil, mapResourceToAvatarSizes: { resource, representations in
- return mapResourceToAvatarSizes(postbox: context.account.postbox, resource: resource, representations: representations)
- }) |> deliverOnMainQueue).start(next: { result in
- switch result {
- case .complete:
- updateState {
- $0.withUpdatedUpdatingAvatar(nil)
- }
- if let peer = peer {
- let _ = cachedAvatarEntries.modify { value in
- let promise = Promise<[AvatarGalleryEntry]>()
- promise.set(fetchedAvatarGalleryEntries(account: context.account, peer: peer))
- return promise
- }
- }
- case .progress:
- break
- }
- }))
- }
- mixin.didFinishWithView = {
- let _ = currentAvatarMixin.swap(nil)
-
- let _ = (context.account.postbox.loadedPeerWithId(context.account.peerId)
- |> take(1)
- |> deliverOnMainQueue).start(next: { peer in
- if peer.smallProfileImage != nil {
- let galleryController = AvatarGalleryController(context: context, peer: peer, remoteEntries: cachedAvatarEntries.with { $0 }, replaceRootController: { controller, ready in
- })
- galleryController.avatarPhotoEditCompletion = { image in
- completedProfilePhotoImpl(image)
- }
- galleryController.avatarVideoEditCompletion = { image, url, adjustments in
- completedProfileVideoImpl(image, url, adjustments)
- }
- presentControllerImpl?(galleryController, AvatarGalleryControllerPresentationArguments(transitionArguments: { entry in
- return nil
- }))
- } else {
- changeProfilePhotoImpl?()
- }
- })
- }
- mixin.didDismiss = { [weak legacyController] in
- let _ = currentAvatarMixin.swap(nil)
- legacyController?.dismiss()
- }
- let menuController = mixin.present()
- if let menuController = menuController {
- menuController.customRemoveFromParentViewController = { [weak legacyController] in
- legacyController?.dismiss()
- }
- }
- })
- }
-
- let hapticFeedback = HapticFeedback()
- errorImpl = { [weak controller] in
- hapticFeedback.error()
- controller?.forEachItemNode { itemNode in
- if let itemNode = itemNode as? ItemListMultilineInputItemNode {
- itemNode.animateError()
- }
- }
- }
-
- getNavigationController = { [weak controller] in
- return controller?.navigationController as? NavigationController
- }
-
- return controller
-}
-
diff --git a/submodules/SettingsUI/Sources/OpenSettings.swift b/submodules/SettingsUI/Sources/OpenSettings.swift
deleted file mode 100644
index 333a2810fc..0000000000
--- a/submodules/SettingsUI/Sources/OpenSettings.swift
+++ /dev/null
@@ -1,57 +0,0 @@
-import Foundation
-import Display
-import SwiftSignalKit
-import Postbox
-import TelegramCore
-import SyncCore
-import OverlayStatusController
-import AccountContext
-import PresentationDataUtils
-import AccountUtils
-
-func openEditSettings(context: AccountContext, accountsAndPeers: Signal<((Account, Peer)?, [(Account, Peer, Int32)]), NoError>, focusOnItemTag: EditSettingsEntryTag? = nil, presentController: @escaping (ViewController, Any?) -> Void, pushController: @escaping (ViewController) -> Void) -> Disposable {
- let openEditingDisposable = MetaDisposable()
- var cancelImpl: (() -> Void)?
- let presentationData = context.sharedContext.currentPresentationData.with { $0 }
- let progressSignal = Signal { subscriber in
- let controller = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: {
- cancelImpl?()
- }))
- presentController(controller, nil)
- return ActionDisposable { [weak controller] in
- Queue.mainQueue().async() {
- controller?.dismiss()
- }
- }
- }
- |> runOn(Queue.mainQueue())
- |> delay(0.15, queue: Queue.mainQueue())
- let progressDisposable = progressSignal.start()
-
- let peerKey: PostboxViewKey = .peer(peerId: context.account.peerId, components: [])
- let cachedDataKey: PostboxViewKey = .cachedPeerData(peerId: context.account.peerId)
- let signal = (combineLatest(accountsAndPeers |> take(1), context.account.postbox.combinedView(keys: [peerKey, cachedDataKey]))
- |> mapToSignal { accountsAndPeers, view -> Signal<(TelegramUser, CachedUserData, Bool), NoError> in
- guard let cachedDataView = view.views[cachedDataKey] as? CachedPeerDataView, let cachedData = cachedDataView.cachedPeerData as? CachedUserData else {
- return .complete()
- }
- guard let peerView = view.views[peerKey] as? PeerView, let peer = peerView.peers[context.account.peerId] as? TelegramUser else {
- return .complete()
- }
- return .single((peer, cachedData, accountsAndPeers.1.count + 1 < maximumNumberOfAccounts))
- }
- |> take(1))
- |> afterDisposed {
- Queue.mainQueue().async {
- progressDisposable.dispose()
- }
- }
- cancelImpl = {
- openEditingDisposable.set(nil)
- }
- openEditingDisposable.set((signal
- |> deliverOnMainQueue).start(next: { peer, cachedData, canAddAccounts in
- pushController(editSettingsController(context: context, currentName: .personName(firstName: peer.firstName ?? "", lastName: peer.lastName ?? "", phone: ""), currentBioText: cachedData.about ?? "", accountManager: context.sharedContext.accountManager, canAddAccounts: canAddAccounts, focusOnItemTag: focusOnItemTag))
- }))
- return openEditingDisposable
-}
diff --git a/submodules/SettingsUI/Sources/Search/SettingsSearchableItems.swift b/submodules/SettingsUI/Sources/Search/SettingsSearchableItems.swift
index 2cbfcec749..c0fea2181e 100644
--- a/submodules/SettingsUI/Sources/Search/SettingsSearchableItems.swift
+++ b/submodules/SettingsUI/Sources/Search/SettingsSearchableItems.swift
@@ -188,22 +188,7 @@ private func profileSearchableItems(context: AccountContext, canAddAccount: Bool
let icon: SettingsSearchableItemIcon = .profile
let strings = context.sharedContext.currentPresentationData.with { $0 }.strings
- let presentProfileSettings: (AccountContext, @escaping (SettingsSearchableItemPresentation, ViewController?) -> Void, EditSettingsEntryTag?) -> Void = { context, present, itemTag in
- let _ = openEditSettings(context: context, accountsAndPeers: activeAccountsAndPeers(context: context), focusOnItemTag: itemTag, presentController: { controller, _ in
- present(.immediate, controller)
- }, pushController: { controller in
- present(.push, controller)
- })
- }
-
var items: [SettingsSearchableItem] = []
-// items.append(SettingsSearchableItem(id: .profile(0), title: strings.EditProfile_Title, alternate: synonyms(strings.SettingsSearch_Synonyms_EditProfile_Title), icon: icon, breadcrumbs: [], present: { context, _, present in
-// presentProfileSettings(context, present, nil)
-// }))
-//
-// items.append(SettingsSearchableItem(id: .profile(1), title: strings.UserInfo_About_Placeholder, alternate: synonyms(strings.SettingsSearch_Synonyms_EditProfile_Title), icon: icon, breadcrumbs: [strings.EditProfile_Title], present: { context, _, present in
-// presentProfileSettings(context, present, .bio)
-// }))
items.append(SettingsSearchableItem(id: .profile(2), title: strings.Settings_PhoneNumber, alternate: synonyms(strings.SettingsSearch_Synonyms_EditProfile_PhoneNumber), icon: icon, breadcrumbs: [strings.EditProfile_Title], present: { context, _, present in
let _ = (context.account.postbox.transaction { transaction -> String in
return (transaction.getPeer(context.account.peerId) as? TelegramUser)?.phone ?? ""
diff --git a/submodules/SettingsUI/Sources/SettingsController.swift b/submodules/SettingsUI/Sources/SettingsController.swift
index b0ded4b2fa..19fb415862 100644
--- a/submodules/SettingsUI/Sources/SettingsController.swift
+++ b/submodules/SettingsUI/Sources/SettingsController.swift
@@ -998,7 +998,7 @@ public func settingsController(context: AccountContext, accountManager: AccountM
if let data = image.jpegData(compressionQuality: 0.6) {
let resource = LocalFileMediaResource(fileId: arc4random64())
context.account.postbox.mediaBox.storeResourceData(resource.id, data: data)
- let representation = TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 640, height: 640), resource: resource)
+ let representation = TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 640, height: 640), resource: resource, progressiveSizes: [])
updateState { state in
var state = state
state.updatingAvatar = .image(representation, true)
@@ -1025,7 +1025,7 @@ public func settingsController(context: AccountContext, accountManager: AccountM
if let data = image.jpegData(compressionQuality: 0.6) {
let photoResource = LocalFileMediaResource(fileId: arc4random64())
context.account.postbox.mediaBox.storeResourceData(photoResource.id, data: data)
- let representation = TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 640, height: 640), resource: photoResource)
+ let representation = TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 640, height: 640), resource: photoResource, progressiveSizes: [])
updateState { state in
var state = state
state.updatingAvatar = .image(representation, true)
@@ -1266,13 +1266,6 @@ public func settingsController(context: AccountContext, accountManager: AccountM
resolvedUrlPromise.set(resolvedUrl)
openFaq(resolvedUrlPromise, anchor)
}, openEditing: {
- let _ = (contextValue.get()
- |> deliverOnMainQueue
- |> take(1)).start(next: { context in
- if let presentControllerImpl = presentControllerImpl, let pushControllerImpl = pushControllerImpl {
- openEditingDisposable.set(openEditSettings(context: context, accountsAndPeers: accountsAndPeers.get(), presentController: presentControllerImpl, pushController: pushControllerImpl))
- }
- })
}, displayCopyContextMenu: {
let _ = (contextValue.get()
|> deliverOnMainQueue
diff --git a/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift b/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift
index f2b4aafdf5..9c01ad1baa 100644
--- a/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift
+++ b/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift
@@ -214,7 +214,7 @@ private final class TextSizeSelectionControllerNode: ASDisplayNode, UIScrollView
var items: [ChatListItem] = []
let interaction = ChatListNodeInteraction(activateSearch: {}, peerSelected: { _, _ in }, disabledPeerSelected: { _ in }, togglePeerSelected: { _ in }, additionalCategorySelected: { _ in
- }, messageSelected: { _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, deletePeer: { _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, hidePsa: { _ in
+ }, messageSelected: { _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, deletePeer: { _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, hidePsa: { _ in
}, activateChatPreview: { _, _, gesture in
gesture?.cancel()
}, present: { _ in })
diff --git a/submodules/SettingsUI/Sources/Themes/CustomWallpaperPicker.swift b/submodules/SettingsUI/Sources/Themes/CustomWallpaperPicker.swift
index 8db3243b87..940dc2ce69 100644
--- a/submodules/SettingsUI/Sources/Themes/CustomWallpaperPicker.swift
+++ b/submodules/SettingsUI/Sources/Themes/CustomWallpaperPicker.swift
@@ -175,7 +175,7 @@ func uploadCustomWallpaper(context: AccountContext, wallpaper: WallpaperGalleryE
let apply: () -> Void = {
let settings = WallpaperSettings(blur: mode.contains(.blur), motion: mode.contains(.motion), color: nil, intensity: nil)
- let wallpaper: TelegramWallpaper = .image([TelegramMediaImageRepresentation(dimensions: PixelDimensions(thumbnailDimensions), resource: thumbnailResource), TelegramMediaImageRepresentation(dimensions: PixelDimensions(croppedImage.size), resource: resource)], settings)
+ let wallpaper: TelegramWallpaper = .image([TelegramMediaImageRepresentation(dimensions: PixelDimensions(thumbnailDimensions), resource: thumbnailResource, progressiveSizes: []), TelegramMediaImageRepresentation(dimensions: PixelDimensions(croppedImage.size), resource: resource, progressiveSizes: [])], settings)
updateWallpaper(wallpaper)
DispatchQueue.main.async {
completion()
diff --git a/submodules/SettingsUI/Sources/Themes/EditThemeController.swift b/submodules/SettingsUI/Sources/Themes/EditThemeController.swift
index 2b7af5c72c..14713d7a87 100644
--- a/submodules/SettingsUI/Sources/Themes/EditThemeController.swift
+++ b/submodules/SettingsUI/Sources/Themes/EditThemeController.swift
@@ -359,7 +359,7 @@ public func editThemeController(context: AccountContext, mode: EditThemeControll
|> mapToSignal { wallpaper -> Signal in
if let wallpaper = wallpaper, case let .file(file) = wallpaper.wallpaper {
var convertedRepresentations: [ImageRepresentationWithReference] = []
- convertedRepresentations.append(ImageRepresentationWithReference(representation: TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 100, height: 100), resource: file.file.resource), reference: .wallpaper(wallpaper: .slug(file.slug), resource: file.file.resource)))
+ convertedRepresentations.append(ImageRepresentationWithReference(representation: TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 100, height: 100), resource: file.file.resource, progressiveSizes: []), reference: .wallpaper(wallpaper: .slug(file.slug), resource: file.file.resource)))
return wallpaperDatas(account: context.account, accountManager: context.sharedContext.accountManager, fileReference: .standalone(media: file.file), representations: convertedRepresentations, alwaysShowThumbnailFirst: false, thumbnail: false, onlyFullSize: true, autoFetchFullSize: true, synchronousLoad: false)
|> mapToSignal { _, fullSizeData, complete -> Signal in
guard complete, let fullSizeData = fullSizeData else {
diff --git a/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift b/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift
index d30215f04e..5d38dc26fd 100644
--- a/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift
+++ b/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift
@@ -456,7 +456,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
for representation in file.file.previewRepresentations {
convertedRepresentations.append(ImageRepresentationWithReference(representation: representation, reference: .wallpaper(wallpaper: .slug(file.slug), resource: representation.resource)))
}
- convertedRepresentations.append(ImageRepresentationWithReference(representation: .init(dimensions: dimensions, resource: file.file.resource), reference: .wallpaper(wallpaper: .slug(file.slug), resource: file.file.resource)))
+ convertedRepresentations.append(ImageRepresentationWithReference(representation: .init(dimensions: dimensions, resource: file.file.resource, progressiveSizes: []), reference: .wallpaper(wallpaper: .slug(file.slug), resource: file.file.resource)))
wallpaperSignal = patternWallpaperImage(account: context.account, accountManager: context.sharedContext.accountManager, representations: convertedRepresentations, mode: .screen, autoFetchFullSize: true)
} else if let bottomColor = backgroundColors.1 {
@@ -767,7 +767,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
var items: [ChatListItem] = []
let interaction = ChatListNodeInteraction(activateSearch: {}, peerSelected: { _, _ in }, disabledPeerSelected: { _ in }, togglePeerSelected: { _ in }, additionalCategorySelected: { _ in
- }, messageSelected: { _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, deletePeer: { _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, hidePsa: { _ in
+ }, messageSelected: { _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, deletePeer: { _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, hidePsa: { _ in
}, activateChatPreview: { _, _, gesture in
gesture?.cancel()
}, present: { _ in
diff --git a/submodules/SettingsUI/Sources/Themes/ThemeGridSearchItem.swift b/submodules/SettingsUI/Sources/Themes/ThemeGridSearchItem.swift
index 243283c155..04d8256dce 100644
--- a/submodules/SettingsUI/Sources/Themes/ThemeGridSearchItem.swift
+++ b/submodules/SettingsUI/Sources/Themes/ThemeGridSearchItem.swift
@@ -109,10 +109,10 @@ final class ThemeGridSearchItemNode: GridItemNode {
var representations: [TelegramMediaImageRepresentation] = []
if let thumbnailResource = thumbnailResource, let thumbnailDimensions = thumbnailDimensions {
- representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(thumbnailDimensions), resource: thumbnailResource))
+ representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(thumbnailDimensions), resource: thumbnailResource, progressiveSizes: []))
}
if let imageResource = imageResource, let imageDimensions = imageDimensions {
- representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(imageDimensions), resource: imageResource))
+ representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(imageDimensions), resource: imageResource, progressiveSizes: []))
}
if !representations.isEmpty {
let tmpImage = TelegramMediaImage(imageId: MediaId(namespace: 0, id: 0), representations: representations, immediateThumbnailData: immediateThumbnailData, reference: nil, partialReference: nil, flags: [])
diff --git a/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift b/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift
index c273166376..86079f02cf 100644
--- a/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift
+++ b/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift
@@ -231,7 +231,7 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
for representation in file.file.previewRepresentations {
convertedRepresentations.append(ImageRepresentationWithReference(representation: representation, reference: .wallpaper(wallpaper: .slug(file.slug), resource: representation.resource)))
}
- convertedRepresentations.append(ImageRepresentationWithReference(representation: .init(dimensions: dimensions, resource: file.file.resource), reference: .wallpaper(wallpaper: .slug(file.slug), resource: file.file.resource)))
+ convertedRepresentations.append(ImageRepresentationWithReference(representation: .init(dimensions: dimensions, resource: file.file.resource, progressiveSizes: []), reference: .wallpaper(wallpaper: .slug(file.slug), resource: file.file.resource)))
let signal: Signal<(TransformImageArguments) -> DrawingContext?, NoError>
let fileReference = FileMediaReference.standalone(media: file.file)
@@ -351,7 +351,7 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
var items: [ChatListItem] = []
let interaction = ChatListNodeInteraction(activateSearch: {}, peerSelected: { _, _ in }, disabledPeerSelected: { _ in }, togglePeerSelected: { _ in }, additionalCategorySelected: { _ in
- }, messageSelected: { _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, deletePeer: { _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, hidePsa: { _ in
+ }, messageSelected: { _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, deletePeer: { _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, hidePsa: { _ in
}, activateChatPreview: { _, _, gesture in
gesture?.cancel()
}, present: { _ in
diff --git a/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift b/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift
index f885ba6877..932101a4f4 100644
--- a/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift
+++ b/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift
@@ -273,7 +273,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
for representation in file.file.previewRepresentations {
convertedRepresentations.append(ImageRepresentationWithReference(representation: representation, reference: reference(for: representation.resource, media: file.file, message: message)))
}
- convertedRepresentations.append(ImageRepresentationWithReference(representation: .init(dimensions: dimensions, resource: file.file.resource), reference: reference(for: file.file.resource, media: file.file, message: message)))
+ convertedRepresentations.append(ImageRepresentationWithReference(representation: .init(dimensions: dimensions, resource: file.file.resource, progressiveSizes: []), reference: reference(for: file.file.resource, media: file.file, message: message)))
if wallpaper.isPattern {
var patternColors: [UIColor] = []
@@ -448,9 +448,9 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
var representations: [TelegramMediaImageRepresentation] = []
if let thumbnailResource = thumbnailResource, let thumbnailDimensions = thumbnailDimensions {
- representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(thumbnailDimensions), resource: thumbnailResource))
+ representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(thumbnailDimensions), resource: thumbnailResource, progressiveSizes: []))
}
- representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(imageDimensions), resource: imageResource))
+ representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(imageDimensions), resource: imageResource, progressiveSizes: []))
let tmpImage = TelegramMediaImage(imageId: MediaId(namespace: 0, id: 0), representations: representations, immediateThumbnailData: nil, reference: nil, partialReference: nil, flags: [])
signal = chatMessagePhoto(postbox: context.account.postbox, photoReference: .standalone(media: tmpImage))
diff --git a/submodules/ShareController/Sources/ShareController.swift b/submodules/ShareController/Sources/ShareController.swift
index 5c7ca39005..c87d6f1d32 100644
--- a/submodules/ShareController/Sources/ShareController.swift
+++ b/submodules/ShareController/Sources/ShareController.swift
@@ -290,6 +290,8 @@ public final class ShareController: ViewController {
private let presetText: String?
private let switchableAccounts: [AccountWithInfo]
private let immediatePeerId: PeerId?
+ private let openStats: (() -> Void)?
+ private let shares: Int?
private let peers = Promise<([(RenderedPeer, PeerPresence?)], Peer)>()
private let peersDisposable = MetaDisposable()
@@ -300,11 +302,11 @@ public final class ShareController: ViewController {
public var dismissed: ((Bool) -> Void)?
- public convenience init(context: AccountContext, subject: ShareControllerSubject, presetText: String? = nil, preferredAction: ShareControllerPreferredAction = .default, showInChat: ((Message) -> Void)? = nil, externalShare: Bool = true, immediateExternalShare: Bool = false, switchableAccounts: [AccountWithInfo] = [], immediatePeerId: PeerId? = nil) {
- self.init(sharedContext: context.sharedContext, currentContext: context, subject: subject, presetText: presetText, preferredAction: preferredAction, showInChat: showInChat, externalShare: externalShare, immediateExternalShare: immediateExternalShare, switchableAccounts: switchableAccounts, immediatePeerId: immediatePeerId)
+ public convenience init(context: AccountContext, subject: ShareControllerSubject, presetText: String? = nil, preferredAction: ShareControllerPreferredAction = .default, showInChat: ((Message) -> Void)? = nil, openStats: (() -> Void)? = nil, shares: Int? = nil, externalShare: Bool = true, immediateExternalShare: Bool = false, switchableAccounts: [AccountWithInfo] = [], immediatePeerId: PeerId? = nil) {
+ self.init(sharedContext: context.sharedContext, currentContext: context, subject: subject, presetText: presetText, preferredAction: preferredAction, showInChat: showInChat, openStats: openStats, shares: shares, externalShare: externalShare, immediateExternalShare: immediateExternalShare, switchableAccounts: switchableAccounts, immediatePeerId: immediatePeerId)
}
- public init(sharedContext: SharedAccountContext, currentContext: AccountContext, subject: ShareControllerSubject, presetText: String? = nil, preferredAction: ShareControllerPreferredAction = .default, showInChat: ((Message) -> Void)? = nil, externalShare: Bool = true, immediateExternalShare: Bool = false, switchableAccounts: [AccountWithInfo] = [], immediatePeerId: PeerId? = nil) {
+ public init(sharedContext: SharedAccountContext, currentContext: AccountContext, subject: ShareControllerSubject, presetText: String? = nil, preferredAction: ShareControllerPreferredAction = .default, showInChat: ((Message) -> Void)? = nil, openStats: (() -> Void)? = nil, shares: Int? = nil, externalShare: Bool = true, immediateExternalShare: Bool = false, switchableAccounts: [AccountWithInfo] = [], immediatePeerId: PeerId? = nil) {
self.sharedContext = sharedContext
self.currentContext = currentContext
self.currentAccount = currentContext.account
@@ -314,6 +316,8 @@ public final class ShareController: ViewController {
self.immediateExternalShare = immediateExternalShare
self.switchableAccounts = switchableAccounts
self.immediatePeerId = immediatePeerId
+ self.openStats = openStats
+ self.shares = shares
self.presentationData = self.sharedContext.currentPresentationData.with { $0 }
@@ -437,7 +441,7 @@ public final class ShareController: ViewController {
return
}
strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: title, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
- }, externalShare: self.externalShare, immediateExternalShare: self.immediateExternalShare, immediatePeerId: self.immediatePeerId)
+ }, externalShare: self.externalShare, immediateExternalShare: self.immediateExternalShare, immediatePeerId: self.immediatePeerId, shares: self.shares)
self.controllerNode.dismiss = { [weak self] shared in
self?.presentingViewController?.dismiss(animated: false, completion: nil)
self?.dismissed?(shared)
@@ -714,6 +718,11 @@ public final class ShareController: ViewController {
strongSelf.view.endEditing(true)
strongSelf.present(controller, in: .window(.root), with: ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
}
+ if case .messages = self.subject, let openStats = self.openStats {
+ self.controllerNode.openStats = {
+ openStats()
+ }
+ }
self.displayNodeDidLoad()
self.peersDisposable.set((self.peers.get()
diff --git a/submodules/ShareController/Sources/ShareControllerNode.swift b/submodules/ShareController/Sources/ShareControllerNode.swift
index a504392dbd..5d17164394 100644
--- a/submodules/ShareController/Sources/ShareControllerNode.swift
+++ b/submodules/ShareController/Sources/ShareControllerNode.swift
@@ -32,6 +32,7 @@ final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate
private let externalShare: Bool
private let immediateExternalShare: Bool
private var immediatePeerId: PeerId?
+ private let shares: Int?
private let defaultAction: ShareControllerAction?
private let requestLayout: (ContainedViewLayoutTransition) -> Void
@@ -61,6 +62,7 @@ final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate
var share: ((String, [PeerId]) -> Signal)?
var shareExternal: (() -> Signal)?
var switchToAnotherAccount: (() -> Void)?
+ var openStats: (() -> Void)?
let ready = Promise()
private var didSetReady = false
@@ -78,12 +80,13 @@ final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate
private let presetText: String?
- init(sharedContext: SharedAccountContext, presetText: String?, defaultAction: ShareControllerAction?, requestLayout: @escaping (ContainedViewLayoutTransition) -> Void, presentError: @escaping (String?, String) -> Void, externalShare: Bool, immediateExternalShare: Bool, immediatePeerId: PeerId?) {
+ init(sharedContext: SharedAccountContext, presetText: String?, defaultAction: ShareControllerAction?, requestLayout: @escaping (ContainedViewLayoutTransition) -> Void, presentError: @escaping (String?, String) -> Void, externalShare: Bool, immediateExternalShare: Bool, immediatePeerId: PeerId?, shares: Int?) {
self.sharedContext = sharedContext
self.presentationData = sharedContext.currentPresentationData.with { $0 }
self.externalShare = externalShare
self.immediateExternalShare = immediateExternalShare
self.immediatePeerId = immediatePeerId
+ self.shares = shares
self.presentError = presentError
self.presetText = presetText
@@ -670,7 +673,7 @@ final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate
let animated = self.peersContentNode == nil
let peersContentNode = SharePeersContainerNode(sharedContext: self.sharedContext, context: context, switchableAccounts: switchableAccounts, theme: self.presentationData.theme, strings: self.presentationData.strings, nameDisplayOrder: self.presentationData.nameDisplayOrder, peers: peers, accountPeer: accountPeer, controllerInteraction: self.controllerInteraction!, externalShare: self.externalShare, switchToAnotherAccount: { [weak self] in
self?.switchToAnotherAccount?()
- }, extendedInitialReveal: self.presetText != nil)
+ }, extendedInitialReveal: self.presetText != nil, statsCount: self.shares)
self.peersContentNode = peersContentNode
peersContentNode.openSearch = { [weak self] in
let _ = (recentlySearchedPeers(postbox: context.account.postbox)
@@ -736,6 +739,14 @@ final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate
peersContentNode.openShare = {
openShare(false)
}
+ if let openStats = self.openStats {
+ peersContentNode.openStats = { [weak self] in
+ openStats()
+ self?.animateOut(shared: true, completion: {
+ self?.dismiss?(true)
+ })
+ }
+ }
if self.immediateExternalShare {
openShare(true)
} else {
diff --git a/submodules/ShareController/Sources/SharePeersContainerNode.swift b/submodules/ShareController/Sources/SharePeersContainerNode.swift
index 2ee580c627..e55b8e754d 100644
--- a/submodules/ShareController/Sources/SharePeersContainerNode.swift
+++ b/submodules/ShareController/Sources/SharePeersContainerNode.swift
@@ -81,6 +81,7 @@ final class SharePeersContainerNode: ASDisplayNode, ShareContentContainerNode {
private let controllerInteraction: ShareControllerInteraction
private let switchToAnotherAccount: () -> Void
private let extendedInitialReveal: Bool
+ private let statsCount: Int?
let accountPeer: Peer
private let foundPeers = Promise<[RenderedPeer]>([])
@@ -96,11 +97,13 @@ final class SharePeersContainerNode: ASDisplayNode, ShareContentContainerNode {
private let contentSeparatorNode: ASDisplayNode
private let searchButtonNode: HighlightableButtonNode
private let shareButtonNode: HighlightableButtonNode
+ private let statsButtonNode: HighlightableButtonNode
private var contentOffsetUpdated: ((CGFloat, ContainedViewLayoutTransition) -> Void)?
var openSearch: (() -> Void)?
var openShare: (() -> Void)?
+ var openStats: (() -> Void)?
private var ensurePeerVisibleOnLayout: PeerId?
private var validLayout: (CGSize, CGFloat)?
@@ -108,7 +111,7 @@ final class SharePeersContainerNode: ASDisplayNode, ShareContentContainerNode {
let peersValue = Promise<[(RenderedPeer, PeerPresence?)]>()
- init(sharedContext: SharedAccountContext, context: AccountContext, switchableAccounts: [AccountWithInfo], theme: PresentationTheme, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, peers: [(RenderedPeer, PeerPresence?)], accountPeer: Peer, controllerInteraction: ShareControllerInteraction, externalShare: Bool, switchToAnotherAccount: @escaping () -> Void, extendedInitialReveal: Bool) {
+ init(sharedContext: SharedAccountContext, context: AccountContext, switchableAccounts: [AccountWithInfo], theme: PresentationTheme, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, peers: [(RenderedPeer, PeerPresence?)], accountPeer: Peer, controllerInteraction: ShareControllerInteraction, externalShare: Bool, switchToAnotherAccount: @escaping () -> Void, extendedInitialReveal: Bool, statsCount: Int?) {
self.sharedContext = sharedContext
self.context = context
self.theme = theme
@@ -118,6 +121,7 @@ final class SharePeersContainerNode: ASDisplayNode, ShareContentContainerNode {
self.accountPeer = accountPeer
self.switchToAnotherAccount = switchToAnotherAccount
self.extendedInitialReveal = extendedInitialReveal
+ self.statsCount = statsCount
self.peersValue.set(.single(peers))
@@ -174,6 +178,13 @@ final class SharePeersContainerNode: ASDisplayNode, ShareContentContainerNode {
self.shareButtonNode = HighlightableButtonNode()
self.shareButtonNode.setImage(generateTintedImage(image: UIImage(bundleImageName: "Share/ShareIcon"), color: self.theme.actionSheet.controlAccentColor), for: [])
+ self.statsButtonNode = HighlightableButtonNode()
+ self.statsButtonNode.setAttributedTitle(NSAttributedString(string: "\(statsCount ?? 0) Shares", font: Font.regular(17.0), textColor: self.theme.actionSheet.controlAccentColor), for: .normal)
+ self.statsButtonNode.isHidden = statsCount == nil
+
+ self.contentTitleNode.isHidden = !self.statsButtonNode.isHidden
+ self.contentSubtitleNode.isHidden = !self.statsButtonNode.isHidden
+
self.contentSeparatorNode = ASDisplayNode()
self.contentSeparatorNode.isLayerBacked = true
self.contentSeparatorNode.displaysAsynchronously = false
@@ -192,6 +203,7 @@ final class SharePeersContainerNode: ASDisplayNode, ShareContentContainerNode {
self.addSubnode(self.contentTitleAccountNode)
self.addSubnode(self.searchButtonNode)
self.addSubnode(self.shareButtonNode)
+ self.addSubnode(self.statsButtonNode)
self.addSubnode(self.contentSeparatorNode)
let previousItems = Atomic<[SharePeerEntry]?>(value: [])
@@ -213,6 +225,7 @@ final class SharePeersContainerNode: ASDisplayNode, ShareContentContainerNode {
self.searchButtonNode.addTarget(self, action: #selector(self.searchPressed), forControlEvents: .touchUpInside)
self.shareButtonNode.addTarget(self, action: #selector(self.sharePressed), forControlEvents: .touchUpInside)
+ self.statsButtonNode.addTarget(self, action: #selector(self.statsPressed), forControlEvents: .touchUpInside)
self.contentTitleAccountNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.accountTapGesture(_:))))
}
@@ -341,6 +354,9 @@ final class SharePeersContainerNode: ASDisplayNode, ShareContentContainerNode {
self.contentSubtitleNode.frame = originalSubtitleFrame
transition.updateFrame(node: self.contentSubtitleNode, frame: subtitleFrame)
+ let statsSize = self.statsButtonNode.measure(CGSize(width: size.width - 44.0 * 2.0 - 8.0 * 2.0, height: titleAreaHeight))
+ transition.updateFrame(node: self.statsButtonNode, frame: CGRect(origin: CGPoint(x: floor((size.width - statsSize.width) / 2.0), y: titleOffset + 22.0), size: statsSize))
+
let titleButtonSize = CGSize(width: 44.0, height: 44.0)
let searchButtonFrame = CGRect(origin: CGPoint(x: 12.0, y: titleOffset + 12.0), size: titleButtonSize)
transition.updateFrame(node: self.searchButtonNode, frame: searchButtonFrame)
@@ -376,25 +392,34 @@ final class SharePeersContainerNode: ASDisplayNode, ShareContentContainerNode {
}
func updateSelectedPeers() {
- var subtitleText = self.strings.ShareMenu_SelectChats
- if !self.controllerInteraction.selectedPeers.isEmpty {
- subtitleText = self.controllerInteraction.selectedPeers.reduce("", { string, peer in
- let text: String
- if peer.peerId == self.accountPeer.id {
- text = self.strings.DialogList_SavedMessages
- } else {
- text = peer.chatMainPeer?.displayTitle(strings: self.strings, displayOrder: self.nameDisplayOrder) ?? ""
- }
-
- if !string.isEmpty {
- return string + ", " + text
- } else {
- return string + text
- }
- })
+ if let _ = self.openStats, self.controllerInteraction.selectedPeers.isEmpty {
+ self.statsButtonNode.isHidden = false
+ self.contentTitleNode.isHidden = true
+ self.contentSubtitleNode.isHidden = true
+ } else {
+ self.statsButtonNode.isHidden = true
+ self.contentTitleNode.isHidden = false
+ self.contentSubtitleNode.isHidden = false
+
+ var subtitleText = self.strings.ShareMenu_SelectChats
+ if !self.controllerInteraction.selectedPeers.isEmpty {
+ subtitleText = self.controllerInteraction.selectedPeers.reduce("", { string, peer in
+ let text: String
+ if peer.peerId == self.accountPeer.id {
+ text = self.strings.DialogList_SavedMessages
+ } else {
+ text = peer.chatMainPeer?.displayTitle(strings: self.strings, displayOrder: self.nameDisplayOrder) ?? ""
+ }
+
+ if !string.isEmpty {
+ return string + ", " + text
+ } else {
+ return string + text
+ }
+ })
+ }
+ self.contentSubtitleNode.attributedText = NSAttributedString(string: subtitleText, font: subtitleFont, textColor: self.theme.actionSheet.secondaryTextColor)
}
- self.contentSubtitleNode.attributedText = NSAttributedString(string: subtitleText, font: subtitleFont, textColor: self.theme.actionSheet.secondaryTextColor)
-
self.contentGridNode.forEachItemNode { itemNode in
if let itemNode = itemNode as? ShareControllerPeerGridItemNode {
itemNode.updateSelection(animated: true)
@@ -410,6 +435,10 @@ final class SharePeersContainerNode: ASDisplayNode, ShareContentContainerNode {
self.openShare?()
}
+ @objc func statsPressed() {
+ self.openStats?()
+ }
+
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
let nodes: [ASDisplayNode] = [self.searchButtonNode, self.shareButtonNode, self.contentTitleAccountNode]
for node in nodes {
diff --git a/submodules/StatisticsUI/Sources/ChannelStatsController.swift b/submodules/StatisticsUI/Sources/ChannelStatsController.swift
index 316162a40a..1e2a2fb821 100644
--- a/submodules/StatisticsUI/Sources/ChannelStatsController.swift
+++ b/submodules/StatisticsUI/Sources/ChannelStatsController.swift
@@ -5,7 +5,6 @@ import SwiftSignalKit
import Postbox
import TelegramCore
import SyncCore
-import MapKit
import TelegramPresentationData
import TelegramUIPreferences
import TelegramStringFormatting
diff --git a/submodules/StatisticsUI/Sources/GroupStatsController.swift b/submodules/StatisticsUI/Sources/GroupStatsController.swift
index c3c457d815..263e5a9ac1 100644
--- a/submodules/StatisticsUI/Sources/GroupStatsController.swift
+++ b/submodules/StatisticsUI/Sources/GroupStatsController.swift
@@ -5,7 +5,6 @@ import SwiftSignalKit
import Postbox
import TelegramCore
import SyncCore
-import MapKit
import TelegramPresentationData
import TelegramUIPreferences
import TelegramStringFormatting
diff --git a/submodules/StatisticsUI/Sources/MessageStatsController.swift b/submodules/StatisticsUI/Sources/MessageStatsController.swift
new file mode 100644
index 0000000000..8db5d7f899
--- /dev/null
+++ b/submodules/StatisticsUI/Sources/MessageStatsController.swift
@@ -0,0 +1,265 @@
+import Foundation
+import UIKit
+import Display
+import SwiftSignalKit
+import Postbox
+import TelegramCore
+import SyncCore
+import TelegramPresentationData
+import TelegramUIPreferences
+import TelegramStringFormatting
+import ItemListUI
+import ItemListPeerItem
+import PresentationDataUtils
+import AccountContext
+import PresentationDataUtils
+import AppBundle
+import GraphUI
+
+private final class MessageStatsControllerArguments {
+ let context: AccountContext
+ let loadDetailedGraph: (StatsGraph, Int64) -> Signal
+ let openMessage: (MessageId) -> Void
+
+ init(context: AccountContext, loadDetailedGraph: @escaping (StatsGraph, Int64) -> Signal, openMessage: @escaping (MessageId) -> Void) {
+ self.context = context
+ self.loadDetailedGraph = loadDetailedGraph
+ self.openMessage = openMessage
+ }
+}
+
+private enum StatsSection: Int32 {
+ case overview
+ case interactions
+ case publicForwards
+}
+
+private enum StatsEntry: ItemListNodeEntry {
+ case overviewTitle(PresentationTheme, String)
+ case overview(PresentationTheme, MessageStats, Int32?)
+
+ case interactionsTitle(PresentationTheme, String)
+ case interactionsGraph(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, StatsGraph, ChartType)
+
+ case publicForwardsTitle(PresentationTheme, String)
+ case publicForward(Int32, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, Message)
+
+ var section: ItemListSectionId {
+ switch self {
+ case .overviewTitle, .overview:
+ return StatsSection.overview.rawValue
+ case .interactionsTitle, .interactionsGraph:
+ return StatsSection.interactions.rawValue
+ case .publicForwardsTitle, .publicForward:
+ return StatsSection.publicForwards.rawValue
+ }
+ }
+
+ var stableId: Int32 {
+ switch self {
+ case .overviewTitle:
+ return 0
+ case .overview:
+ return 1
+ case .interactionsTitle:
+ return 2
+ case .interactionsGraph:
+ return 3
+ case .publicForwardsTitle:
+ return 4
+ case let .publicForward(index, _, _, _, _):
+ return 5 + index
+ }
+ }
+
+ static func ==(lhs: StatsEntry, rhs: StatsEntry) -> Bool {
+ switch lhs {
+ case let .overviewTitle(lhsTheme, lhsText):
+ if case let .overviewTitle(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText{
+ return true
+ } else {
+ return false
+ }
+ case let .overview(lhsTheme, lhsStats, lhsPublicShares):
+ if case let .overview(rhsTheme, rhsStats, rhsPublicShares) = rhs, lhsTheme === rhsTheme, lhsStats == rhsStats, lhsPublicShares == rhsPublicShares {
+ return true
+ } else {
+ return false
+ }
+ case let .interactionsTitle(lhsTheme, lhsText):
+ if case let .interactionsTitle(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
+ return true
+ } else {
+ return false
+ }
+ case let .interactionsGraph(lhsTheme, lhsStrings, lhsDateTimeFormat, lhsGraph, lhsType):
+ if case let .interactionsGraph(rhsTheme, rhsStrings, rhsDateTimeFormat, rhsGraph, rhsType) = rhs, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsDateTimeFormat == rhsDateTimeFormat, lhsGraph == rhsGraph, lhsType == rhsType {
+ return true
+ } else {
+ return false
+ }
+ case let .publicForwardsTitle(lhsTheme, lhsText):
+ if case let .publicForwardsTitle(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
+ return true
+ } else {
+ return false
+ }
+ case let .publicForward(lhsIndex, lhsTheme, lhsStrings, lhsDateTimeFormat, lhsMessage):
+ if case let .publicForward(rhsIndex, rhsTheme, rhsStrings, rhsDateTimeFormat, rhsMessage) = rhs, lhsIndex == rhsIndex, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsDateTimeFormat == rhsDateTimeFormat, lhsMessage.id == rhsMessage.id {
+ return true
+ } else {
+ return false
+ }
+ }
+ }
+
+ static func <(lhs: StatsEntry, rhs: StatsEntry) -> Bool {
+ return lhs.stableId < rhs.stableId
+ }
+
+ func item(presentationData: ItemListPresentationData, arguments: Any) -> ListViewItem {
+ let arguments = arguments as! MessageStatsControllerArguments
+ switch self {
+ case let .overviewTitle(_, text),
+ let .interactionsTitle(_, text),
+ let .publicForwardsTitle(_, text):
+ return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
+ case let .overview(_, stats, publicShares):
+ return MessageStatsOverviewItem(presentationData: presentationData, stats: stats, publicShares: publicShares, sectionId: self.section, style: .blocks)
+ case let .interactionsGraph(_, _, _, graph, type):
+ return StatsGraphItem(presentationData: presentationData, graph: graph, type: type, getDetailsData: { date, completion in
+ let _ = arguments.loadDetailedGraph(graph, Int64(date.timeIntervalSince1970) * 1000).start(next: { graph in
+ if let graph = graph, case let .Loaded(_, data) = graph {
+ completion(data)
+ }
+ })
+ }, sectionId: self.section, style: .blocks)
+ case let .publicForward(_, _, _, _, message):
+ var views: Int = 0
+ for attribute in message.attributes {
+ if let viewsAttribute = attribute as? ViewCountMessageAttribute {
+ views = viewsAttribute.count
+ break
+ }
+ }
+
+ var text: String = ""
+ text += "\(views) views"
+ return ItemListPeerItem(presentationData: presentationData, dateTimeFormat: PresentationDateTimeFormat(timeFormat: .military, dateFormat: .dayFirst, dateSeparator: ".", decimalSeparator: ",", groupingSeparator: ""), nameDisplayOrder: .firstLast, context: arguments.context, peer: message.peers[message.id.peerId]!, height: .generic, aliasHandling: .standard, nameColor: .primary, nameStyle: .plain, presence: nil, text: .text(text), label: .none, editing: ItemListPeerItemEditing(editable: false, editing: false, revealed: nil), revealOptions: nil, switchValue: nil, enabled: true, highlighted: false, selectable: true, sectionId: self.section, action: {
+ arguments.openMessage(message.id)
+ }, setPeerIdWithRevealedOptions: { _, _ in }, removePeer: { _ in }, toggleUpdated: nil, contextAction: nil)
+// return StatsMessageItem(context: arguments.context, presentationData: presentationData, message: message, views: 0, forwards: 0, sectionId: self.section, style: .blocks, action: {
+// arguments.openMessage(message.id)
+// })
+ }
+ }
+}
+
+private func messageStatsControllerEntries(data: MessageStats?, messages: SearchMessagesResult?, presentationData: PresentationData) -> [StatsEntry] {
+ var entries: [StatsEntry] = []
+
+ if let data = data {
+ entries.append(.overviewTitle(presentationData.theme, presentationData.strings.Stats_MessageOverview.uppercased()))
+ entries.append(.overview(presentationData.theme, data, messages?.totalCount))
+
+ if !data.interactionsGraph.isEmpty {
+ entries.append(.interactionsTitle(presentationData.theme, presentationData.strings.Stats_MessageInteractionsTitle.uppercased()))
+ entries.append(.interactionsGraph(presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, data.interactionsGraph, .twoAxisStep))
+ }
+
+ if let messages = messages, !messages.messages.isEmpty {
+ entries.append(.publicForwardsTitle(presentationData.theme, presentationData.strings.Stats_MessagePublicForwardsTitle.uppercased()))
+ var index: Int32 = 0
+ for message in messages.messages {
+ entries.append(.publicForward(index, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, message))
+ index += 1
+ }
+ }
+ }
+
+ return entries
+}
+
+public func messageStatsController(context: AccountContext, messageId: MessageId, cachedPeerData: CachedPeerData) -> ViewController {
+ var navigateToMessageImpl: ((MessageId) -> Void)?
+
+ let actionsDisposable = DisposableSet()
+ let dataPromise = Promise(nil)
+ let messagesPromise = Promise<(SearchMessagesResult, SearchMessagesState)?>(nil)
+
+ var datacenterId: Int32 = 0
+ if let cachedData = cachedPeerData as? CachedChannelData {
+ datacenterId = cachedData.statsDatacenterId
+ }
+
+ let statsContext = MessageStatsContext(postbox: context.account.postbox, network: context.account.network, datacenterId: datacenterId, messageId: messageId)
+ let dataSignal: Signal = statsContext.state
+ |> map { state in
+ return state.stats
+ }
+ dataPromise.set(.single(nil) |> then(dataSignal))
+
+ let arguments = MessageStatsControllerArguments(context: context, loadDetailedGraph: { graph, x -> Signal in
+ return statsContext.loadDetailedGraph(graph, x: x)
+ }, openMessage: { messageId in
+ navigateToMessageImpl?(messageId)
+ })
+
+ let longLoadingSignal: Signal = .single(false) |> then(.single(true) |> delay(2.0, queue: Queue.mainQueue()))
+
+ let previousData = Atomic(value: nil)
+
+ let searchSignal = searchMessages(account: context.account, location: .publicForwards(messageId: messageId, datacenterId: Int(datacenterId)), query: "", state: nil)
+ |> map(Optional.init)
+ |> afterNext { result in
+ if let result = result {
+ for message in result.0.messages {
+ if let peer = message.peers[message.id.peerId], let peerReference = PeerReference(peer) {
+ let _ = updatedRemotePeer(postbox: context.account.postbox, network: context.account.network, peer: peerReference).start()
+ }
+ }
+ }
+ }
+ messagesPromise.set(.single(nil) |> then(searchSignal))
+
+ let signal = combineLatest(context.sharedContext.presentationData, dataPromise.get(), messagesPromise.get(), longLoadingSignal)
+ |> deliverOnMainQueue
+ |> map { presentationData, data, search, longLoading -> (ItemListControllerState, (ItemListNodeState, Any)) in
+ let previous = previousData.swap(data)
+ var emptyStateItem: ItemListControllerEmptyStateItem?
+ if data == nil {
+ if longLoading {
+ emptyStateItem = StatsEmptyStateItem(theme: presentationData.theme, strings: presentationData.strings)
+ } else {
+ emptyStateItem = ItemListLoadingIndicatorEmptyStateItem(theme: presentationData.theme)
+ }
+ }
+
+ let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(presentationData.strings.Stats_MessageTitle), leftNavigationButton: nil, rightNavigationButton: nil, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: true)
+ let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: messageStatsControllerEntries(data: data, messages: search?.0, presentationData: presentationData), style: .blocks, emptyStateItem: emptyStateItem, crossfadeState: previous == nil, animateChanges: false)
+
+ return (controllerState, (listState, arguments))
+ }
+ |> afterDisposed {
+ actionsDisposable.dispose()
+ let _ = statsContext.state
+ }
+
+ let controller = ItemListController(context: context, state: signal)
+ controller.contentOffsetChanged = { [weak controller] _, _ in
+ controller?.forEachItemNode({ itemNode in
+ if let itemNode = itemNode as? StatsGraphItemNode {
+ itemNode.resetInteraction()
+ }
+ })
+ }
+ controller.didDisappear = { [weak controller] _ in
+ controller?.clearItemNodesHighlight(animated: true)
+ }
+ navigateToMessageImpl = { [weak controller] messageId in
+ if let navigationController = controller?.navigationController as? NavigationController {
+ context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(messageId.peerId), subject: .message(messageId), keepStack: .always, useExisting: false, purposefulAction: {}, peekData: nil))
+ }
+ }
+ return controller
+}
diff --git a/submodules/StatisticsUI/Sources/MessageStatsOverviewItem.swift b/submodules/StatisticsUI/Sources/MessageStatsOverviewItem.swift
new file mode 100644
index 0000000000..1775d8868b
--- /dev/null
+++ b/submodules/StatisticsUI/Sources/MessageStatsOverviewItem.swift
@@ -0,0 +1,294 @@
+import Foundation
+import UIKit
+import Display
+import AsyncDisplayKit
+import SwiftSignalKit
+import TelegramCore
+import SyncCore
+import TelegramPresentationData
+import ItemListUI
+import PresentationDataUtils
+
+class MessageStatsOverviewItem: ListViewItem, ItemListItem {
+ let presentationData: ItemListPresentationData
+ let stats: MessageStats
+ let publicShares: Int32?
+ let sectionId: ItemListSectionId
+ let style: ItemListStyle
+
+ init(presentationData: ItemListPresentationData, stats: MessageStats, publicShares: Int32?, sectionId: ItemListSectionId, style: ItemListStyle) {
+ self.presentationData = presentationData
+ self.stats = stats
+ self.publicShares = publicShares
+ self.sectionId = sectionId
+ 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) {
+ async {
+ let node = MessageStatsOverviewItemNode()
+ let (layout, apply) = node.asyncLayout()(self, params, itemListNeighbors(item: self, topItem: previousItem as? ItemListItem, bottomItem: nextItem as? ItemListItem))
+
+ node.contentSize = layout.contentSize
+ node.insets = layout.insets
+
+ Queue.mainQueue().async {
+ completion(node, {
+ return (nil, { _ in apply() })
+ })
+ }
+ }
+ }
+
+ func updateNode(async: @escaping (@escaping () -> Void) -> Void, node: @escaping () -> ListViewItemNode, params: ListViewItemLayoutParams, previousItem: ListViewItem?, nextItem: ListViewItem?, animation: ListViewItemUpdateAnimation, completion: @escaping (ListViewItemNodeLayout, @escaping (ListViewItemApply) -> Void) -> Void) {
+ Queue.mainQueue().async {
+ if let nodeValue = node() as? MessageStatsOverviewItemNode {
+ let makeLayout = nodeValue.asyncLayout()
+
+ async {
+ let (layout, apply) = makeLayout(self, params, itemListNeighbors(item: self, topItem: previousItem as? ItemListItem, bottomItem: nextItem as? ItemListItem))
+ Queue.mainQueue().async {
+ completion(layout, { _ in
+ apply()
+ })
+ }
+ }
+ }
+ }
+ }
+
+ var selectable: Bool = false
+}
+
+class MessageStatsOverviewItemNode: ListViewItemNode {
+ private let backgroundNode: ASDisplayNode
+ private let topStripeNode: ASDisplayNode
+ private let bottomStripeNode: ASDisplayNode
+ private let maskNode: ASImageNode
+
+ private let leftValueLabel: ImmediateTextNode
+ private let centerValueLabel: ImmediateTextNode
+ private let rightValueLabel: ImmediateTextNode
+
+ private let leftTitleLabel: ImmediateTextNode
+ private let centerTitleLabel: ImmediateTextNode
+ private let rightTitleLabel: ImmediateTextNode
+
+ private var item: MessageStatsOverviewItem?
+
+ init() {
+ self.backgroundNode = ASDisplayNode()
+ self.backgroundNode.isLayerBacked = true
+ self.backgroundNode.backgroundColor = .white
+
+ self.topStripeNode = ASDisplayNode()
+ self.topStripeNode.isLayerBacked = true
+
+ self.bottomStripeNode = ASDisplayNode()
+ self.bottomStripeNode.isLayerBacked = true
+
+ self.maskNode = ASImageNode()
+
+ self.leftValueLabel = ImmediateTextNode()
+ self.centerValueLabel = ImmediateTextNode()
+ self.rightValueLabel = ImmediateTextNode()
+
+ self.leftTitleLabel = ImmediateTextNode()
+ self.centerTitleLabel = ImmediateTextNode()
+ self.rightTitleLabel = ImmediateTextNode()
+
+ super.init(layerBacked: false, dynamicBounce: false)
+
+ self.clipsToBounds = true
+
+ self.addSubnode(self.leftValueLabel)
+ self.addSubnode(self.centerValueLabel)
+ self.addSubnode(self.rightValueLabel)
+
+ self.addSubnode(self.leftTitleLabel)
+ self.addSubnode(self.centerTitleLabel)
+ self.addSubnode(self.rightTitleLabel)
+ }
+
+ func asyncLayout() -> (_ item: MessageStatsOverviewItem, _ params: ListViewItemLayoutParams, _ insets: ItemListNeighbors) -> (ListViewItemNodeLayout, () -> Void) {
+ let makeLeftValueLabelLayout = TextNode.asyncLayout(self.leftValueLabel)
+ let makeRightValueLabelLayout = TextNode.asyncLayout(self.rightValueLabel)
+ let makeCenterValueLabelLayout = TextNode.asyncLayout(self.centerValueLabel)
+
+ let makeLeftTitleLabelLayout = TextNode.asyncLayout(self.leftTitleLabel)
+ let makeRightTitleLabelLayout = TextNode.asyncLayout(self.rightTitleLabel)
+ let makeCenterTitleLabelLayout = TextNode.asyncLayout(self.centerTitleLabel)
+
+ let currentItem = self.item
+
+ return { item, params, neighbors in
+ let insets: UIEdgeInsets
+ let separatorHeight = UIScreenPixel
+ let itemBackgroundColor: UIColor
+ let itemSeparatorColor: UIColor
+
+ let topInset: CGFloat = 14.0
+ let sideInset: CGFloat = 16.0
+
+ var height: CGFloat = topInset * 2.0
+
+ let leftInset = params.leftInset
+ let rightInset = params.rightInset
+ var updatedTheme: PresentationTheme?
+
+ if currentItem?.presentationData.theme !== item.presentationData.theme {
+ updatedTheme = item.presentationData.theme
+ }
+
+ switch item.style {
+ case .plain:
+ itemBackgroundColor = item.presentationData.theme.list.plainBackgroundColor
+ itemSeparatorColor = item.presentationData.theme.list.itemPlainSeparatorColor
+ insets = itemListNeighborsPlainInsets(neighbors)
+ case .blocks:
+ itemBackgroundColor = item.presentationData.theme.list.itemBlocksBackgroundColor
+ itemSeparatorColor = item.presentationData.theme.list.itemBlocksSeparatorColor
+ insets = itemListNeighborsGroupedInsets(neighbors)
+ }
+
+ let valueFont = Font.semibold(item.presentationData.fontSize.itemListBaseFontSize)
+ let titleFont = Font.regular(item.presentationData.fontSize.itemListBaseHeaderFontSize)
+
+ let leftValueLabelLayoutAndApply: ((Display.TextNodeLayout, () -> Display.TextNode))?
+ let rightValueLabelLayoutAndApply: ((Display.TextNodeLayout, () -> Display.TextNode))?
+ let centerValueLabelLayoutAndApply: ((Display.TextNodeLayout, () -> Display.TextNode))?
+
+ let leftTitleLabelLayoutAndApply: ((Display.TextNodeLayout, () -> Display.TextNode))?
+ let rightTitleLabelLayoutAndApply: ((Display.TextNodeLayout, () -> Display.TextNode))?
+ let centerTitleLabelLayoutAndApply: ((Display.TextNodeLayout, () -> Display.TextNode))?
+
+ leftValueLabelLayoutAndApply = makeLeftValueLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: compactNumericCountString(item.stats.views), font: valueFont, textColor: item.presentationData.theme.list.itemPrimaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
+
+ centerValueLabelLayoutAndApply = makeCenterValueLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.publicShares.flatMap { compactNumericCountString(Int($0)) } ?? "–", font: valueFont, textColor: item.presentationData.theme.list.itemPrimaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
+
+ rightValueLabelLayoutAndApply = makeRightValueLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.publicShares.flatMap { "≈\( compactNumericCountString(item.stats.forwards - Int($0)))" } ?? "–", font: valueFont, textColor: item.presentationData.theme.list.itemPrimaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
+
+ leftTitleLabelLayoutAndApply = makeLeftTitleLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: "Views", font: titleFont, textColor: item.presentationData.theme.list.sectionHeaderTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
+
+ centerTitleLabelLayoutAndApply = makeCenterTitleLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: "Public Shares", font: titleFont, textColor: item.presentationData.theme.list.sectionHeaderTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
+
+ rightTitleLabelLayoutAndApply = makeRightTitleLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: "Private Shares", font: titleFont, textColor: item.presentationData.theme.list.sectionHeaderTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
+
+ height += rightValueLabelLayoutAndApply!.0.size.height + rightTitleLabelLayoutAndApply!.0.size.height
+
+ let contentSize = CGSize(width: params.width, height: height)
+ return (ListViewItemNodeLayout(contentSize: contentSize, insets: insets), { [weak self] in
+ if let strongSelf = self {
+ strongSelf.item = item
+
+ let _ = leftValueLabelLayoutAndApply?.1()
+ let _ = centerValueLabelLayoutAndApply?.1()
+ let _ = rightValueLabelLayoutAndApply?.1()
+ let _ = leftTitleLabelLayoutAndApply?.1()
+ let _ = centerTitleLabelLayoutAndApply?.1()
+ let _ = rightTitleLabelLayoutAndApply?.1()
+
+ if let _ = updatedTheme {
+ strongSelf.topStripeNode.backgroundColor = itemSeparatorColor
+ strongSelf.bottomStripeNode.backgroundColor = itemSeparatorColor
+ strongSelf.backgroundNode.backgroundColor = itemBackgroundColor
+ }
+
+ switch item.style {
+ case .plain:
+ if strongSelf.backgroundNode.supernode != nil {
+ strongSelf.backgroundNode.removeFromSupernode()
+ }
+ if strongSelf.topStripeNode.supernode != nil {
+ strongSelf.topStripeNode.removeFromSupernode()
+ }
+ if strongSelf.bottomStripeNode.supernode == nil {
+ strongSelf.insertSubnode(strongSelf.bottomStripeNode, at: 0)
+ }
+ if strongSelf.maskNode.supernode != nil {
+ strongSelf.maskNode.removeFromSupernode()
+ }
+ strongSelf.bottomStripeNode.frame = CGRect(origin: CGPoint(x: leftInset, y: contentSize.height - separatorHeight), size: CGSize(width: params.width - leftInset, height: separatorHeight))
+ case .blocks:
+ if strongSelf.backgroundNode.supernode == nil {
+ strongSelf.insertSubnode(strongSelf.backgroundNode, at: 0)
+ }
+ if strongSelf.topStripeNode.supernode == nil {
+ strongSelf.insertSubnode(strongSelf.topStripeNode, at: 1)
+ }
+ if strongSelf.bottomStripeNode.supernode == nil {
+ strongSelf.insertSubnode(strongSelf.bottomStripeNode, at: 2)
+ }
+ if strongSelf.maskNode.supernode == nil {
+ strongSelf.insertSubnode(strongSelf.maskNode, at: 3)
+ }
+
+ let hasCorners = itemListHasRoundedBlockLayout(params)
+ var hasTopCorners = false
+ var hasBottomCorners = false
+ switch neighbors.top {
+ case .sameSection(false):
+ strongSelf.topStripeNode.isHidden = true
+ default:
+ hasTopCorners = true
+ strongSelf.topStripeNode.isHidden = hasCorners
+ }
+ let bottomStripeInset: CGFloat
+ switch neighbors.bottom {
+ case .sameSection(false):
+ bottomStripeInset = leftInset
+ default:
+ bottomStripeInset = 0.0
+ hasBottomCorners = true
+ strongSelf.bottomStripeNode.isHidden = hasCorners
+ }
+
+ strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.presentationData.theme, top: hasTopCorners, bottom: hasBottomCorners) : nil
+
+ strongSelf.backgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: contentSize.height + min(insets.top, separatorHeight) + min(insets.bottom, separatorHeight)))
+ strongSelf.maskNode.frame = strongSelf.backgroundNode.frame.insetBy(dx: params.leftInset, dy: 0.0)
+ strongSelf.topStripeNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: separatorHeight))
+ strongSelf.bottomStripeNode.frame = CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height - separatorHeight), size: CGSize(width: params.width - bottomStripeInset, height: separatorHeight))
+ }
+
+ let maxLeftWidth = max(leftValueLabelLayoutAndApply?.0.size.width ?? 0.0, leftTitleLabelLayoutAndApply?.0.size.width ?? 0.0)
+ let maxCenterWidth = max(centerValueLabelLayoutAndApply?.0.size.width ?? 0.0, centerTitleLabelLayoutAndApply?.0.size.width ?? 0.0)
+ let maxRightWidth = max(rightValueLabelLayoutAndApply?.0.size.width ?? 0.0, rightTitleLabelLayoutAndApply?.0.size.width ?? 0.0)
+
+ let horizontalSpacing = min(60, (params.width - leftInset - rightInset - sideInset * 2.0 - maxLeftWidth - maxCenterWidth - maxRightWidth) / 2.0)
+
+ var x: CGFloat = leftInset + (params.width - maxLeftWidth - maxCenterWidth - maxRightWidth - horizontalSpacing * 2.0) / 2.0
+ if let leftValueLabelLayout = leftValueLabelLayoutAndApply?.0, let leftTitleLabelLayout = leftTitleLabelLayoutAndApply?.0 {
+ strongSelf.leftValueLabel.frame = CGRect(origin: CGPoint(x: x, y: topInset), size: leftValueLabelLayout.size)
+ strongSelf.leftTitleLabel.frame = CGRect(origin: CGPoint(x: x, y: strongSelf.leftValueLabel.frame.maxY), size: leftTitleLabelLayout.size)
+ x += max(leftValueLabelLayout.size.width, leftTitleLabelLayout.size.width) + horizontalSpacing
+ }
+
+ if let centerValueLabelLayout = centerValueLabelLayoutAndApply?.0, let centerTitleLabelLayout = centerTitleLabelLayoutAndApply?.0 {
+ strongSelf.centerValueLabel.frame = CGRect(origin: CGPoint(x: x, y: topInset), size: centerValueLabelLayout.size)
+ strongSelf.centerTitleLabel.frame = CGRect(origin: CGPoint(x: x, y: strongSelf.centerValueLabel.frame.maxY), size: centerTitleLabelLayout.size)
+ x += max(centerValueLabelLayout.size.width, centerTitleLabelLayout.size.width) + horizontalSpacing
+ }
+
+ if let rightValueLabelLayout = rightValueLabelLayoutAndApply?.0, let rightTitleLabelLayout = rightTitleLabelLayoutAndApply?.0 {
+ strongSelf.rightValueLabel.frame = CGRect(origin: CGPoint(x: x, y: topInset), size: rightValueLabelLayout.size)
+ strongSelf.rightTitleLabel.frame = CGRect(origin: CGPoint(x: x, y: strongSelf.rightValueLabel.frame.maxY), size: rightTitleLabelLayout.size)
+ }
+ }
+ })
+ }
+ }
+
+ override func animateInsertion(_ currentTimestamp: Double, duration: Double, short: Bool) {
+ self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.4)
+ }
+
+ override func animateAdded(_ currentTimestamp: Double, duration: Double) {
+ self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
+ }
+
+ override func animateRemoved(_ currentTimestamp: Double, duration: Double) {
+ self.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false)
+ }
+}
+
diff --git a/submodules/StatisticsUI/Sources/StatsEmptyItem.swift b/submodules/StatisticsUI/Sources/StatsEmptyItem.swift
index 93974e5e49..5ffd086023 100644
--- a/submodules/StatisticsUI/Sources/StatsEmptyItem.swift
+++ b/submodules/StatisticsUI/Sources/StatsEmptyItem.swift
@@ -55,7 +55,7 @@ final class StatsEmptyStateItemNode: ItemListControllerEmptyStateItemNode {
self.animationNode = AnimatedStickerNode()
if let path = getAppBundle().path(forResource: "Charts", ofType: "tgs") {
- self.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(path: path), width: 192, height: 192, playbackMode: .loop, mode: .direct)
+ self.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(path: path), width: 192, height: 192, playbackMode: .loop, mode: .direct(cachePathPrefix: nil))
self.animationNode.visibility = true
}
diff --git a/submodules/StatisticsUI/Sources/StatsMessageItem.swift b/submodules/StatisticsUI/Sources/StatsMessageItem.swift
index 4d5c8fde37..0a8b31facc 100644
--- a/submodules/StatisticsUI/Sources/StatsMessageItem.swift
+++ b/submodules/StatisticsUI/Sources/StatsMessageItem.swift
@@ -173,7 +173,7 @@ public class StatsMessageItemNode: ListViewItemNode, ItemListItemNode {
let leftInset = 16.0 + params.leftInset
let rightInset = 16.0 + params.rightInset
var totalLeftInset = leftInset
- let additionalRightInset: CGFloat = 93.0
+ let additionalRightInset: CGFloat = 128.0
let titleFont = Font.regular(item.presentationData.fontSize.itemListBaseFontSize)
@@ -229,9 +229,9 @@ public class StatsMessageItemNode: ListViewItemNode, ItemListItemNode {
let (labelLayout, labelApply) = makeLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: label, font: labelFont, textColor: item.presentationData.theme.list.itemSecondaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - totalLeftInset - rightInset - additionalRightInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
- let (viewsLayout, viewsApply) = makeViewsLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.presentationData.strings.Stats_MessageViews(item.views), font: labelFont, textColor: item.presentationData.theme.list.itemPrimaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: 100.0, height: CGFloat.greatestFiniteMagnitude), alignment: .right, cutout: nil, insets: UIEdgeInsets()))
+ let (viewsLayout, viewsApply) = makeViewsLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.presentationData.strings.Stats_MessageViews(item.views), font: labelFont, textColor: item.presentationData.theme.list.itemPrimaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: 128.0, height: CGFloat.greatestFiniteMagnitude), alignment: .right, cutout: nil, insets: UIEdgeInsets()))
- let (forwardsLayout, forwardsApply) = makeForwardsLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.presentationData.strings.Stats_MessageForwards(item.forwards), font: labelFont, textColor: item.presentationData.theme.list.itemSecondaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: 100.0, height: CGFloat.greatestFiniteMagnitude), alignment: .right, cutout: nil, insets: UIEdgeInsets()))
+ let (forwardsLayout, forwardsApply) = makeForwardsLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.presentationData.strings.Stats_MessageForwards(item.forwards), font: labelFont, textColor: item.presentationData.theme.list.itemSecondaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: 128.0, height: CGFloat.greatestFiniteMagnitude), alignment: .right, cutout: nil, insets: UIEdgeInsets()))
let verticalInset: CGFloat = 11.0
let titleSpacing: CGFloat = 3.0
diff --git a/submodules/StickerPackPreviewUI/Sources/StickerPreviewPeekContent.swift b/submodules/StickerPackPreviewUI/Sources/StickerPreviewPeekContent.swift
index f758b215c9..6af3b952f5 100644
--- a/submodules/StickerPackPreviewUI/Sources/StickerPreviewPeekContent.swift
+++ b/submodules/StickerPackPreviewUI/Sources/StickerPreviewPeekContent.swift
@@ -93,7 +93,7 @@ private final class StickerPreviewPeekContentNode: ASDisplayNode, PeekController
let dimensions = item.file.dimensions ?? PixelDimensions(width: 512, height: 512)
let fittedDimensions = dimensions.cgSize.aspectFitted(CGSize(width: 400.0, height: 400.0))
- self.animationNode?.setup(source: AnimatedStickerResourceSource(account: account, resource: item.file.resource), width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .direct)
+ self.animationNode?.setup(source: AnimatedStickerResourceSource(account: account, resource: item.file.resource), width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .direct(cachePathPrefix: nil))
self.animationNode?.visibility = true
self.animationNode?.addSubnode(self.textNode)
} else {
diff --git a/submodules/SyncCore/Sources/CachedUserData.swift b/submodules/SyncCore/Sources/CachedUserData.swift
index 4832227812..720ca4eb66 100644
--- a/submodules/SyncCore/Sources/CachedUserData.swift
+++ b/submodules/SyncCore/Sources/CachedUserData.swift
@@ -8,7 +8,8 @@ public final class CachedUserData: CachedPeerData {
public let pinnedMessageId: MessageId?
public let isBlocked: Bool
public let commonGroupCount: Int32
- public let callsAvailable: Bool
+ public let voiceCallsAvailable: Bool
+ public let videoCallsAvailable: Bool
public let callsPrivate: Bool
public let canPinMessages: Bool
public let hasScheduledMessages: Bool
@@ -24,21 +25,23 @@ public final class CachedUserData: CachedPeerData {
self.pinnedMessageId = nil
self.isBlocked = false
self.commonGroupCount = 0
- self.callsAvailable = false
+ self.voiceCallsAvailable = false
+ self.videoCallsAvailable = false
self.callsPrivate = false
self.canPinMessages = false
self.hasScheduledMessages = false
self.messageIds = Set()
}
- public init(about: String?, botInfo: BotInfo?, peerStatusSettings: PeerStatusSettings?, pinnedMessageId: MessageId?, isBlocked: Bool, commonGroupCount: Int32, callsAvailable: Bool, callsPrivate: Bool, canPinMessages: Bool, hasScheduledMessages: Bool) {
+ public init(about: String?, botInfo: BotInfo?, peerStatusSettings: PeerStatusSettings?, pinnedMessageId: MessageId?, isBlocked: Bool, commonGroupCount: Int32, voiceCallsAvailable: Bool, videoCallsAvailable: Bool, callsPrivate: Bool, canPinMessages: Bool, hasScheduledMessages: Bool) {
self.about = about
self.botInfo = botInfo
self.peerStatusSettings = peerStatusSettings
self.pinnedMessageId = pinnedMessageId
self.isBlocked = isBlocked
self.commonGroupCount = commonGroupCount
- self.callsAvailable = callsAvailable
+ self.voiceCallsAvailable = voiceCallsAvailable
+ self.videoCallsAvailable = videoCallsAvailable
self.callsPrivate = callsPrivate
self.canPinMessages = canPinMessages
self.hasScheduledMessages = hasScheduledMessages
@@ -66,7 +69,8 @@ public final class CachedUserData: CachedPeerData {
}
self.isBlocked = decoder.decodeInt32ForKey("b", orElse: 0) != 0
self.commonGroupCount = decoder.decodeInt32ForKey("cg", orElse: 0)
- self.callsAvailable = decoder.decodeInt32ForKey("ca", orElse: 0) != 0
+ self.voiceCallsAvailable = decoder.decodeInt32ForKey("ca", orElse: 0) != 0
+ self.videoCallsAvailable = decoder.decodeInt32ForKey("vca", orElse: 0) != 0
self.callsPrivate = decoder.decodeInt32ForKey("cp", orElse: 0) != 0
self.canPinMessages = decoder.decodeInt32ForKey("cpm", orElse: 0) != 0
self.hasScheduledMessages = decoder.decodeBoolForKey("hsm", orElse: false)
@@ -105,7 +109,8 @@ public final class CachedUserData: CachedPeerData {
}
encoder.encodeInt32(self.isBlocked ? 1 : 0, forKey: "b")
encoder.encodeInt32(self.commonGroupCount, forKey: "cg")
- encoder.encodeInt32(self.callsAvailable ? 1 : 0, forKey: "ca")
+ encoder.encodeInt32(self.voiceCallsAvailable ? 1 : 0, forKey: "ca")
+ encoder.encodeInt32(self.videoCallsAvailable ? 1 : 0, forKey: "vca")
encoder.encodeInt32(self.callsPrivate ? 1 : 0, forKey: "cp")
encoder.encodeInt32(self.canPinMessages ? 1 : 0, forKey: "cpm")
encoder.encodeBool(self.hasScheduledMessages, forKey: "hsm")
@@ -123,46 +128,50 @@ public final class CachedUserData: CachedPeerData {
return false
}
- return other.about == self.about && other.botInfo == self.botInfo && self.peerStatusSettings == other.peerStatusSettings && self.isBlocked == other.isBlocked && self.commonGroupCount == other.commonGroupCount && self.callsAvailable == other.callsAvailable && self.callsPrivate == other.callsPrivate && self.hasScheduledMessages == other.hasScheduledMessages
+ return other.about == self.about && other.botInfo == self.botInfo && self.peerStatusSettings == other.peerStatusSettings && self.isBlocked == other.isBlocked && self.commonGroupCount == other.commonGroupCount && self.voiceCallsAvailable == other.voiceCallsAvailable && self.videoCallsAvailable == other.videoCallsAvailable && self.callsPrivate == other.callsPrivate && self.hasScheduledMessages == other.hasScheduledMessages
}
public func withUpdatedAbout(_ about: String?) -> CachedUserData {
- return CachedUserData(about: about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages)
+ return CachedUserData(about: about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages)
}
public func withUpdatedBotInfo(_ botInfo: BotInfo?) -> CachedUserData {
- return CachedUserData(about: self.about, botInfo: botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages)
+ return CachedUserData(about: self.about, botInfo: botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages)
}
public func withUpdatedPeerStatusSettings(_ peerStatusSettings: PeerStatusSettings) -> CachedUserData {
- return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages)
+ return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages)
}
public func withUpdatedPinnedMessageId(_ pinnedMessageId: MessageId?) -> CachedUserData {
- return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages)
+ return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages)
}
public func withUpdatedIsBlocked(_ isBlocked: Bool) -> CachedUserData {
- return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: isBlocked, commonGroupCount: self.commonGroupCount, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages)
+ return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages)
}
public func withUpdatedCommonGroupCount(_ commonGroupCount: Int32) -> CachedUserData {
- return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: commonGroupCount, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages)
+ return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages)
}
- public func withUpdatedCallsAvailable(_ callsAvailable: Bool) -> CachedUserData {
- return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, callsAvailable: callsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages)
+ public func withUpdatedVoiceCallsAvailable(_ voiceCallsAvailable: Bool) -> CachedUserData {
+ return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages)
+ }
+
+ public func withUpdatedVideoCallsAvailable(_ videoCallsAvailable: Bool) -> CachedUserData {
+ return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages)
}
public func withUpdatedCallsPrivate(_ callsPrivate: Bool) -> CachedUserData {
- return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, callsAvailable: self.callsAvailable, callsPrivate: callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages)
+ return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: self.hasScheduledMessages)
}
public func withUpdatedCanPinMessages(_ canPinMessages: Bool) -> CachedUserData {
- return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, canPinMessages: canPinMessages, hasScheduledMessages: self.hasScheduledMessages)
+ return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: canPinMessages, hasScheduledMessages: self.hasScheduledMessages)
}
public func withUpdatedHasScheduledMessages(_ hasScheduledMessages: Bool) -> CachedUserData {
- return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: hasScheduledMessages)
+ return CachedUserData(about: self.about, botInfo: self.botInfo, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, isBlocked: self.isBlocked, commonGroupCount: self.commonGroupCount, voiceCallsAvailable: self.voiceCallsAvailable, videoCallsAvailable: self.videoCallsAvailable, callsPrivate: self.callsPrivate, canPinMessages: self.canPinMessages, hasScheduledMessages: hasScheduledMessages)
}
}
diff --git a/submodules/SyncCore/Sources/ForwardCountMessageAttribute.swift b/submodules/SyncCore/Sources/ForwardCountMessageAttribute.swift
new file mode 100644
index 0000000000..ac1953ac4c
--- /dev/null
+++ b/submodules/SyncCore/Sources/ForwardCountMessageAttribute.swift
@@ -0,0 +1,20 @@
+import Foundation
+import Postbox
+
+public class ForwardCountMessageAttribute: MessageAttribute {
+ public let count: Int
+
+ public var associatedMessageIds: [MessageId] = []
+
+ public init(count: Int) {
+ self.count = count
+ }
+
+ required public init(decoder: PostboxDecoder) {
+ self.count = Int(decoder.decodeInt32ForKey("c", orElse: 0))
+ }
+
+ public func encode(_ encoder: PostboxEncoder) {
+ encoder.encodeInt32(Int32(self.count), forKey: "c")
+ }
+}
diff --git a/submodules/SyncCore/Sources/TelegramMediaImage.swift b/submodules/SyncCore/Sources/TelegramMediaImage.swift
index 273a4d2e89..644a023398 100644
--- a/submodules/SyncCore/Sources/TelegramMediaImage.swift
+++ b/submodules/SyncCore/Sources/TelegramMediaImage.swift
@@ -303,21 +303,25 @@ public final class TelegramMediaImage: Media, Equatable, Codable {
public final class TelegramMediaImageRepresentation: PostboxCoding, Equatable, CustomStringConvertible {
public let dimensions: PixelDimensions
public let resource: TelegramMediaResource
+ public let progressiveSizes: [Int32]
- public init(dimensions: PixelDimensions, resource: TelegramMediaResource) {
+ public init(dimensions: PixelDimensions, resource: TelegramMediaResource, progressiveSizes: [Int32]) {
self.dimensions = dimensions
self.resource = resource
+ self.progressiveSizes = progressiveSizes
}
public init(decoder: PostboxDecoder) {
self.dimensions = PixelDimensions(width: decoder.decodeInt32ForKey("dx", orElse: 0), height: decoder.decodeInt32ForKey("dy", orElse: 0))
self.resource = decoder.decodeObjectForKey("r") as? TelegramMediaResource ?? EmptyMediaResource()
+ self.progressiveSizes = decoder.decodeInt32ArrayForKey("ps")
}
public func encode(_ encoder: PostboxEncoder) {
encoder.encodeInt32(self.dimensions.width, forKey: "dx")
encoder.encodeInt32(self.dimensions.height, forKey: "dy")
encoder.encodeObject(self.resource, forKey: "r")
+ encoder.encodeInt32Array(self.progressiveSizes, forKey: "ps")
}
public var description: String {
@@ -331,6 +335,9 @@ public final class TelegramMediaImageRepresentation: PostboxCoding, Equatable, C
if !self.resource.id.isEqual(to: other.resource.id) {
return false
}
+ if self.progressiveSizes != other.progressiveSizes {
+ return false
+ }
return true
}
}
diff --git a/submodules/SyncCore/Sources/ViewCountMessageAttribute.swift b/submodules/SyncCore/Sources/ViewCountMessageAttribute.swift
index 100ecee759..f10fdb3f72 100644
--- a/submodules/SyncCore/Sources/ViewCountMessageAttribute.swift
+++ b/submodules/SyncCore/Sources/ViewCountMessageAttribute.swift
@@ -1,7 +1,6 @@
import Foundation
import Postbox
-
public class ViewCountMessageAttribute: MessageAttribute {
public let count: Int
diff --git a/submodules/TelegramApi/Sources/Api0.swift b/submodules/TelegramApi/Sources/Api0.swift
index 734280d303..f36922f701 100644
--- a/submodules/TelegramApi/Sources/Api0.swift
+++ b/submodules/TelegramApi/Sources/Api0.swift
@@ -254,6 +254,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-1512627963] = { return Api.Update.parse_updateDialogFilterOrder($0) }
dict[889491791] = { return Api.Update.parse_updateDialogFilters($0) }
dict[643940105] = { return Api.Update.parse_updatePhoneCallSignalingData($0) }
+ dict[1708307556] = { return Api.Update.parse_updateChannelParticipant($0) }
dict[136574537] = { return Api.messages.VotesList.parse_votesList($0) }
dict[1558266229] = { return Api.PopularContact.parse_popularContact($0) }
dict[-373643672] = { return Api.FolderPeer.parse_folderPeer($0) }
@@ -290,6 +291,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[2009052699] = { return Api.PhotoSize.parse_photoSize($0) }
dict[-374917894] = { return Api.PhotoSize.parse_photoCachedSize($0) }
dict[-525288402] = { return Api.PhotoSize.parse_photoStrippedSize($0) }
+ dict[1520986705] = { return Api.PhotoSize.parse_photoSizeProgressive($0) }
dict[-244016606] = { return Api.messages.Stickers.parse_stickersNotModified($0) }
dict[-463889475] = { return Api.messages.Stickers.parse_stickers($0) }
dict[-1096616924] = { return Api.GlobalPrivacySettings.parse_globalPrivacySettings($0) }
@@ -703,6 +705,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-395967805] = { return Api.messages.AllStickers.parse_allStickersNotModified($0) }
dict[-302170017] = { return Api.messages.AllStickers.parse_allStickers($0) }
dict[-1655957568] = { return Api.PhoneConnection.parse_phoneConnection($0) }
+ dict[1667228533] = { return Api.PhoneConnection.parse_phoneConnectionWebrtc($0) }
dict[-206688531] = { return Api.help.UserInfo.parse_userInfoEmpty($0) }
dict[32192344] = { return Api.help.UserInfo.parse_userInfo($0) }
dict[-1194283041] = { return Api.AccountDaysTTL.parse_accountDaysTTL($0) }
diff --git a/submodules/TelegramApi/Sources/Api1.swift b/submodules/TelegramApi/Sources/Api1.swift
index 779642624a..025d00a4aa 100644
--- a/submodules/TelegramApi/Sources/Api1.swift
+++ b/submodules/TelegramApi/Sources/Api1.swift
@@ -6037,6 +6037,7 @@ public extension Api {
case updateDialogFilterOrder(order: [Int32])
case updateDialogFilters
case updatePhoneCallSignalingData(phoneCallId: Int64, data: Buffer)
+ case updateChannelParticipant(flags: Int32, channelId: Int32, date: Int32, userId: Int32, prevParticipant: Api.ChannelParticipant?, newParticipant: Api.ChannelParticipant?, qts: Int32)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
@@ -6717,6 +6718,18 @@ public extension Api {
serializeInt64(phoneCallId, buffer: buffer, boxed: false)
serializeBytes(data, buffer: buffer, boxed: false)
break
+ case .updateChannelParticipant(let flags, let channelId, let date, let userId, let prevParticipant, let newParticipant, let qts):
+ if boxed {
+ buffer.appendInt32(1708307556)
+ }
+ serializeInt32(flags, buffer: buffer, boxed: false)
+ serializeInt32(channelId, buffer: buffer, boxed: false)
+ serializeInt32(date, buffer: buffer, boxed: false)
+ serializeInt32(userId, buffer: buffer, boxed: false)
+ if Int(flags) & Int(1 << 0) != 0 {prevParticipant!.serialize(buffer, true)}
+ if Int(flags) & Int(1 << 1) != 0 {newParticipant!.serialize(buffer, true)}
+ serializeInt32(qts, buffer: buffer, boxed: false)
+ break
}
}
@@ -6884,6 +6897,8 @@ public extension Api {
return ("updateDialogFilters", [])
case .updatePhoneCallSignalingData(let phoneCallId, let data):
return ("updatePhoneCallSignalingData", [("phoneCallId", phoneCallId), ("data", data)])
+ case .updateChannelParticipant(let flags, let channelId, let date, let userId, let prevParticipant, let newParticipant, let qts):
+ return ("updateChannelParticipant", [("flags", flags), ("channelId", channelId), ("date", date), ("userId", userId), ("prevParticipant", prevParticipant), ("newParticipant", newParticipant), ("qts", qts)])
}
}
@@ -8229,6 +8244,39 @@ public extension Api {
return nil
}
}
+ public static func parse_updateChannelParticipant(_ reader: BufferReader) -> Update? {
+ var _1: Int32?
+ _1 = reader.readInt32()
+ var _2: Int32?
+ _2 = reader.readInt32()
+ var _3: Int32?
+ _3 = reader.readInt32()
+ var _4: Int32?
+ _4 = reader.readInt32()
+ var _5: Api.ChannelParticipant?
+ if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() {
+ _5 = Api.parse(reader, signature: signature) as? Api.ChannelParticipant
+ } }
+ var _6: Api.ChannelParticipant?
+ if Int(_1!) & Int(1 << 1) != 0 {if let signature = reader.readInt32() {
+ _6 = Api.parse(reader, signature: signature) as? Api.ChannelParticipant
+ } }
+ var _7: Int32?
+ _7 = reader.readInt32()
+ let _c1 = _1 != nil
+ let _c2 = _2 != nil
+ let _c3 = _3 != nil
+ let _c4 = _4 != nil
+ let _c5 = (Int(_1!) & Int(1 << 0) == 0) || _5 != nil
+ let _c6 = (Int(_1!) & Int(1 << 1) == 0) || _6 != nil
+ let _c7 = _7 != nil
+ if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 {
+ return Api.Update.updateChannelParticipant(flags: _1!, channelId: _2!, date: _3!, userId: _4!, prevParticipant: _5, newParticipant: _6, qts: _7!)
+ }
+ else {
+ return nil
+ }
+ }
}
public enum PopularContact: TypeConstructorDescription {
@@ -9090,6 +9138,7 @@ public extension Api {
case photoSize(type: String, location: Api.FileLocation, w: Int32, h: Int32, size: Int32)
case photoCachedSize(type: String, location: Api.FileLocation, w: Int32, h: Int32, bytes: Buffer)
case photoStrippedSize(type: String, bytes: Buffer)
+ case photoSizeProgressive(type: String, location: Api.FileLocation, w: Int32, h: Int32, sizes: [Int32])
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
@@ -9126,6 +9175,20 @@ public extension Api {
serializeString(type, buffer: buffer, boxed: false)
serializeBytes(bytes, buffer: buffer, boxed: false)
break
+ case .photoSizeProgressive(let type, let location, let w, let h, let sizes):
+ if boxed {
+ buffer.appendInt32(1520986705)
+ }
+ serializeString(type, buffer: buffer, boxed: false)
+ location.serialize(buffer, true)
+ serializeInt32(w, buffer: buffer, boxed: false)
+ serializeInt32(h, buffer: buffer, boxed: false)
+ buffer.appendInt32(481674261)
+ buffer.appendInt32(Int32(sizes.count))
+ for item in sizes {
+ serializeInt32(item, buffer: buffer, boxed: false)
+ }
+ break
}
}
@@ -9139,6 +9202,8 @@ public extension Api {
return ("photoCachedSize", [("type", type), ("location", location), ("w", w), ("h", h), ("bytes", bytes)])
case .photoStrippedSize(let type, let bytes):
return ("photoStrippedSize", [("type", type), ("bytes", bytes)])
+ case .photoSizeProgressive(let type, let location, let w, let h, let sizes):
+ return ("photoSizeProgressive", [("type", type), ("location", location), ("w", w), ("h", h), ("sizes", sizes)])
}
}
@@ -9217,6 +9282,33 @@ public extension Api {
return nil
}
}
+ public static func parse_photoSizeProgressive(_ reader: BufferReader) -> PhotoSize? {
+ var _1: String?
+ _1 = parseString(reader)
+ var _2: Api.FileLocation?
+ if let signature = reader.readInt32() {
+ _2 = Api.parse(reader, signature: signature) as? Api.FileLocation
+ }
+ var _3: Int32?
+ _3 = reader.readInt32()
+ var _4: Int32?
+ _4 = reader.readInt32()
+ var _5: [Int32]?
+ if let _ = reader.readInt32() {
+ _5 = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self)
+ }
+ let _c1 = _1 != nil
+ let _c2 = _2 != nil
+ let _c3 = _3 != nil
+ let _c4 = _4 != nil
+ let _c5 = _5 != nil
+ if _c1 && _c2 && _c3 && _c4 && _c5 {
+ return Api.PhotoSize.photoSizeProgressive(type: _1!, location: _2!, w: _3!, h: _4!, sizes: _5!)
+ }
+ else {
+ return nil
+ }
+ }
}
public enum GlobalPrivacySettings: TypeConstructorDescription {
@@ -19749,6 +19841,7 @@ public extension Api {
}
public enum PhoneConnection: TypeConstructorDescription {
case phoneConnection(id: Int64, ip: String, ipv6: String, port: Int32, peerTag: Buffer)
+ case phoneConnectionWebrtc(flags: Int32, id: Int64, ip: String, ipv6: String, port: Int32, username: String, password: String)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
@@ -19762,6 +19855,18 @@ public extension Api {
serializeInt32(port, buffer: buffer, boxed: false)
serializeBytes(peerTag, buffer: buffer, boxed: false)
break
+ case .phoneConnectionWebrtc(let flags, let id, let ip, let ipv6, let port, let username, let password):
+ if boxed {
+ buffer.appendInt32(1667228533)
+ }
+ serializeInt32(flags, buffer: buffer, boxed: false)
+ serializeInt64(id, buffer: buffer, boxed: false)
+ serializeString(ip, buffer: buffer, boxed: false)
+ serializeString(ipv6, buffer: buffer, boxed: false)
+ serializeInt32(port, buffer: buffer, boxed: false)
+ serializeString(username, buffer: buffer, boxed: false)
+ serializeString(password, buffer: buffer, boxed: false)
+ break
}
}
@@ -19769,6 +19874,8 @@ public extension Api {
switch self {
case .phoneConnection(let id, let ip, let ipv6, let port, let peerTag):
return ("phoneConnection", [("id", id), ("ip", ip), ("ipv6", ipv6), ("port", port), ("peerTag", peerTag)])
+ case .phoneConnectionWebrtc(let flags, let id, let ip, let ipv6, let port, let username, let password):
+ return ("phoneConnectionWebrtc", [("flags", flags), ("id", id), ("ip", ip), ("ipv6", ipv6), ("port", port), ("username", username), ("password", password)])
}
}
@@ -19795,6 +19902,35 @@ public extension Api {
return nil
}
}
+ public static func parse_phoneConnectionWebrtc(_ reader: BufferReader) -> PhoneConnection? {
+ var _1: Int32?
+ _1 = reader.readInt32()
+ var _2: Int64?
+ _2 = reader.readInt64()
+ var _3: String?
+ _3 = parseString(reader)
+ var _4: String?
+ _4 = parseString(reader)
+ var _5: Int32?
+ _5 = reader.readInt32()
+ var _6: String?
+ _6 = parseString(reader)
+ var _7: String?
+ _7 = parseString(reader)
+ let _c1 = _1 != nil
+ let _c2 = _2 != nil
+ let _c3 = _3 != nil
+ let _c4 = _4 != nil
+ let _c5 = _5 != nil
+ let _c6 = _6 != nil
+ let _c7 = _7 != nil
+ if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 {
+ return Api.PhoneConnection.phoneConnectionWebrtc(flags: _1!, id: _2!, ip: _3!, ipv6: _4!, port: _5!, username: _6!, password: _7!)
+ }
+ else {
+ return nil
+ }
+ }
}
public enum AccountDaysTTL: TypeConstructorDescription {
diff --git a/submodules/TelegramApi/Sources/Api3.swift b/submodules/TelegramApi/Sources/Api3.swift
index d4e63b4654..c79744cf01 100644
--- a/submodules/TelegramApi/Sources/Api3.swift
+++ b/submodules/TelegramApi/Sources/Api3.swift
@@ -5369,6 +5369,34 @@ public extension Api {
})
}
+ public static func getPromoData() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) {
+ let buffer = Buffer()
+ buffer.appendInt32(-1063816159)
+
+ return (FunctionDescription(name: "help.getPromoData", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.help.PromoData? in
+ let reader = BufferReader(buffer)
+ var result: Api.help.PromoData?
+ if let signature = reader.readInt32() {
+ result = Api.parse(reader, signature: signature) as? Api.help.PromoData
+ }
+ return result
+ })
+ }
+
+ public static func hidePromoData(peer: Api.InputPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) {
+ let buffer = Buffer()
+ buffer.appendInt32(505748629)
+ peer.serialize(buffer, true)
+ return (FunctionDescription(name: "help.hidePromoData", parameters: [("peer", peer)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in
+ let reader = BufferReader(buffer)
+ var result: Api.Bool?
+ if let signature = reader.readInt32() {
+ result = Api.parse(reader, signature: signature) as? Api.Bool
+ }
+ return result
+ })
+ }
+
public static func editUserInfo(userId: Api.InputUser, message: String, entities: [Api.MessageEntity]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) {
let buffer = Buffer()
buffer.appendInt32(1723407216)
@@ -5403,34 +5431,6 @@ public extension Api {
})
}
- public static func getPromoData() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) {
- let buffer = Buffer()
- buffer.appendInt32(-1063816159)
-
- return (FunctionDescription(name: "help.getPromoData", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.help.PromoData? in
- let reader = BufferReader(buffer)
- var result: Api.help.PromoData?
- if let signature = reader.readInt32() {
- result = Api.parse(reader, signature: signature) as? Api.help.PromoData
- }
- return result
- })
- }
-
- public static func hidePromoData(peer: Api.InputPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) {
- let buffer = Buffer()
- buffer.appendInt32(505748629)
- peer.serialize(buffer, true)
- return (FunctionDescription(name: "help.hidePromoData", parameters: [("peer", peer)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in
- let reader = BufferReader(buffer)
- var result: Api.Bool?
- if let signature = reader.readInt32() {
- result = Api.parse(reader, signature: signature) as? Api.Bool
- }
- return result
- })
- }
-
public static func dismissSuggestion(suggestion: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) {
let buffer = Buffer()
buffer.appendInt32(125807007)
@@ -6767,20 +6767,6 @@ public extension Api {
}
}
public struct photos {
- public static func updateProfilePhoto(id: Api.InputPhoto) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) {
- let buffer = Buffer()
- buffer.appendInt32(-256159406)
- id.serialize(buffer, true)
- return (FunctionDescription(name: "photos.updateProfilePhoto", parameters: [("id", id)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.UserProfilePhoto? in
- let reader = BufferReader(buffer)
- var result: Api.UserProfilePhoto?
- if let signature = reader.readInt32() {
- result = Api.parse(reader, signature: signature) as? Api.UserProfilePhoto
- }
- return result
- })
- }
-
public static func deletePhotos(id: [Api.InputPhoto]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<[Int64]>) {
let buffer = Buffer()
buffer.appendInt32(-2016444625)
@@ -6816,6 +6802,20 @@ public extension Api {
})
}
+ public static func updateProfilePhoto(id: Api.InputPhoto) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) {
+ let buffer = Buffer()
+ buffer.appendInt32(1926525996)
+ id.serialize(buffer, true)
+ return (FunctionDescription(name: "photos.updateProfilePhoto", parameters: [("id", id)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.photos.Photo? in
+ let reader = BufferReader(buffer)
+ var result: Api.photos.Photo?
+ if let signature = reader.readInt32() {
+ result = Api.parse(reader, signature: signature) as? Api.photos.Photo
+ }
+ return result
+ })
+ }
+
public static func uploadProfilePhoto(flags: Int32, file: Api.InputFile?, video: Api.InputFile?, videoStartTs: Double?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) {
let buffer = Buffer()
buffer.appendInt32(-1980559511)
diff --git a/submodules/TelegramAudio/Sources/ManagedAudioSession.swift b/submodules/TelegramAudio/Sources/ManagedAudioSession.swift
index 058077e14e..a1fcb3c8b8 100644
--- a/submodules/TelegramAudio/Sources/ManagedAudioSession.swift
+++ b/submodules/TelegramAudio/Sources/ManagedAudioSession.swift
@@ -8,6 +8,7 @@ public enum ManagedAudioSessionType: Equatable {
case playWithPossiblePortOverride
case record(speaker: Bool)
case voiceCall
+ case videoCall
var isPlay: Bool {
switch self {
@@ -23,7 +24,7 @@ private func nativeCategoryForType(_ type: ManagedAudioSessionType, headphones:
switch type {
case .play:
return .playback
- case .record, .voiceCall:
+ case .record, .voiceCall, .videoCall:
return .playAndRecord
case .playWithPossiblePortOverride:
if headphones {
@@ -244,6 +245,22 @@ public final class ManagedAudioSession {
if let availableInputs = audioSession.availableInputs {
var hasHeadphones = false
+ var hasBluetoothHeadphones = false
+
+ var headphonesAreActive = false
+ loop: for currentOutput in audioSession.currentRoute.outputs {
+ switch currentOutput.portType {
+ case .headphones, .bluetoothA2DP, .bluetoothHFP:
+ headphonesAreActive = true
+ hasHeadphones = true
+ hasBluetoothHeadphones = [.bluetoothA2DP, .bluetoothHFP].contains(currentOutput.portType)
+ activeOutput = .headphones
+ break loop
+ default:
+ break
+ }
+ }
+
for input in availableInputs {
var isActive = false
for currentInput in audioSession.currentRoute.inputs {
@@ -253,7 +270,7 @@ public final class ManagedAudioSession {
}
if input.portType == .builtInMic {
- if isActive {
+ if isActive && !headphonesAreActive {
activeOutput = .builtin
inner: for currentOutput in audioSession.currentRoute.outputs {
if currentOutput.portType == .builtInSpeaker {
@@ -282,7 +299,7 @@ public final class ManagedAudioSession {
availableOutputs.insert(.speaker, at: 0)
}
- if hasHeadphones {
+ if hasHeadphones && !hasBluetoothHeadphones {
availableOutputs.insert(.headphones, at: 0)
}
availableOutputs.insert(.builtin, at: 0)
@@ -658,15 +675,24 @@ public final class ManagedAudioSession {
options.insert(.allowBluetooth)
}
}
- case .record, .voiceCall:
+ case .record, .voiceCall, .videoCall:
options.insert(.allowBluetooth)
}
print("ManagedAudioSession setting active true")
+ let mode: AVAudioSession.Mode
+ switch type {
+ case .voiceCall:
+ mode = .voiceChat
+ case .videoCall:
+ mode = .videoChat
+ default:
+ mode = .default
+ }
if #available(iOSApplicationExtension 11.0, iOS 11.0, *) {
- try AVAudioSession.sharedInstance().setCategory(nativeCategory, mode: type == .voiceCall ? .voiceChat : .default, policy: .default, options: options)
+ try AVAudioSession.sharedInstance().setCategory(nativeCategory, mode: mode, policy: .default, options: options)
} else {
AVAudioSession.sharedInstance().perform(NSSelectorFromString("setCategory:error:"), with: nativeCategory)
- try AVAudioSession.sharedInstance().setMode(type == .voiceCall ? .voiceChat : .default)
+ try AVAudioSession.sharedInstance().setMode(mode)
}
} catch let error {
print("ManagedAudioSession setup error \(error)")
@@ -739,13 +765,28 @@ public final class ManagedAudioSession {
case .voiceCall, .playWithPossiblePortOverride, .record(true):
try AVAudioSession.sharedInstance().overrideOutputAudioPort(.none)
if let routes = AVAudioSession.sharedInstance().availableInputs {
- for route in routes {
- if route.portType == .builtInMic {
- if case .record = updatedType, self.isHeadsetPluggedInValue {
- } else {
+ var alreadySet = false
+ if self.isHeadsetPluggedInValue {
+ loop: for route in routes {
+ switch route.portType {
+ case .headphones, .bluetoothA2DP, .bluetoothHFP:
let _ = try? AVAudioSession.sharedInstance().setPreferredInput(route)
+ alreadySet = true
+ break loop
+ default:
+ break
+ }
+ }
+ }
+ if !alreadySet {
+ for route in routes {
+ if route.portType == .builtInMic {
+ if case .record = updatedType, self.isHeadsetPluggedInValue {
+ } else {
+ let _ = try? AVAudioSession.sharedInstance().setPreferredInput(route)
+ }
+ break
}
- break
}
}
}
diff --git a/submodules/TelegramBaseController/Sources/MediaNavigationAccessoryHeaderNode.swift b/submodules/TelegramBaseController/Sources/MediaNavigationAccessoryHeaderNode.swift
index 78dca72176..13364d4dfd 100644
--- a/submodules/TelegramBaseController/Sources/MediaNavigationAccessoryHeaderNode.swift
+++ b/submodules/TelegramBaseController/Sources/MediaNavigationAccessoryHeaderNode.swift
@@ -331,7 +331,7 @@ public final class MediaNavigationAccessoryHeaderNode: ASDisplayNode, UIScrollVi
switch status {
case .paused:
paused = true
- case let .buffering(_, whilePlaying):
+ case let .buffering(_, whilePlaying, _):
paused = !whilePlaying
case .playing:
paused = false
diff --git a/submodules/TelegramCallsUI/BUCK b/submodules/TelegramCallsUI/BUCK
index afd94c2abb..618ee191c6 100644
--- a/submodules/TelegramCallsUI/BUCK
+++ b/submodules/TelegramCallsUI/BUCK
@@ -7,7 +7,7 @@ static_library(
]),
deps = [
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit#shared",
- "//submodules/Display:Display#shared",
+ "//submodules/Display:Display#shared",
"//submodules/TelegramPresentationData:TelegramPresentationData",
"//submodules/TelegramUIPreferences:TelegramUIPreferences",
"//submodules/PhotoResources:PhotoResources",
@@ -21,6 +21,8 @@ static_library(
"//submodules/AppBundle:AppBundle",
"//submodules/PresentationDataUtils:PresentationDataUtils",
"//submodules/TelegramCallsUI/CallsEmoji:CallsEmoji",
+ "//submodules/SemanticStatusNode:SemanticStatusNode",
+ "//submodules/TooltipUI:TooltipUI",
],
frameworks = [
"$SDKROOT/System/Library/Frameworks/Foundation.framework",
diff --git a/submodules/TelegramCallsUI/BUILD b/submodules/TelegramCallsUI/BUILD
index fd4fdc9f2f..3e6639c620 100644
--- a/submodules/TelegramCallsUI/BUILD
+++ b/submodules/TelegramCallsUI/BUILD
@@ -22,6 +22,8 @@ swift_library(
"//submodules/AppBundle:AppBundle",
"//submodules/PresentationDataUtils:PresentationDataUtils",
"//submodules/TelegramCallsUI/CallsEmoji:CallsEmoji",
+ "//submodules/SemanticStatusNode:SemanticStatusNode",
+ "//submodules/TooltipUI:TooltipUI",
],
visibility = [
"//visibility:public",
diff --git a/submodules/TelegramCallsUI/CallsEmoji/PublicHeaders/CallsEmoji/CallsEmoji.h b/submodules/TelegramCallsUI/CallsEmoji/PublicHeaders/CallsEmoji/CallsEmoji.h
index d46ac50e5d..d4ba405743 100644
--- a/submodules/TelegramCallsUI/CallsEmoji/PublicHeaders/CallsEmoji/CallsEmoji.h
+++ b/submodules/TelegramCallsUI/CallsEmoji/PublicHeaders/CallsEmoji/CallsEmoji.h
@@ -3,6 +3,7 @@
#import
+NSString *randomCallsEmoji();
NSString *stringForEmojiHashOfData(NSData *data, NSInteger count);
#endif /* CallsEmoji_h */
diff --git a/submodules/TelegramCallsUI/CallsEmoji/Sources/CallsEmoji.m b/submodules/TelegramCallsUI/CallsEmoji/Sources/CallsEmoji.m
index b196e5d94e..5f5f29ac44 100644
--- a/submodules/TelegramCallsUI/CallsEmoji/Sources/CallsEmoji.m
+++ b/submodules/TelegramCallsUI/CallsEmoji/Sources/CallsEmoji.m
@@ -7,15 +7,23 @@ static int32_t positionExtractor(uint8_t *bytes, int32_t i, int32_t count) {
return num % count;
}
+NSArray *emojisArray() {
+ return @[ @"😉", @"😍", @"😛", @"😭", @"😱", @"😡", @"😎", @"😴", @"😵", @"😈", @"😬", @"😇", @"😏", @"👮", @"👷", @"💂", @"👶", @"👨", @"👩", @"👴", @"👵", @"😻", @"😽", @"🙀", @"👺", @"🙈", @"🙉", @"🙊", @"💀", @"👽", @"💩", @"🔥", @"💥", @"💤", @"👂", @"👀", @"👃", @"👅", @"👄", @"👍", @"👎", @"👌", @"👊", @"✌️", @"✋️", @"👐", @"👆", @"👇", @"👉", @"👈", @"🙏", @"👏", @"💪", @"🚶", @"🏃", @"💃", @"👫", @"👪", @"👬", @"👭", @"💅", @"🎩", @"👑", @"👒", @"👟", @"👞", @"👠", @"👕", @"👗", @"👖", @"👙", @"👜", @"👓", @"🎀", @"💄", @"💛", @"💙", @"💜", @"💚", @"💍", @"💎", @"🐶", @"🐺", @"🐱", @"🐭", @"🐹", @"🐰", @"🐸", @"🐯", @"🐨", @"🐻", @"🐷", @"🐮", @"🐗", @"🐴", @"🐑", @"🐘", @"🐼", @"🐧", @"🐥", @"🐔", @"🐍", @"🐢", @"🐛", @"🐝", @"🐜", @"🐞", @"🐌", @"🐙", @"🐚", @"🐟", @"🐬", @"🐋", @"🐐", @"🐊", @"🐫", @"🍀", @"🌹", @"🌻", @"🍁", @"🌾", @"🍄", @"🌵", @"🌴", @"🌳", @"🌞", @"🌚", @"🌙", @"🌎", @"🌋", @"⚡️", @"☔️", @"❄️", @"⛄️", @"🌀", @"🌈", @"🌊", @"🎓", @"🎆", @"🎃", @"👻", @"🎅", @"🎄", @"🎁", @"🎈", @"🔮", @"🎥", @"📷", @"💿", @"💻", @"☎️", @"📡", @"📺", @"📻", @"🔉", @"🔔", @"⏳", @"⏰", @"⌚️", @"🔒", @"🔑", @"🔎", @"💡", @"🔦", @"🔌", @"🔋", @"🚿", @"🚽", @"🔧", @"🔨", @"🚪", @"🚬", @"💣", @"🔫", @"🔪", @"💊", @"💉", @"💰", @"💵", @"💳", @"✉️", @"📫", @"📦", @"📅", @"📁", @"✂️", @"📌", @"📎", @"✒️", @"✏️", @"📐", @"📚", @"🔬", @"🔭", @"🎨", @"🎬", @"🎤", @"🎧", @"🎵", @"🎹", @"🎻", @"🎺", @"🎸", @"👾", @"🎮", @"🃏", @"🎲", @"🎯", @"🏈", @"🏀", @"⚽️", @"⚾️", @"🎾", @"🎱", @"🏉", @"🎳", @"🏁", @"🏇", @"🏆", @"🏊", @"🏄", @"☕️", @"🍼", @"🍺", @"🍷", @"🍴", @"🍕", @"🍔", @"🍟", @"🍗", @"🍱", @"🍚", @"🍜", @"🍡", @"🍳", @"🍞", @"🍩", @"🍦", @"🎂", @"🍰", @"🍪", @"🍫", @"🍭", @"🍯", @"🍎", @"🍏", @"🍊", @"🍋", @"🍒", @"🍇", @"🍉", @"🍓", @"🍑", @"🍌", @"🍐", @"🍍", @"🍆", @"🍅", @"🌽", @"🏡", @"🏥", @"🏦", @"⛪️", @"🏰", @"⛺️", @"🏭", @"🗻", @"🗽", @"🎠", @"🎡", @"⛲️", @"🎢", @"🚢", @"🚤", @"⚓️", @"🚀", @"✈️", @"🚁", @"🚂", @"🚋", @"🚎", @"🚌", @"🚙", @"🚗", @"🚕", @"🚛", @"🚨", @"🚔", @"🚒", @"🚑", @"🚲", @"🚠", @"🚜", @"🚦", @"⚠️", @"🚧", @"⛽️", @"🎰", @"🗿", @"🎪", @"🎭", @"🇯🇵", @"🇰🇷", @"🇩🇪", @"🇨🇳", @"🇺🇸", @"🇫🇷", @"🇪🇸", @"🇮🇹", @"🇷🇺", @"🇬🇧", @"1️⃣", @"2️⃣", @"3️⃣", @"4️⃣", @"5️⃣", @"6️⃣", @"7️⃣", @"8️⃣", @"9️⃣", @"0️⃣", @"🔟", @"❗️", @"❓", @"♥️", @"♦️", @"💯", @"🔗", @"🔱", @"🔴", @"🔵", @"🔶", @"🔷" ];
+}
+
+NSString *randomCallsEmoji() {
+ NSArray *emojis = emojisArray();
+ return emojis[arc4random() % emojis.count];
+}
+
NSString *stringForEmojiHashOfData(NSData *data, NSInteger count) {
if (data.length != 32)
return @"";
- NSArray *emojis = @[ @"😉", @"😍", @"😛", @"😭", @"😱", @"😡", @"😎", @"😴", @"😵", @"😈", @"😬", @"😇", @"😏", @"👮", @"👷", @"💂", @"👶", @"👨", @"👩", @"👴", @"👵", @"😻", @"😽", @"🙀", @"👺", @"🙈", @"🙉", @"🙊", @"💀", @"👽", @"💩", @"🔥", @"💥", @"💤", @"👂", @"👀", @"👃", @"👅", @"👄", @"👍", @"👎", @"👌", @"👊", @"✌️", @"✋️", @"👐", @"👆", @"👇", @"👉", @"👈", @"🙏", @"👏", @"💪", @"🚶", @"🏃", @"💃", @"👫", @"👪", @"👬", @"👭", @"💅", @"🎩", @"👑", @"👒", @"👟", @"👞", @"👠", @"👕", @"👗", @"👖", @"👙", @"👜", @"👓", @"🎀", @"💄", @"💛", @"💙", @"💜", @"💚", @"💍", @"💎", @"🐶", @"🐺", @"🐱", @"🐭", @"🐹", @"🐰", @"🐸", @"🐯", @"🐨", @"🐻", @"🐷", @"🐮", @"🐗", @"🐴", @"🐑", @"🐘", @"🐼", @"🐧", @"🐥", @"🐔", @"🐍", @"🐢", @"🐛", @"🐝", @"🐜", @"🐞", @"🐌", @"🐙", @"🐚", @"🐟", @"🐬", @"🐋", @"🐐", @"🐊", @"🐫", @"🍀", @"🌹", @"🌻", @"🍁", @"🌾", @"🍄", @"🌵", @"🌴", @"🌳", @"🌞", @"🌚", @"🌙", @"🌎", @"🌋", @"⚡️", @"☔️", @"❄️", @"⛄️", @"🌀", @"🌈", @"🌊", @"🎓", @"🎆", @"🎃", @"👻", @"🎅", @"🎄", @"🎁", @"🎈", @"🔮", @"🎥", @"📷", @"💿", @"💻", @"☎️", @"📡", @"📺", @"📻", @"🔉", @"🔔", @"⏳", @"⏰", @"⌚️", @"🔒", @"🔑", @"🔎", @"💡", @"🔦", @"🔌", @"🔋", @"🚿", @"🚽", @"🔧", @"🔨", @"🚪", @"🚬", @"💣", @"🔫", @"🔪", @"💊", @"💉", @"💰", @"💵", @"💳", @"✉️", @"📫", @"📦", @"📅", @"📁", @"✂️", @"📌", @"📎", @"✒️", @"✏️", @"📐", @"📚", @"🔬", @"🔭", @"🎨", @"🎬", @"🎤", @"🎧", @"🎵", @"🎹", @"🎻", @"🎺", @"🎸", @"👾", @"🎮", @"🃏", @"🎲", @"🎯", @"🏈", @"🏀", @"⚽️", @"⚾️", @"🎾", @"🎱", @"🏉", @"🎳", @"🏁", @"🏇", @"🏆", @"🏊", @"🏄", @"☕️", @"🍼", @"🍺", @"🍷", @"🍴", @"🍕", @"🍔", @"🍟", @"🍗", @"🍱", @"🍚", @"🍜", @"🍡", @"🍳", @"🍞", @"🍩", @"🍦", @"🎂", @"🍰", @"🍪", @"🍫", @"🍭", @"🍯", @"🍎", @"🍏", @"🍊", @"🍋", @"🍒", @"🍇", @"🍉", @"🍓", @"🍑", @"🍌", @"🍐", @"🍍", @"🍆", @"🍅", @"🌽", @"🏡", @"🏥", @"🏦", @"⛪️", @"🏰", @"⛺️", @"🏭", @"🗻", @"🗽", @"🎠", @"🎡", @"⛲️", @"🎢", @"🚢", @"🚤", @"⚓️", @"🚀", @"✈️", @"🚁", @"🚂", @"🚋", @"🚎", @"🚌", @"🚙", @"🚗", @"🚕", @"🚛", @"🚨", @"🚔", @"🚒", @"🚑", @"🚲", @"🚠", @"🚜", @"🚦", @"⚠️", @"🚧", @"⛽️", @"🎰", @"🗿", @"🎪", @"🎭", @"🇯🇵", @"🇰🇷", @"🇩🇪", @"🇨🇳", @"🇺🇸", @"🇫🇷", @"🇪🇸", @"🇮🇹", @"🇷🇺", @"🇬🇧", @"1️⃣", @"2️⃣", @"3️⃣", @"4️⃣", @"5️⃣", @"6️⃣", @"7️⃣", @"8️⃣", @"9️⃣", @"0️⃣", @"🔟", @"❗️", @"❓", @"♥️", @"♦️", @"💯", @"🔗", @"🔱", @"🔴", @"🔵", @"🔶", @"🔷" ];
-
uint8_t bytes[32];
[data getBytes:bytes length:32];
+ NSArray *emojis = emojisArray();
NSString *result = @"";
for (int32_t i = 0; i < count; i++)
{
diff --git a/submodules/TelegramCallsUI/Sources/CallController.swift b/submodules/TelegramCallsUI/Sources/CallController.swift
index 45891d7192..06c7c49653 100644
--- a/submodules/TelegramCallsUI/Sources/CallController.swift
+++ b/submodules/TelegramCallsUI/Sources/CallController.swift
@@ -13,20 +13,22 @@ import TelegramAudio
import AccountContext
import TelegramNotices
import AppBundle
+import TooltipUI
protocol CallControllerNodeProtocol: class {
var isMuted: Bool { get set }
var toggleMute: (() -> Void)? { get set }
var setCurrentAudioOutput: ((AudioSessionOutput) -> Void)? { get set }
- var beginAudioOuputSelection: (() -> Void)? { get set }
+ var beginAudioOuputSelection: ((Bool) -> Void)? { get set }
var acceptCall: (() -> Void)? { get set }
var endCall: (() -> Void)? { get set }
- var setIsVideoPaused: ((Bool) -> Void)? { get set }
var back: (() -> Void)? { get set }
var presentCallRating: ((CallId) -> Void)? { get set }
+ var present: ((ViewController) -> Void)? { get set }
var callEnded: ((Bool) -> Void)? { get set }
var dismissedInteractively: (() -> Void)? { get set }
+ var dismissAllTooltips: (() -> Void)? { get set }
func updateAudioOutputs(availableOutputs: [AudioSessionOutput], currentOutput: AudioSessionOutput?)
func updateCallState(_ callState: PresentationCallState)
@@ -149,7 +151,7 @@ public final class CallController: ViewController {
self?.call.setCurrentAudioOutput(output)
}
- self.controllerNode.beginAudioOuputSelection = { [weak self] in
+ self.controllerNode.beginAudioOuputSelection = { [weak self] hasMute in
guard let strongSelf = self, let (availableOutputs, currentOutput) = strongSelf.audioOutputState else {
return
}
@@ -167,6 +169,9 @@ public final class CallController: ViewController {
let actionSheet = ActionSheetController(presentationData: strongSelf.presentationData)
var items: [ActionSheetItem] = []
for output in availableOutputs {
+ if hasMute, case .builtin = output {
+ continue
+ }
let title: String
var icon: UIImage?
switch output {
@@ -174,13 +179,20 @@ public final class CallController: ViewController {
title = UIDevice.current.model
case .speaker:
title = strongSelf.presentationData.strings.Call_AudioRouteSpeaker
- icon = UIImage(bundleImageName: "Call/CallRouteSpeaker")
+ icon = generateScaledImage(image: UIImage(bundleImageName: "Call/CallSpeakerButton"), size: CGSize(width: 48.0, height: 48.0), opaque: false)
case .headphones:
title = strongSelf.presentationData.strings.Call_AudioRouteHeadphones
case let .port(port):
title = port.name
if port.type == .bluetooth {
- icon = UIImage(bundleImageName: "Call/CallRouteBluetooth")
+ var image = UIImage(bundleImageName: "Call/CallBluetoothButton")
+ let portName = port.name.lowercased()
+ if portName.contains("airpods pro") {
+ image = UIImage(bundleImageName: "Call/CallAirpodsProButton")
+ } else if portName.contains("airpods") {
+ image = UIImage(bundleImageName: "Call/CallAirpodsButton")
+ }
+ icon = generateScaledImage(image: image, size: CGSize(width: 48.0, height: 48.0), opaque: false)
}
}
items.append(CallRouteActionSheetItem(title: title, icon: icon, selected: output == currentOutput, action: { [weak actionSheet] in
@@ -189,8 +201,15 @@ public final class CallController: ViewController {
}))
}
+ if hasMute {
+ items.append(CallRouteActionSheetItem(title: strongSelf.presentationData.strings.Call_AudioRouteMute, icon: generateScaledImage(image: UIImage(bundleImageName: "Call/CallMuteButton"), size: CGSize(width: 48.0, height: 48.0), opaque: false), selected: strongSelf.isMuted, action: { [weak actionSheet] in
+ actionSheet?.dismissAnimated()
+ self?.call.toggleIsMuted()
+ }))
+ }
+
actionSheet.setItemGroups([ActionSheetItemGroup(items: items), ActionSheetItemGroup(items: [
- ActionSheetButtonItem(title: strongSelf.presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak actionSheet] in
+ ActionSheetButtonItem(title: strongSelf.presentationData.strings.Call_AudioRouteHide, color: .accent, font: .bold, action: { [weak actionSheet] in
actionSheet?.dismissAnimated()
})
])
@@ -207,10 +226,6 @@ public final class CallController: ViewController {
let _ = self?.call.hangUp()
}
- self.controllerNode.setIsVideoPaused = { [weak self] isPaused in
- self?.call.setOutgoingVideoIsPaused(isPaused)
- }
-
self.controllerNode.back = { [weak self] in
let _ = self?.dismiss()
}
@@ -236,6 +251,23 @@ public final class CallController: ViewController {
}
}
+ self.controllerNode.present = { [weak self] controller in
+ if let strongSelf = self {
+ strongSelf.present(controller, in: .window(.root))
+ }
+ }
+
+ self.controllerNode.dismissAllTooltips = { [weak self] in
+ if let strongSelf = self {
+ strongSelf.forEachController({ controller in
+ if let controller = controller as? TooltipScreen {
+ controller.dismiss()
+ }
+ return true
+ })
+ }
+ }
+
self.controllerNode.callEnded = { [weak self] didPresentRating in
if let strongSelf = self, !didPresentRating {
let _ = (combineLatest(strongSelf.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.callListSettings]), ApplicationSpecificNotice.getCallsTabTip(accountManager: strongSelf.sharedContext.accountManager))
diff --git a/submodules/TelegramCallsUI/Sources/CallControllerButton.swift b/submodules/TelegramCallsUI/Sources/CallControllerButton.swift
index ad64ab15cc..6f8c40f51a 100644
--- a/submodules/TelegramCallsUI/Sources/CallControllerButton.swift
+++ b/submodules/TelegramCallsUI/Sources/CallControllerButton.swift
@@ -4,6 +4,7 @@ import Display
import AsyncDisplayKit
import SwiftSignalKit
import AppBundle
+import SemanticStatusNode
private let labelFont = Font.regular(13.0)
@@ -17,6 +18,14 @@ final class CallControllerButtonItemNode: HighlightTrackingButtonNode {
case blurred(isFilled: Bool)
case color(Color)
+
+ var isFilled: Bool {
+ if case let .blurred(isFilled) = self {
+ return isFilled
+ } else {
+ return false
+ }
+ }
}
enum Image {
@@ -25,18 +34,31 @@ final class CallControllerButtonItemNode: HighlightTrackingButtonNode {
case flipCamera
case bluetooth
case speaker
+ case airpods
+ case airpodsPro
case accept
case end
}
var appearance: Appearance
var image: Image
+ var isEnabled: Bool
+ var hasProgress: Bool
+
+ init(appearance: Appearance, image: Image, isEnabled: Bool = true, hasProgress: Bool = false) {
+ self.appearance = appearance
+ self.image = image
+ self.isEnabled = isEnabled
+ self.hasProgress = hasProgress
+ }
}
private let contentContainer: ASDisplayNode
private let effectView: UIVisualEffectView
+ private let contentBackgroundNode: ASImageNode
private let contentNode: ASImageNode
private let overlayHighlightNode: ASImageNode
+ private var statusNode: SemanticStatusNode?
private let textNode: ImmediateTextNode
private let largeButtonSize: CGFloat = 72.0
@@ -53,6 +75,9 @@ final class CallControllerButtonItemNode: HighlightTrackingButtonNode {
self.effectView.clipsToBounds = true
self.effectView.isUserInteractionEnabled = false
+ self.contentBackgroundNode = ASImageNode()
+ self.contentBackgroundNode.isUserInteractionEnabled = false
+
self.contentNode = ASImageNode()
self.contentNode.isUserInteractionEnabled = false
@@ -72,6 +97,7 @@ final class CallControllerButtonItemNode: HighlightTrackingButtonNode {
self.addSubnode(self.textNode)
self.contentContainer.view.addSubview(self.effectView)
+ self.contentContainer.addSubnode(self.contentBackgroundNode)
self.contentContainer.addSubnode(self.contentNode)
self.contentContainer.addSubnode(self.overlayHighlightNode)
@@ -81,9 +107,13 @@ final class CallControllerButtonItemNode: HighlightTrackingButtonNode {
}
if highlighted {
strongSelf.overlayHighlightNode.alpha = 1.0
+ let transition: ContainedViewLayoutTransition = .animated(duration: 0.3, curve: .spring)
+ transition.updateSublayerTransformScale(node: strongSelf, scale: 0.9)
} else {
strongSelf.overlayHighlightNode.alpha = 0.0
strongSelf.overlayHighlightNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2)
+ let transition: ContainedViewLayoutTransition = .animated(duration: 0.5, curve: .spring)
+ transition.updateSublayerTransformScale(node: strongSelf, scale: 1.0)
}
}
}
@@ -94,12 +124,35 @@ final class CallControllerButtonItemNode: HighlightTrackingButtonNode {
let isSmall = self.largeButtonSize > size.width
self.effectView.frame = CGRect(origin: CGPoint(), size: CGSize(width: self.largeButtonSize, height: self.largeButtonSize))
+ self.contentBackgroundNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: self.largeButtonSize, height: self.largeButtonSize))
self.contentNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: self.largeButtonSize, height: self.largeButtonSize))
self.overlayHighlightNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: self.largeButtonSize, height: self.largeButtonSize))
if self.currentContent != content {
+ let previousContent = self.currentContent
self.currentContent = content
+ if content.hasProgress {
+ if self.statusNode == nil {
+ let statusNode = SemanticStatusNode(backgroundNodeColor: .white, foregroundNodeColor: .clear, hollow: true)
+ self.statusNode = statusNode
+ self.contentContainer.insertSubnode(statusNode, belowSubnode: self.contentNode)
+ statusNode.transitionToState(.progress(value: nil, cancelEnabled: false, appearance: SemanticStatusNodeState.ProgressAppearance(inset: 4.0, lineWidth: 3.0)), animated: false, completion: {})
+ }
+ if let statusNode = self.statusNode {
+ statusNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: self.largeButtonSize, height: self.largeButtonSize))
+ if transition.isAnimated {
+ statusNode.layer.animateScale(from: 0.1, to: 1.0, duration: 0.2)
+ statusNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
+ }
+ }
+ } else if let statusNode = self.statusNode {
+ self.statusNode = nil
+ transition.updateAlpha(node: statusNode, alpha: 0.0, completion: { [weak statusNode] _ in
+ statusNode?.removeFromSupernode()
+ })
+ }
+
switch content.appearance {
case .blurred:
self.effectView.isHidden = false
@@ -107,22 +160,34 @@ final class CallControllerButtonItemNode: HighlightTrackingButtonNode {
self.effectView.isHidden = true
}
+ transition.updateAlpha(node: self, alpha: content.isEnabled ? 1.0 : 0.4)
+ self.isUserInteractionEnabled = content.isEnabled
+
+ let contentBackgroundImage: UIImage? = nil
+
let contentImage = generateImage(CGSize(width: self.largeButtonSize, height: self.largeButtonSize), contextGenerator: { size, context in
context.clear(CGRect(origin: CGPoint(), size: size))
+ var ellipseRect = CGRect(origin: CGPoint(), size: size)
var fillColor: UIColor = .clear
+ let imageColor: UIColor = .white
var drawOverMask = false
context.setBlendMode(.normal)
- var imageScale: CGFloat = 1.0
+ let imageScale: CGFloat = 1.0
switch content.appearance {
case let .blurred(isFilled):
- if isFilled {
+ if content.hasProgress {
fillColor = .white
drawOverMask = true
context.setBlendMode(.copy)
+ ellipseRect = ellipseRect.insetBy(dx: 7.0, dy: 7.0)
+ } else {
+ if isFilled {
+ fillColor = .white
+ drawOverMask = true
+ context.setBlendMode(.copy)
+ }
}
- let smallButtonSize: CGFloat = 60.0
- imageScale = self.largeButtonSize / smallButtonSize
case let .color(color):
switch color {
case .red:
@@ -133,25 +198,29 @@ final class CallControllerButtonItemNode: HighlightTrackingButtonNode {
}
context.setFillColor(fillColor.cgColor)
- context.fillEllipse(in: CGRect(origin: CGPoint(), size: size))
+ context.fillEllipse(in: ellipseRect)
var image: UIImage?
switch content.image {
case .camera:
- image = generateTintedImage(image: UIImage(bundleImageName: "Call/CallCameraButton"), color: .white)
+ image = generateTintedImage(image: UIImage(bundleImageName: "Call/CallCameraButton"), color: imageColor)
case .mute:
- image = generateTintedImage(image: UIImage(bundleImageName: "Call/CallMuteButton"), color: .white)
+ image = generateTintedImage(image: UIImage(bundleImageName: "Call/CallMuteButton"), color: imageColor)
case .flipCamera:
- image = generateTintedImage(image: UIImage(bundleImageName: "Call/CallSwitchCameraButton"), color: .white)
+ image = generateTintedImage(image: UIImage(bundleImageName: "Call/CallSwitchCameraButton"), color: imageColor)
case .bluetooth:
- image = generateTintedImage(image: UIImage(bundleImageName: "Call/CallBluetoothButton"), color: .white)
+ image = generateTintedImage(image: UIImage(bundleImageName: "Call/CallBluetoothButton"), color: imageColor)
case .speaker:
- image = generateTintedImage(image: UIImage(bundleImageName: "Call/CallSpeakerButton"), color: .white)
+ image = generateTintedImage(image: UIImage(bundleImageName: "Call/CallSpeakerButton"), color: imageColor)
+ case .airpods:
+ image = generateTintedImage(image: UIImage(bundleImageName: "Call/CallAirpodsButton"), color: imageColor)
+ case .airpodsPro:
+ image = generateTintedImage(image: UIImage(bundleImageName: "Call/CallAirpodsProButton"), color: imageColor)
case .accept:
- image = generateTintedImage(image: UIImage(bundleImageName: "Call/CallAcceptButton"), color: .white)
+ image = generateTintedImage(image: UIImage(bundleImageName: "Call/CallAcceptButton"), color: imageColor)
case .end:
- image = generateTintedImage(image: UIImage(bundleImageName: "Call/CallDeclineButton"), color: .white)
+ image = generateTintedImage(image: UIImage(bundleImageName: "Call/CallDeclineButton"), color: imageColor)
}
if let image = image {
@@ -170,7 +239,29 @@ final class CallControllerButtonItemNode: HighlightTrackingButtonNode {
}
}
})
- if transition.isAnimated, let contentImage = contentImage, let previousContent = self.contentNode.image {
+
+ if transition.isAnimated, let contentBackgroundImage = contentBackgroundImage, let previousContent = self.contentBackgroundNode.image {
+ self.contentBackgroundNode.image = contentBackgroundImage
+ self.contentBackgroundNode.layer.animate(from: previousContent.cgImage!, to: contentBackgroundImage.cgImage!, keyPath: "contents", timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, duration: 0.2)
+ } else {
+ self.contentBackgroundNode.image = contentBackgroundImage
+ }
+
+ if transition.isAnimated, let previousContent = previousContent, previousContent.image == .accept && content.image == .end {
+ let rotation = CGFloat.pi / 4.0 * 3.0
+
+ if let snapshotView = self.contentNode.view.snapshotContentTree() {
+ snapshotView.frame = self.contentNode.view.frame
+ self.contentContainer.view.addSubview(snapshotView)
+
+ snapshotView.layer.animateRotation(from: 0.0, to: rotation, duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring)
+ snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.4, removeOnCompletion: false, completion: { [weak snapshotView] _ in
+ snapshotView?.removeFromSuperview()
+ })
+ }
+ self.contentNode.image = contentImage
+ self.contentNode.layer.animateRotation(from: -rotation, to: 0.0, duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring)
+ } else if transition.isAnimated, let contentImage = contentImage, let previousContent = self.contentNode.image {
self.contentNode.image = contentImage
self.contentNode.layer.animate(from: previousContent.cgImage!, to: contentImage.cgImage!, keyPath: "contents", timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, duration: 0.2)
} else {
diff --git a/submodules/TelegramCallsUI/Sources/CallControllerButtonsNode.swift b/submodules/TelegramCallsUI/Sources/CallControllerButtonsNode.swift
index 31a79f72ca..466d3f63ec 100644
--- a/submodules/TelegramCallsUI/Sources/CallControllerButtonsNode.swift
+++ b/submodules/TelegramCallsUI/Sources/CallControllerButtonsNode.swift
@@ -6,32 +6,39 @@ import SwiftSignalKit
import MediaPlayer
import TelegramPresentationData
-enum CallControllerButtonsSpeakerMode {
+enum CallControllerButtonsSpeakerMode: Equatable {
+ enum BluetoothType: Equatable {
+ case generic
+ case airpods
+ case airpodsPro
+ }
+
case none
case builtin
case speaker
case headphones
- case bluetooth
+ case bluetooth(BluetoothType)
}
enum CallControllerButtonsMode: Equatable {
- enum VideoState: Equatable {
- case notAvailable
- case possible
- case outgoingRequested
- case incomingRequested
- case active
+ struct VideoState: Equatable {
+ var isAvailable: Bool
+ var isCameraActive: Bool
+ var canChangeStatus: Bool
+ var hasVideo: Bool
+ var isInitializingCamera: Bool
}
- case active(speakerMode: CallControllerButtonsSpeakerMode, videoState: VideoState)
- case incoming(speakerMode: CallControllerButtonsSpeakerMode, videoState: VideoState)
- case outgoingRinging(speakerMode: CallControllerButtonsSpeakerMode, videoState: VideoState)
+ case active(speakerMode: CallControllerButtonsSpeakerMode, hasAudioRouteMenu: Bool, videoState: VideoState)
+ case incoming(speakerMode: CallControllerButtonsSpeakerMode, hasAudioRouteMenu: Bool, videoState: VideoState)
+ case outgoingRinging(speakerMode: CallControllerButtonsSpeakerMode, hasAudioRouteMenu: Bool, videoState: VideoState)
}
private enum ButtonDescription: Equatable {
enum Key: Hashable {
case accept
- case end
+ case acceptOrEnd
+ case decline
case enableCamera
case switchCamera
case soundOutput
@@ -42,6 +49,8 @@ private enum ButtonDescription: Equatable {
case builtin
case speaker
case bluetooth
+ case airpods
+ case airpodsPro
}
enum EndType {
@@ -52,17 +61,21 @@ private enum ButtonDescription: Equatable {
case accept
case end(EndType)
- case enableCamera(Bool)
- case switchCamera
+ case enableCamera(Bool, Bool, Bool)
+ case switchCamera(Bool)
case soundOutput(SoundOutput)
case mute(Bool)
var key: Key {
switch self {
case .accept:
- return .accept
- case .end:
- return .end
+ return .acceptOrEnd
+ case let .end(type):
+ if type == .decline {
+ return .decline
+ } else {
+ return .acceptOrEnd
+ }
case .enableCamera:
return .enableCamera
case .switchCamera:
@@ -83,11 +96,10 @@ final class CallControllerButtonsNode: ASDisplayNode {
private var validLayout: (CGFloat, CGFloat)?
var isMuted = false
- var isCameraPaused = false
- var accept: (() -> Void)?
+ var acceptOrEnd: (() -> Void)?
+ var decline: (() -> Void)?
var mute: (() -> Void)?
- var end: (() -> Void)?
var speaker: (() -> Void)?
var toggleVideo: (() -> Void)?
var rotateCamera: (() -> Void)?
@@ -110,6 +122,10 @@ final class CallControllerButtonsNode: ASDisplayNode {
private var appliedMode: CallControllerButtonsMode?
+ func videoButtonFrame() -> CGRect? {
+ return self.buttonNodes[.enableCamera]?.frame
+ }
+
private func updateButtonsLayout(strings: PresentationStrings, mode: CallControllerButtonsMode, width: CGFloat, bottomInset: CGFloat, animated: Bool) -> CGFloat {
let transition: ContainedViewLayoutTransition
if animated {
@@ -133,7 +149,7 @@ final class CallControllerButtonsNode: ASDisplayNode {
}
}
- let minSmallButtonSideInset: CGFloat = 34.0
+ let minSmallButtonSideInset: CGFloat = width > 320.0 ? 34.0 : 16.0
let maxSmallButtonSpacing: CGFloat = 34.0
let smallButtonSize: CGFloat = 60.0
let topBottomSpacing: CGFloat = 84.0
@@ -151,10 +167,12 @@ final class CallControllerButtonsNode: ASDisplayNode {
let speakerMode: CallControllerButtonsSpeakerMode
var videoState: CallControllerButtonsMode.VideoState
+ let hasAudioRouteMenu: Bool
switch mode {
- case .incoming(let speakerModeValue, let videoStateValue), .outgoingRinging(let speakerModeValue, let videoStateValue), .active(let speakerModeValue, let videoStateValue):
+ case .incoming(let speakerModeValue, let hasAudioRouteMenuValue, let videoStateValue), .outgoingRinging(let speakerModeValue, let hasAudioRouteMenuValue, let videoStateValue), .active(let speakerModeValue, let hasAudioRouteMenuValue, let videoStateValue):
speakerMode = speakerModeValue
videoState = videoStateValue
+ hasAudioRouteMenu = hasAudioRouteMenuValue
}
enum MappedState {
@@ -169,17 +187,9 @@ final class CallControllerButtonsNode: ASDisplayNode {
mappedState = .incomingRinging
case .outgoingRinging:
mappedState = .outgoingRinging
- case let .active(_, videoStateValue):
- switch videoStateValue {
- case .incomingRequested:
- mappedState = .incomingRinging
- videoState = .outgoingRequested
- case .outgoingRequested:
- mappedState = .outgoingRinging
- videoState = .outgoingRequested
- case .active, .possible, .notAvailable:
- mappedState = .active
- }
+ case let .active(_, _, videoStateValue):
+ mappedState = .active
+ videoState = videoStateValue
}
var buttons: [PlacedButton] = []
@@ -190,44 +200,61 @@ final class CallControllerButtonsNode: ASDisplayNode {
let soundOutput: ButtonDescription.SoundOutput
switch speakerMode {
- case .none, .builtin:
- soundOutput = .builtin
- case .speaker:
- soundOutput = .speaker
- case .headphones:
- soundOutput = .bluetooth
- case .bluetooth:
- soundOutput = .bluetooth
+ case .none, .builtin:
+ soundOutput = .builtin
+ case .speaker:
+ soundOutput = .speaker
+ case .headphones:
+ soundOutput = .bluetooth
+ case let .bluetooth(type):
+ switch type {
+ case .generic:
+ soundOutput = .bluetooth
+ case .airpods:
+ soundOutput = .airpods
+ case .airpodsPro:
+ soundOutput = .airpodsPro
+ }
}
- switch videoState {
- case .active, .possible, .incomingRequested, .outgoingRequested:
+ if videoState.isAvailable {
let isCameraActive: Bool
- if case .possible = videoState {
- isCameraActive = false
+ let isCameraEnabled: Bool
+ let isCameraInitializing: Bool
+ if videoState.hasVideo {
+ isCameraActive = videoState.isCameraActive
+ isCameraEnabled = videoState.canChangeStatus
+ isCameraInitializing = videoState.isInitializingCamera
} else {
- isCameraActive = !self.isCameraPaused
+ isCameraActive = false
+ isCameraEnabled = videoState.canChangeStatus
+ isCameraInitializing = videoState.isInitializingCamera
}
- topButtons.append(.enableCamera(isCameraActive))
- topButtons.append(.mute(self.isMuted))
- if case .possible = videoState {
+ topButtons.append(.enableCamera(isCameraActive, false, isCameraInitializing))
+ if !videoState.hasVideo {
+ topButtons.append(.mute(self.isMuted))
topButtons.append(.soundOutput(soundOutput))
} else {
- topButtons.append(.switchCamera)
+ if hasAudioRouteMenu {
+ topButtons.append(.soundOutput(soundOutput))
+ } else {
+ topButtons.append(.mute(self.isMuted))
+ }
+ topButtons.append(.switchCamera(isCameraActive && !isCameraInitializing))
}
- case .notAvailable:
+ } else {
topButtons.append(.mute(self.isMuted))
topButtons.append(.soundOutput(soundOutput))
}
- let topButtonsContentWidth = CGFloat(topButtons.count) * smallButtonSize
+ let topButtonsContentWidth = CGFloat(topButtons.count) * largeButtonSize
let topButtonsAvailableSpacingWidth = width - topButtonsContentWidth - minSmallButtonSideInset * 2.0
let topButtonsSpacing = min(maxSmallButtonSpacing, topButtonsAvailableSpacingWidth / CGFloat(topButtons.count - 1))
- let topButtonsWidth = CGFloat(topButtons.count) * smallButtonSize + CGFloat(topButtons.count - 1) * topButtonsSpacing
+ let topButtonsWidth = CGFloat(topButtons.count) * largeButtonSize + CGFloat(topButtons.count - 1) * topButtonsSpacing
var topButtonsLeftOffset = floor((width - topButtonsWidth) / 2.0)
for button in topButtons {
- buttons.append(PlacedButton(button: button, frame: CGRect(origin: CGPoint(x: topButtonsLeftOffset, y: 0.0), size: CGSize(width: smallButtonSize, height: smallButtonSize))))
- topButtonsLeftOffset += smallButtonSize + topButtonsSpacing
+ buttons.append(PlacedButton(button: button, frame: CGRect(origin: CGPoint(x: topButtonsLeftOffset, y: 0.0), size: CGSize(width: largeButtonSize, height: largeButtonSize))))
+ topButtonsLeftOffset += largeButtonSize + topButtonsSpacing
}
if case .incomingRinging = mappedState {
@@ -243,38 +270,54 @@ final class CallControllerButtonsNode: ASDisplayNode {
let bottomButtonsWidth = CGFloat(bottomButtons.count) * largeButtonSize + CGFloat(bottomButtons.count - 1) * bottomButtonsSpacing
var bottomButtonsLeftOffset = floor((width - bottomButtonsWidth) / 2.0)
for button in bottomButtons {
- buttons.append(PlacedButton(button: button, frame: CGRect(origin: CGPoint(x: bottomButtonsLeftOffset, y: smallButtonSize + topBottomSpacing), size: CGSize(width: largeButtonSize, height: largeButtonSize))))
+ buttons.append(PlacedButton(button: button, frame: CGRect(origin: CGPoint(x: bottomButtonsLeftOffset, y: largeButtonSize + topBottomSpacing), size: CGSize(width: largeButtonSize, height: largeButtonSize))))
bottomButtonsLeftOffset += largeButtonSize + bottomButtonsSpacing
}
- height = smallButtonSize + topBottomSpacing + largeButtonSize + max(bottomInset + 32.0, 46.0)
+ height = largeButtonSize + topBottomSpacing + largeButtonSize + max(bottomInset + 32.0, 46.0)
case .active:
- switch videoState {
- case .active, .incomingRequested, .outgoingRequested:
+ if videoState.hasVideo {
let isCameraActive: Bool
- if case .possible = videoState {
- isCameraActive = false
+ let isCameraEnabled: Bool
+ let isCameraInitializing: Bool
+ if videoState.hasVideo {
+ isCameraActive = videoState.isCameraActive
+ isCameraEnabled = videoState.canChangeStatus
+ isCameraInitializing = videoState.isInitializingCamera
} else {
- isCameraActive = !self.isCameraPaused
+ isCameraActive = false
+ isCameraEnabled = videoState.canChangeStatus
+ isCameraInitializing = videoState.isInitializingCamera
}
var topButtons: [ButtonDescription] = []
let soundOutput: ButtonDescription.SoundOutput
switch speakerMode {
- case .none, .builtin:
- soundOutput = .builtin
- case .speaker:
- soundOutput = .speaker
- case .headphones:
- soundOutput = .builtin
- case .bluetooth:
- soundOutput = .bluetooth
+ case .none, .builtin:
+ soundOutput = .builtin
+ case .speaker:
+ soundOutput = .speaker
+ case .headphones:
+ soundOutput = .builtin
+ case let .bluetooth(type):
+ switch type {
+ case .generic:
+ soundOutput = .bluetooth
+ case .airpods:
+ soundOutput = .airpods
+ case .airpodsPro:
+ soundOutput = .airpodsPro
+ }
}
- topButtons.append(.enableCamera(isCameraActive))
- topButtons.append(.mute(isMuted))
- topButtons.append(.switchCamera)
+ topButtons.append(.enableCamera(isCameraActive, isCameraEnabled, isCameraInitializing))
+ if hasAudioRouteMenu {
+ topButtons.append(.soundOutput(soundOutput))
+ } else {
+ topButtons.append(.mute(isMuted))
+ }
+ topButtons.append(.switchCamera(isCameraActive && !isCameraInitializing))
topButtons.append(.end(.end))
let topButtonsContentWidth = CGFloat(topButtons.count) * smallButtonSize
@@ -288,34 +331,54 @@ final class CallControllerButtonsNode: ASDisplayNode {
}
height = smallButtonSize + max(bottomInset + 19.0, 46.0)
- case .notAvailable, .possible:
+ } else {
var topButtons: [ButtonDescription] = []
var bottomButtons: [ButtonDescription] = []
- let soundOutput: ButtonDescription.SoundOutput
- switch speakerMode {
- case .none, .builtin:
- soundOutput = .builtin
- case .speaker:
- soundOutput = .speaker
- case .headphones:
- soundOutput = .bluetooth
- case .bluetooth:
- soundOutput = .bluetooth
+ let isCameraActive: Bool
+ let isCameraEnabled: Bool
+ let isCameraInitializing: Bool
+ if videoState.hasVideo {
+ isCameraActive = videoState.isCameraActive
+ isCameraEnabled = videoState.canChangeStatus
+ isCameraInitializing = videoState.isInitializingCamera
+ } else {
+ isCameraActive = false
+ isCameraEnabled = videoState.canChangeStatus
+ isCameraInitializing = videoState.isInitializingCamera
}
- topButtons.append(.enableCamera(false))
+ let soundOutput: ButtonDescription.SoundOutput
+ switch speakerMode {
+ case .none, .builtin:
+ soundOutput = .builtin
+ case .speaker:
+ soundOutput = .speaker
+ case .headphones:
+ soundOutput = .bluetooth
+ case let .bluetooth(type):
+ switch type {
+ case .generic:
+ soundOutput = .bluetooth
+ case .airpods:
+ soundOutput = .airpods
+ case .airpodsPro:
+ soundOutput = .airpodsPro
+ }
+ }
+
+ topButtons.append(.enableCamera(isCameraActive, isCameraEnabled, isCameraInitializing))
topButtons.append(.mute(self.isMuted))
topButtons.append(.soundOutput(soundOutput))
- let topButtonsContentWidth = CGFloat(topButtons.count) * smallButtonSize
+ let topButtonsContentWidth = CGFloat(topButtons.count) * largeButtonSize
let topButtonsAvailableSpacingWidth = width - topButtonsContentWidth - minSmallButtonSideInset * 2.0
let topButtonsSpacing = min(maxSmallButtonSpacing, topButtonsAvailableSpacingWidth / CGFloat(topButtons.count - 1))
- let topButtonsWidth = CGFloat(topButtons.count) * smallButtonSize + CGFloat(topButtons.count - 1) * topButtonsSpacing
+ let topButtonsWidth = CGFloat(topButtons.count) * largeButtonSize + CGFloat(topButtons.count - 1) * topButtonsSpacing
var topButtonsLeftOffset = floor((width - topButtonsWidth) / 2.0)
for button in topButtons {
- buttons.append(PlacedButton(button: button, frame: CGRect(origin: CGPoint(x: topButtonsLeftOffset, y: 0.0), size: CGSize(width: smallButtonSize, height: smallButtonSize))))
- topButtonsLeftOffset += smallButtonSize + topButtonsSpacing
+ buttons.append(PlacedButton(button: button, frame: CGRect(origin: CGPoint(x: topButtonsLeftOffset, y: 0.0), size: CGSize(width: largeButtonSize, height: largeButtonSize))))
+ topButtonsLeftOffset += largeButtonSize + topButtonsSpacing
}
bottomButtons.append(.end(.outgoing))
@@ -326,11 +389,11 @@ final class CallControllerButtonsNode: ASDisplayNode {
let bottomButtonsWidth = CGFloat(bottomButtons.count) * largeButtonSize + CGFloat(bottomButtons.count - 1) * bottomButtonsSpacing
var bottomButtonsLeftOffset = floor((width - bottomButtonsWidth) / 2.0)
for button in bottomButtons {
- buttons.append(PlacedButton(button: button, frame: CGRect(origin: CGPoint(x: bottomButtonsLeftOffset, y: smallButtonSize + topBottomSpacing), size: CGSize(width: largeButtonSize, height: largeButtonSize))))
+ buttons.append(PlacedButton(button: button, frame: CGRect(origin: CGPoint(x: bottomButtonsLeftOffset, y: largeButtonSize + topBottomSpacing), size: CGSize(width: largeButtonSize, height: largeButtonSize))))
bottomButtonsLeftOffset += largeButtonSize + bottomButtonsSpacing
}
- height = smallButtonSize + topBottomSpacing + largeButtonSize + max(bottomInset + 32.0, 46.0)
+ height = largeButtonSize + topBottomSpacing + largeButtonSize + max(bottomInset + 32.0, 46.0)
}
}
@@ -373,21 +436,25 @@ final class CallControllerButtonsNode: ASDisplayNode {
case .end:
buttonText = strings.Call_End
}
- case let .enableCamera(isEnabled):
+ case let .enableCamera(isActivated, isEnabled, isInitializing):
buttonContent = CallControllerButtonItemNode.Content(
- appearance: .blurred(isFilled: isEnabled),
- image: .camera
+ appearance: .blurred(isFilled: isActivated),
+ image: .camera,
+ isEnabled: isEnabled,
+ hasProgress: isInitializing
)
buttonText = strings.Call_Camera
- case .switchCamera:
+ case let .switchCamera(isEnabled):
buttonContent = CallControllerButtonItemNode.Content(
appearance: .blurred(isFilled: false),
- image: .flipCamera
+ image: .flipCamera,
+ isEnabled: isEnabled
)
buttonText = strings.Call_Flip
case let .soundOutput(value):
let image: CallControllerButtonItemNode.Content.Image
var isFilled = false
+ var title: String = strings.Call_Speaker
switch value {
case .builtin:
image = .speaker
@@ -396,12 +463,19 @@ final class CallControllerButtonsNode: ASDisplayNode {
isFilled = true
case .bluetooth:
image = .bluetooth
+ title = strings.Call_Audio
+ case .airpods:
+ image = .airpods
+ title = strings.Call_Audio
+ case .airpodsPro:
+ image = .airpodsPro
+ title = strings.Call_Audio
}
buttonContent = CallControllerButtonItemNode.Content(
appearance: .blurred(isFilled: isFilled),
image: image
)
- buttonText = strings.Call_Speaker
+ buttonText = title
case let .mute(isMuted):
buttonContent = CallControllerButtonItemNode.Content(
appearance: .blurred(isFilled: isMuted),
@@ -418,7 +492,7 @@ final class CallControllerButtonsNode: ASDisplayNode {
buttonDelay = delayIncrement * 1.0
case .switchCamera:
buttonDelay = delayIncrement * 2.0
- case .end:
+ case .acceptOrEnd:
buttonDelay = delayIncrement * 3.0
default:
break
@@ -436,17 +510,11 @@ final class CallControllerButtonsNode: ASDisplayNode {
if !validKeys.contains(key) {
removedKeys.append(key)
if animated {
- if case .accept = key {
- if let endButton = self.buttonNodes[.end] {
- transition.updateFrame(node: button, frame: endButton.frame)
- if let content = button.currentContent {
- button.update(size: endButton.frame.size, content: content, text: button.currentText, transition: transition)
- }
- transition.updateTransformScale(node: button, scale: 0.1)
- transition.updateAlpha(node: button, alpha: 0.0, completion: { [weak button] _ in
- button?.removeFromSupernode()
- })
- }
+ if case .decline = key {
+ transition.updateTransformScale(node: button, scale: 0.1)
+ transition.updateAlpha(node: button, alpha: 0.0, completion: { [weak button] _ in
+ button?.removeFromSupernode()
+ })
} else {
transition.updateAlpha(node: button, alpha: 0.0, completion: { [weak button] _ in
button?.removeFromSupernode()
@@ -469,9 +537,11 @@ final class CallControllerButtonsNode: ASDisplayNode {
if button === listButton {
switch key {
case .accept:
- self.accept?()
- case .end:
- self.end?()
+ self.acceptOrEnd?()
+ case .acceptOrEnd:
+ self.acceptOrEnd?()
+ case .decline:
+ self.decline?()
case .enableCamera:
self.toggleVideo?()
case .switchCamera:
diff --git a/submodules/TelegramCallsUI/Sources/CallControllerKeyButton.swift b/submodules/TelegramCallsUI/Sources/CallControllerKeyButton.swift
new file mode 100644
index 0000000000..bd3f7ac3c4
--- /dev/null
+++ b/submodules/TelegramCallsUI/Sources/CallControllerKeyButton.swift
@@ -0,0 +1,127 @@
+import Foundation
+import UIKit
+import Display
+import AsyncDisplayKit
+import CallsEmoji
+
+private let labelFont = Font.regular(22.0)
+private let animationNodesCount = 3
+
+private class EmojiSlotNode: ASDisplayNode {
+ var emoji: String = "" {
+ didSet {
+ self.node.attributedText = NSAttributedString(string: emoji, font: labelFont, textColor: .black)
+ let _ = self.node.updateLayout(CGSize(width: 100.0, height: 100.0))
+ }
+ }
+
+ private let maskNode: ASDisplayNode
+ private let containerNode: ASDisplayNode
+ private let node: ImmediateTextNode
+ private let animationNodes: [ImmediateTextNode]
+
+ override init() {
+ self.maskNode = ASDisplayNode()
+ self.containerNode = ASDisplayNode()
+ self.node = ImmediateTextNode()
+ self.animationNodes = (0 ..< animationNodesCount).map { _ in ImmediateTextNode() }
+
+ super.init()
+
+ let maskLayer = CAGradientLayer()
+ maskLayer.colors = [UIColor.clear.cgColor, UIColor.white.cgColor, UIColor.white.cgColor, UIColor.clear.cgColor]
+ maskLayer.locations = [0.0, 0.2, 0.8, 1.0]
+ maskLayer.startPoint = CGPoint(x: 0.5, y: 0.0)
+ maskLayer.endPoint = CGPoint(x: 0.5, y: 1.0)
+ self.maskNode.layer.mask = maskLayer
+
+ self.addSubnode(self.maskNode)
+ self.maskNode.addSubnode(self.containerNode)
+ self.containerNode.addSubnode(self.node)
+ self.animationNodes.forEach({ self.containerNode.addSubnode($0) })
+ }
+
+ func animateIn(duration: Double) {
+ for node in self.animationNodes {
+ node.attributedText = NSAttributedString(string: randomCallsEmoji(), font: labelFont, textColor: .black)
+ let _ = node.updateLayout(CGSize(width: 100.0, height: 100.0))
+ }
+ self.containerNode.layer.animatePosition(from: CGPoint(x: 0.0, y: -self.containerNode.frame.height + self.bounds.height), to: CGPoint(), duration: duration, delay: 0.1, timingFunction: kCAMediaTimingFunctionSpring, additive: true)
+ }
+
+ override func layout() {
+ super.layout()
+
+ let maskInset: CGFloat = 4.0
+ let maskFrame = self.bounds.insetBy(dx: 0.0, dy: -maskInset)
+ self.maskNode.frame = maskFrame
+ self.maskNode.layer.mask?.frame = CGRect(origin: CGPoint(), size: maskFrame.size)
+
+ let spacing: CGFloat = 2.0
+ let containerSize = CGSize(width: self.bounds.width, height: self.bounds.height * CGFloat(animationNodesCount + 1) + spacing * CGFloat(animationNodesCount))
+ self.containerNode.frame = CGRect(origin: CGPoint(x: 0.0, y: maskInset), size: containerSize)
+
+ self.node.frame = CGRect(origin: CGPoint(), size: self.bounds.size)
+ var offset: CGFloat = self.bounds.height + spacing
+ for node in self.animationNodes {
+ node.frame = CGRect(origin: CGPoint(x: 0.0, y: offset), size: self.bounds.size)
+ offset += self.bounds.height + spacing
+ }
+ }
+}
+
+final class CallControllerKeyButton: HighlightableButtonNode {
+ private let containerNode: ASDisplayNode
+ private let nodes: [EmojiSlotNode]
+
+ var key: String = "" {
+ didSet {
+ var index = 0
+ for emoji in self.key {
+ guard index < 4 else {
+ return
+ }
+ self.nodes[index].emoji = String(emoji)
+ index += 1
+ }
+ }
+ }
+
+ init() {
+ self.containerNode = ASDisplayNode()
+ self.nodes = (0 ..< 4).map { _ in EmojiSlotNode() }
+
+ super.init(pointerStyle: nil)
+
+ self.addSubnode(self.containerNode)
+ self.nodes.forEach({ self.containerNode.addSubnode($0) })
+ }
+
+ func animateIn() {
+ self.layoutIfNeeded()
+ self.containerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
+
+ var duration: Double = 0.75
+ for node in self.nodes {
+ node.animateIn(duration: duration)
+ duration += 0.3
+ }
+ }
+
+ override func measure(_ constrainedSize: CGSize) -> CGSize {
+ return CGSize(width: 114.0, height: 26.0)
+ }
+
+ override func layout() {
+ super.layout()
+
+ self.containerNode.frame = self.bounds
+ var index = 0
+ let nodeSize = CGSize(width: 29.0, height: self.bounds.size.height)
+ for node in self.nodes {
+ node.frame = CGRect(origin: CGPoint(x: CGFloat(index) * nodeSize.width, y: 0.0), size: nodeSize)
+ index += 1
+ }
+ self.nodes.forEach({ self.containerNode.addSubnode($0) })
+ }
+}
diff --git a/submodules/TelegramCallsUI/Sources/CallControllerKeyPreviewNode.swift b/submodules/TelegramCallsUI/Sources/CallControllerKeyPreviewNode.swift
index a95979375a..8671bc6b2b 100644
--- a/submodules/TelegramCallsUI/Sources/CallControllerKeyPreviewNode.swift
+++ b/submodules/TelegramCallsUI/Sources/CallControllerKeyPreviewNode.swift
@@ -32,7 +32,7 @@ final class CallControllerKeyPreviewNode: ASDisplayNode {
super.init()
- self.keyTextNode.attributedText = NSAttributedString(string: keyText, attributes: [NSAttributedString.Key.font: Font.regular(58.0), NSAttributedString.Key.kern: 9.0 as NSNumber])
+ self.keyTextNode.attributedText = NSAttributedString(string: keyText, attributes: [NSAttributedString.Key.font: Font.regular(58.0), NSAttributedString.Key.kern: 11.0 as NSNumber])
self.infoTextNode.attributedText = NSAttributedString(string: infoText, font: Font.regular(14.0), textColor: UIColor.white, paragraphAlignment: .center)
@@ -53,7 +53,7 @@ final class CallControllerKeyPreviewNode: ASDisplayNode {
let keyTextSize = self.keyTextNode.measure(CGSize(width: 300.0, height: 300.0))
transition.updateFrame(node: self.keyTextNode, frame: CGRect(origin: CGPoint(x: floor((size.width - keyTextSize.width) / 2) + 6.0, y: floor((size.height - keyTextSize.height) / 2) - 50.0), size: keyTextSize))
- let infoTextSize = self.infoTextNode.measure(CGSize(width: size.width - 20.0, height: CGFloat.greatestFiniteMagnitude))
+ let infoTextSize = self.infoTextNode.measure(CGSize(width: size.width - 32.0, height: CGFloat.greatestFiniteMagnitude))
transition.updateFrame(node: self.infoTextNode, frame: CGRect(origin: CGPoint(x: floor((size.width - infoTextSize.width) / 2.0), y: floor((size.height - infoTextSize.height) / 2.0) + 30.0), size: infoTextSize))
}
@@ -83,10 +83,10 @@ final class CallControllerKeyPreviewNode: ASDisplayNode {
}
func animateOut(to rect: CGRect, toNode: ASDisplayNode, completion: @escaping () -> Void) {
- self.keyTextNode.layer.animatePosition(from: self.keyTextNode.layer.position, to: CGPoint(x: rect.midX, y: rect.midY), duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, completion: { _ in
+ self.keyTextNode.layer.animatePosition(from: self.keyTextNode.layer.position, to: CGPoint(x: rect.midX + 2.0, y: rect.midY), duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, completion: { _ in
completion()
})
- self.keyTextNode.layer.animateScale(from: 1.0, to: rect.size.width / self.keyTextNode.frame.size.width, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false)
+ self.keyTextNode.layer.animateScale(from: 1.0, to: rect.size.width / (self.keyTextNode.frame.size.width - 2.0), duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false)
self.infoTextNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false)
diff --git a/submodules/TelegramCallsUI/Sources/CallControllerNode.swift b/submodules/TelegramCallsUI/Sources/CallControllerNode.swift
index 1b469a0dd4..9d5f27e8cb 100644
--- a/submodules/TelegramCallsUI/Sources/CallControllerNode.swift
+++ b/submodules/TelegramCallsUI/Sources/CallControllerNode.swift
@@ -13,6 +13,9 @@ import AccountContext
import LocalizedPeerData
import PhotoResources
import CallsEmoji
+import TooltipUI
+import AlertUI
+import PresentationDataUtils
private func interpolateFrame(from fromValue: CGRect, to toValue: CGRect, t: CGFloat) -> CGRect {
return CGRect(x: floorToScreenPixels(toValue.origin.x * t + fromValue.origin.x * (1.0 - t)), y: floorToScreenPixels(toValue.origin.y * t + fromValue.origin.y * (1.0 - t)), width: floorToScreenPixels(toValue.size.width * t + fromValue.size.width * (1.0 - t)), height: floorToScreenPixels(toValue.size.height * t + fromValue.size.height * (1.0 - t)))
@@ -27,6 +30,8 @@ private final class CallVideoNode: ASDisplayNode {
private let videoView: PresentationCallVideoView
private var effectView: UIVisualEffectView?
+ private let videoPausedNode: ImmediateTextNode
+
private var isBlurred: Bool = false
private var currentCornerRadius: CGFloat = 0.0
@@ -34,39 +39,86 @@ private final class CallVideoNode: ASDisplayNode {
private(set) var isReady: Bool = false
private var isReadyTimer: SwiftSignalKit.Timer?
- init(videoView: PresentationCallVideoView, isReadyUpdated: @escaping () -> Void) {
+ private let isFlippedUpdated: (CallVideoNode) -> Void
+
+ private(set) var currentOrientation: PresentationCallVideoView.Orientation
+
+ init(videoView: PresentationCallVideoView, disabledText: String?, assumeReadyAfterTimeout: Bool, isReadyUpdated: @escaping () -> Void, orientationUpdated: @escaping () -> Void, isFlippedUpdated: @escaping (CallVideoNode) -> Void) {
self.isReadyUpdated = isReadyUpdated
+ self.isFlippedUpdated = isFlippedUpdated
self.videoTransformContainer = ASDisplayNode()
self.videoTransformContainer.clipsToBounds = true
self.videoView = videoView
- self.videoView.view.layer.transform = CATransform3DMakeScale(-1.0, 1.0, 1.0)
+ videoView.view.clipsToBounds = true
+ videoView.view.backgroundColor = .black
+
+ self.currentOrientation = videoView.getOrientation()
+
+ self.videoPausedNode = ImmediateTextNode()
+ self.videoPausedNode.alpha = 0.0
+ self.videoPausedNode.maximumNumberOfLines = 3
super.init()
+ if #available(iOS 13.0, *) {
+ self.layer.cornerCurve = .continuous
+ self.videoTransformContainer.layer.cornerCurve = .continuous
+ }
+
self.videoTransformContainer.view.addSubview(self.videoView.view)
self.addSubnode(self.videoTransformContainer)
- self.videoView.setOnFirstFrameReceived { [weak self] in
- guard let strongSelf = self else {
- return
- }
- if !strongSelf.isReady {
- strongSelf.isReady = true
- strongSelf.isReadyTimer?.invalidate()
- strongSelf.isReadyUpdated()
+ if let disabledText = disabledText {
+ self.videoPausedNode.attributedText = NSAttributedString(string: disabledText, font: Font.regular(17.0), textColor: .white)
+ self.addSubnode(self.videoPausedNode)
+ }
+
+ self.videoView.setOnFirstFrameReceived { [weak self] aspectRatio in
+ Queue.mainQueue().async {
+ guard let strongSelf = self else {
+ return
+ }
+ if !strongSelf.isReady {
+ strongSelf.isReady = true
+ strongSelf.isReadyTimer?.invalidate()
+ strongSelf.isReadyUpdated()
+ }
}
}
- self.isReadyTimer = SwiftSignalKit.Timer(timeout: 3.0, repeat: false, completion: { [weak self] in
- guard let strongSelf = self else {
- return
+ self.videoView.setOnOrientationUpdated { [weak self] orientation in
+ Queue.mainQueue().async {
+ guard let strongSelf = self else {
+ return
+ }
+ if strongSelf.currentOrientation != orientation {
+ strongSelf.currentOrientation = orientation
+ orientationUpdated()
+ }
}
- if !strongSelf.isReady {
- strongSelf.isReady = true
- strongSelf.isReadyUpdated()
+ }
+
+ self.videoView.setOnIsMirroredUpdated { [weak self] _ in
+ Queue.mainQueue().async {
+ guard let strongSelf = self else {
+ return
+ }
+ strongSelf.isFlippedUpdated(strongSelf)
}
- }, queue: .mainQueue())
+ }
+
+ if assumeReadyAfterTimeout {
+ self.isReadyTimer = SwiftSignalKit.Timer(timeout: 3.0, repeat: false, completion: { [weak self] in
+ guard let strongSelf = self else {
+ return
+ }
+ if !strongSelf.isReady {
+ strongSelf.isReady = true
+ strongSelf.isReadyUpdated()
+ }
+ }, queue: .mainQueue())
+ }
self.isReadyTimer?.start()
}
@@ -74,32 +126,122 @@ private final class CallVideoNode: ASDisplayNode {
self.isReadyTimer?.invalidate()
}
- func updateLayout(size: CGSize, cornerRadius: CGFloat, transition: ContainedViewLayoutTransition) {
- let videoFrame = CGRect(origin: CGPoint(), size: size)
- self.currentCornerRadius = cornerRadius
+ func animateRadialMask(from fromRect: CGRect, to toRect: CGRect) {
+ let maskLayer = CAShapeLayer()
+ maskLayer.frame = fromRect
- let previousVideoFrame = self.videoTransformContainer.frame
- self.videoTransformContainer.frame = videoFrame
- if transition.isAnimated && !videoFrame.height.isZero && !previousVideoFrame.height.isZero {
- transition.animatePositionAdditive(node: self.videoTransformContainer, offset: CGPoint(x: previousVideoFrame.midX - videoFrame.midX, y: previousVideoFrame.midY - videoFrame.midY))
- transition.animateTransformScale(node: self.videoTransformContainer, from: previousVideoFrame.height / videoFrame.height)
+ let path = CGMutablePath()
+ path.addEllipse(in: CGRect(origin: CGPoint(), size: fromRect.size))
+ maskLayer.path = path
+
+ self.layer.mask = maskLayer
+
+ let topLeft = CGPoint(x: 0.0, y: 0.0)
+ let topRight = CGPoint(x: self.bounds.width, y: 0.0)
+ let bottomLeft = CGPoint(x: 0.0, y: self.bounds.height)
+ let bottomRight = CGPoint(x: self.bounds.width, y: self.bounds.height)
+
+ func distance(_ v1: CGPoint, _ v2: CGPoint) -> CGFloat {
+ let dx = v1.x - v2.x
+ let dy = v1.y - v2.y
+ return sqrt(dx * dx + dy * dy)
}
- self.videoView.view.frame = videoFrame
+ var maxRadius = distance(toRect.center, topLeft)
+ maxRadius = max(maxRadius, distance(toRect.center, topRight))
+ maxRadius = max(maxRadius, distance(toRect.center, bottomLeft))
+ maxRadius = max(maxRadius, distance(toRect.center, bottomRight))
+ maxRadius = ceil(maxRadius)
+
+ let targetFrame = CGRect(origin: CGPoint(x: toRect.center.x - maxRadius, y: toRect.center.y - maxRadius), size: CGSize(width: maxRadius * 2.0, height: maxRadius * 2.0))
+
+ let transition: ContainedViewLayoutTransition = .animated(duration: 0.3, curve: .easeInOut)
+ transition.updatePosition(layer: maskLayer, position: targetFrame.center)
+ transition.updateTransformScale(layer: maskLayer, scale: maxRadius * 2.0 / fromRect.width, completion: { [weak self] _ in
+ self?.layer.mask = nil
+ })
+ }
+
+ func updateLayout(size: CGSize, cornerRadius: CGFloat, transition: ContainedViewLayoutTransition) {
+ self.currentCornerRadius = cornerRadius
+
+ var rotationAngle: CGFloat
+ var rotateFrame: Bool
+ switch self.currentOrientation {
+ case .rotation0:
+ rotationAngle = 0.0
+ rotateFrame = false
+ case .rotation90:
+ rotationAngle = -CGFloat.pi / 2.0
+ rotateFrame = true
+ case .rotation180:
+ rotationAngle = -CGFloat.pi
+ rotateFrame = false
+ case .rotation270:
+ rotationAngle = -CGFloat.pi * 3.0 / 2.0
+ rotateFrame = true
+ }
+ var originalRotateFrame = rotateFrame
+ if size.width > size.height {
+ rotateFrame = !rotateFrame
+ if rotateFrame {
+ originalRotateFrame = true
+ }
+ } else {
+ if rotateFrame {
+ originalRotateFrame = false
+ }
+ }
+ let videoFrame: CGRect
+ let scale: CGFloat
+ if rotateFrame {
+ let frameSize = CGSize(width: size.height, height: size.width).aspectFitted(size)
+ videoFrame = CGRect(origin: CGPoint(x: floor((size.width - frameSize.width) / 2.0), y: floor((size.height - frameSize.height) / 2.0)), size: frameSize)
+ if size.width > size.height {
+ scale = frameSize.height / size.width
+ } else {
+ scale = frameSize.width / size.height
+ }
+ } else {
+ videoFrame = CGRect(origin: CGPoint(), size: size)
+ if size.width > size.height {
+ scale = 1.0
+ } else {
+ scale = 1.0
+ }
+ }
+
+ let videoPausedSize = self.videoPausedNode.updateLayout(CGSize(width: size.width - 16.0, height: 100.0))
+ transition.updateFrame(node: self.videoPausedNode, frame: CGRect(origin: CGPoint(x: floor((size.width - videoPausedSize.width) / 2.0), y: floor((size.height - videoPausedSize.height) / 2.0)), size: videoPausedSize))
+
+ let previousVideoFrame = self.videoTransformContainer.frame
+ self.videoTransformContainer.bounds = CGRect(origin: CGPoint(), size: size)
+ if transition.isAnimated && !videoFrame.height.isZero && !previousVideoFrame.height.isZero {
+ transition.animateTransformScale(node: self.videoTransformContainer, from: previousVideoFrame.height / size.height, additive: true)
+ }
+ transition.updatePosition(node: self.videoTransformContainer, position: videoFrame.center)
+ transition.updateSublayerTransformScale(node: self.videoTransformContainer, scale: scale)
+
+ let localVideoSize = originalRotateFrame ? CGSize(width: size.height, height: size.width) : size
+ let localVideoFrame = CGRect(origin: CGPoint(x: floor((size.width - localVideoSize.width) / 2.0), y: floor((size.height - localVideoSize.height) / 2.0)), size: localVideoSize)
+
+ self.videoView.view.bounds = localVideoFrame
+ self.videoView.view.center = localVideoFrame.center
+ transition.updateTransformRotation(view: self.videoView.view, angle: rotationAngle)
if let effectView = self.effectView {
- effectView.frame = videoFrame
- transition.animatePositionAdditive(layer: effectView.layer, offset: CGPoint(x: previousVideoFrame.midX - videoFrame.midX, y: previousVideoFrame.midY - videoFrame.midY))
- transition.animateTransformScale(view: effectView, from: previousVideoFrame.height / videoFrame.height)
+ transition.updateFrame(view: effectView, frame: videoFrame)
}
transition.updateCornerRadius(layer: self.videoTransformContainer.layer, cornerRadius: self.currentCornerRadius)
if let effectView = self.effectView {
transition.updateCornerRadius(layer: effectView.layer, cornerRadius: self.currentCornerRadius)
}
+
+ transition.updateCornerRadius(layer: self.layer, cornerRadius: self.currentCornerRadius)
}
- func updateIsBlurred(isBlurred: Bool) {
+ func updateIsBlurred(isBlurred: Bool, light: Bool = false, animated: Bool = true) {
if self.isBlurred == isBlurred {
return
}
@@ -111,21 +253,43 @@ private final class CallVideoNode: ASDisplayNode {
effectView.clipsToBounds = true
effectView.layer.cornerRadius = self.currentCornerRadius
self.effectView = effectView
- effectView.frame = self.videoView.view.frame
- self.view.addSubview(effectView)
+ effectView.frame = self.videoTransformContainer.bounds
+ self.videoTransformContainer.view.addSubview(effectView)
+ }
+ if animated {
+ UIView.animate(withDuration: 0.3, animations: {
+ self.videoPausedNode.alpha = 1.0
+ self.effectView?.effect = UIBlurEffect(style: light ? .light : .dark)
+ })
+ } else {
+ self.effectView?.effect = UIBlurEffect(style: light ? .light : .dark)
}
- UIView.animate(withDuration: 0.3, animations: {
- self.effectView?.effect = UIBlurEffect(style: .dark)
- })
} else if let effectView = self.effectView {
self.effectView = nil
UIView.animate(withDuration: 0.3, animations: {
+ self.videoPausedNode.alpha = 0.0
effectView.effect = nil
}, completion: { [weak effectView] _ in
effectView?.removeFromSuperview()
})
}
}
+
+ func flip(withBackground: Bool) {
+ if withBackground {
+ self.backgroundColor = .black
+ }
+ UIView.transition(with: withBackground ? self.videoTransformContainer.view : self.view, duration: 0.4, options: [.transitionFlipFromLeft, .curveEaseOut], animations: {
+ UIView.performWithoutAnimation {
+ self.updateIsBlurred(isBlurred: true, light: true, animated: false)
+ }
+ }) { finished in
+ self.backgroundColor = nil
+ Queue.mainQueue().after(0.5) {
+ self.updateIsBlurred(isBlurred: false)
+ }
+ }
+ }
}
final class CallControllerNode: ViewControllerTracingNode, CallControllerNodeProtocol {
@@ -152,13 +316,27 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
private let containerNode: ASDisplayNode
private let imageNode: TransformImageNode
- private let dimNode: ASDisplayNode
+ private let dimNode: ASImageNode
+ private var candidateIncomingVideoNodeValue: CallVideoNode?
private var incomingVideoNodeValue: CallVideoNode?
private var incomingVideoViewRequested: Bool = false
+ private var candidateOutgoingVideoNodeValue: CallVideoNode?
private var outgoingVideoNodeValue: CallVideoNode?
private var outgoingVideoViewRequested: Bool = false
+ private var removedMinimizedVideoNodeValue: CallVideoNode?
+ private var removedExpandedVideoNodeValue: CallVideoNode?
+
+ private var isRequestingVideo: Bool = false
+ private var animateRequestedVideoOnce: Bool = false
+
+ private var hiddenUIForActiveVideoCallOnce: Bool = false
+ private var hideUIForActiveVideoCallTimer: SwiftSignalKit.Timer?
+
+ private var displayedCameraConfirmation: Bool = false
+ private var displayedCameraTooltip: Bool = false
+
private var expandedVideoNode: CallVideoNode?
private var minimizedVideoNode: CallVideoNode?
private var disableAnimationForExpandedVideoOnce: Bool = false
@@ -168,20 +346,24 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
private let backButtonArrowNode: ASImageNode
private let backButtonNode: HighlightableButtonNode
private let statusNode: CallControllerStatusNode
- private let videoPausedNode: ImmediateTextNode
+ private let toastNode: CallControllerToastContainerNode
private let buttonsNode: CallControllerButtonsNode
private var keyPreviewNode: CallControllerKeyPreviewNode?
private var debugNode: CallDebugNode?
private var keyTextData: (Data, String)?
- private let keyButtonNode: HighlightableButtonNode
+ private let keyButtonNode: CallControllerKeyButton
private var validLayout: (ContainerViewLayout, CGFloat)?
+ private var disableActionsUntilTimestamp: Double = 0.0
+
+ private var displayedVersionOutdatedAlert: Bool = false
var isMuted: Bool = false {
didSet {
self.buttonsNode.isMuted = self.isMuted
+ self.updateToastContent()
if let (layout, navigationBarHeight) = self.validLayout {
self.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .animated(duration: 0.3, curve: .easeInOut))
}
@@ -195,14 +377,18 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
var toggleMute: (() -> Void)?
var setCurrentAudioOutput: ((AudioSessionOutput) -> Void)?
- var beginAudioOuputSelection: (() -> Void)?
+ var beginAudioOuputSelection: ((Bool) -> Void)?
var acceptCall: (() -> Void)?
var endCall: (() -> Void)?
- var setIsVideoPaused: ((Bool) -> Void)?
var back: (() -> Void)?
var presentCallRating: ((CallId) -> Void)?
var callEnded: ((Bool) -> Void)?
var dismissedInteractively: (() -> Void)?
+ var present: ((ViewController) -> Void)?
+ var dismissAllTooltips: (() -> Void)?
+
+ private var toastContent: CallControllerToastContent?
+ private var displayToastsAfterTimestamp: Double?
private var buttonsMode: CallControllerButtonsMode?
@@ -233,15 +419,13 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
self.containerTransformationNode.clipsToBounds = true
self.containerNode = ASDisplayNode()
- if self.shouldStayHiddenUntilConnection {
- self.containerNode.alpha = 0.0
- }
self.imageNode = TransformImageNode()
self.imageNode.contentAnimations = [.subsequentUpdates]
- self.dimNode = ASDisplayNode()
+ self.dimNode = ASImageNode()
+ self.dimNode.contentMode = .scaleToFill
self.dimNode.isUserInteractionEnabled = false
- self.dimNode.backgroundColor = UIColor(white: 0.0, alpha: 0.4)
+ self.dimNode.backgroundColor = UIColor(white: 0.0, alpha: 0.3)
self.backButtonArrowNode = ASImageNode()
self.backButtonArrowNode.displayWithoutProcessing = true
@@ -251,11 +435,9 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
self.statusNode = CallControllerStatusNode()
- self.videoPausedNode = ImmediateTextNode()
- self.videoPausedNode.alpha = 0.0
-
self.buttonsNode = CallControllerButtonsNode(strings: self.presentationData.strings)
- self.keyButtonNode = HighlightableButtonNode()
+ self.toastNode = CallControllerToastContainerNode(strings: self.presentationData.strings)
+ self.keyButtonNode = CallControllerKeyButton()
super.init()
@@ -285,31 +467,35 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
self.containerNode.addSubnode(self.imageNode)
self.containerNode.addSubnode(self.dimNode)
self.containerNode.addSubnode(self.statusNode)
- self.containerNode.addSubnode(self.videoPausedNode)
self.containerNode.addSubnode(self.buttonsNode)
+ self.containerNode.addSubnode(self.toastNode)
self.containerNode.addSubnode(self.keyButtonNode)
self.containerNode.addSubnode(self.backButtonArrowNode)
self.containerNode.addSubnode(self.backButtonNode)
self.buttonsNode.mute = { [weak self] in
self?.toggleMute?()
+ self?.cancelScheduledUIHiding()
}
self.buttonsNode.speaker = { [weak self] in
- self?.beginAudioOuputSelection?()
+ guard let strongSelf = self else {
+ return
+ }
+ strongSelf.beginAudioOuputSelection?(strongSelf.hasVideoNodes)
+ strongSelf.cancelScheduledUIHiding()
}
-
- self.buttonsNode.end = { [weak self] in
- self?.endCall?()
- }
-
- self.buttonsNode.accept = { [weak self] in
+
+ self.buttonsNode.acceptOrEnd = { [weak self] in
guard let strongSelf = self, let callState = strongSelf.callState else {
return
}
switch callState.state {
case .active, .connecting, .reconnecting:
- strongSelf.call.acceptVideo()
+ strongSelf.endCall?()
+ strongSelf.cancelScheduledUIHiding()
+ case .requesting:
+ strongSelf.endCall?()
case .ringing:
strongSelf.acceptCall?()
default:
@@ -317,37 +503,106 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
}
}
+ self.buttonsNode.decline = { [weak self] in
+ self?.endCall?()
+ }
+
self.buttonsNode.toggleVideo = { [weak self] in
- guard let strongSelf = self else {
+ guard let strongSelf = self, let callState = strongSelf.callState else {
return
}
- if strongSelf.outgoingVideoNodeValue == nil {
- strongSelf.call.requestVideo()
- } else {
- strongSelf.isVideoPaused = !strongSelf.isVideoPaused
- strongSelf.outgoingVideoNodeValue?.updateIsBlurred(isBlurred: strongSelf.isVideoPaused)
- strongSelf.buttonsNode.isCameraPaused = strongSelf.isVideoPaused
- strongSelf.setIsVideoPaused?(strongSelf.isVideoPaused)
-
- if let (layout, navigationBarHeight) = strongSelf.validLayout {
- strongSelf.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .animated(duration: 0.3, curve: .easeInOut))
+ switch callState.state {
+ case .active:
+ if strongSelf.outgoingVideoNodeValue == nil {
+ let proceed = {
+ strongSelf.displayedCameraConfirmation = true
+ switch callState.videoState {
+ case .inactive:
+ strongSelf.isRequestingVideo = true
+ strongSelf.updateButtonsMode()
+ default:
+ break
+ }
+ strongSelf.call.requestVideo()
+ }
+
+ if strongSelf.displayedCameraConfirmation {
+ proceed()
+ } else {
+ strongSelf.present?(textAlertController(sharedContext: strongSelf.sharedContext, title: nil, text: strongSelf.presentationData.strings.Call_CameraConfirmationText, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Call_CameraConfirmationConfirm, action: {
+ proceed()
+ })]))
+ }
+ } else {
+ strongSelf.call.disableVideo()
+ strongSelf.cancelScheduledUIHiding()
}
+ default:
+ break
}
}
self.buttonsNode.rotateCamera = { [weak self] in
- self?.call.switchVideoCamera()
+ guard let strongSelf = self, !strongSelf.areUserActionsDisabledNow() else {
+ return
+ }
+ strongSelf.disableActionsUntilTimestamp = CACurrentMediaTime() + 1.0
+ if let outgoingVideoNode = strongSelf.outgoingVideoNodeValue {
+ outgoingVideoNode.flip(withBackground: outgoingVideoNode !== strongSelf.minimizedVideoNode)
+ }
+ strongSelf.call.switchVideoCamera()
+ if let _ = strongSelf.outgoingVideoNodeValue {
+ if let (layout, navigationBarHeight) = strongSelf.validLayout {
+ strongSelf.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .immediate)
+ }
+ }
+ strongSelf.cancelScheduledUIHiding()
}
self.keyButtonNode.addTarget(self, action: #selector(self.keyPressed), forControlEvents: .touchUpInside)
self.backButtonNode.addTarget(self, action: #selector(self.backPressed), forControlEvents: .touchUpInside)
+
+ if shouldStayHiddenUntilConnection {
+ self.containerNode.alpha = 0.0
+ Queue.mainQueue().after(3.0, { [weak self] in
+ self?.containerNode.alpha = 1.0
+ self?.animateIn()
+ })
+ } else if call.isVideo && call.isOutgoing {
+ self.containerNode.alpha = 0.0
+ Queue.mainQueue().after(1.0, { [weak self] in
+ self?.containerNode.alpha = 1.0
+ self?.animateIn()
+ })
+ }
+ }
+
+ func displayCameraTooltip() {
+ guard self.pictureInPictureTransitionFraction.isZero, let location = self.buttonsNode.videoButtonFrame().flatMap({ frame -> CGRect in
+ return self.buttonsNode.view.convert(frame, to: self.view)
+ }) else {
+ return
+ }
+
+ self.present?(TooltipScreen(text: self.presentationData.strings.Call_CameraTooltip, style: .light, icon: nil, location: .point(location.offsetBy(dx: 0.0, dy: -14.0)), displayDuration: .custom(5.0), shouldDismissOnTouch: { _ in
+ return .dismiss(consume: false)
+ }))
}
override func didLoad() {
super.didLoad()
- let panRecognizer = UIPanGestureRecognizer(target: self, action: #selector(self.panGesture(_:)))
+ let panRecognizer = CallPanGestureRecognizer(target: self, action: #selector(self.panGesture(_:)))
+ panRecognizer.shouldBegin = { [weak self] _ in
+ guard let strongSelf = self else {
+ return false
+ }
+ if strongSelf.areUserActionsDisabledNow() {
+ return false
+ }
+ return true
+ }
self.view.addGestureRecognizer(panRecognizer)
let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:)))
@@ -366,17 +621,16 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
self.dimNode.isHidden = true
}
+ self.toastNode.title = peer.compactDisplayTitle
self.statusNode.title = peer.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder)
if hasOther {
self.statusNode.subtitle = self.presentationData.strings.Call_AnsweringWithAccount(accountPeer.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder)).0
- if let callState = callState {
+ if let callState = self.callState {
self.updateCallState(callState)
}
}
- self.videoPausedNode.attributedText = NSAttributedString(string: self.presentationData.strings.Call_RemoteVideoPaused(peer.compactDisplayTitle).0, font: Font.regular(17.0), textColor: .white)
-
if let (layout, navigationBarHeight) = self.validLayout {
self.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .immediate)
}
@@ -387,6 +641,23 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
if self.audioOutputState?.0 != availableOutputs || self.audioOutputState?.1 != currentOutput {
self.audioOutputState = (availableOutputs, currentOutput)
self.updateButtonsMode()
+
+ self.setupAudioOutputs()
+ }
+ }
+
+ private func setupAudioOutputs() {
+ if self.outgoingVideoNodeValue != nil || self.incomingVideoNodeValue != nil || self.candidateOutgoingVideoNodeValue != nil || self.candidateIncomingVideoNodeValue != nil {
+ if let audioOutputState = self.audioOutputState, let currentOutput = audioOutputState.currentOutput {
+ switch currentOutput {
+ case .headphones, .speaker:
+ break
+ case let .port(port) where port.type == .bluetooth:
+ break
+ default:
+ self.setCurrentAudioOutput?(.speaker)
+ }
+ }
}
}
@@ -396,71 +667,186 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
let statusValue: CallControllerStatusValue
var statusReception: Int32?
- switch callState.videoState {
- case .active:
+ switch callState.remoteVideoState {
+ case .active, .paused:
if !self.incomingVideoViewRequested {
self.incomingVideoViewRequested = true
+ let delayUntilInitialized = true
self.call.makeIncomingVideoView(completion: { [weak self] incomingVideoView in
guard let strongSelf = self else {
return
}
if let incomingVideoView = incomingVideoView {
- let incomingVideoNode = CallVideoNode(videoView: incomingVideoView, isReadyUpdated: {
+ incomingVideoView.view.backgroundColor = .black
+ incomingVideoView.view.clipsToBounds = true
+
+ let applyNode: () -> Void = {
+ guard let strongSelf = self, let incomingVideoNode = strongSelf.candidateIncomingVideoNodeValue else {
+ return
+ }
+ strongSelf.candidateIncomingVideoNodeValue = nil
+
+ strongSelf.incomingVideoNodeValue = incomingVideoNode
+ if let expandedVideoNode = strongSelf.expandedVideoNode {
+ strongSelf.minimizedVideoNode = expandedVideoNode
+ strongSelf.containerNode.insertSubnode(incomingVideoNode, belowSubnode: expandedVideoNode)
+ } else {
+ strongSelf.containerNode.insertSubnode(incomingVideoNode, belowSubnode: strongSelf.dimNode)
+ }
+ strongSelf.expandedVideoNode = incomingVideoNode
+ strongSelf.updateButtonsMode(transition: .animated(duration: 0.4, curve: .spring))
+
+ strongSelf.updateDimVisibility()
+ strongSelf.maybeScheduleUIHidingForActiveVideoCall()
+ }
+
+ let incomingVideoNode = CallVideoNode(videoView: incomingVideoView, disabledText: strongSelf.presentationData.strings.Call_RemoteVideoPaused(strongSelf.peer?.compactDisplayTitle ?? "").0, assumeReadyAfterTimeout: false, isReadyUpdated: {
+ if delayUntilInitialized {
+ Queue.mainQueue().after(0.1, {
+ applyNode()
+ })
+ }
+ }, orientationUpdated: {
guard let strongSelf = self else {
return
}
if let (layout, navigationBarHeight) = strongSelf.validLayout {
- strongSelf.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .animated(duration: 0.5, curve: .spring))
+ strongSelf.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .animated(duration: 0.3, curve: .easeInOut))
}
+ }, isFlippedUpdated: { _ in
})
- strongSelf.incomingVideoNodeValue = incomingVideoNode
- strongSelf.expandedVideoNode = incomingVideoNode
- strongSelf.containerNode.insertSubnode(incomingVideoNode, aboveSubnode: strongSelf.dimNode)
- if let (layout, navigationBarHeight) = strongSelf.validLayout {
- strongSelf.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .animated(duration: 0.5, curve: .spring))
+ strongSelf.candidateIncomingVideoNodeValue = incomingVideoNode
+ strongSelf.setupAudioOutputs()
+
+ if !delayUntilInitialized {
+ applyNode()
}
}
})
}
- default:
- break
+ case .inactive:
+ self.candidateIncomingVideoNodeValue = nil
+ if let incomingVideoNodeValue = self.incomingVideoNodeValue {
+ if self.minimizedVideoNode == incomingVideoNodeValue {
+ self.minimizedVideoNode = nil
+ self.removedMinimizedVideoNodeValue = incomingVideoNodeValue
+ }
+ if self.expandedVideoNode == incomingVideoNodeValue {
+ self.expandedVideoNode = nil
+ self.removedExpandedVideoNodeValue = incomingVideoNodeValue
+
+ if let minimizedVideoNode = self.minimizedVideoNode {
+ self.expandedVideoNode = minimizedVideoNode
+ self.minimizedVideoNode = nil
+ }
+ }
+ self.incomingVideoNodeValue = nil
+ self.incomingVideoViewRequested = false
+ }
}
switch callState.videoState {
- case .active, .outgoingRequested, .incomingRequested:
+ case .active, .paused:
if !self.outgoingVideoViewRequested {
self.outgoingVideoViewRequested = true
+ let delayUntilInitialized = self.isRequestingVideo
self.call.makeOutgoingVideoView(completion: { [weak self] outgoingVideoView in
guard let strongSelf = self else {
return
}
+
if let outgoingVideoView = outgoingVideoView {
outgoingVideoView.view.backgroundColor = .black
outgoingVideoView.view.clipsToBounds = true
- if let audioOutputState = strongSelf.audioOutputState, let currentOutput = audioOutputState.currentOutput {
- switch currentOutput {
- case .speaker, .builtin:
- break
- default:
- strongSelf.setCurrentAudioOutput?(.speaker)
+
+ let applyNode: () -> Void = {
+ guard let strongSelf = self, let outgoingVideoNode = strongSelf.candidateOutgoingVideoNodeValue else {
+ return
}
+ strongSelf.candidateOutgoingVideoNodeValue = nil
+
+ if strongSelf.isRequestingVideo {
+ strongSelf.isRequestingVideo = false
+ strongSelf.animateRequestedVideoOnce = true
+ }
+
+ strongSelf.outgoingVideoNodeValue = outgoingVideoNode
+ if let expandedVideoNode = strongSelf.expandedVideoNode {
+ strongSelf.minimizedVideoNode = outgoingVideoNode
+ strongSelf.containerNode.insertSubnode(outgoingVideoNode, aboveSubnode: expandedVideoNode)
+ } else {
+ strongSelf.expandedVideoNode = outgoingVideoNode
+ strongSelf.containerNode.insertSubnode(outgoingVideoNode, belowSubnode: strongSelf.dimNode)
+ }
+ strongSelf.updateButtonsMode(transition: .animated(duration: 0.4, curve: .spring))
+
+ strongSelf.updateDimVisibility()
+ strongSelf.maybeScheduleUIHidingForActiveVideoCall()
}
- let outgoingVideoNode = CallVideoNode(videoView: outgoingVideoView, isReadyUpdated: {})
- strongSelf.outgoingVideoNodeValue = outgoingVideoNode
- strongSelf.minimizedVideoNode = outgoingVideoNode
- if let expandedVideoNode = strongSelf.expandedVideoNode {
- strongSelf.containerNode.insertSubnode(outgoingVideoNode, aboveSubnode: expandedVideoNode)
- } else {
- strongSelf.containerNode.insertSubnode(outgoingVideoNode, aboveSubnode: strongSelf.dimNode)
- }
- if let (layout, navigationBarHeight) = strongSelf.validLayout {
- strongSelf.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .animated(duration: 0.4, curve: .spring))
+
+ let outgoingVideoNode = CallVideoNode(videoView: outgoingVideoView, disabledText: nil, assumeReadyAfterTimeout: true, isReadyUpdated: {
+ if delayUntilInitialized {
+ Queue.mainQueue().after(0.4, {
+ applyNode()
+ })
+ }
+ }, orientationUpdated: {
+ guard let strongSelf = self else {
+ return
+ }
+ if let (layout, navigationBarHeight) = strongSelf.validLayout {
+ strongSelf.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .animated(duration: 0.3, curve: .easeInOut))
+ }
+ }, isFlippedUpdated: { videoNode in
+ guard let _ = self else {
+ return
+ }
+ /*if videoNode === strongSelf.minimizedVideoNode, let tempView = videoNode.view.snapshotView(afterScreenUpdates: true) {
+ videoNode.view.superview?.insertSubview(tempView, aboveSubview: videoNode.view)
+ videoNode.view.frame = videoNode.frame
+ let transitionOptions: UIView.AnimationOptions = [.transitionFlipFromRight, .showHideTransitionViews]
+
+ UIView.transition(with: tempView, duration: 1.0, options: transitionOptions, animations: {
+ tempView.isHidden = true
+ }, completion: { [weak tempView] _ in
+ tempView?.removeFromSuperview()
+ })
+
+ videoNode.view.isHidden = true
+ UIView.transition(with: videoNode.view, duration: 1.0, options: transitionOptions, animations: {
+ videoNode.view.isHidden = false
+ })
+ }*/
+ })
+
+ strongSelf.candidateOutgoingVideoNodeValue = outgoingVideoNode
+ strongSelf.setupAudioOutputs()
+
+ if !delayUntilInitialized {
+ applyNode()
}
}
})
}
- default:
- break
+ case .notAvailable, .inactive:
+ self.candidateOutgoingVideoNodeValue = nil
+ if let outgoingVideoNodeValue = self.outgoingVideoNodeValue {
+ if self.minimizedVideoNode == outgoingVideoNodeValue {
+ self.minimizedVideoNode = nil
+ self.removedMinimizedVideoNodeValue = outgoingVideoNodeValue
+ }
+ if self.expandedVideoNode == self.outgoingVideoNodeValue {
+ self.expandedVideoNode = nil
+ self.removedExpandedVideoNodeValue = outgoingVideoNodeValue
+
+ if let minimizedVideoNode = self.minimizedVideoNode {
+ self.expandedVideoNode = minimizedVideoNode
+ self.minimizedVideoNode = nil
+ }
+ }
+ self.outgoingVideoNodeValue = nil
+ self.outgoingVideoViewRequested = false
+ }
}
if let incomingVideoNode = self.incomingVideoNodeValue {
@@ -470,24 +856,15 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
default:
let isActive: Bool
switch callState.remoteVideoState {
- case .inactive:
+ case .inactive, .paused:
isActive = false
case .active:
isActive = true
}
incomingVideoNode.updateIsBlurred(isBlurred: !isActive)
- if isActive != self.videoPausedNode.alpha.isZero {
- if isActive {
- self.videoPausedNode.alpha = 0.0
- self.videoPausedNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3)
- } else {
- self.videoPausedNode.alpha = 1.0
- self.videoPausedNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
- }
- }
}
}
-
+
switch callState.state {
case .waiting, .connecting:
statusValue = .text(string: self.presentationData.strings.Call_StatusConnecting, displayLogo: false)
@@ -509,8 +886,20 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
case .hungUp, .missed:
statusValue = .text(string: self.presentationData.strings.Call_StatusEnded, displayLogo: false)
}
- case .error:
- statusValue = .text(string: self.presentationData.strings.Call_StatusFailed, displayLogo: false)
+ case let .error(error):
+ let text = self.presentationData.strings.Call_StatusFailed
+ switch error {
+ case .notSupportedByPeer:
+ if !self.displayedVersionOutdatedAlert, let peer = self.peer {
+ self.displayedVersionOutdatedAlert = true
+
+ self.present?(textAlertController(sharedContext: self.sharedContext, title: nil, text: self.presentationData.strings.Call_ParticipantVersionOutdatedError(peer.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder)).0, actions: [TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: {
+ })]))
+ }
+ default:
+ break
+ }
+ statusValue = .text(string: text, displayLogo: false)
}
} else {
statusValue = .text(string: self.presentationData.strings.Call_StatusEnded, displayLogo: false)
@@ -525,7 +914,7 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
if !self.statusNode.subtitle.isEmpty {
text += "\n\(self.statusNode.subtitle)"
}
- statusValue = .text(string: text, displayLogo: true)
+ statusValue = .text(string: text, displayLogo: false)
case .active(let timestamp, let reception, let keyVisualHash), .reconnecting(let timestamp, let reception, let keyVisualHash):
let strings = self.presentationData.strings
var isReconnecting = false
@@ -535,36 +924,28 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
if self.keyTextData?.0 != keyVisualHash {
let text = stringForEmojiHashOfData(keyVisualHash, 4)!
self.keyTextData = (keyVisualHash, text)
-
- self.keyButtonNode.setAttributedTitle(NSAttributedString(string: text, attributes: [NSAttributedString.Key.font: Font.regular(22.0), NSAttributedString.Key.kern: 2.5 as NSNumber]), for: [])
+
+ self.keyButtonNode.key = text
let keyTextSize = self.keyButtonNode.measure(CGSize(width: 200.0, height: 200.0))
- self.keyButtonNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
self.keyButtonNode.frame = CGRect(origin: self.keyButtonNode.frame.origin, size: keyTextSize)
+ self.keyButtonNode.animateIn()
+
if let (layout, navigationBarHeight) = self.validLayout {
self.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .immediate)
}
}
- switch callState.videoState {
- case .notAvailable, .active, .possible:
- statusValue = .timer({ value in
- if isReconnecting {
- return strings.Call_StatusConnecting
- } else {
- return value
- }
- }, timestamp)
- statusReception = reception
- case .incomingRequested:
- var text: String
- text = self.presentationData.strings.Call_IncomingVideoCall
- if !self.statusNode.subtitle.isEmpty {
- text += "\n\(self.statusNode.subtitle)"
+
+ statusValue = .timer({ value, measure in
+ if isReconnecting || (self.outgoingVideoViewRequested && value == "00:00" && !measure) {
+ return strings.Call_StatusConnecting
+ } else {
+ return value
}
- statusValue = .text(string: text, displayLogo: true)
- case .outgoingRequested:
- statusValue = .text(string: self.presentationData.strings.Call_StatusRequesting, displayLogo: false)
+ }, timestamp)
+ if case .active = callState.state {
+ statusReception = reception
}
}
if self.shouldStayHiddenUntilConnection {
@@ -587,7 +968,20 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
}
}
+ self.updateToastContent()
self.updateButtonsMode()
+ self.updateDimVisibility()
+
+ if self.incomingVideoViewRequested && self.outgoingVideoViewRequested {
+ self.displayedCameraTooltip = true
+ self.displayedCameraConfirmation = true
+ }
+ if self.incomingVideoViewRequested && !self.outgoingVideoViewRequested && !self.displayedCameraTooltip && (self.toastContent?.isEmpty ?? true) {
+ self.displayedCameraTooltip = true
+ Queue.mainQueue().after(2.0) {
+ self.displayCameraTooltip()
+ }
+ }
if case let .terminated(id, _, reportRating) = callState.state, let callId = id {
let presentRating = reportRating || self.forceReportRating
@@ -598,15 +992,109 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
}
}
+ private func updateToastContent() {
+ guard let callState = self.callState else {
+ return
+ }
+ if case .terminating = callState.state {
+ } else if case .terminated = callState.state {
+ } else {
+ var toastContent: CallControllerToastContent = []
+ if case .active = callState.state {
+ if let displayToastsAfterTimestamp = self.displayToastsAfterTimestamp {
+ if CACurrentMediaTime() > displayToastsAfterTimestamp {
+ if case .inactive = callState.remoteVideoState, self.hasVideoNodes {
+ toastContent.insert(.camera)
+ }
+ if case .muted = callState.remoteAudioState {
+ toastContent.insert(.microphone)
+ }
+ if case .low = callState.remoteBatteryLevel {
+ toastContent.insert(.battery)
+ }
+ }
+ } else {
+ self.displayToastsAfterTimestamp = CACurrentMediaTime() + 1.5
+ }
+ }
+ if self.isMuted, let (availableOutputs, _) = self.audioOutputState, availableOutputs.count > 2 {
+ toastContent.insert(.mute)
+ }
+ self.toastContent = toastContent
+ }
+ }
+
+ private func updateDimVisibility(transition: ContainedViewLayoutTransition = .animated(duration: 0.3, curve: .easeInOut)) {
+ guard let callState = self.callState else {
+ return
+ }
+
+ var visible = true
+ if case .active = callState.state, self.incomingVideoNodeValue != nil || self.outgoingVideoNodeValue != nil {
+ visible = false
+ }
+
+ let currentVisible = self.dimNode.image == nil
+ if visible != currentVisible {
+ let color = visible ? UIColor(rgb: 0x000000, alpha: 0.3) : UIColor.clear
+ let image: UIImage? = visible ? nil : generateGradientImage(size: CGSize(width: 1.0, height: 640.0), colors: [UIColor.black.withAlphaComponent(0.3), UIColor.clear, UIColor.clear, UIColor.black.withAlphaComponent(0.3)], locations: [0.0, 0.22, 0.7, 1.0])
+ if case let .animated(duration, _) = transition {
+ UIView.transition(with: self.dimNode.view, duration: duration, options: .transitionCrossDissolve, animations: {
+ self.dimNode.backgroundColor = color
+ self.dimNode.image = image
+ }, completion: nil)
+ } else {
+ self.dimNode.backgroundColor = color
+ self.dimNode.image = image
+ }
+ }
+ self.statusNode.setVisible(visible || self.keyPreviewNode != nil, transition: transition)
+ }
+
+ private func maybeScheduleUIHidingForActiveVideoCall() {
+ guard let callState = self.callState, case .active = callState.state, self.incomingVideoNodeValue != nil && self.outgoingVideoNodeValue != nil, !self.hiddenUIForActiveVideoCallOnce && self.keyPreviewNode == nil else {
+ return
+ }
+
+ let timer = SwiftSignalKit.Timer(timeout: 3.0, repeat: false, completion: { [weak self] in
+ if let strongSelf = self {
+ var updated = false
+ if let callState = strongSelf.callState, !strongSelf.isUIHidden {
+ switch callState.state {
+ case .active, .connecting, .reconnecting:
+ strongSelf.isUIHidden = true
+ updated = true
+ default:
+ break
+ }
+ }
+ if updated, let (layout, navigationBarHeight) = strongSelf.validLayout {
+ strongSelf.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .animated(duration: 0.3, curve: .easeInOut))
+ }
+ strongSelf.hideUIForActiveVideoCallTimer = nil
+ }
+ }, queue: Queue.mainQueue())
+ timer.start()
+ self.hideUIForActiveVideoCallTimer = timer
+ self.hiddenUIForActiveVideoCallOnce = true
+ }
+
+ private func cancelScheduledUIHiding() {
+ self.hideUIForActiveVideoCallTimer?.invalidate()
+ self.hideUIForActiveVideoCallTimer = nil
+ }
+
private var buttonsTerminationMode: CallControllerButtonsMode?
- private func updateButtonsMode() {
+ private func updateButtonsMode(transition: ContainedViewLayoutTransition = .animated(duration: 0.3, curve: .spring)) {
guard let callState = self.callState else {
return
}
var mode: CallControllerButtonsSpeakerMode = .none
+ var hasAudioRouteMenu: Bool = false
if let (availableOutputs, maybeCurrentOutput) = self.audioOutputState, let currentOutput = maybeCurrentOutput {
+ hasAudioRouteMenu = availableOutputs.count > 2
switch currentOutput {
case .builtin:
mode = .builtin
@@ -614,71 +1102,79 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
mode = .speaker
case .headphones:
mode = .headphones
- case .port:
- mode = .bluetooth
+ case let .port(port):
+ var type: CallControllerButtonsSpeakerMode.BluetoothType = .generic
+ let portName = port.name.lowercased()
+ if portName.contains("airpods pro") {
+ type = .airpodsPro
+ } else if portName.contains("airpods") {
+ type = .airpods
+ }
+ mode = .bluetooth(type)
}
if availableOutputs.count <= 1 {
mode = .none
}
}
- let mappedVideoState: CallControllerButtonsMode.VideoState
+ var mappedVideoState = CallControllerButtonsMode.VideoState(isAvailable: false, isCameraActive: self.outgoingVideoNodeValue != nil, canChangeStatus: false, hasVideo: self.outgoingVideoNodeValue != nil || self.incomingVideoNodeValue != nil, isInitializingCamera: self.isRequestingVideo)
switch callState.videoState {
case .notAvailable:
- mappedVideoState = .notAvailable
- case .possible:
- mappedVideoState = .possible
- case .outgoingRequested:
- mappedVideoState = .outgoingRequested
- case .incomingRequested:
- mappedVideoState = .incomingRequested
- case .active:
- mappedVideoState = .active
+ break
+ case .inactive:
+ mappedVideoState.isAvailable = true
+ mappedVideoState.canChangeStatus = true
+ case .active, .paused:
+ mappedVideoState.isAvailable = true
+ mappedVideoState.canChangeStatus = true
}
switch callState.state {
case .ringing:
- self.buttonsMode = .incoming(speakerMode: mode, videoState: mappedVideoState)
+ self.buttonsMode = .incoming(speakerMode: mode, hasAudioRouteMenu: hasAudioRouteMenu, videoState: mappedVideoState)
self.buttonsTerminationMode = buttonsMode
case .waiting, .requesting:
- self.buttonsMode = .outgoingRinging(speakerMode: mode, videoState: mappedVideoState)
+ self.buttonsMode = .outgoingRinging(speakerMode: mode, hasAudioRouteMenu: hasAudioRouteMenu, videoState: mappedVideoState)
self.buttonsTerminationMode = buttonsMode
case .active, .connecting, .reconnecting:
- self.buttonsMode = .active(speakerMode: mode, videoState: mappedVideoState)
+ self.buttonsMode = .active(speakerMode: mode, hasAudioRouteMenu: hasAudioRouteMenu, videoState: mappedVideoState)
self.buttonsTerminationMode = buttonsMode
case .terminating, .terminated:
if let buttonsTerminationMode = self.buttonsTerminationMode {
self.buttonsMode = buttonsTerminationMode
} else {
- self.buttonsMode = .active(speakerMode: mode, videoState: mappedVideoState)
+ self.buttonsMode = .active(speakerMode: mode, hasAudioRouteMenu: hasAudioRouteMenu, videoState: mappedVideoState)
}
}
-
+
if let (layout, navigationHeight) = self.validLayout {
- self.pictureInPictureTransitionFraction = 0.0
-
- self.containerLayoutUpdated(layout, navigationBarHeight: navigationHeight, transition: .animated(duration: 0.3, curve: .spring))
+ self.containerLayoutUpdated(layout, navigationBarHeight: navigationHeight, transition: transition)
}
}
func animateIn() {
- var bounds = self.bounds
- bounds.origin = CGPoint()
- self.bounds = bounds
- self.layer.removeAnimation(forKey: "bounds")
- self.statusBar.layer.removeAnimation(forKey: "opacity")
- self.containerNode.layer.removeAnimation(forKey: "opacity")
- self.containerNode.layer.removeAnimation(forKey: "scale")
- self.statusBar.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
- if !self.shouldStayHiddenUntilConnection {
- self.containerNode.layer.animateScale(from: 1.04, to: 1.0, duration: 0.3)
- self.containerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
+ if !self.containerNode.alpha.isZero {
+ var bounds = self.bounds
+ bounds.origin = CGPoint()
+ self.bounds = bounds
+ self.layer.removeAnimation(forKey: "bounds")
+ self.statusBar.layer.removeAnimation(forKey: "opacity")
+ self.containerNode.layer.removeAnimation(forKey: "opacity")
+ self.containerNode.layer.removeAnimation(forKey: "scale")
+ self.statusBar.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
+ if !self.shouldStayHiddenUntilConnection {
+ self.containerNode.layer.animateScale(from: 1.04, to: 1.0, duration: 0.3)
+ self.containerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
+ }
}
}
func animateOut(completion: @escaping () -> Void) {
self.statusBar.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false)
if !self.shouldStayHiddenUntilConnection || self.containerNode.alpha > 0.0 {
- self.containerNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false)
+ self.containerNode.layer.allowsGroupOpacity = true
+ self.containerNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { [weak self] _ in
+ self?.containerNode.layer.allowsGroupOpacity = true
+ })
self.containerNode.layer.animateScale(from: 1.0, to: 1.04, duration: 0.3, removeOnCompletion: false, completion: { _ in
completion()
})
@@ -696,20 +1192,19 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
}
private func calculatePreviewVideoRect(layout: ContainerViewLayout, navigationHeight: CGFloat) -> CGRect {
- var uiDisplayTransition: CGFloat = self.isUIHidden ? 0.0 : 1.0
- uiDisplayTransition *= 1.0 - self.pictureInPictureTransitionFraction
-
let buttonsHeight: CGFloat = self.buttonsNode.bounds.height
+ let toastHeight: CGFloat = self.toastNode.bounds.height
+ let toastInset = (toastHeight > 0.0 ? toastHeight + 22.0 : 0.0)
var fullInsets = layout.insets(options: .statusBar)
-
+
var cleanInsets = fullInsets
- cleanInsets.bottom = layout.intrinsicInsets.bottom
+ cleanInsets.bottom = max(layout.intrinsicInsets.bottom, 20.0) + toastInset
cleanInsets.left = 20.0
cleanInsets.right = 20.0
fullInsets.top += 44.0 + 8.0
- fullInsets.bottom = buttonsHeight + 27.0
+ fullInsets.bottom = buttonsHeight + 22.0 + toastInset
fullInsets.left = 20.0
fullInsets.right = 20.0
@@ -723,7 +1218,16 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
insets.right = interpolate(from: expandedInset, to: insets.right, value: 1.0 - self.pictureInPictureTransitionFraction)
let previewVideoSide = interpolate(from: 350.0, to: 200.0, value: 1.0 - self.pictureInPictureTransitionFraction)
- let previewVideoSize = layout.size.aspectFitted(CGSize(width: previewVideoSide, height: previewVideoSide))
+ var previewVideoSize = layout.size.aspectFitted(CGSize(width: previewVideoSide, height: previewVideoSide))
+ previewVideoSize = CGSize(width: 30.0, height: 45.0).aspectFitted(previewVideoSize)
+ if let minimizedVideoNode = minimizedVideoNode {
+ switch minimizedVideoNode.currentOrientation {
+ case .rotation90, .rotation270:
+ previewVideoSize = CGSize(width: previewVideoSize.height, height: previewVideoSize.width)
+ default:
+ break
+ }
+ }
let previewVideoY: CGFloat
let previewVideoX: CGFloat
@@ -768,8 +1272,21 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) {
self.validLayout = (layout, navigationBarHeight)
- var uiDisplayTransition: CGFloat = self.isUIHidden ? 0.0 : 1.0
- uiDisplayTransition *= 1.0 - self.pictureInPictureTransitionFraction
+ var isUIHidden = self.isUIHidden
+ switch self.callState?.state {
+ case .terminated, .terminating:
+ isUIHidden = false
+ default:
+ break
+ }
+
+ var uiDisplayTransition: CGFloat = isUIHidden ? 0.0 : 1.0
+ let pipTransitionAlpha: CGFloat = 1.0 - self.pictureInPictureTransitionFraction
+ uiDisplayTransition *= pipTransitionAlpha
+
+ let previousVideoButtonFrame = self.buttonsNode.videoButtonFrame().flatMap { frame -> CGRect in
+ return self.buttonsNode.view.convert(frame, to: self.view)
+ }
let buttonsHeight: CGFloat
if let buttonsMode = self.buttonsMode {
@@ -778,13 +1295,22 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
buttonsHeight = 0.0
}
let defaultButtonsOriginY = layout.size.height - buttonsHeight
- let buttonsOriginY = interpolate(from: layout.size.height + 10.0, to: defaultButtonsOriginY, value: uiDisplayTransition)
+ let buttonsCollapsedOriginY = self.pictureInPictureTransitionFraction > 0.0 ? layout.size.height + 30.0 : layout.size.height + 10.0
+ let buttonsOriginY = interpolate(from: buttonsCollapsedOriginY, to: defaultButtonsOriginY, value: uiDisplayTransition)
+
+ let toastHeight = self.toastNode.updateLayout(strings: self.presentationData.strings, content: self.toastContent, constrainedWidth: layout.size.width, bottomInset: layout.intrinsicInsets.bottom + buttonsHeight, transition: transition)
+
+ let toastSpacing: CGFloat = 22.0
+ let toastCollapsedOriginY = self.pictureInPictureTransitionFraction > 0.0 ? layout.size.height : layout.size.height - max(layout.intrinsicInsets.bottom, 20.0) - toastHeight
+ let toastOriginY = interpolate(from: toastCollapsedOriginY, to: defaultButtonsOriginY - toastSpacing - toastHeight, value: uiDisplayTransition)
var overlayAlpha: CGFloat = uiDisplayTransition
+ var toastAlpha: CGFloat = pipTransitionAlpha
switch self.callState?.state {
case .terminated, .terminating:
overlayAlpha *= 0.5
+ toastAlpha *= 0.5
default:
break
}
@@ -812,15 +1338,17 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
apply()
let navigationOffset: CGFloat = max(20.0, layout.safeInsets.top)
+ let topOriginY = interpolate(from: -20.0, to: navigationOffset, value: uiDisplayTransition)
let backSize = self.backButtonNode.measure(CGSize(width: 320.0, height: 100.0))
if let image = self.backButtonArrowNode.image {
- transition.updateFrame(node: self.backButtonArrowNode, frame: CGRect(origin: CGPoint(x: 10.0, y: navigationOffset + 11.0), size: image.size))
+ transition.updateFrame(node: self.backButtonArrowNode, frame: CGRect(origin: CGPoint(x: 10.0, y: topOriginY + 11.0), size: image.size))
}
- transition.updateFrame(node: self.backButtonNode, frame: CGRect(origin: CGPoint(x: 29.0, y: navigationOffset + 11.0), size: backSize))
+ transition.updateFrame(node: self.backButtonNode, frame: CGRect(origin: CGPoint(x: 29.0, y: topOriginY + 11.0), size: backSize))
transition.updateAlpha(node: self.backButtonArrowNode, alpha: overlayAlpha)
transition.updateAlpha(node: self.backButtonNode, alpha: overlayAlpha)
+ transition.updateAlpha(node: self.toastNode, alpha: toastAlpha)
var statusOffset: CGFloat
if layout.metrics.widthClass == .regular && layout.metrics.heightClass == .regular {
@@ -845,53 +1373,106 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
transition.updateFrame(node: self.statusNode, frame: CGRect(origin: CGPoint(x: 0.0, y: statusOffset), size: CGSize(width: layout.size.width, height: statusHeight)))
transition.updateAlpha(node: self.statusNode, alpha: overlayAlpha)
- let videoPausedSize = self.videoPausedNode.updateLayout(CGSize(width: layout.size.width - 16.0, height: 100.0))
- transition.updateFrame(node: self.videoPausedNode, frame: CGRect(origin: CGPoint(x: floor((layout.size.width - videoPausedSize.width) / 2.0), y: floor((layout.size.height - videoPausedSize.height) / 2.0)), size: videoPausedSize))
-
+ transition.updateFrame(node: self.toastNode, frame: CGRect(origin: CGPoint(x: 0.0, y: toastOriginY), size: CGSize(width: layout.size.width, height: toastHeight)))
transition.updateFrame(node: self.buttonsNode, frame: CGRect(origin: CGPoint(x: 0.0, y: buttonsOriginY), size: CGSize(width: layout.size.width, height: buttonsHeight)))
transition.updateAlpha(node: self.buttonsNode, alpha: overlayAlpha)
let fullscreenVideoFrame = CGRect(origin: CGPoint(), size: layout.size)
+
let previewVideoFrame = self.calculatePreviewVideoRect(layout: layout, navigationHeight: navigationBarHeight)
+ if let removedMinimizedVideoNodeValue = self.removedMinimizedVideoNodeValue {
+ self.removedMinimizedVideoNodeValue = nil
+
+ if transition.isAnimated {
+ removedMinimizedVideoNodeValue.layer.animateScale(from: 1.0, to: 0.1, duration: 0.3, removeOnCompletion: false)
+ removedMinimizedVideoNodeValue.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { [weak removedMinimizedVideoNodeValue] _ in
+ removedMinimizedVideoNodeValue?.removeFromSupernode()
+ })
+ } else {
+ removedMinimizedVideoNodeValue.removeFromSupernode()
+ }
+ }
+
if let expandedVideoNode = self.expandedVideoNode {
+ transition.updateAlpha(node: expandedVideoNode, alpha: 1.0)
var expandedVideoTransition = transition
if expandedVideoNode.frame.isEmpty || self.disableAnimationForExpandedVideoOnce {
expandedVideoTransition = .immediate
self.disableAnimationForExpandedVideoOnce = false
}
- expandedVideoTransition.updateFrame(node: expandedVideoNode, frame: fullscreenVideoFrame)
+
+ if let removedExpandedVideoNodeValue = self.removedExpandedVideoNodeValue {
+ self.removedExpandedVideoNodeValue = nil
+
+ expandedVideoTransition.updateFrame(node: expandedVideoNode, frame: fullscreenVideoFrame, completion: { [weak removedExpandedVideoNodeValue] _ in
+ removedExpandedVideoNodeValue?.removeFromSupernode()
+ })
+ } else {
+ expandedVideoTransition.updateFrame(node: expandedVideoNode, frame: fullscreenVideoFrame)
+ }
+
expandedVideoNode.updateLayout(size: expandedVideoNode.frame.size, cornerRadius: 0.0, transition: expandedVideoTransition)
+
+ if self.animateRequestedVideoOnce {
+ self.animateRequestedVideoOnce = false
+ if expandedVideoNode === self.outgoingVideoNodeValue {
+ let videoButtonFrame = self.buttonsNode.videoButtonFrame().flatMap { frame -> CGRect in
+ return self.buttonsNode.view.convert(frame, to: self.view)
+ }
+
+ if let previousVideoButtonFrame = previousVideoButtonFrame, let videoButtonFrame = videoButtonFrame {
+ expandedVideoNode.animateRadialMask(from: previousVideoButtonFrame, to: videoButtonFrame)
+ }
+ }
+ }
+ } else {
+ if let removedExpandedVideoNodeValue = self.removedExpandedVideoNodeValue {
+ self.removedExpandedVideoNodeValue = nil
+
+ if transition.isAnimated {
+ removedExpandedVideoNodeValue.layer.animateScale(from: 1.0, to: 0.1, duration: 0.3, removeOnCompletion: false)
+ removedExpandedVideoNodeValue.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { [weak removedExpandedVideoNodeValue] _ in
+ removedExpandedVideoNodeValue?.removeFromSupernode()
+ })
+ } else {
+ removedExpandedVideoNodeValue.removeFromSupernode()
+ }
+ }
}
+
+
if let minimizedVideoNode = self.minimizedVideoNode {
+ transition.updateAlpha(node: minimizedVideoNode, alpha: pipTransitionAlpha)
var minimizedVideoTransition = transition
+ var didAppear = false
if minimizedVideoNode.frame.isEmpty {
minimizedVideoTransition = .immediate
+ didAppear = true
}
- if let expandedVideoNode = self.expandedVideoNode, expandedVideoNode.isReady {
- if self.minimizedVideoDraggingPosition == nil {
- if let animationForExpandedVideoSnapshotView = self.animationForExpandedVideoSnapshotView {
- self.containerNode.view.addSubview(animationForExpandedVideoSnapshotView)
- transition.updateAlpha(layer: animationForExpandedVideoSnapshotView.layer, alpha: 0.0, completion: { [weak animationForExpandedVideoSnapshotView] _ in
- animationForExpandedVideoSnapshotView?.removeFromSuperview()
- })
- transition.updateTransformScale(layer: animationForExpandedVideoSnapshotView.layer, scale: previewVideoFrame.width / fullscreenVideoFrame.width)
-
- transition.updatePosition(layer: animationForExpandedVideoSnapshotView.layer, position: CGPoint(x: previewVideoFrame.minX + previewVideoFrame.center.x / fullscreenVideoFrame.width * previewVideoFrame.width, y: previewVideoFrame.minY + previewVideoFrame.center.y / fullscreenVideoFrame.height * previewVideoFrame.height))
- self.animationForExpandedVideoSnapshotView = nil
- }
- minimizedVideoTransition.updateFrame(node: minimizedVideoNode, frame: previewVideoFrame)
- minimizedVideoNode.updateLayout(size: minimizedVideoNode.frame.size, cornerRadius: interpolate(from: 14.0, to: 24.0, value: self.pictureInPictureTransitionFraction), transition: minimizedVideoTransition)
+ if self.minimizedVideoDraggingPosition == nil {
+ if let animationForExpandedVideoSnapshotView = self.animationForExpandedVideoSnapshotView {
+ self.containerNode.view.addSubview(animationForExpandedVideoSnapshotView)
+ transition.updateAlpha(layer: animationForExpandedVideoSnapshotView.layer, alpha: 0.0, completion: { [weak animationForExpandedVideoSnapshotView] _ in
+ animationForExpandedVideoSnapshotView?.removeFromSuperview()
+ })
+ transition.updateTransformScale(layer: animationForExpandedVideoSnapshotView.layer, scale: previewVideoFrame.width / fullscreenVideoFrame.width)
+
+ transition.updatePosition(layer: animationForExpandedVideoSnapshotView.layer, position: CGPoint(x: previewVideoFrame.minX + previewVideoFrame.center.x / fullscreenVideoFrame.width * previewVideoFrame.width, y: previewVideoFrame.minY + previewVideoFrame.center.y / fullscreenVideoFrame.height * previewVideoFrame.height))
+ self.animationForExpandedVideoSnapshotView = nil
+ }
+ minimizedVideoTransition.updateFrame(node: minimizedVideoNode, frame: previewVideoFrame)
+ minimizedVideoNode.updateLayout(size: previewVideoFrame.size, cornerRadius: interpolate(from: 14.0, to: 24.0, value: self.pictureInPictureTransitionFraction), transition: minimizedVideoTransition)
+ if transition.isAnimated && didAppear {
+ minimizedVideoNode.layer.animateSpring(from: 0.1 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: 0.5)
}
- } else {
- minimizedVideoNode.frame = fullscreenVideoFrame
- minimizedVideoNode.updateLayout(size: layout.size, cornerRadius: 0.0, transition: minimizedVideoTransition)
}
+
self.animationForExpandedVideoSnapshotView = nil
}
let keyTextSize = self.keyButtonNode.frame.size
- transition.updateFrame(node: self.keyButtonNode, frame: CGRect(origin: CGPoint(x: layout.size.width - keyTextSize.width - 8.0, y: navigationOffset + 8.0), size: keyTextSize))
+ transition.updateFrame(node: self.keyButtonNode, frame: CGRect(origin: CGPoint(x: layout.size.width - keyTextSize.width - 8.0, y: topOriginY + 8.0), size: keyTextSize))
transition.updateAlpha(node: self.keyButtonNode, alpha: overlayAlpha)
if let debugNode = self.debugNode {
@@ -916,6 +1497,8 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
self.keyButtonNode.isHidden = true
keyPreviewNode.animateIn(from: self.keyButtonNode.frame, fromNode: self.keyButtonNode)
}
+
+ self.updateDimVisibility()
}
}
@@ -926,16 +1509,32 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
self?.keyButtonNode.isHidden = false
keyPreviewNode?.removeFromSupernode()
})
+ self.updateDimVisibility()
+ } else if self.hasVideoNodes {
+ if let (layout, navigationHeight) = self.validLayout {
+ self.pictureInPictureTransitionFraction = 1.0
+ self.containerLayoutUpdated(layout, navigationBarHeight: navigationHeight, transition: .animated(duration: 0.4, curve: .spring))
+ }
} else {
self.back?()
}
}
+ private var hasVideoNodes: Bool {
+ return self.expandedVideoNode != nil || self.minimizedVideoNode != nil
+ }
+
private var debugTapCounter: (Double, Int) = (0.0, 0)
+ private func areUserActionsDisabledNow() -> Bool {
+ return CACurrentMediaTime() < self.disableActionsUntilTimestamp
+ }
+
@objc func tapGesture(_ recognizer: UITapGestureRecognizer) {
if case .ended = recognizer.state {
if !self.pictureInPictureTransitionFraction.isZero {
+ self.view.window?.endEditing(true)
+
if let (layout, navigationHeight) = self.validLayout {
self.pictureInPictureTransitionFraction = 0.0
@@ -947,17 +1546,20 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
if let expandedVideoNode = self.expandedVideoNode, let minimizedVideoNode = self.minimizedVideoNode {
let point = recognizer.location(in: recognizer.view)
if minimizedVideoNode.frame.contains(point) {
- let copyView = minimizedVideoNode.view.snapshotView(afterScreenUpdates: false)
- copyView?.frame = minimizedVideoNode.frame
- self.expandedVideoNode = minimizedVideoNode
- self.minimizedVideoNode = expandedVideoNode
- if let supernode = expandedVideoNode.supernode {
- supernode.insertSubnode(expandedVideoNode, aboveSubnode: minimizedVideoNode)
- }
- if let (layout, navigationBarHeight) = self.validLayout {
- self.disableAnimationForExpandedVideoOnce = true
- self.animationForExpandedVideoSnapshotView = copyView
- self.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .animated(duration: 0.3, curve: .easeInOut))
+ if !self.areUserActionsDisabledNow() {
+ let copyView = minimizedVideoNode.view.snapshotView(afterScreenUpdates: false)
+ copyView?.frame = minimizedVideoNode.frame
+ self.expandedVideoNode = minimizedVideoNode
+ self.minimizedVideoNode = expandedVideoNode
+ if let supernode = expandedVideoNode.supernode {
+ supernode.insertSubnode(expandedVideoNode, aboveSubnode: minimizedVideoNode)
+ }
+ self.disableActionsUntilTimestamp = CACurrentMediaTime() + 0.3
+ if let (layout, navigationBarHeight) = self.validLayout {
+ self.disableAnimationForExpandedVideoOnce = true
+ self.animationForExpandedVideoSnapshotView = copyView
+ self.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .animated(duration: 0.3, curve: .easeInOut))
+ }
}
} else {
var updated = false
@@ -1135,20 +1737,25 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
}
}
- @objc private func panGesture(_ recognizer: UIPanGestureRecognizer) {
+ @objc private func panGesture(_ recognizer: CallPanGestureRecognizer) {
switch recognizer.state {
case .began:
- let location = recognizer.location(in: self.view)
- if self.self.pictureInPictureTransitionFraction.isZero, let _ = self.expandedVideoNode, let minimizedVideoNode = self.minimizedVideoNode, minimizedVideoNode.frame.contains(location) {
+ guard let location = recognizer.firstLocation else {
+ return
+ }
+ if self.pictureInPictureTransitionFraction.isZero, let expandedVideoNode = self.expandedVideoNode, let minimizedVideoNode = self.minimizedVideoNode, minimizedVideoNode.frame.contains(location), expandedVideoNode.frame != minimizedVideoNode.frame {
self.minimizedVideoInitialPosition = minimizedVideoNode.position
- } else {
+ } else if self.hasVideoNodes {
self.minimizedVideoInitialPosition = nil
if !self.pictureInPictureTransitionFraction.isZero {
self.pictureInPictureGestureState = .dragging(initialPosition: self.containerTransformationNode.position, draggingPosition: self.containerTransformationNode.position)
} else {
self.pictureInPictureGestureState = .collapsing(didSelectCorner: false)
}
+ } else {
+ self.pictureInPictureGestureState = .none
}
+ self.dismissAllTooltips?()
case .changed:
if let minimizedVideoNode = self.minimizedVideoNode, let minimizedVideoInitialPosition = self.minimizedVideoInitialPosition {
let translation = recognizer.translation(in: self.view)
@@ -1228,7 +1835,7 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
case .collapsing:
self.pictureInPictureGestureState = .none
let velocity = recognizer.velocity(in: self.view).y
- if abs(velocity) < 100.0 {
+ if abs(velocity) < 100.0 && self.pictureInPictureTransitionFraction < 0.5 {
if let (layout, navigationHeight) = self.validLayout {
self.pictureInPictureTransitionFraction = 0.0
@@ -1260,9 +1867,47 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
}
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
+ if self.debugNode != nil {
+ return super.hitTest(point, with: event)
+ }
if self.containerTransformationNode.frame.contains(point) {
return self.containerTransformationNode.view.hitTest(self.view.convert(point, to: self.containerTransformationNode.view), with: event)
}
return nil
}
}
+
+private final class CallPanGestureRecognizer: UIPanGestureRecognizer {
+ private(set) var firstLocation: CGPoint?
+
+ public var shouldBegin: ((CGPoint) -> Bool)?
+
+ override public init(target: Any?, action: Selector?) {
+ super.init(target: target, action: action)
+
+ self.maximumNumberOfTouches = 1
+ }
+
+ override public func reset() {
+ super.reset()
+
+ self.firstLocation = nil
+ }
+
+ override public func touchesBegan(_ touches: Set, with event: UIEvent) {
+ super.touchesBegan(touches, with: event)
+
+ let touch = touches.first!
+ let point = touch.location(in: self.view)
+ if let shouldBegin = self.shouldBegin, !shouldBegin(point) {
+ self.state = .failed
+ return
+ }
+
+ self.firstLocation = point
+ }
+
+ override public func touchesMoved(_ touches: Set, with event: UIEvent) {
+ super.touchesMoved(touches, with: event)
+ }
+}
diff --git a/submodules/TelegramCallsUI/Sources/CallControllerStatusNode.swift b/submodules/TelegramCallsUI/Sources/CallControllerStatusNode.swift
index a0566d6aee..9e84d73aaa 100644
--- a/submodules/TelegramCallsUI/Sources/CallControllerStatusNode.swift
+++ b/submodules/TelegramCallsUI/Sources/CallControllerStatusNode.swift
@@ -12,7 +12,7 @@ private let regularStatusFont = Font.regular(18.0)
enum CallControllerStatusValue: Equatable {
case text(string: String, displayLogo: Bool)
- case timer((String) -> String, Double)
+ case timer((String, Bool) -> String, Double)
static func ==(lhs: CallControllerStatusValue, rhs: CallControllerStatusValue) -> Bool {
switch lhs {
@@ -34,6 +34,7 @@ enum CallControllerStatusValue: Equatable {
final class CallControllerStatusNode: ASDisplayNode {
private let titleNode: TextNode
+ private let statusContainerNode: ASDisplayNode
private let statusNode: TextNode
private let statusMeasureNode: TextNode
private let receptionNode: CallControllerReceptionNode
@@ -46,6 +47,21 @@ final class CallControllerStatusNode: ASDisplayNode {
if self.status != oldValue {
self.statusTimer?.invalidate()
+ if let snapshotView = self.statusContainerNode.view.snapshotView(afterScreenUpdates: false) {
+ snapshotView.frame = self.statusContainerNode.frame
+ self.view.insertSubview(snapshotView, belowSubview: self.statusContainerNode.view)
+
+ snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { [weak snapshotView] _ in
+ snapshotView?.removeFromSuperview()
+ })
+ snapshotView.layer.animateScale(from: 1.0, to: 0.3, duration: 0.3, removeOnCompletion: false)
+ snapshotView.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: snapshotView.frame.height / 2.0), duration: 0.3, delay: 0.0, removeOnCompletion: false, additive: true)
+
+ self.statusContainerNode.layer.animateScale(from: 0.3, to: 1.0, duration: 0.3)
+ self.statusContainerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
+ self.statusContainerNode.layer.animatePosition(from: CGPoint(x: 0.0, y: -snapshotView.frame.height / 2.0), to: CGPoint(), duration: 0.3, delay: 0.0, additive: true)
+ }
+
if case .timer = self.status {
self.statusTimer = SwiftSignalKit.Timer(timeout: 0.5, repeat: true, completion: { [weak self] in
if let strongSelf = self, let validLayoutWidth = strongSelf.validLayoutWidth {
@@ -90,6 +106,7 @@ final class CallControllerStatusNode: ASDisplayNode {
override init() {
self.titleNode = TextNode()
+ self.statusContainerNode = ASDisplayNode()
self.statusNode = TextNode()
self.statusNode.displaysAsynchronously = false
self.statusMeasureNode = TextNode()
@@ -106,15 +123,22 @@ final class CallControllerStatusNode: ASDisplayNode {
self.isUserInteractionEnabled = false
self.addSubnode(self.titleNode)
- self.addSubnode(self.statusNode)
- self.addSubnode(self.receptionNode)
- self.addSubnode(self.logoNode)
+ self.addSubnode(self.statusContainerNode)
+ self.statusContainerNode.addSubnode(self.statusNode)
+ self.statusContainerNode.addSubnode(self.receptionNode)
+ self.statusContainerNode.addSubnode(self.logoNode)
}
deinit {
self.statusTimer?.invalidate()
}
+ func setVisible(_ visible: Bool, transition: ContainedViewLayoutTransition) {
+ let alpha: CGFloat = visible ? 1.0 : 0.0
+ transition.updateAlpha(node: self.titleNode, alpha: alpha)
+ transition.updateAlpha(node: self.statusContainerNode, alpha: alpha)
+ }
+
func updateLayout(constrainedWidth: CGFloat, transition: ContainedViewLayoutTransition) -> CGFloat {
self.validLayoutWidth = constrainedWidth
@@ -151,8 +175,8 @@ final class CallControllerStatusNode: ASDisplayNode {
durationString = String(format: "%02d:%02d", arguments: [(duration / 60) % 60, duration % 60])
measureDurationString = "00:00"
}
- statusText = format(durationString)
- statusMeasureText = format(measureDurationString)
+ statusText = format(durationString, false)
+ statusMeasureText = format(measureDurationString, true)
if self.reception != nil {
statusOffset += 8.0
}
@@ -168,12 +192,13 @@ final class CallControllerStatusNode: ASDisplayNode {
let _ = statusMeasureApply()
self.titleNode.frame = CGRect(origin: CGPoint(x: floor((constrainedWidth - titleLayout.size.width) / 2.0), y: 0.0), size: titleLayout.size)
- self.statusNode.frame = CGRect(origin: CGPoint(x: floor((constrainedWidth - statusMeasureLayout.size.width) / 2.0) + statusOffset, y: titleLayout.size.height + spacing), size: statusLayout.size)
- self.receptionNode.frame = CGRect(origin: CGPoint(x: self.statusNode.frame.minX - receptionNodeSize.width, y: titleLayout.size.height + spacing + 9.0), size: receptionNodeSize)
+ self.statusContainerNode.frame = CGRect(origin: CGPoint(x: 0.0, y: titleLayout.size.height + spacing), size: CGSize(width: constrainedWidth, height: statusLayout.size.height))
+ self.statusNode.frame = CGRect(origin: CGPoint(x: floor((constrainedWidth - statusMeasureLayout.size.width) / 2.0) + statusOffset, y: 0.0), size: statusLayout.size)
+ self.receptionNode.frame = CGRect(origin: CGPoint(x: self.statusNode.frame.minX - receptionNodeSize.width, y: 9.0), size: receptionNodeSize)
self.logoNode.isHidden = !statusDisplayLogo
if let image = self.logoNode.image, let firstLineRect = statusMeasureLayout.linesRects().first {
let firstLineOffset = floor((statusMeasureLayout.size.width - firstLineRect.width) / 2.0)
- self.logoNode.frame = CGRect(origin: CGPoint(x: self.statusNode.frame.minX + firstLineOffset - image.size.width - 7.0, y: self.statusNode.frame.minY + 5.0), size: image.size)
+ self.logoNode.frame = CGRect(origin: CGPoint(x: self.statusNode.frame.minX + firstLineOffset - image.size.width - 7.0, y: 5.0), size: image.size)
}
return titleLayout.size.height + spacing + statusLayout.size.height
diff --git a/submodules/TelegramCallsUI/Sources/CallControllerToastNode.swift b/submodules/TelegramCallsUI/Sources/CallControllerToastNode.swift
new file mode 100644
index 0000000000..3fd4376d5b
--- /dev/null
+++ b/submodules/TelegramCallsUI/Sources/CallControllerToastNode.swift
@@ -0,0 +1,321 @@
+import Foundation
+import UIKit
+import Display
+import AsyncDisplayKit
+import SwiftSignalKit
+import TelegramPresentationData
+
+private let labelFont = Font.regular(17.0)
+private let smallLabelFont = Font.regular(15.0)
+
+private enum ToastDescription: Equatable {
+ enum Key: Hashable {
+ case camera
+ case microphone
+ case mute
+ case battery
+ }
+
+ case camera
+ case microphone
+ case mute
+ case battery
+
+ var key: Key {
+ switch self {
+ case .camera:
+ return .camera
+ case .microphone:
+ return .microphone
+ case .mute:
+ return .mute
+ case .battery:
+ return .battery
+ }
+ }
+}
+
+struct CallControllerToastContent: OptionSet {
+ public var rawValue: Int32
+
+ public init(rawValue: Int32) {
+ self.rawValue = rawValue
+ }
+
+ public static let camera = CallControllerToastContent(rawValue: 1 << 0)
+ public static let microphone = CallControllerToastContent(rawValue: 1 << 1)
+ public static let mute = CallControllerToastContent(rawValue: 1 << 2)
+ public static let battery = CallControllerToastContent(rawValue: 1 << 3)
+}
+
+final class CallControllerToastContainerNode: ASDisplayNode {
+ private var toastNodes: [ToastDescription.Key: CallControllerToastItemNode] = [:]
+ private var visibleToastNodes: [CallControllerToastItemNode] = []
+
+ private let strings: PresentationStrings
+
+ private var validLayout: (CGFloat, CGFloat)?
+
+ private var content: CallControllerToastContent?
+ private var appliedContent: CallControllerToastContent?
+ var title: String = ""
+
+ init(strings: PresentationStrings) {
+ self.strings = strings
+
+ super.init()
+ }
+
+ private func updateToastsLayout(strings: PresentationStrings, content: CallControllerToastContent, width: CGFloat, bottomInset: CGFloat, animated: Bool) -> CGFloat {
+ let transition: ContainedViewLayoutTransition
+ if animated {
+ transition = .animated(duration: 0.3, curve: .spring)
+ } else {
+ transition = .immediate
+ }
+
+ self.appliedContent = content
+
+ let spacing: CGFloat = 18.0
+
+ var height: CGFloat = 0.0
+ var toasts: [ToastDescription] = []
+
+ if content.contains(.camera) {
+ toasts.append(.camera)
+ }
+ if content.contains(.microphone) {
+ toasts.append(.microphone)
+ }
+ if content.contains(.mute) {
+ toasts.append(.mute)
+ }
+ if content.contains(.battery) {
+ toasts.append(.battery)
+ }
+
+ var transitions: [ToastDescription.Key: (ContainedViewLayoutTransition, CGFloat, Bool)] = [:]
+ var validKeys: [ToastDescription.Key] = []
+ for toast in toasts {
+ validKeys.append(toast.key)
+ var toastTransition = transition
+ var animateIn = false
+ let toastNode: CallControllerToastItemNode
+ if let current = self.toastNodes[toast.key] {
+ toastNode = current
+ } else {
+ toastNode = CallControllerToastItemNode()
+ self.toastNodes[toast.key] = toastNode
+ self.addSubnode(toastNode)
+ self.visibleToastNodes.append(toastNode)
+ toastTransition = .immediate
+ animateIn = transition.isAnimated
+ }
+ let toastContent: CallControllerToastItemNode.Content
+ switch toast {
+ case .camera:
+ toastContent = CallControllerToastItemNode.Content(
+ key: .camera,
+ image: .camera,
+ text: strings.Call_CameraOff(self.title).0
+ )
+ case .microphone:
+ toastContent = CallControllerToastItemNode.Content(
+ key: .microphone,
+ image: .microphone,
+ text: strings.Call_MicrophoneOff(self.title).0
+ )
+ case .mute:
+ toastContent = CallControllerToastItemNode.Content(
+ key: .mute,
+ image: .microphone,
+ text: strings.Call_YourMicrophoneOff
+ )
+ case .battery:
+ toastContent = CallControllerToastItemNode.Content(
+ key: .battery,
+ image: .battery,
+ text: strings.Call_BatteryLow(self.title).0
+ )
+ }
+ let toastHeight = toastNode.update(width: width, content: toastContent, transition: toastTransition)
+ transitions[toast.key] = (toastTransition, toastHeight, animateIn)
+ }
+
+ var removedKeys: [ToastDescription.Key] = []
+ for (key, toastNode) in self.toastNodes {
+ if !validKeys.contains(key) {
+ removedKeys.append(key)
+ self.visibleToastNodes.removeAll { $0 === toastNode }
+ if animated {
+ toastNode.animateOut(transition: transition) { [weak toastNode] in
+ toastNode?.removeFromSupernode()
+ }
+ } else {
+ toastNode.removeFromSupernode()
+ }
+ }
+ }
+ for key in removedKeys {
+ self.toastNodes.removeValue(forKey: key)
+ }
+
+ for toastNode in self.visibleToastNodes {
+ if let content = toastNode.currentContent, let (transition, toastHeight, animateIn) = transitions[content.key] {
+ transition.updateFrame(node: toastNode, frame: CGRect(x: 0.0, y: height, width: width, height: toastHeight))
+ height += toastHeight + spacing
+
+ if animateIn {
+ toastNode.animateIn()
+ }
+ }
+ }
+ if height > 0.0 {
+ height -= spacing
+ }
+
+ return height
+ }
+
+ func updateLayout(strings: PresentationStrings, content: CallControllerToastContent?, constrainedWidth: CGFloat, bottomInset: CGFloat, transition: ContainedViewLayoutTransition) -> CGFloat {
+ self.validLayout = (constrainedWidth, bottomInset)
+
+ self.content = content
+
+ if let content = self.content {
+ return self.updateToastsLayout(strings: strings, content: content, width: constrainedWidth, bottomInset: bottomInset, animated: transition.isAnimated)
+ } else {
+ return 0.0
+ }
+ }
+}
+
+private class CallControllerToastItemNode: ASDisplayNode {
+ struct Content: Equatable {
+ enum Image {
+ case camera
+ case microphone
+ case battery
+ }
+
+ var key: ToastDescription.Key
+ var image: Image
+ var text: String
+
+ init(key: ToastDescription.Key, image: Image, text: String) {
+ self.key = key
+ self.image = image
+ self.text = text
+ }
+ }
+
+ let clipNode: ASDisplayNode
+ let effectView: UIVisualEffectView
+ let iconNode: ASImageNode
+ let textNode: ImmediateTextNode
+
+ private(set) var currentContent: Content?
+ private(set) var currentWidth: CGFloat?
+ private(set) var currentHeight: CGFloat?
+
+ override init() {
+ self.clipNode = ASDisplayNode()
+ self.clipNode.clipsToBounds = true
+ self.clipNode.layer.cornerRadius = 14.0
+ if #available(iOS 13.0, *) {
+ self.clipNode.layer.cornerCurve = .continuous
+ }
+
+ self.effectView = UIVisualEffectView()
+ self.effectView.effect = UIBlurEffect(style: .light)
+ self.effectView.isUserInteractionEnabled = false
+
+ self.iconNode = ASImageNode()
+ self.iconNode.displaysAsynchronously = false
+ self.iconNode.displayWithoutProcessing = true
+ self.iconNode.contentMode = .center
+
+ self.textNode = ImmediateTextNode()
+ self.textNode.maximumNumberOfLines = 2
+ self.textNode.displaysAsynchronously = false
+ self.textNode.isUserInteractionEnabled = false
+
+ super.init()
+
+ self.addSubnode(self.clipNode)
+ self.clipNode.view.addSubview(self.effectView)
+ self.clipNode.addSubnode(self.iconNode)
+ self.clipNode.addSubnode(self.textNode)
+ }
+
+ func update(width: CGFloat, content: Content, transition: ContainedViewLayoutTransition) -> CGFloat {
+ let inset: CGFloat = 30.0
+ let isNarrowScreen = width <= 320.0
+ let font = isNarrowScreen ? smallLabelFont : labelFont
+ let topInset: CGFloat = isNarrowScreen ? 5.0 : 4.0
+
+ if self.currentContent != content || self.currentWidth != width {
+ self.currentContent = content
+ self.currentWidth = width
+
+ var image: UIImage?
+ switch content.image {
+ case .camera:
+ image = generateTintedImage(image: UIImage(bundleImageName: "Call/CallToastCamera"), color: .white)
+ case .microphone:
+ image = generateTintedImage(image: UIImage(bundleImageName: "Call/CallToastMicrophone"), color: .white)
+ case .battery:
+ image = generateTintedImage(image: UIImage(bundleImageName: "Call/CallToastBattery"), color: .white)
+ }
+
+ if transition.isAnimated, let image = image, let previousContent = self.iconNode.image {
+ self.iconNode.image = image
+ self.iconNode.layer.animate(from: previousContent.cgImage!, to: image.cgImage!, keyPath: "contents", timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, duration: 0.2)
+ } else {
+ self.iconNode.image = image
+ }
+
+ self.textNode.attributedText = NSAttributedString(string: content.text, font: font, textColor: .white)
+
+ let iconSize = CGSize(width: 44.0, height: 28.0)
+ let iconSpacing: CGFloat = isNarrowScreen ? 0.0 : 1.0
+ let textSize = self.textNode.updateLayout(CGSize(width: width - inset * 2.0 - iconSize.width - iconSpacing, height: 100.0))
+
+ let backgroundSize = CGSize(width: iconSize.width + iconSpacing + textSize.width + 6.0 * 2.0, height: max(28.0, textSize.height + 4.0 * 2.0))
+ let backgroundFrame = CGRect(origin: CGPoint(x: floor((width - backgroundSize.width) / 2.0), y: 0.0), size: backgroundSize)
+
+ transition.updateFrame(node: self.clipNode, frame: backgroundFrame)
+ transition.updateFrame(view: self.effectView, frame: CGRect(origin: CGPoint(), size: backgroundFrame.size))
+
+ self.iconNode.frame = CGRect(origin: CGPoint(), size: iconSize)
+ self.textNode.frame = CGRect(origin: CGPoint(x: iconSize.width + iconSpacing, y: topInset), size: textSize)
+
+ self.currentHeight = backgroundSize.height
+ }
+ return self.currentHeight ?? 28.0
+ }
+
+ func animateIn() {
+ let targetFrame = self.clipNode.frame
+ let initialFrame = CGRect(x: floor((self.frame.width - 44.0) / 2.0), y: 0.0, width: 44.0, height: 28.0)
+
+ self.clipNode.frame = initialFrame
+// self.effectView.frame = CGRect(origin: CGPoint(), size: initialFrame.size)
+
+ self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
+ self.layer.animateSpring(from: 0.01 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: 0.45, damping: 105.0, completion: { _ in
+ self.clipNode.frame = targetFrame
+// self.effectView.frame = CGRect(origin: CGPoint(), size: targetFrame.size)
+
+ self.clipNode.layer.animateFrame(from: initialFrame, to: targetFrame, duration: 0.35, timingFunction: kCAMediaTimingFunctionSpring)
+// self.effectView.layer.animateFrame(from: initialFrame, to: targetFrame, duration: 0.35, timingFunction: kCAMediaTimingFunctionSpring)
+ })
+ }
+
+ func animateOut(transition: ContainedViewLayoutTransition, completion: @escaping () -> Void) {
+ transition.updateTransformScale(node: self, scale: 0.1)
+ transition.updateAlpha(node: self, alpha: 0.0, completion: { _ in
+ completion()
+ })
+ }
+}
diff --git a/submodules/TelegramCallsUI/Sources/CallKitIntegration.swift b/submodules/TelegramCallsUI/Sources/CallKitIntegration.swift
index 09a7f05696..b148c695db 100644
--- a/submodules/TelegramCallsUI/Sources/CallKitIntegration.swift
+++ b/submodules/TelegramCallsUI/Sources/CallKitIntegration.swift
@@ -29,7 +29,7 @@ public final class CallKitIntegration {
return self.audioSessionActivePromise.get()
}
- init?(enableVideoCalls: Bool, startCall: @escaping (Account, UUID, String, Bool) -> Signal, answerCall: @escaping (UUID) -> Void, endCall: @escaping (UUID) -> Signal, setCallMuted: @escaping (UUID, Bool) -> Void, audioSessionActivationChanged: @escaping (Bool) -> Void) {
+ init?(startCall: @escaping (Account, UUID, String, Bool) -> Signal, answerCall: @escaping (UUID) -> Void, endCall: @escaping (UUID) -> Signal, setCallMuted: @escaping (UUID, Bool) -> Void, audioSessionActivationChanged: @escaping (Bool) -> Void) {
if !CallKitIntegration.isAvailable {
return nil
}
@@ -40,7 +40,7 @@ public final class CallKitIntegration {
if #available(iOSApplicationExtension 10.0, iOS 10.0, *) {
if sharedProviderDelegate == nil {
- sharedProviderDelegate = CallKitProviderDelegate(enableVideoCalls: enableVideoCalls)
+ sharedProviderDelegate = CallKitProviderDelegate()
}
(sharedProviderDelegate as? CallKitProviderDelegate)?.setup(audioSessionActivePromise: self.audioSessionActivePromise, startCall: startCall, answerCall: answerCall, endCall: endCall, setCallMuted: setCallMuted, audioSessionActivationChanged: audioSessionActivationChanged)
} else {
@@ -112,8 +112,8 @@ class CallKitProviderDelegate: NSObject, CXProviderDelegate {
fileprivate var audioSessionActivePromise: ValuePromise?
- init(enableVideoCalls: Bool) {
- self.provider = CXProvider(configuration: CallKitProviderDelegate.providerConfiguration(enableVideoCalls: enableVideoCalls))
+ override init() {
+ self.provider = CXProvider(configuration: CallKitProviderDelegate.providerConfiguration())
super.init()
@@ -129,10 +129,10 @@ class CallKitProviderDelegate: NSObject, CXProviderDelegate {
self.audioSessionActivationChanged = audioSessionActivationChanged
}
- private static func providerConfiguration(enableVideoCalls: Bool) -> CXProviderConfiguration {
+ private static func providerConfiguration() -> CXProviderConfiguration {
let providerConfiguration = CXProviderConfiguration(localizedName: "Telegram")
- providerConfiguration.supportsVideo = false
+ providerConfiguration.supportsVideo = true
providerConfiguration.maximumCallsPerCallGroup = 1
providerConfiguration.maximumCallGroups = 1
providerConfiguration.supportedHandleTypes = [.phoneNumber, .generic]
diff --git a/submodules/TelegramCallsUI/Sources/LegacyCallControllerNode.swift b/submodules/TelegramCallsUI/Sources/LegacyCallControllerNode.swift
index fcf202151b..7bacc290ae 100644
--- a/submodules/TelegramCallsUI/Sources/LegacyCallControllerNode.swift
+++ b/submodules/TelegramCallsUI/Sources/LegacyCallControllerNode.swift
@@ -14,75 +14,6 @@ import LocalizedPeerData
import PhotoResources
import CallsEmoji
-private final class IncomingVideoNode: ASDisplayNode {
- private let videoView: UIView
- private var effectView: UIVisualEffectView?
- private var isBlurred: Bool = false
-
- init(videoView: UIView) {
- self.videoView = videoView
-
- super.init()
-
- self.view.addSubview(self.videoView)
- }
-
- func updateLayout(size: CGSize) {
- self.videoView.frame = CGRect(origin: CGPoint(), size: size)
- }
-
- func updateIsBlurred(isBlurred: Bool) {
- if self.isBlurred == isBlurred {
- return
- }
- self.isBlurred = isBlurred
-
- if isBlurred {
- if self.effectView == nil {
- let effectView = UIVisualEffectView()
- self.effectView = effectView
- effectView.frame = self.videoView.frame
- self.view.addSubview(effectView)
- }
- UIView.animate(withDuration: 0.3, animations: {
- self.effectView?.effect = UIBlurEffect(style: .dark)
- })
- } else if let effectView = self.effectView {
- UIView.animate(withDuration: 0.3, animations: {
- effectView.effect = nil
- })
- }
- }
-}
-
-private final class OutgoingVideoNode: ASDisplayNode {
- private let videoView: UIView
- private let switchCameraButton: HighlightableButtonNode
- private let switchCamera: () -> Void
-
- init(videoView: UIView, switchCamera: @escaping () -> Void) {
- self.videoView = videoView
- self.switchCameraButton = HighlightableButtonNode()
- self.switchCamera = switchCamera
-
- super.init()
-
- self.view.addSubview(self.videoView)
- self.addSubnode(self.switchCameraButton)
- self.switchCameraButton.addTarget(self, action: #selector(self.buttonPressed), forControlEvents: .touchUpInside)
- }
-
- @objc private func buttonPressed() {
- self.switchCamera()
- }
-
- func updateLayout(size: CGSize, isExpanded: Bool, transition: ContainedViewLayoutTransition) {
- transition.updateFrame(view: self.videoView, frame: CGRect(origin: CGPoint(), size: size))
- transition.updateCornerRadius(layer: self.videoView.layer, cornerRadius: isExpanded ? 0.0 : 16.0)
- self.switchCameraButton.frame = CGRect(origin: CGPoint(), size: size)
- }
-}
-
final class LegacyCallControllerNode: ASDisplayNode, CallControllerNodeProtocol {
private let sharedContext: SharedAccountContext
private let account: Account
@@ -100,14 +31,9 @@ final class LegacyCallControllerNode: ASDisplayNode, CallControllerNodeProtocol
private let imageNode: TransformImageNode
private let dimNode: ASDisplayNode
- private var incomingVideoNode: IncomingVideoNode?
- private var incomingVideoViewRequested: Bool = false
- private var outgoingVideoNode: OutgoingVideoNode?
- private var outgoingVideoViewRequested: Bool = false
private let backButtonArrowNode: ASImageNode
private let backButtonNode: HighlightableButtonNode
private let statusNode: LegacyCallControllerStatusNode
- private let videoPausedNode: ImmediateTextNode
private let buttonsNode: LegacyCallControllerButtonsNode
private var keyPreviewNode: CallControllerKeyPreviewNode?
@@ -131,15 +57,16 @@ final class LegacyCallControllerNode: ASDisplayNode, CallControllerNodeProtocol
var toggleMute: (() -> Void)?
var setCurrentAudioOutput: ((AudioSessionOutput) -> Void)?
- var beginAudioOuputSelection: (() -> Void)?
+ var beginAudioOuputSelection: ((Bool) -> Void)?
var acceptCall: (() -> Void)?
var endCall: (() -> Void)?
- var toggleVideo: (() -> Void)?
+ var setIsVideoPaused: ((Bool) -> Void)?
var back: (() -> Void)?
var presentCallRating: ((CallId) -> Void)?
var callEnded: ((Bool) -> Void)?
var dismissedInteractively: (() -> Void)?
- var setIsVideoPaused: ((Bool) -> Void)?
+ var present: ((ViewController) -> Void)?
+ var dismissAllTooltips: (() -> Void)?
init(sharedContext: SharedAccountContext, account: Account, presentationData: PresentationData, statusBar: StatusBar, debugInfo: Signal<(String, String), NoError>, shouldStayHiddenUntilConnection: Bool = false, easyDebugAccess: Bool, call: PresentationCall) {
self.sharedContext = sharedContext
@@ -170,9 +97,6 @@ final class LegacyCallControllerNode: ASDisplayNode, CallControllerNodeProtocol
self.statusNode = LegacyCallControllerStatusNode()
- self.videoPausedNode = ImmediateTextNode()
- self.videoPausedNode.alpha = 0.0
-
self.buttonsNode = LegacyCallControllerButtonsNode(strings: self.presentationData.strings)
self.keyButtonNode = HighlightableButtonNode()
@@ -207,7 +131,6 @@ final class LegacyCallControllerNode: ASDisplayNode, CallControllerNodeProtocol
self.containerNode.addSubnode(self.imageNode)
self.containerNode.addSubnode(self.dimNode)
self.containerNode.addSubnode(self.statusNode)
- self.containerNode.addSubnode(self.videoPausedNode)
self.containerNode.addSubnode(self.buttonsNode)
self.containerNode.addSubnode(self.keyButtonNode)
self.containerNode.addSubnode(self.backButtonArrowNode)
@@ -218,7 +141,7 @@ final class LegacyCallControllerNode: ASDisplayNode, CallControllerNodeProtocol
}
self.buttonsNode.speaker = { [weak self] in
- self?.beginAudioOuputSelection?()
+ self?.beginAudioOuputSelection?(false)
}
self.buttonsNode.end = { [weak self] in
@@ -229,10 +152,6 @@ final class LegacyCallControllerNode: ASDisplayNode, CallControllerNodeProtocol
self?.acceptCall?()
}
- self.buttonsNode.rotateCamera = { [weak self] in
- self?.call.switchVideoCamera()
- }
-
self.keyButtonNode.addTarget(self, action: #selector(self.keyPressed), forControlEvents: .touchUpInside)
self.backButtonNode.addTarget(self, action: #selector(self.backPressed), forControlEvents: .touchUpInside)
@@ -269,8 +188,6 @@ final class LegacyCallControllerNode: ASDisplayNode, CallControllerNodeProtocol
}
}
- self.videoPausedNode.attributedText = NSAttributedString(string: self.presentationData.strings.Call_RemoteVideoPaused(peer.compactDisplayTitle).0, font: Font.regular(17.0), textColor: .white)
-
if let (layout, navigationBarHeight) = self.validLayout {
self.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .immediate)
}
@@ -290,84 +207,6 @@ final class LegacyCallControllerNode: ASDisplayNode, CallControllerNodeProtocol
let statusValue: LegacyCallControllerStatusValue
var statusReception: Int32?
- switch callState.videoState {
- case .active:
- if !self.incomingVideoViewRequested {
- self.incomingVideoViewRequested = true
- self.call.makeIncomingVideoView(completion: { [weak self] incomingVideoView in
- guard let strongSelf = self else {
- return
- }
- if let incomingVideoView = incomingVideoView {
- strongSelf.setCurrentAudioOutput?(.speaker)
- let incomingVideoNode = IncomingVideoNode(videoView: incomingVideoView.view)
- strongSelf.incomingVideoNode = incomingVideoNode
- strongSelf.containerNode.insertSubnode(incomingVideoNode, aboveSubnode: strongSelf.dimNode)
- strongSelf.statusNode.isHidden = true
- if let (layout, navigationBarHeight) = strongSelf.validLayout {
- strongSelf.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .immediate)
- }
- }
- })
- }
- default:
- break
- }
- switch callState.videoState {
- case .active, .outgoingRequested:
- if !self.outgoingVideoViewRequested {
- self.outgoingVideoViewRequested = true
- self.call.makeOutgoingVideoView(completion: { [weak self] outgoingVideoView in
- guard let strongSelf = self else {
- return
- }
- if let outgoingVideoView = outgoingVideoView?.view {
- outgoingVideoView.backgroundColor = .black
- outgoingVideoView.clipsToBounds = true
- outgoingVideoView.layer.cornerRadius = 16.0
- strongSelf.setCurrentAudioOutput?(.speaker)
- let outgoingVideoNode = OutgoingVideoNode(videoView: outgoingVideoView, switchCamera: {
- guard let strongSelf = self else {
- return
- }
- strongSelf.call.switchVideoCamera()
- })
- strongSelf.outgoingVideoNode = outgoingVideoNode
- if let incomingVideoNode = strongSelf.incomingVideoNode {
- strongSelf.containerNode.insertSubnode(outgoingVideoNode, aboveSubnode: incomingVideoNode)
- } else {
- strongSelf.containerNode.insertSubnode(outgoingVideoNode, aboveSubnode: strongSelf.dimNode)
- }
- if let (layout, navigationBarHeight) = strongSelf.validLayout {
- strongSelf.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .immediate)
- }
- }
- })
- }
- default:
- break
- }
-
- if let incomingVideoNode = self.incomingVideoNode {
- let isActive: Bool
- switch callState.remoteVideoState {
- case .inactive:
- isActive = false
- case .active:
- isActive = true
- }
- incomingVideoNode.updateIsBlurred(isBlurred: !isActive)
- if isActive != self.videoPausedNode.alpha.isZero {
- if isActive {
- self.videoPausedNode.alpha = 0.0
- self.videoPausedNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3)
- } else {
- self.videoPausedNode.alpha = 1.0
- self.videoPausedNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
- }
- }
- }
-
switch callState.state {
case .waiting, .connecting:
statusValue = .text(self.presentationData.strings.Call_StatusConnecting)
@@ -591,33 +430,10 @@ final class LegacyCallControllerNode: ASDisplayNode, CallControllerNodeProtocol
let statusHeight = self.statusNode.updateLayout(constrainedWidth: layout.size.width, transition: transition)
transition.updateFrame(node: self.statusNode, frame: CGRect(origin: CGPoint(x: 0.0, y: statusOffset), size: CGSize(width: layout.size.width, height: statusHeight)))
- let videoPausedSize = self.videoPausedNode.updateLayout(CGSize(width: layout.size.width - 16.0, height: 100.0))
- transition.updateFrame(node: self.videoPausedNode, frame: CGRect(origin: CGPoint(x: floor((layout.size.width - videoPausedSize.width) / 2.0), y: floor((layout.size.height - videoPausedSize.height) / 2.0)), size: videoPausedSize))
-
self.buttonsNode.updateLayout(constrainedWidth: layout.size.width, transition: transition)
let buttonsOriginY: CGFloat = layout.size.height - (buttonsOffset - 40.0) - buttonsHeight - layout.intrinsicInsets.bottom
transition.updateFrame(node: self.buttonsNode, frame: CGRect(origin: CGPoint(x: 0.0, y: buttonsOriginY), size: CGSize(width: layout.size.width, height: buttonsHeight)))
- var outgoingVideoTransition = transition
- if let incomingVideoNode = self.incomingVideoNode {
- if incomingVideoNode.frame.width.isZero, let outgoingVideoNode = self.outgoingVideoNode, !outgoingVideoNode.frame.width.isZero, !transition.isAnimated {
- outgoingVideoTransition = .animated(duration: 0.3, curve: .easeInOut)
- }
- incomingVideoNode.frame = CGRect(origin: CGPoint(), size: layout.size)
- incomingVideoNode.updateLayout(size: layout.size)
- }
- if let outgoingVideoNode = self.outgoingVideoNode {
- if self.incomingVideoNode == nil {
- outgoingVideoNode.frame = CGRect(origin: CGPoint(), size: layout.size)
- outgoingVideoNode.updateLayout(size: layout.size, isExpanded: true, transition: transition)
- } else {
- let outgoingSize = layout.size.aspectFitted(CGSize(width: 200.0, height: 200.0))
- let outgoingFrame = CGRect(origin: CGPoint(x: layout.size.width - 16.0 - outgoingSize.width, y: buttonsOriginY - 32.0 - outgoingSize.height), size: outgoingSize)
- outgoingVideoTransition.updateFrame(node: outgoingVideoNode, frame: outgoingFrame)
- outgoingVideoNode.updateLayout(size: outgoingFrame.size, isExpanded: false, transition: outgoingVideoTransition)
- }
- }
-
let keyTextSize = self.keyButtonNode.frame.size
transition.updateFrame(node: self.keyButtonNode, frame: CGRect(origin: CGPoint(x: layout.size.width - keyTextSize.width - 8.0, y: navigationOffset + 8.0), size: keyTextSize))
diff --git a/submodules/TelegramCallsUI/Sources/PresentationCall.swift b/submodules/TelegramCallsUI/Sources/PresentationCall.swift
index b7b01ea222..2f74c9d32e 100644
--- a/submodules/TelegramCallsUI/Sources/PresentationCall.swift
+++ b/submodules/TelegramCallsUI/Sources/PresentationCall.swift
@@ -169,6 +169,7 @@ public final class PresentationCallImpl: PresentationCall {
public let isOutgoing: Bool
public var isVideo: Bool
public var isVideoPossible: Bool
+ public let preferredVideoCodec: String?
public let peer: Peer?
private let serializedData: String?
@@ -187,9 +188,15 @@ public final class PresentationCallImpl: PresentationCall {
private var receptionDisposable: Disposable?
private var reportedIncomingCall = false
+ private var batteryLevelDisposable: Disposable?
+
private var callWasActive = false
private var shouldPresentCallRating = false
- private var videoWasActive = false
+
+ private var previousVideoState: PresentationCallState.VideoState?
+ private var previousRemoteVideoState: PresentationCallState.RemoteVideoState?
+ private var previousRemoteAudioState: PresentationCallState.RemoteAudioState?
+ private var previousRemoteBatteryLevel: PresentationCallState.RemoteBatteryLevel?
private var sessionStateDisposable: Disposable?
@@ -257,7 +264,8 @@ public final class PresentationCallImpl: PresentationCall {
currentNetworkType: NetworkType,
updatedNetworkType: Signal,
startWithVideo: Bool,
- isVideoPossible: Bool
+ isVideoPossible: Bool,
+ preferredVideoCodec: String?
) {
self.account = account
self.audioSession = audioSession
@@ -284,13 +292,14 @@ public final class PresentationCallImpl: PresentationCall {
self.isOutgoing = isOutgoing
self.isVideo = initialState?.type == .video
self.isVideoPossible = isVideoPossible
+ self.preferredVideoCodec = preferredVideoCodec
self.peer = peer
self.isVideo = startWithVideo
if self.isVideo {
self.videoCapturer = OngoingCallVideoCapturer()
- self.statePromise.set(PresentationCallState(state: isOutgoing ? .waiting : .ringing, videoState: .outgoingRequested, remoteVideoState: .inactive))
+ self.statePromise.set(PresentationCallState(state: isOutgoing ? .waiting : .ringing, videoState: .active, remoteVideoState: .inactive, remoteAudioState: .active, remoteBatteryLevel: .normal))
} else {
- self.statePromise.set(PresentationCallState(state: isOutgoing ? .waiting : .ringing, videoState: self.isVideoPossible ? .possible : .notAvailable, remoteVideoState: .inactive))
+ self.statePromise.set(PresentationCallState(state: isOutgoing ? .waiting : .ringing, videoState: self.isVideoPossible ? .inactive : .notAvailable, remoteVideoState: .inactive, remoteAudioState: .active, remoteBatteryLevel: .normal))
}
self.serializedData = serializedData
@@ -373,7 +382,7 @@ public final class PresentationCallImpl: PresentationCall {
audioSessionActive = callKitIntegration.audioSessionActive
|> filter { $0 }
|> timeout(2.0, queue: Queue.mainQueue(), alternate: Signal { subscriber in
- if let strongSelf = self, let audioSessionControl = strongSelf.audioSessionControl {
+ if let strongSelf = self, let _ = strongSelf.audioSessionControl {
//audioSessionControl.activate({ _ in })
}
subscriber.putNext(true)
@@ -408,6 +417,7 @@ public final class PresentationCallImpl: PresentationCall {
self.sessionStateDisposable?.dispose()
self.ongoingContextStateDisposable?.dispose()
self.receptionDisposable?.dispose()
+ self.batteryLevelDisposable?.dispose()
self.audioSessionDisposable?.dispose()
if let dropCallKitCallTimer = self.dropCallKitCallTimer {
@@ -441,7 +451,7 @@ public final class PresentationCallImpl: PresentationCall {
switch previous.state {
case .active:
wasActive = true
- case .terminated:
+ case .terminated, .dropping:
wasTerminated = true
default:
break
@@ -455,44 +465,71 @@ public final class PresentationCallImpl: PresentationCall {
let mappedVideoState: PresentationCallState.VideoState
let mappedRemoteVideoState: PresentationCallState.RemoteVideoState
+ let mappedRemoteAudioState: PresentationCallState.RemoteAudioState
+ let mappedRemoteBatteryLevel: PresentationCallState.RemoteBatteryLevel
if let callContextState = callContextState {
switch callContextState.videoState {
case .notAvailable:
mappedVideoState = .notAvailable
- case .possible:
- mappedVideoState = .possible
- case .outgoingRequested:
- mappedVideoState = .outgoingRequested
- case .incomingRequested:
- mappedVideoState = .incomingRequested
case .active:
mappedVideoState = .active
- self.videoWasActive = true
+ case .inactive:
+ mappedVideoState = .inactive
+ case .paused:
+ mappedVideoState = .paused
}
switch callContextState.remoteVideoState {
case .inactive:
mappedRemoteVideoState = .inactive
case .active:
mappedRemoteVideoState = .active
+ case .paused:
+ mappedRemoteVideoState = .paused
}
+ switch callContextState.remoteAudioState {
+ case .active:
+ mappedRemoteAudioState = .active
+ case .muted:
+ mappedRemoteAudioState = .muted
+ }
+ switch callContextState.remoteBatteryLevel {
+ case .normal:
+ mappedRemoteBatteryLevel = .normal
+ case .low:
+ mappedRemoteBatteryLevel = .low
+ }
+ self.previousVideoState = mappedVideoState
+ self.previousRemoteVideoState = mappedRemoteVideoState
+ self.previousRemoteAudioState = mappedRemoteAudioState
+ self.previousRemoteBatteryLevel = mappedRemoteBatteryLevel
} else {
- if self.isVideo {
- mappedVideoState = .outgoingRequested
- } else if self.isVideoPossible {
- mappedVideoState = .possible
+ if let previousVideoState = self.previousVideoState {
+ mappedVideoState = previousVideoState
} else {
- mappedVideoState = .notAvailable
+ if self.isVideo {
+ mappedVideoState = .active
+ } else if self.isVideoPossible && sessionState.isVideoPossible {
+ mappedVideoState = .inactive
+ } else {
+ mappedVideoState = .notAvailable
+ }
}
- if videoWasActive {
- mappedRemoteVideoState = .active
+ mappedRemoteVideoState = .inactive
+ if let previousRemoteAudioState = self.previousRemoteAudioState {
+ mappedRemoteAudioState = previousRemoteAudioState
} else {
- mappedRemoteVideoState = .inactive
+ mappedRemoteAudioState = .active
+ }
+ if let previousRemoteBatteryLevel = self.previousRemoteBatteryLevel {
+ mappedRemoteBatteryLevel = previousRemoteBatteryLevel
+ } else {
+ mappedRemoteBatteryLevel = .normal
}
}
switch sessionState.state {
case .ringing:
- presentationState = PresentationCallState(state: .ringing, videoState: mappedVideoState, remoteVideoState: mappedRemoteVideoState)
+ presentationState = PresentationCallState(state: .ringing, videoState: mappedVideoState, remoteVideoState: mappedRemoteVideoState, remoteAudioState: mappedRemoteAudioState, remoteBatteryLevel: mappedRemoteBatteryLevel)
if previous == nil || previousControl == nil {
if !self.reportedIncomingCall {
self.reportedIncomingCall = true
@@ -519,21 +556,21 @@ public final class PresentationCallImpl: PresentationCall {
}
case .accepting:
self.callWasActive = true
- presentationState = PresentationCallState(state: .connecting(nil), videoState: mappedVideoState, remoteVideoState: mappedRemoteVideoState)
- case .dropping:
- presentationState = PresentationCallState(state: .terminating, videoState: mappedVideoState, remoteVideoState: mappedRemoteVideoState)
+ presentationState = PresentationCallState(state: .connecting(nil), videoState: mappedVideoState, remoteVideoState: mappedRemoteVideoState, remoteAudioState: mappedRemoteAudioState, remoteBatteryLevel: mappedRemoteBatteryLevel)
+ case let .dropping(reason):
+ presentationState = PresentationCallState(state: .terminating(reason), videoState: mappedVideoState, remoteVideoState: .inactive, remoteAudioState: mappedRemoteAudioState, remoteBatteryLevel: mappedRemoteBatteryLevel)
case let .terminated(id, reason, options):
- presentationState = PresentationCallState(state: .terminated(id, reason, self.callWasActive && (options.contains(.reportRating) || self.shouldPresentCallRating)), videoState: mappedVideoState, remoteVideoState: mappedRemoteVideoState)
+ presentationState = PresentationCallState(state: .terminated(id, reason, self.callWasActive && (options.contains(.reportRating) || self.shouldPresentCallRating)), videoState: mappedVideoState, remoteVideoState: .inactive, remoteAudioState: mappedRemoteAudioState, remoteBatteryLevel: mappedRemoteBatteryLevel)
case let .requesting(ringing):
- presentationState = PresentationCallState(state: .requesting(ringing), videoState: mappedVideoState, remoteVideoState: mappedRemoteVideoState)
+ presentationState = PresentationCallState(state: .requesting(ringing), videoState: mappedVideoState, remoteVideoState: mappedRemoteVideoState, remoteAudioState: mappedRemoteAudioState, remoteBatteryLevel: mappedRemoteBatteryLevel)
case let .active(_, _, keyVisualHash, _, _, _, _):
self.callWasActive = true
if let callContextState = callContextState {
switch callContextState.state {
case .initializing:
- presentationState = PresentationCallState(state: .connecting(keyVisualHash), videoState: mappedVideoState, remoteVideoState: mappedRemoteVideoState)
+ presentationState = PresentationCallState(state: .connecting(keyVisualHash), videoState: mappedVideoState, remoteVideoState: mappedRemoteVideoState, remoteAudioState: mappedRemoteAudioState, remoteBatteryLevel: mappedRemoteBatteryLevel)
case .failed:
- presentationState = nil
+ presentationState = PresentationCallState(state: .terminating(.error(.disconnected)), videoState: mappedVideoState, remoteVideoState: mappedRemoteVideoState, remoteAudioState: mappedRemoteAudioState, remoteBatteryLevel: mappedRemoteBatteryLevel)
self.callSessionManager.drop(internalId: self.internalId, reason: .disconnect, debugLog: .single(nil))
case .connected:
let timestamp: Double
@@ -543,7 +580,7 @@ public final class PresentationCallImpl: PresentationCall {
timestamp = CFAbsoluteTimeGetCurrent()
self.activeTimestamp = timestamp
}
- presentationState = PresentationCallState(state: .active(timestamp, reception, keyVisualHash), videoState: mappedVideoState, remoteVideoState: mappedRemoteVideoState)
+ presentationState = PresentationCallState(state: .active(timestamp, reception, keyVisualHash), videoState: mappedVideoState, remoteVideoState: mappedRemoteVideoState, remoteAudioState: mappedRemoteAudioState, remoteBatteryLevel: mappedRemoteBatteryLevel)
case .reconnecting:
let timestamp: Double
if let activeTimestamp = self.activeTimestamp {
@@ -552,10 +589,10 @@ public final class PresentationCallImpl: PresentationCall {
timestamp = CFAbsoluteTimeGetCurrent()
self.activeTimestamp = timestamp
}
- presentationState = PresentationCallState(state: .reconnecting(timestamp, reception, keyVisualHash), videoState: mappedVideoState, remoteVideoState: mappedRemoteVideoState)
+ presentationState = PresentationCallState(state: .reconnecting(timestamp, reception, keyVisualHash), videoState: mappedVideoState, remoteVideoState: mappedRemoteVideoState, remoteAudioState: mappedRemoteAudioState, remoteBatteryLevel: mappedRemoteBatteryLevel)
}
} else {
- presentationState = PresentationCallState(state: .connecting(keyVisualHash), videoState: mappedVideoState, remoteVideoState: mappedRemoteVideoState)
+ presentationState = PresentationCallState(state: .connecting(keyVisualHash), videoState: mappedVideoState, remoteVideoState: mappedRemoteVideoState, remoteAudioState: mappedRemoteAudioState, remoteBatteryLevel: mappedRemoteBatteryLevel)
}
}
@@ -569,7 +606,7 @@ public final class PresentationCallImpl: PresentationCall {
if let _ = audioSessionControl, !wasActive || previousControl == nil {
let logName = "\(id.id)_\(id.accessHash)"
- let ongoingContext = OngoingCallContext(account: account, callSessionManager: self.callSessionManager, internalId: self.internalId, proxyServer: proxyServer, auxiliaryServers: auxiliaryServers, initialNetworkType: self.currentNetworkType, updatedNetworkType: self.updatedNetworkType, serializedData: self.serializedData, dataSaving: dataSaving, derivedState: self.derivedState, key: key, isOutgoing: sessionState.isOutgoing, video: self.videoCapturer, connections: connections, maxLayer: maxLayer, version: version, allowP2P: allowsP2P, audioSessionActive: self.audioSessionActive.get(), logName: logName)
+ let ongoingContext = OngoingCallContext(account: account, callSessionManager: self.callSessionManager, internalId: self.internalId, proxyServer: proxyServer, initialNetworkType: self.currentNetworkType, updatedNetworkType: self.updatedNetworkType, serializedData: self.serializedData, dataSaving: dataSaving, derivedState: self.derivedState, key: key, isOutgoing: sessionState.isOutgoing, video: self.videoCapturer, connections: connections, maxLayer: maxLayer, version: version, allowP2P: allowsP2P, audioSessionActive: self.audioSessionActive.get(), logName: logName, preferredVideoCodec: self.preferredVideoCodec)
self.ongoingContext = ongoingContext
ongoingContext.setIsMuted(self.isMutedValue)
@@ -597,6 +634,35 @@ public final class PresentationCallImpl: PresentationCall {
}
})
+ func batteryLevelIsLowSignal() -> Signal {
+ return Signal { subscriber in
+ let device = UIDevice.current
+ device.isBatteryMonitoringEnabled = true
+
+ var previousBatteryLevelIsLow = false
+ let timer = SwiftSignalKit.Timer(timeout: 30.0, repeat: true, completion: {
+ let batteryLevelIsLow = device.batteryLevel >= 0.0 && device.batteryLevel < 0.1 && device.batteryState != .charging
+ if batteryLevelIsLow != previousBatteryLevelIsLow {
+ previousBatteryLevelIsLow = batteryLevelIsLow
+ subscriber.putNext(batteryLevelIsLow)
+ }
+ }, queue: Queue.mainQueue())
+ timer.start()
+
+ return ActionDisposable {
+ device.isBatteryMonitoringEnabled = false
+ timer.invalidate()
+ }
+ }
+ }
+
+ self.batteryLevelDisposable = (batteryLevelIsLowSignal()
+ |> deliverOnMainQueue).start(next: { [weak self] batteryLevelIsLow in
+ if let strongSelf = self, let ongoingContext = strongSelf.ongoingContext {
+ ongoingContext.setIsLowBatteryLevel(batteryLevelIsLow)
+ }
+ })
+
if sessionState.isOutgoing {
self.callKitIntegration?.reportOutgoingCallConnected(uuid: sessionState.id, at: Date())
}
@@ -614,15 +680,22 @@ public final class PresentationCallImpl: PresentationCall {
self.ongoingContext?.stop(debugLogValue: debugLogValue)
}
}
- if case .terminated = sessionState.state, !wasTerminated {
+ var terminating = false
+ if case .terminated = sessionState.state {
+ terminating = true
+ } else if case .dropping = sessionState.state {
+ terminating = true
+ }
+
+ if terminating, !wasTerminated {
if !self.didSetCanBeRemoved {
self.didSetCanBeRemoved = true
- self.canBeRemovedPromise.set(.single(true) |> delay(2.4, queue: Queue.mainQueue()))
+ self.canBeRemovedPromise.set(.single(true) |> delay(2.0, queue: Queue.mainQueue()))
}
self.hungUpPromise.set(true)
if sessionState.isOutgoing {
if !self.droppedCall && self.dropCallKitCallTimer == nil {
- let dropCallKitCallTimer = SwiftSignalKit.Timer(timeout: 2.4, repeat: false, completion: { [weak self] in
+ let dropCallKitCallTimer = SwiftSignalKit.Timer(timeout: 2.0, repeat: false, completion: { [weak self] in
if let strongSelf = self {
strongSelf.dropCallKitCallTimer = nil
if !strongSelf.droppedCall {
@@ -647,7 +720,9 @@ public final class PresentationCallImpl: PresentationCall {
private func updateTone(_ state: PresentationCallState, callContextState: OngoingCallContextState?, previous: CallSession?) {
var tone: PresentationCallTone?
if let callContextState = callContextState, case .reconnecting = callContextState.state {
- tone = .connecting
+ if !self.isVideo {
+ tone = .connecting
+ }
} else if let previous = previous {
switch previous.state {
case .accepting, .active, .dropping, .requesting:
@@ -656,7 +731,9 @@ public final class PresentationCallImpl: PresentationCall {
if case .requesting = previous.state {
tone = .ringing
} else {
- tone = .connecting
+ if !self.isVideo {
+ tone = .connecting
+ }
}
case .requesting(true):
tone = .ringing
@@ -712,7 +789,7 @@ public final class PresentationCallImpl: PresentationCall {
}
if value {
if strongSelf.isVideo {
- DeviceAccess.authorizeAccess(to: .camera, presentationData: presentationData, present: { c, a in
+ DeviceAccess.authorizeAccess(to: .camera(.videoCall), presentationData: presentationData, present: { c, a in
present(c, a)
}, openSettings: {
openSettings()
@@ -771,13 +848,10 @@ public final class PresentationCallImpl: PresentationCall {
}
}
- public func acceptVideo() {
- if self.videoCapturer == nil {
- let videoCapturer = OngoingCallVideoCapturer()
- self.videoCapturer = videoCapturer
- }
- if let videoCapturer = self.videoCapturer {
- self.ongoingContext?.acceptVideo(videoCapturer)
+ public func disableVideo() {
+ if let _ = self.videoCapturer {
+ self.videoCapturer = nil
+ self.ongoingContext?.disableVideo()
}
}
@@ -810,10 +884,51 @@ public final class PresentationCallImpl: PresentationCall {
self.ongoingContext?.makeIncomingVideoView(completion: { view in
if let view = view {
let setOnFirstFrameReceived = view.setOnFirstFrameReceived
+ let setOnOrientationUpdated = view.setOnOrientationUpdated
+ let setOnIsMirroredUpdated = view.setOnIsMirroredUpdated
completion(PresentationCallVideoView(
view: view.view,
setOnFirstFrameReceived: { f in
setOnFirstFrameReceived(f)
+ },
+ getOrientation: { [weak view] in
+ if let view = view {
+ let mappedValue: PresentationCallVideoView.Orientation
+ switch view.getOrientation() {
+ case .rotation0:
+ mappedValue = .rotation0
+ case .rotation90:
+ mappedValue = .rotation90
+ case .rotation180:
+ mappedValue = .rotation180
+ case .rotation270:
+ mappedValue = .rotation270
+ }
+ return mappedValue
+ } else {
+ return .rotation0
+ }
+ },
+ setOnOrientationUpdated: { f in
+ setOnOrientationUpdated { value in
+ let mappedValue: PresentationCallVideoView.Orientation
+ switch value {
+ case .rotation0:
+ mappedValue = .rotation0
+ case .rotation90:
+ mappedValue = .rotation90
+ case .rotation180:
+ mappedValue = .rotation180
+ case .rotation270:
+ mappedValue = .rotation270
+ }
+ f?(mappedValue)
+ }
+ },
+ setOnIsMirroredUpdated: { f in
+ setOnIsMirroredUpdated { value in
+ f?(value)
+ }
}
))
} else {
@@ -831,10 +946,51 @@ public final class PresentationCallImpl: PresentationCall {
self.videoCapturer?.makeOutgoingVideoView(completion: { view in
if let view = view {
let setOnFirstFrameReceived = view.setOnFirstFrameReceived
+ let setOnOrientationUpdated = view.setOnOrientationUpdated
+ let setOnIsMirroredUpdated = view.setOnIsMirroredUpdated
completion(PresentationCallVideoView(
view: view.view,
setOnFirstFrameReceived: { f in
setOnFirstFrameReceived(f)
+ },
+ getOrientation: { [weak view] in
+ if let view = view {
+ let mappedValue: PresentationCallVideoView.Orientation
+ switch view.getOrientation() {
+ case .rotation0:
+ mappedValue = .rotation0
+ case .rotation90:
+ mappedValue = .rotation90
+ case .rotation180:
+ mappedValue = .rotation180
+ case .rotation270:
+ mappedValue = .rotation270
+ }
+ return mappedValue
+ } else {
+ return .rotation0
+ }
+ },
+ setOnOrientationUpdated: { f in
+ setOnOrientationUpdated { value in
+ let mappedValue: PresentationCallVideoView.Orientation
+ switch value {
+ case .rotation0:
+ mappedValue = .rotation0
+ case .rotation90:
+ mappedValue = .rotation90
+ case .rotation180:
+ mappedValue = .rotation180
+ case .rotation270:
+ mappedValue = .rotation270
+ }
+ f?(mappedValue)
+ }
+ },
+ setOnIsMirroredUpdated: { f in
+ setOnIsMirroredUpdated { value in
+ f?(value)
+ }
}
))
} else {
diff --git a/submodules/TelegramCallsUI/Sources/PresentationCallManager.swift b/submodules/TelegramCallsUI/Sources/PresentationCallManager.swift
index 07e10639f9..aef43dc9a0 100644
--- a/submodules/TelegramCallsUI/Sources/PresentationCallManager.swift
+++ b/submodules/TelegramCallsUI/Sources/PresentationCallManager.swift
@@ -10,6 +10,7 @@ import TelegramAudio
import TelegramVoip
import TelegramUIPreferences
import AccountContext
+import CallKit
private func callKitIntegrationIfEnabled(_ integration: CallKitIntegration?, settings: VoiceCallSettings?) -> CallKitIntegration? {
let enabled = settings?.enableSystemIntegration ?? true
@@ -82,7 +83,6 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
private let accountManager: AccountManager
private let audioSession: ManagedAudioSession
private let callKitIntegration: CallKitIntegration?
- private var isVideoPossible: Bool
private var currentCallValue: PresentationCallImpl?
private var currentCall: PresentationCallImpl? {
@@ -117,15 +117,14 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
return OngoingCallContext.maxLayer
}
- public static func voipVersions(includeExperimental: Bool) -> [String] {
- return OngoingCallContext.versions(includeExperimental: includeExperimental)
+ public static func voipVersions(includeExperimental: Bool, includeReference: Bool) -> [(version: String, supportsVideo: Bool)] {
+ return OngoingCallContext.versions(includeExperimental: includeExperimental, includeReference: includeReference)
}
- public init(accountManager: AccountManager, enableVideoCalls: Bool, getDeviceAccessData: @escaping () -> (presentationData: PresentationData, present: (ViewController, Any?) -> Void, openSettings: () -> Void), isMediaPlaying: @escaping () -> Bool, resumeMediaPlayback: @escaping () -> Void, audioSession: ManagedAudioSession, activeAccounts: Signal<[Account], NoError>) {
+ public init(accountManager: AccountManager, getDeviceAccessData: @escaping () -> (presentationData: PresentationData, present: (ViewController, Any?) -> Void, openSettings: () -> Void), isMediaPlaying: @escaping () -> Bool, resumeMediaPlayback: @escaping () -> Void, audioSession: ManagedAudioSession, activeAccounts: Signal<[Account], NoError>) {
self.getDeviceAccessData = getDeviceAccessData
self.accountManager = accountManager
self.audioSession = audioSession
- self.isVideoPossible = enableVideoCalls
self.isMediaPlaying = isMediaPlaying
self.resumeMediaPlayback = resumeMediaPlayback
@@ -136,7 +135,7 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
var setCallMutedImpl: ((UUID, Bool) -> Void)?
var audioSessionActivationChangedImpl: ((Bool) -> Void)?
- self.callKitIntegration = CallKitIntegration(enableVideoCalls: enableVideoCalls, startCall: { account, uuid, handle, isVideo in
+ self.callKitIntegration = CallKitIntegration(startCall: { account, uuid, handle, isVideo in
if let startCallImpl = startCallImpl {
return startCallImpl(account, uuid, handle, isVideo)
} else {
@@ -214,7 +213,7 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
startCallImpl = { [weak self] account, uuid, handle, isVideo in
if let strongSelf = self, let userId = Int32(handle) {
- return strongSelf.startCall(account: account, peerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: userId), isVideo: isVideo, isVideoPossible: strongSelf.isVideoPossible, internalId: uuid)
+ return strongSelf.startCall(account: account, peerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: userId), isVideo: isVideo, internalId: uuid)
|> take(1)
|> map { result -> Bool in
return result
@@ -283,7 +282,7 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
private func ringingStatesUpdated(_ ringingStates: [(Account, Peer, CallSessionRingingState, Bool, NetworkType)], enableCallKit: Bool) {
if let firstState = ringingStates.first {
if self.currentCall == nil {
- self.currentCallDisposable.set((combineLatest(firstState.0.postbox.preferencesView(keys: [PreferencesKeys.voipConfiguration, ApplicationSpecificPreferencesKeys.voipDerivedState, PreferencesKeys.appConfiguration]) |> take(1), accountManager.sharedData(keys: [SharedDataKeys.autodownloadSettings]) |> take(1))
+ self.currentCallDisposable.set((combineLatest(firstState.0.postbox.preferencesView(keys: [PreferencesKeys.voipConfiguration, ApplicationSpecificPreferencesKeys.voipDerivedState, PreferencesKeys.appConfiguration]) |> take(1), accountManager.sharedData(keys: [SharedDataKeys.autodownloadSettings, ApplicationSpecificSharedDataKeys.experimentalUISettings]) |> take(1))
|> deliverOnMainQueue).start(next: { [weak self] preferences, sharedData in
guard let strongSelf = self else {
return
@@ -292,6 +291,7 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
let configuration = preferences.values[PreferencesKeys.voipConfiguration] as? VoipConfiguration ?? .defaultValue
let derivedState = preferences.values[ApplicationSpecificPreferencesKeys.voipDerivedState] as? VoipDerivedState ?? .default
let autodownloadSettings = sharedData.entries[SharedDataKeys.autodownloadSettings] as? AutodownloadSettings ?? .defaultSettings
+ let experimentalSettings = sharedData.entries[ApplicationSpecificSharedDataKeys.experimentalUISettings] as? ExperimentalUISettings ?? .defaultSettings
let appConfiguration = preferences.values[PreferencesKeys.appConfiguration] as? AppConfiguration ?? AppConfiguration.defaultValue
let call = PresentationCallImpl(
@@ -313,7 +313,8 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
currentNetworkType: firstState.4,
updatedNetworkType: firstState.0.networkType,
startWithVideo: firstState.2.isVideo,
- isVideoPossible: strongSelf.isVideoPossible
+ isVideoPossible: firstState.2.isVideoPossible,
+ preferredVideoCodec: experimentalSettings.preferredVideoCodec
)
strongSelf.updateCurrentCall(call)
strongSelf.currentCallPromise.set(.single(call))
@@ -332,17 +333,32 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
} else {
for (account, _, state, _, _) in ringingStates {
if state.id != self.currentCall?.internalId {
- account.callSessionManager.drop(internalId: state.id, reason: .missed, debugLog: .single(nil))
+ account.callSessionManager.drop(internalId: state.id, reason: .busy, debugLog: .single(nil))
}
}
}
}
}
- public func requestCall(account: Account, peerId: PeerId, isVideo: Bool, endCurrentIfAny: Bool) -> RequestCallResult {
- let isVideoPossible = self.isVideoPossible
- if let call = self.currentCall, !endCurrentIfAny {
- return .alreadyInProgress(call.peerId)
+ public func requestCall(context: AccountContext, peerId: PeerId, isVideo: Bool, endCurrentIfAny: Bool) -> RequestCallResult {
+ let account = context.account
+
+ var alreadyInCall: Bool = false
+ var alreadyInCallWithPeerId: PeerId?
+
+ if let call = self.currentCall {
+ alreadyInCall = true
+ alreadyInCallWithPeerId = call.peerId
+ } else {
+ if #available(iOS 10.0, *) {
+ if CXCallObserver().calls.contains(where: { $0.hasEnded == false }) {
+ alreadyInCall = true
+ }
+ }
+ }
+
+ if alreadyInCall, !endCurrentIfAny {
+ return .alreadyInProgress(alreadyInCallWithPeerId)
}
if let _ = callKitIntegrationIfEnabled(self.callKitIntegration, settings: self.callSettings) {
let begin: () -> Void = { [weak self] in
@@ -357,8 +373,8 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
}, openSettings: {
openSettings()
}, { value in
- if isVideo {
- DeviceAccess.authorizeAccess(to: .camera, presentationData: presentationData, present: { c, a in
+ if isVideo && value {
+ DeviceAccess.authorizeAccess(to: .camera(.videoCall), presentationData: presentationData, present: { c, a in
present(c, a)
}, openSettings: {
openSettings()
@@ -405,7 +421,7 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
guard let strongSelf = self else {
return
}
- let _ = strongSelf.startCall(account: account, peerId: peerId, isVideo: isVideo, isVideoPossible: isVideoPossible).start()
+ let _ = strongSelf.startCall(account: account, peerId: peerId, isVideo: isVideo).start()
}
if let currentCall = self.currentCall {
self.startCallDisposable.set((currentCall.hangUp()
@@ -423,7 +439,6 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
account: Account,
peerId: PeerId,
isVideo: Bool,
- isVideoPossible: Bool,
internalId: CallSessionInternalId = CallSessionInternalId()
) -> Signal {
let (presentationData, present, openSettings) = self.getDeviceAccessData()
@@ -434,8 +449,8 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
}, openSettings: {
openSettings()
}, { value in
- if isVideo {
- DeviceAccess.authorizeAccess(to: .camera, presentationData: presentationData, present: { c, a in
+ if isVideo && value {
+ DeviceAccess.authorizeAccess(to: .camera(.videoCall), presentationData: presentationData, present: { c, a in
present(c, a)
}, openSettings: {
openSettings()
@@ -459,11 +474,38 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
if !accessEnabled {
return .single(false)
}
- return (combineLatest(queue: .mainQueue(), account.callSessionManager.request(peerId: peerId, isVideo: isVideo, internalId: internalId), networkType |> take(1), account.postbox.peerView(id: peerId) |> map { peerView -> Bool in
+
+ let request = account.postbox.transaction { transaction -> (VideoCallsConfiguration, CachedUserData?) in
+ let appConfiguration: AppConfiguration = transaction.getPreferencesEntry(key: PreferencesKeys.appConfiguration) as? AppConfiguration ?? AppConfiguration.defaultValue
+ return (VideoCallsConfiguration(appConfiguration: appConfiguration), transaction.getPeerCachedData(peerId: peerId) as? CachedUserData)
+ }
+ |> mapToSignal { callsConfiguration, cachedUserData -> Signal in
+ var isVideoPossible: Bool
+ switch callsConfiguration.videoCallsSupport {
+ case .disabled:
+ isVideoPossible = isVideo
+ case .full:
+ isVideoPossible = true
+ case .onlyVideo:
+ isVideoPossible = isVideo
+ }
+ if let cachedUserData = cachedUserData, cachedUserData.videoCallsAvailable {
+ } else {
+ isVideoPossible = false
+ }
+
+ return account.callSessionManager.request(peerId: peerId, isVideo: isVideo, enableVideo: isVideoPossible, internalId: internalId)
+ }
+
+ let cachedUserData = account.postbox.transaction { transaction -> CachedUserData? in
+ return transaction.getPeerCachedData(peerId: peerId) as? CachedUserData
+ }
+
+ return (combineLatest(queue: .mainQueue(), request, networkType |> take(1), account.postbox.peerView(id: peerId) |> map { peerView -> Bool in
return peerView.peerIsContact
- } |> take(1), account.postbox.preferencesView(keys: [PreferencesKeys.voipConfiguration, ApplicationSpecificPreferencesKeys.voipDerivedState, PreferencesKeys.appConfiguration]) |> take(1), accountManager.sharedData(keys: [SharedDataKeys.autodownloadSettings]) |> take(1))
+ } |> take(1), account.postbox.preferencesView(keys: [PreferencesKeys.voipConfiguration, ApplicationSpecificPreferencesKeys.voipDerivedState, PreferencesKeys.appConfiguration]) |> take(1), accountManager.sharedData(keys: [SharedDataKeys.autodownloadSettings, ApplicationSpecificSharedDataKeys.experimentalUISettings]) |> take(1), cachedUserData)
|> deliverOnMainQueue
- |> beforeNext { internalId, currentNetworkType, isContact, preferences, sharedData in
+ |> beforeNext { internalId, currentNetworkType, isContact, preferences, sharedData, cachedUserData in
if let strongSelf = self, accessEnabled {
if let currentCall = strongSelf.currentCall {
currentCall.rejectBusy()
@@ -474,6 +516,23 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
let autodownloadSettings = sharedData.entries[SharedDataKeys.autodownloadSettings] as? AutodownloadSettings ?? .defaultSettings
let appConfiguration = preferences.values[PreferencesKeys.appConfiguration] as? AppConfiguration ?? AppConfiguration.defaultValue
+ let callsConfiguration = VideoCallsConfiguration(appConfiguration: appConfiguration)
+ var isVideoPossible: Bool
+ switch callsConfiguration.videoCallsSupport {
+ case .disabled:
+ isVideoPossible = isVideo
+ case .full:
+ isVideoPossible = true
+ case .onlyVideo:
+ isVideoPossible = isVideo
+ }
+ if let cachedUserData = cachedUserData, cachedUserData.videoCallsAvailable {
+ } else {
+ isVideoPossible = false
+ }
+
+ let experimentalSettings = sharedData.entries[ApplicationSpecificSharedDataKeys.experimentalUISettings] as? ExperimentalUISettings ?? .defaultSettings
+
let call = PresentationCallImpl(
account: account,
audioSession: strongSelf.audioSession,
@@ -496,7 +555,8 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
currentNetworkType: currentNetworkType,
updatedNetworkType: account.networkType,
startWithVideo: isVideo,
- isVideoPossible: isVideoPossible
+ isVideoPossible: isVideoPossible,
+ preferredVideoCodec: experimentalSettings.preferredVideoCodec
)
strongSelf.updateCurrentCall(call)
strongSelf.currentCallPromise.set(.single(call))
diff --git a/submodules/TelegramCore/Sources/AccountIntermediateState.swift b/submodules/TelegramCore/Sources/AccountIntermediateState.swift
index e55221d479..47fc5898cb 100644
--- a/submodules/TelegramCore/Sources/AccountIntermediateState.swift
+++ b/submodules/TelegramCore/Sources/AccountIntermediateState.swift
@@ -94,6 +94,7 @@ enum AccountStateMutationOperation {
case UpdatePinnedItemIds(PeerGroupId, AccountStateUpdatePinnedItemIdsOperation)
case ReadMessageContents((PeerId?, [Int32]))
case UpdateMessageImpressionCount(MessageId, Int32)
+ case UpdateMessageForwardsCount(MessageId, Int32)
case UpdateInstalledStickerPacks(AccountStateUpdateStickerPacksOperation)
case UpdateRecentGifs
case UpdateChatInputState(PeerId, SynchronizeableChatInputState?)
@@ -109,6 +110,22 @@ enum AccountStateMutationOperation {
case UpdateChatListFilter(id: Int32, filter: Api.DialogFilter?)
}
+struct HoleFromPreviousState {
+ var validateChannelPts: Int32?
+
+ func mergedWith(_ other: HoleFromPreviousState) -> HoleFromPreviousState {
+ var result = self
+ if let pts = self.validateChannelPts, let otherPts = other.validateChannelPts {
+ result.validateChannelPts = max(pts, otherPts)
+ } else if let pts = self.validateChannelPts {
+ result.validateChannelPts = pts
+ } else if let otherPts = other.validateChannelPts {
+ result.validateChannelPts = otherPts
+ }
+ return result
+ }
+}
+
struct AccountMutableState {
let initialState: AccountInitialState
let branchOperationIndex: Int
@@ -122,7 +139,7 @@ struct AccountMutableState {
var referencedMessageIds: Set
var storedMessages: Set
var readInboxMaxIds: [PeerId: MessageId]
- var namespacesWithHolesFromPreviousState: [PeerId: Set]
+ var namespacesWithHolesFromPreviousState: [PeerId: [MessageId.Namespace: HoleFromPreviousState]]
var updatedOutgoingUniqueMessageIds: [Int64: Int32]
var storedMessagesByPeerIdAndTimestamp: [PeerId: Set]
@@ -154,7 +171,7 @@ struct AccountMutableState {
self.updatedOutgoingUniqueMessageIds = [:]
}
- init(initialState: AccountInitialState, operations: [AccountStateMutationOperation], state: AuthorizedAccountState.State, peers: [PeerId: Peer], channelStates: [PeerId: AccountStateChannelState], peerChatInfos: [PeerId: PeerChatInfo], referencedMessageIds: Set, storedMessages: Set, readInboxMaxIds: [PeerId: MessageId], storedMessagesByPeerIdAndTimestamp: [PeerId: Set], namespacesWithHolesFromPreviousState: [PeerId: Set], updatedOutgoingUniqueMessageIds: [Int64: Int32], displayAlerts: [(text: String, isDropAuth: Bool)], branchOperationIndex: Int) {
+ init(initialState: AccountInitialState, operations: [AccountStateMutationOperation], state: AuthorizedAccountState.State, peers: [PeerId: Peer], channelStates: [PeerId: AccountStateChannelState], peerChatInfos: [PeerId: PeerChatInfo], referencedMessageIds: Set, storedMessages: Set, readInboxMaxIds: [PeerId: MessageId], storedMessagesByPeerIdAndTimestamp: [PeerId: Set], namespacesWithHolesFromPreviousState: [PeerId: [MessageId.Namespace: HoleFromPreviousState]], updatedOutgoingUniqueMessageIds: [Int64: Int32], displayAlerts: [(text: String, isDropAuth: Bool)], branchOperationIndex: Int) {
self.initialState = initialState
self.operations = operations
self.state = state
@@ -187,10 +204,14 @@ struct AccountMutableState {
self.externallyUpdatedPeerId.formUnion(other.externallyUpdatedPeerId)
for (peerId, namespaces) in other.namespacesWithHolesFromPreviousState {
if self.namespacesWithHolesFromPreviousState[peerId] == nil {
- self.namespacesWithHolesFromPreviousState[peerId] = Set()
+ self.namespacesWithHolesFromPreviousState[peerId] = [:]
}
- for namespace in namespaces {
- self.namespacesWithHolesFromPreviousState[peerId]!.insert(namespace)
+ for (namespace, namespaceState) in namespaces {
+ if self.namespacesWithHolesFromPreviousState[peerId]![namespace] == nil {
+ self.namespacesWithHolesFromPreviousState[peerId]![namespace] = namespaceState
+ } else {
+ self.namespacesWithHolesFromPreviousState[peerId]![namespace] = self.namespacesWithHolesFromPreviousState[peerId]![namespace]!.mergedWith(namespaceState)
+ }
}
}
self.updatedOutgoingUniqueMessageIds.merge(other.updatedOutgoingUniqueMessageIds, uniquingKeysWith: { lhs, _ in lhs })
@@ -296,11 +317,16 @@ struct AccountMutableState {
self.addOperation(.UpdateGlobalNotificationSettings(subject, notificationSettings))
}
- mutating func setNeedsHoleFromPreviousState(peerId: PeerId, namespace: MessageId.Namespace) {
+ mutating func setNeedsHoleFromPreviousState(peerId: PeerId, namespace: MessageId.Namespace, validateChannelPts: Int32?) {
if self.namespacesWithHolesFromPreviousState[peerId] == nil {
- self.namespacesWithHolesFromPreviousState[peerId] = Set()
+ self.namespacesWithHolesFromPreviousState[peerId] = [:]
+ }
+ let namespaceState = HoleFromPreviousState(validateChannelPts: validateChannelPts)
+ if self.namespacesWithHolesFromPreviousState[peerId]![namespace] == nil {
+ self.namespacesWithHolesFromPreviousState[peerId]![namespace] = namespaceState
+ } else {
+ self.namespacesWithHolesFromPreviousState[peerId]![namespace] = self.namespacesWithHolesFromPreviousState[peerId]![namespace]!.mergedWith(namespaceState)
}
- self.namespacesWithHolesFromPreviousState[peerId]!.insert(namespace)
}
mutating func mergeChats(_ chats: [Api.Chat]) {
@@ -410,6 +436,10 @@ struct AccountMutableState {
self.addOperation(.UpdateMessageImpressionCount(id, count))
}
+ mutating func addUpdateMessageForwardsCount(id: MessageId, count: Int32) {
+ self.addOperation(.UpdateMessageForwardsCount(id, count))
+ }
+
mutating func addUpdateInstalledStickerPacks(_ operation: AccountStateUpdateStickerPacksOperation) {
self.addOperation(.UpdateInstalledStickerPacks(operation))
}
@@ -444,7 +474,7 @@ struct AccountMutableState {
mutating func addOperation(_ operation: AccountStateMutationOperation) {
switch operation {
- case .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMessagePoll/*, .UpdateMessageReactions*/, .UpdateMedia, .ReadOutbox, .ReadGroupFeedInbox, .MergePeerPresences, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .UpdateCachedPeerData, .UpdatePinnedItemIds, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateInstalledStickerPacks, .UpdateRecentGifs, .UpdateChatInputState, .UpdateCall, .AddCallSignalingData, .UpdateLangPack, .UpdateMinAvailableMessage, .UpdatePeerChatUnreadMark, .UpdateIsContact, .UpdatePeerChatInclusion, .UpdatePeersNearby, .UpdateTheme, .SyncChatListFilters, .UpdateChatListFilterOrder, .UpdateChatListFilter:
+ case .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMessagePoll/*, .UpdateMessageReactions*/, .UpdateMedia, .ReadOutbox, .ReadGroupFeedInbox, .MergePeerPresences, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .UpdateCachedPeerData, .UpdatePinnedItemIds, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateMessageForwardsCount, .UpdateInstalledStickerPacks, .UpdateRecentGifs, .UpdateChatInputState, .UpdateCall, .AddCallSignalingData, .UpdateLangPack, .UpdateMinAvailableMessage, .UpdatePeerChatUnreadMark, .UpdateIsContact, .UpdatePeerChatInclusion, .UpdatePeersNearby, .UpdateTheme, .SyncChatListFilters, .UpdateChatListFilterOrder, .UpdateChatListFilter:
break
case let .AddMessages(messages, location):
for message in messages {
diff --git a/submodules/TelegramCore/Sources/AccountManager.swift b/submodules/TelegramCore/Sources/AccountManager.swift
index 2303d127ff..fb9b223ac9 100644
--- a/submodules/TelegramCore/Sources/AccountManager.swift
+++ b/submodules/TelegramCore/Sources/AccountManager.swift
@@ -35,6 +35,7 @@ private var declaredEncodables: Void = {
declareEncodable(CloudDocumentMediaResource.self, f: { CloudDocumentMediaResource(decoder: $0) })
declareEncodable(TelegramMediaWebpage.self, f: { TelegramMediaWebpage(decoder: $0) })
declareEncodable(ViewCountMessageAttribute.self, f: { ViewCountMessageAttribute(decoder: $0) })
+ declareEncodable(ForwardCountMessageAttribute.self, f: { ForwardCountMessageAttribute(decoder: $0) })
declareEncodable(NotificationInfoMessageAttribute.self, f: { NotificationInfoMessageAttribute(decoder: $0) })
declareEncodable(TelegramMediaAction.self, f: { TelegramMediaAction(decoder: $0) })
declareEncodable(TelegramPeerNotificationSettings.self, f: { TelegramPeerNotificationSettings(decoder: $0) })
diff --git a/submodules/TelegramCore/Sources/AccountStateManagementUtils.swift b/submodules/TelegramCore/Sources/AccountStateManagementUtils.swift
index 430fdbec38..b0a9bb787f 100644
--- a/submodules/TelegramCore/Sources/AccountStateManagementUtils.swift
+++ b/submodules/TelegramCore/Sources/AccountStateManagementUtils.swift
@@ -1253,6 +1253,8 @@ private func finalStateWithUpdatesAndServerTime(postbox: Postbox, network: Netwo
updatedState.addReadMessagesContents((PeerId(namespace: Namespaces.Peer.CloudChannel, id: channelId), messages))
case let .updateChannelMessageViews(channelId, id, views):
updatedState.addUpdateMessageImpressionCount(id: MessageId(peerId: PeerId(namespace: Namespaces.Peer.CloudChannel, id: channelId), namespace: Namespaces.Message.Cloud, id: id), count: views)
+ /*case let .updateChannelMessageForwards(channelId, id, forwards):
+ updatedState.addUpdateMessageForwardsCount(id: MessageId(peerId: PeerId(namespace: Namespaces.Peer.CloudChannel, id: channelId), namespace: Namespaces.Message.Cloud, id: id), count: forwards)*/
case let .updateNewStickerSet(stickerset):
updatedState.addUpdateInstalledStickerPacks(.add(stickerset))
case let .updateStickerSetsOrder(flags, order):
@@ -1510,7 +1512,7 @@ private func resolveMissingPeerChatInfos(network: Network, state: AccountMutable
case let .dialog(_, peer, topMessage, readInboxMaxId, readOutboxMaxId, unreadCount, unreadMentionsCount, notifySettings, pts, _, folderId):
let peerId = peer.peerId
- updatedState.setNeedsHoleFromPreviousState(peerId: peerId, namespace: Namespaces.Message.Cloud)
+ updatedState.setNeedsHoleFromPreviousState(peerId: peerId, namespace: Namespaces.Message.Cloud, validateChannelPts: pts)
if topMessage != 0 {
topMessageIds.insert(MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: topMessage))
@@ -1590,7 +1592,7 @@ private func resolveMissingPeerChatInfos(network: Network, state: AccountMutable
}
func keepPollingChannel(postbox: Postbox, network: Network, peerId: PeerId, stateManager: AccountStateManager) -> Signal {
- return postbox.transaction { transaction -> Signal in
+ let signal: Signal = postbox.transaction { transaction -> Signal in
if let accountState = (transaction.getState() as? AuthorizedAccountState)?.state, let peer = transaction.getPeer(peerId) {
var channelStates: [PeerId: AccountStateChannelState] = [:]
if let channelState = transaction.getPeerChatState(peerId) as? ChannelState {
@@ -1635,6 +1637,9 @@ func keepPollingChannel(postbox: Postbox, network: Network, peerId: PeerId, stat
}
|> switchToLatest
|> restart
+
+ return signal
+ |> delay(1.0, queue: .concurrentDefaultQueue())
}
private func resetChannels(network: Network, peers: [Peer], state: AccountMutableState) -> Signal {
@@ -1751,7 +1756,11 @@ private func resetChannels(network: Network, peers: [Peer], state: AccountMutabl
for message in storeMessages {
if case let .Id(id) = message.id, id.namespace == Namespaces.Message.Cloud {
- updatedState.setNeedsHoleFromPreviousState(peerId: id.peerId, namespace: id.namespace)
+ var channelPts: Int32?
+ if let state = channelStates[id.peerId] {
+ channelPts = state.pts
+ }
+ updatedState.setNeedsHoleFromPreviousState(peerId: id.peerId, namespace: id.namespace, validateChannelPts: channelPts)
channelSynchronizedUntilMessage[id.peerId] = id.id
}
}
@@ -1841,7 +1850,11 @@ private func pollChannel(network: Network, peer: Peer, state: AccountMutableStat
updatedState.mergeUsers(users)
for apiMessage in newMessages {
- if let message = StoreMessage(apiMessage: apiMessage) {
+ if var message = StoreMessage(apiMessage: apiMessage) {
+ var attributes = message.attributes
+ attributes.append(ChannelMessageStateVersionAttribute(pts: pts))
+ message = message.withUpdatedAttributes(attributes)
+
if let preCachedResources = apiMessage.preCachedResources {
for (resource, data) in preCachedResources {
updatedState.addPreCachedResource(resource, data: data)
@@ -1885,6 +1898,8 @@ private func pollChannel(network: Network, peer: Peer, state: AccountMutableStat
updatedState.addReadMessagesContents((peer.id, messages))
case let .updateChannelMessageViews(_, id, views):
updatedState.addUpdateMessageImpressionCount(id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: id), count: views)
+ /*case let .updateChannelMessageForwards(_, id, views):
+ updatedState.addUpdateMessageForwardsCount(id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: id), count: views)*/
case let .updateChannelWebPage(_, apiWebpage, _, _):
switch apiWebpage {
case let .webPageEmpty(id):
@@ -1941,10 +1956,14 @@ private func pollChannel(network: Network, peer: Peer, state: AccountMutableStat
updatedState.mergeChats(chats)
updatedState.mergeUsers(users)
- updatedState.setNeedsHoleFromPreviousState(peerId: peer.peerId, namespace: Namespaces.Message.Cloud)
+ updatedState.setNeedsHoleFromPreviousState(peerId: peer.peerId, namespace: Namespaces.Message.Cloud, validateChannelPts: pts)
for apiMessage in messages {
- if let message = StoreMessage(apiMessage: apiMessage) {
+ if var message = StoreMessage(apiMessage: apiMessage) {
+ var attributes = message.attributes
+ attributes.append(ChannelMessageStateVersionAttribute(pts: pts))
+ message = message.withUpdatedAttributes(attributes)
+
if let preCachedResources = apiMessage.preCachedResources {
for (resource, data) in preCachedResources {
updatedState.addPreCachedResource(resource, data: data)
@@ -2075,7 +2094,7 @@ private func optimizedOperations(_ operations: [AccountStateMutationOperation])
var currentAddScheduledMessages: OptimizeAddMessagesState?
for operation in operations {
switch operation {
- case .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMessagePoll/*, .UpdateMessageReactions*/, .UpdateMedia, .MergeApiChats, .MergeApiUsers, .MergePeerPresences, .UpdatePeer, .ReadInbox, .ReadOutbox, .ReadGroupFeedInbox, .ResetReadState, .ResetIncomingReadState, .UpdatePeerChatUnreadMark, .ResetMessageTagSummary, .UpdateNotificationSettings, .UpdateGlobalNotificationSettings, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .UpdateCachedPeerData, .UpdatePinnedItemIds, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateInstalledStickerPacks, .UpdateRecentGifs, .UpdateChatInputState, .UpdateCall, .AddCallSignalingData, .UpdateLangPack, .UpdateMinAvailableMessage, .UpdateIsContact, .UpdatePeerChatInclusion, .UpdatePeersNearby, .UpdateTheme, .SyncChatListFilters, .UpdateChatListFilter, .UpdateChatListFilterOrder:
+ case .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMessagePoll/*, .UpdateMessageReactions*/, .UpdateMedia, .MergeApiChats, .MergeApiUsers, .MergePeerPresences, .UpdatePeer, .ReadInbox, .ReadOutbox, .ReadGroupFeedInbox, .ResetReadState, .ResetIncomingReadState, .UpdatePeerChatUnreadMark, .ResetMessageTagSummary, .UpdateNotificationSettings, .UpdateGlobalNotificationSettings, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .UpdateCachedPeerData, .UpdatePinnedItemIds, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateMessageForwardsCount, .UpdateInstalledStickerPacks, .UpdateRecentGifs, .UpdateChatInputState, .UpdateCall, .AddCallSignalingData, .UpdateLangPack, .UpdateMinAvailableMessage, .UpdateIsContact, .UpdatePeerChatInclusion, .UpdatePeersNearby, .UpdateTheme, .SyncChatListFilters, .UpdateChatListFilter, .UpdateChatListFilterOrder:
if let currentAddMessages = currentAddMessages, !currentAddMessages.messages.isEmpty {
result.append(.AddMessages(currentAddMessages.messages, currentAddMessages.location))
}
@@ -2175,9 +2194,14 @@ func replayFinalState(accountManager: AccountManager, postbox: Postbox, accountP
var syncChatListFilters = false
var holesFromPreviousStateMessageIds: [MessageId] = []
+ var clearHolesFromPreviousStateForChannelMessagesWithPts: [PeerIdAndMessageNamespace: Int32] = [:]
for (peerId, namespaces) in finalState.state.namespacesWithHolesFromPreviousState {
- for namespace in namespaces {
+ for (namespace, namespaceState) in namespaces {
+ if let pts = namespaceState.validateChannelPts {
+ clearHolesFromPreviousStateForChannelMessagesWithPts[PeerIdAndMessageNamespace(peerId: peerId, namespace: namespace)] = pts
+ }
+
var topId: Int32?
if namespace == Namespaces.Message.Cloud, let channelState = transaction.getPeerChatState(peerId) as? ChannelState {
if let synchronizedUntilMessageId = channelState.synchronizedUntilMessageId {
@@ -2753,6 +2777,21 @@ func replayFinalState(accountManager: AccountManager, postbox: Postbox, accountP
}
return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media))
})
+ case let .UpdateMessageForwardsCount(id, count):
+ transaction.updateMessage(id, update: { currentMessage in
+ var storeForwardInfo: StoreMessageForwardInfo?
+ if let forwardInfo = currentMessage.forwardInfo {
+ storeForwardInfo = StoreMessageForwardInfo(authorId: forwardInfo.author?.id, sourceId: forwardInfo.source?.id, sourceMessageId: forwardInfo.sourceMessageId, date: forwardInfo.date, authorSignature: forwardInfo.authorSignature, psaType: forwardInfo.psaType)
+ }
+ var attributes = currentMessage.attributes
+ loop: for j in 0 ..< attributes.count {
+ if let attribute = attributes[j] as? ForwardCountMessageAttribute {
+ attributes[j] = ForwardCountMessageAttribute(count: max(attribute.count, Int(count)))
+ break loop
+ }
+ }
+ return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media))
+ })
case let .UpdateInstalledStickerPacks(operation):
stickerPackOperations.append(operation)
case .UpdateRecentGifs:
@@ -2838,6 +2877,37 @@ func replayFinalState(accountManager: AccountManager, postbox: Postbox, accountP
}
}
+ // could be the reason for unbounded slowdown, needs investigation
+ /*for (peerIdAndNamespace, pts) in clearHolesFromPreviousStateForChannelMessagesWithPts {
+ var upperMessageId: Int32?
+ var lowerMessageId: Int32?
+ transaction.scanMessageAttributes(peerId: peerIdAndNamespace.peerId, namespace: peerIdAndNamespace.namespace, { id, attributes in
+ for attribute in attributes {
+ if let attribute = attribute as? ChannelMessageStateVersionAttribute {
+ if attribute.pts >= pts {
+ if upperMessageId == nil {
+ upperMessageId = id.id
+ }
+ if let lowerMessageIdValue = lowerMessageId {
+ lowerMessageId = min(id.id, lowerMessageIdValue)
+ } else {
+ lowerMessageId = id.id
+ }
+ return true
+ } else {
+ return false
+ }
+ }
+ }
+ return false
+ })
+ if let upperMessageId = upperMessageId, let lowerMessageId = lowerMessageId {
+ if upperMessageId != lowerMessageId {
+ transaction.removeHole(peerId: peerIdAndNamespace.peerId, namespace: peerIdAndNamespace.namespace, space: .everywhere, range: lowerMessageId ... upperMessageId)
+ }
+ }
+ }*/
+
if !peerActivityTimestamps.isEmpty {
updatePeerPresenceLastActivities(transaction: transaction, accountPeerId: accountPeerId, activities: peerActivityTimestamps)
}
diff --git a/submodules/TelegramCore/Sources/AccountViewTracker.swift b/submodules/TelegramCore/Sources/AccountViewTracker.swift
index b2e591aba4..c439133ebb 100644
--- a/submodules/TelegramCore/Sources/AccountViewTracker.swift
+++ b/submodules/TelegramCore/Sources/AccountViewTracker.swift
@@ -599,20 +599,34 @@ public final class AccountViewTracker {
return account.postbox.transaction { transaction -> Void in
for i in 0 ..< messageIds.count {
if i < viewCounts.count {
- transaction.updateMessage(messageIds[i], update: { currentMessage in
- let storeForwardInfo = currentMessage.forwardInfo.flatMap(StoreMessageForwardInfo.init)
- var attributes = currentMessage.attributes
- loop: for j in 0 ..< attributes.count {
- if let attribute = attributes[j] as? ViewCountMessageAttribute {
- if attribute.count >= Int(viewCounts[i]) {
- return .skip
+ let views = viewCounts[i]
+ do {
+ transaction.updateMessage(messageIds[i], update: { currentMessage in
+ let storeForwardInfo = currentMessage.forwardInfo.flatMap(StoreMessageForwardInfo.init)
+ var attributes = currentMessage.attributes
+ loop: for j in 0 ..< attributes.count {
+ if let attribute = attributes[j] as? ViewCountMessageAttribute {
+ attributes[j] = ViewCountMessageAttribute(count: max(attribute.count, Int(views)))
}
- attributes[j] = ViewCountMessageAttribute(count: max(attribute.count, Int(viewCounts[i])))
- break loop
}
- }
- return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media))
- })
+ return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media))
+ })
+ }
+ /*if case let .messageViews(views, forwards) = viewCounts[i] {
+ transaction.updateMessage(messageIds[i], update: { currentMessage in
+ let storeForwardInfo = currentMessage.forwardInfo.flatMap(StoreMessageForwardInfo.init)
+ var attributes = currentMessage.attributes
+ loop: for j in 0 ..< attributes.count {
+ if let attribute = attributes[j] as? ViewCountMessageAttribute {
+ attributes[j] = ViewCountMessageAttribute(count: max(attribute.count, Int(views)))
+ }
+ if let _ = attributes[j] as? ForwardCountMessageAttribute {
+ attributes[j] = ForwardCountMessageAttribute(count: Int(forwards))
+ }
+ }
+ return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media))
+ })
+ }*/
}
}
}
@@ -1111,13 +1125,13 @@ public final class AccountViewTracker {
let combinedDisposable = MetaDisposable()
self.queue.async {
var addHole = false
- /*if let context = self.channelPollingContexts[peerId] {
+ if let context = self.channelPollingContexts[peerId] {
if context.subscribers.isEmpty {
addHole = true
}
} else {
addHole = true
- }*/
+ }
if addHole {
let _ = self.account?.postbox.transaction({ transaction -> Void in
if transaction.getPeerChatListIndex(peerId) == nil {
@@ -1318,11 +1332,13 @@ public final class AccountViewTracker {
if lhsTimestamp != rhsTimestamp {
return false
}
+ var lhsVideo = false
var lhsMissed = false
var lhsOther = false
inner: for media in lhs.media {
if let action = media as? TelegramMediaAction {
- if case let .phoneCall(_, discardReason, _, _) = action.action {
+ if case let .phoneCall(_, discardReason, _, video) = action.action {
+ lhsVideo = video
if lhs.flags.contains(.Incoming), let discardReason = discardReason, case .missed = discardReason {
lhsMissed = true
} else {
@@ -1332,11 +1348,13 @@ public final class AccountViewTracker {
}
}
}
+ var rhsVideo = false
var rhsMissed = false
var rhsOther = false
inner: for media in rhs.media {
if let action = media as? TelegramMediaAction {
- if case let .phoneCall(_, discardReason, _, _) = action.action {
+ if case let .phoneCall(_, discardReason, _, video) = action.action {
+ rhsVideo = video
if rhs.flags.contains(.Incoming), let discardReason = discardReason, case .missed = discardReason {
rhsMissed = true
} else {
@@ -1346,7 +1364,7 @@ public final class AccountViewTracker {
}
}
}
- if lhsMissed != rhsMissed || lhsOther != rhsOther {
+ if lhsMissed != rhsMissed || lhsOther != rhsOther || lhsVideo != rhsVideo {
return false
}
return true
diff --git a/submodules/TelegramCore/Sources/ApiGroupOrChannel.swift b/submodules/TelegramCore/Sources/ApiGroupOrChannel.swift
index fdc02d6982..d5898d42f8 100644
--- a/submodules/TelegramCore/Sources/ApiGroupOrChannel.swift
+++ b/submodules/TelegramCore/Sources/ApiGroupOrChannel.swift
@@ -18,8 +18,8 @@ func imageRepresentationsForApiChatPhoto(_ photo: Api.ChatPhoto) -> [TelegramMed
case let .fileLocationToBeDeprecated(volumeId, localId):
fullSizeResource = CloudPeerPhotoSizeMediaResource(datacenterId: dcId, sizeSpec: .fullSize, volumeId: volumeId, localId: localId)
}
- representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 80, height: 80), resource: smallResource))
- representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 640, height: 640), resource: fullSizeResource))
+ representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 80, height: 80), resource: smallResource, progressiveSizes: []))
+ representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 640, height: 640), resource: fullSizeResource, progressiveSizes: []))
case .chatPhotoEmpty:
break
}
diff --git a/submodules/TelegramCore/Sources/CallSessionManager.swift b/submodules/TelegramCore/Sources/CallSessionManager.swift
index b888f3bf84..a34a1100c9 100644
--- a/submodules/TelegramCore/Sources/CallSessionManager.swift
+++ b/submodules/TelegramCore/Sources/CallSessionManager.swift
@@ -97,7 +97,7 @@ enum CallSessionInternalState {
case requested(id: Int64, accessHash: Int64, a: Data, gA: Data, config: SecretChatEncryptionConfig, remoteConfirmationTimestamp: Int32?)
case confirming(id: Int64, accessHash: Int64, key: Data, keyId: Int64, keyVisualHash: Data, disposable: Disposable)
case active(id: Int64, accessHash: Int64, beginTimestamp: Int32, key: Data, keyId: Int64, keyVisualHash: Data, connections: CallSessionConnectionSet, maxLayer: Int32, version: String, allowsP2P: Bool)
- case dropping(Disposable)
+ case dropping(reason: CallSessionTerminationReason, disposable: Disposable)
case terminated(id: Int64?, accessHash: Int64?, reason: CallSessionTerminationReason, reportRating: Bool, sendDebugLogs: Bool)
}
@@ -108,10 +108,7 @@ public struct CallSessionRingingState: Equatable {
public let id: CallSessionInternalId
public let peerId: PeerId
public let isVideo: Bool
-
- public static func ==(lhs: CallSessionRingingState, rhs: CallSessionRingingState) -> Bool {
- return lhs.id == rhs.id && lhs.peerId == rhs.peerId && lhs.isVideo == rhs.isVideo
- }
+ public let isVideoPossible: Bool
}
public enum DropCallReason {
@@ -141,7 +138,7 @@ public enum CallSessionState {
case accepting
case requesting(ringing: Bool)
case active(id: CallId, key: Data, keyVisualHash: Data, connections: CallSessionConnectionSet, maxLayer: Int32, version: String, allowsP2P: Bool)
- case dropping
+ case dropping(reason: CallSessionTerminationReason)
case terminated(id: CallId?, reason: CallSessionTerminationReason, options: CallTerminationOptions)
fileprivate init(_ context: CallSessionContext) {
@@ -158,8 +155,8 @@ public enum CallSessionState {
self = .requesting(ringing: remoteConfirmationTimestamp != nil)
case let .active(id, accessHash, _, key, _, keyVisualHash, connections, maxLayer, version, allowsP2P):
self = .active(id: CallId(id: id, accessHash: accessHash), key: key, keyVisualHash: keyVisualHash, connections: connections, maxLayer: maxLayer, version: version, allowsP2P: allowsP2P)
- case .dropping:
- self = .dropping
+ case let .dropping(reason, _):
+ self = .dropping(reason: reason)
case let .terminated(id, accessHash, reason, reportRating, sendDebugLogs):
var options = CallTerminationOptions()
if reportRating {
@@ -189,20 +186,82 @@ public struct CallSession {
public let isOutgoing: Bool
public let type: CallType
public let state: CallSessionState
+ public let isVideoPossible: Bool
}
-public struct CallSessionConnection {
- public let id: Int64
- public let ip: String
- public let ipv6: String
- public let port: Int32
- public let peerTag: Data
+public enum CallSessionConnection: Equatable {
+ public struct Reflector: Equatable {
+ public let id: Int64
+ public let ip: String
+ public let ipv6: String
+ public let port: Int32
+ public let peerTag: Data
+
+ public init(
+ id: Int64,
+ ip: String,
+ ipv6: String,
+ port: Int32,
+ peerTag: Data
+ ) {
+ self.id = id
+ self.ip = ip
+ self.ipv6 = ipv6
+ self.port = port
+ self.peerTag = peerTag
+ }
+ }
+
+ public struct WebRtcReflector: Equatable {
+ public let id: Int64
+ public let hasStun: Bool
+ public let hasTurn: Bool
+ public let ip: String
+ public let ipv6: String
+ public let port: Int32
+ public let username: String
+ public let password: String
+
+ public init(
+ id: Int64,
+ hasStun: Bool,
+ hasTurn: Bool,
+ ip: String,
+ ipv6: String,
+ port: Int32,
+ username: String,
+ password: String
+ ) {
+ self.id = id
+ self.hasStun = hasStun
+ self.hasTurn = hasTurn
+ self.ip = ip
+ self.ipv6 = ipv6
+ self.port = port
+ self.username = username
+ self.password = password
+ }
+ }
+
+ case reflector(Reflector)
+ case webRtcReflector(WebRtcReflector)
}
private func parseConnection(_ apiConnection: Api.PhoneConnection) -> CallSessionConnection {
switch apiConnection {
case let .phoneConnection(id, ip, ipv6, port, peerTag):
- return CallSessionConnection(id: id, ip: ip, ipv6: ipv6, port: port, peerTag: peerTag.makeData())
+ return .reflector(CallSessionConnection.Reflector(id: id, ip: ip, ipv6: ipv6, port: port, peerTag: peerTag.makeData()))
+ case let .phoneConnectionWebrtc(flags, id, ip, ipv6, port, username, password):
+ return .webRtcReflector(CallSessionConnection.WebRtcReflector(
+ id: id,
+ hasStun: (flags & (1 << 1)) != 0,
+ hasTurn: (flags & (1 << 0)) != 0,
+ ip: ip,
+ ipv6: ipv6,
+ port: port,
+ username: username,
+ password: password
+ ))
}
}
@@ -219,6 +278,7 @@ private final class CallSessionContext {
let peerId: PeerId
let isOutgoing: Bool
var type: CallSession.CallType
+ var isVideoPossible: Bool
var state: CallSessionInternalState
let subscribers = Bag<(CallSession) -> Void>()
let signalingSubscribers = Bag<(Data) -> Void>()
@@ -235,10 +295,11 @@ private final class CallSessionContext {
}
}
- init(peerId: PeerId, isOutgoing: Bool, type: CallSession.CallType, state: CallSessionInternalState) {
+ init(peerId: PeerId, isOutgoing: Bool, type: CallSession.CallType, isVideoPossible: Bool, state: CallSessionInternalState) {
self.peerId = peerId
self.isOutgoing = isOutgoing
self.type = type
+ self.isVideoPossible = isVideoPossible
self.state = state
}
@@ -247,8 +308,8 @@ private final class CallSessionContext {
}
}
-private func selectVersionOnAccept(localVersions: [String], remoteVersions: [String]) -> [String]? {
- let filteredVersions = localVersions.filter(remoteVersions.contains)
+private func selectVersionOnAccept(localVersions: [CallSessionManagerImplementationVersion], remoteVersions: [String]) -> [String]? {
+ let filteredVersions = localVersions.map({ $0.version }).filter(remoteVersions.contains)
if filteredVersions.isEmpty {
return nil
} else {
@@ -256,12 +317,22 @@ private func selectVersionOnAccept(localVersions: [String], remoteVersions: [Str
}
}
+public struct CallSessionManagerImplementationVersion: Hashable {
+ public var version: String
+ public var supportsVideo: Bool
+
+ public init(version: String, supportsVideo: Bool) {
+ self.version = version
+ self.supportsVideo = supportsVideo
+ }
+}
+
private final class CallSessionManagerContext {
private let queue: Queue
private let postbox: Postbox
private let network: Network
private let maxLayer: Int32
- private var versions: [String]
+ private var versions: [CallSessionManagerImplementationVersion]
private let addUpdates: (Api.Updates) -> Void
private let ringingSubscribers = Bag<([CallSessionRingingState]) -> Void>()
@@ -270,7 +341,7 @@ private final class CallSessionManagerContext {
private let disposables = DisposableSet()
- init(queue: Queue, postbox: Postbox, network: Network, maxLayer: Int32, versions: [String], addUpdates: @escaping (Api.Updates) -> Void) {
+ init(queue: Queue, postbox: Postbox, network: Network, maxLayer: Int32, versions: [CallSessionManagerImplementationVersion], addUpdates: @escaping (Api.Updates) -> Void) {
self.queue = queue
self.postbox = postbox
self.network = network
@@ -284,10 +355,32 @@ private final class CallSessionManagerContext {
self.disposables.dispose()
}
- func updateVersions(versions: [String]) {
+ func updateVersions(versions: [CallSessionManagerImplementationVersion]) {
self.versions = versions.reversed()
}
+ func filteredVersions(enableVideo: Bool) -> [String] {
+ return self.versions.compactMap { version -> String? in
+ if enableVideo {
+ return version.version
+ } else if !version.supportsVideo {
+ return version.version
+ } else {
+ return nil
+ }
+ }
+ }
+
+ func videoVersions() -> [String] {
+ return self.versions.compactMap { version -> String? in
+ if version.supportsVideo {
+ return version.version
+ } else {
+ return nil
+ }
+ }
+ }
+
func ringingStates() -> Signal<[CallSessionRingingState], NoError> {
let queue = self.queue
return Signal { [weak self] subscriber in
@@ -320,7 +413,7 @@ private final class CallSessionManagerContext {
let index = context.subscribers.add { next in
subscriber.putNext(next)
}
- subscriber.putNext(CallSession(id: internalId, isOutgoing: context.isOutgoing, type: context.type, state: CallSessionState(context)))
+ subscriber.putNext(CallSession(id: internalId, isOutgoing: context.isOutgoing, type: context.type, state: CallSessionState(context), isVideoPossible: context.isVideoPossible))
disposable.set(ActionDisposable {
queue.async {
if let strongSelf = self, let context = strongSelf.contexts[internalId] {
@@ -366,7 +459,7 @@ private final class CallSessionManagerContext {
var ringingContexts: [CallSessionRingingState] = []
for (id, context) in self.contexts {
if case .ringing = context.state {
- ringingContexts.append(CallSessionRingingState(id: id, peerId: context.peerId, isVideo: context.type == .video))
+ ringingContexts.append(CallSessionRingingState(id: id, peerId: context.peerId, isVideo: context.type == .video, isVideoPossible: context.isVideoPossible))
}
}
return ringingContexts
@@ -381,7 +474,7 @@ private final class CallSessionManagerContext {
private func contextUpdated(internalId: CallSessionInternalId) {
if let context = self.contexts[internalId] {
- let session = CallSession(id: internalId, isOutgoing: context.isOutgoing, type: context.type, state: CallSessionState(context))
+ let session = CallSession(id: internalId, isOutgoing: context.isOutgoing, type: context.type, state: CallSessionState(context), isVideoPossible: context.isVideoPossible)
for subscriber in context.subscribers.copyItems() {
subscriber(session)
}
@@ -398,8 +491,13 @@ private final class CallSessionManagerContext {
let b = Data(bytesNoCopy: bBytes, count: 256, deallocator: .free)
if randomStatus == 0 {
+ var isVideoPossible = self.videoVersions().contains(where: { versions.contains($0) })
+ //#if DEBUG
+ isVideoPossible = true
+ //#endif
+
let internalId = CallSessionInternalId()
- let context = CallSessionContext(peerId: peerId, isOutgoing: false, type: isVideo ? .video : .audio, state: .ringing(id: stableId, accessHash: accessHash, gAHash: gAHash, b: b, versions: versions))
+ let context = CallSessionContext(peerId: peerId, isOutgoing: false, type: isVideo ? .video : .audio, isVideoPossible: isVideoPossible, state: .ringing(id: stableId, accessHash: accessHash, gAHash: gAHash, b: b, versions: versions))
self.contexts[internalId] = context
let queue = self.queue
context.acknowledgeIncomingCallDisposable.set(self.network.request(Api.functions.phone.receivedCall(peer: .inputPhoneCall(id: stableId, accessHash: accessHash))).start(error: { [weak self] _ in
@@ -429,7 +527,9 @@ private final class CallSessionManagerContext {
wasRinging = true
let internalReason: DropCallSessionReason
switch reason {
- case .busy, .hangUp:
+ case .busy:
+ internalReason = .busy
+ case .hangUp:
internalReason = .hangUp(0)
case .disconnect:
internalReason = .disconnect
@@ -481,7 +581,8 @@ private final class CallSessionManagerContext {
if let (id, accessHash, reason) = dropData {
self.contextIdByStableId.removeValue(forKey: id)
- context.state = .dropping((dropCallSession(network: self.network, addUpdates: self.addUpdates, stableId: id, accessHash: accessHash, isVideo: isVideo, reason: reason)
+ let mappedReason: CallSessionTerminationReason = .ended(.hungUp)
+ context.state = .dropping(reason: mappedReason, disposable: (dropCallSession(network: self.network, addUpdates: self.addUpdates, stableId: id, accessHash: accessHash, isVideo: isVideo, reason: reason)
|> deliverOn(self.queue)).start(next: { [weak self] reportRating, sendDebugLogs in
if let strongSelf = self {
if let context = strongSelf.contexts[internalId] {
@@ -529,11 +630,7 @@ private final class CallSessionManagerContext {
if let context = self.contexts[internalId] {
switch context.state {
case let .ringing(id, accessHash, gAHash, b, remoteVersions):
- guard var acceptVersions = selectVersionOnAccept(localVersions: self.versions, remoteVersions: remoteVersions) else {
- self.drop(internalId: internalId, reason: .disconnect, debugLog: .single(nil))
- return
- }
- acceptVersions = self.versions
+ let acceptVersions = self.versions.map({ $0.version })
context.state = .accepting(id: id, accessHash: accessHash, gAHash: gAHash, b: b, disposable: (acceptCallSession(postbox: self.postbox, network: self.network, stableId: id, accessHash: accessHash, b: b, maxLayer: self.maxLayer, versions: acceptVersions) |> deliverOn(self.queue)).start(next: { [weak self] result in
if let strongSelf = self, let context = strongSelf.contexts[internalId] {
if case .accepting = context.state {
@@ -710,6 +807,9 @@ private final class CallSessionManagerContext {
switch callProtocol {
case let .phoneCallProtocol(_, _, maxLayer, versions):
if !versions.isEmpty {
+ let isVideoPossible = self.videoVersions().contains(where: { versions.contains($0) })
+ context.isVideoPossible = isVideoPossible
+
context.state = .active(id: id, accessHash: accessHash, beginTimestamp: startDate, key: key, keyId: calculatedKeyId, keyVisualHash: keyVisualHash, connections: parseConnectionSet(primary: connections.first!, alternative: Array(connections[1...])), maxLayer: maxLayer, version: versions[0], allowsP2P: allowsP2P)
self.contextUpdated(internalId: internalId)
} else {
@@ -726,6 +826,9 @@ private final class CallSessionManagerContext {
switch callProtocol {
case let .phoneCallProtocol(_, _, maxLayer, versions):
if !versions.isEmpty {
+ let isVideoPossible = self.videoVersions().contains(where: { versions.contains($0) })
+ context.isVideoPossible = isVideoPossible
+
context.state = .active(id: id, accessHash: accessHash, beginTimestamp: startDate, key: key, keyId: keyId, keyVisualHash: keyVisualHash, connections: parseConnectionSet(primary: connections.first!, alternative: Array(connections[1...])), maxLayer: maxLayer, version: versions[0], allowsP2P: allowsP2P)
self.contextUpdated(internalId: internalId)
} else {
@@ -738,7 +841,7 @@ private final class CallSessionManagerContext {
}
}
case let .phoneCallRequested(flags, id, accessHash, date, adminId, _, gAHash, requestedProtocol):
- let isVideo = (flags & (1 << 5)) != 0
+ let isVideo = (flags & (1 << 6)) != 0
let versions: [String]
switch requestedProtocol {
case let .phoneCallProtocol(_, _, _, libraryVersions):
@@ -755,7 +858,7 @@ private final class CallSessionManagerContext {
}
}
if let context = self.contexts[internalId] {
- let callSession = CallSession(id: internalId, isOutgoing: context.isOutgoing, type: context.type, state: CallSessionState(context))
+ let callSession = CallSession(id: internalId, isOutgoing: context.isOutgoing, type: context.type, state: CallSessionState(context), isVideoPossible: context.isVideoPossible)
if let resultRingingStateValue = resultRingingStateValue {
resultRingingState = (resultRingingStateValue, callSession)
}
@@ -819,12 +922,12 @@ private final class CallSessionManagerContext {
return (key, keyId, keyVisualHash)
}
- func request(peerId: PeerId, internalId: CallSessionInternalId, isVideo: Bool) -> CallSessionInternalId? {
+ func request(peerId: PeerId, internalId: CallSessionInternalId, isVideo: Bool, enableVideo: Bool) -> CallSessionInternalId? {
let aBytes = malloc(256)!
let randomStatus = SecRandomCopyBytes(nil, 256, aBytes.assumingMemoryBound(to: UInt8.self))
let a = Data(bytesNoCopy: aBytes, count: 256, deallocator: .free)
if randomStatus == 0 {
- self.contexts[internalId] = CallSessionContext(peerId: peerId, isOutgoing: true, type: isVideo ? .video : .audio, state: .requesting(a: a, disposable: (requestCallSession(postbox: self.postbox, network: self.network, peerId: peerId, a: a, maxLayer: self.maxLayer, versions: self.versions, isVideo: isVideo) |> deliverOn(queue)).start(next: { [weak self] result in
+ self.contexts[internalId] = CallSessionContext(peerId: peerId, isOutgoing: true, type: isVideo ? .video : .audio, isVideoPossible: enableVideo || isVideo, state: .requesting(a: a, disposable: (requestCallSession(postbox: self.postbox, network: self.network, peerId: peerId, a: a, maxLayer: self.maxLayer, versions: self.filteredVersions(enableVideo: enableVideo), isVideo: isVideo) |> deliverOn(queue)).start(next: { [weak self] result in
if let strongSelf = self, let context = strongSelf.contexts[internalId] {
if case .requesting = context.state {
switch result {
@@ -858,7 +961,7 @@ public final class CallSessionManager {
private let queue = Queue()
private var contextRef: Unmanaged?
- init(postbox: Postbox, network: Network, maxLayer: Int32, versions: [String], addUpdates: @escaping (Api.Updates) -> Void) {
+ init(postbox: Postbox, network: Network, maxLayer: Int32, versions: [CallSessionManagerImplementationVersion], addUpdates: @escaping (Api.Updates) -> Void) {
self.queue.async {
let context = CallSessionManagerContext(queue: self.queue, postbox: postbox, network: network, maxLayer: maxLayer, versions: versions, addUpdates: addUpdates)
self.contextRef = Unmanaged.passRetained(context)
@@ -917,12 +1020,12 @@ public final class CallSessionManager {
}
}
- public func request(peerId: PeerId, isVideo: Bool, internalId: CallSessionInternalId = CallSessionInternalId()) -> Signal {
+ public func request(peerId: PeerId, isVideo: Bool, enableVideo: Bool, internalId: CallSessionInternalId = CallSessionInternalId()) -> Signal {
return Signal { [weak self] subscriber in
let disposable = MetaDisposable()
self?.withContext { context in
- if let internalId = context.request(peerId: peerId, internalId: internalId, isVideo: isVideo) {
+ if let internalId = context.request(peerId: peerId, internalId: internalId, isVideo: isVideo, enableVideo: enableVideo) {
subscriber.putNext(internalId)
subscriber.putCompletion()
}
@@ -944,7 +1047,7 @@ public final class CallSessionManager {
}
}
- public func updateVersions(versions: [String]) {
+ public func updateVersions(versions: [CallSessionManagerImplementationVersion]) {
self.withContext { context in
context.updateVersions(versions: versions)
}
diff --git a/submodules/TelegramCore/Sources/ImageRepresentationsUtils.swift b/submodules/TelegramCore/Sources/ImageRepresentationsUtils.swift
index 96f118189f..2c92d63720 100644
--- a/submodules/TelegramCore/Sources/ImageRepresentationsUtils.swift
+++ b/submodules/TelegramCore/Sources/ImageRepresentationsUtils.swift
@@ -69,6 +69,15 @@ public func imageRepresentationLargerThan(_ representations: [TelegramMediaImage
}
}
+public func progressiveImageRepresentation(_ representations: [TelegramMediaImageRepresentation]) -> TelegramMediaImageRepresentation? {
+ for representation in representations {
+ if representation.progressiveSizes.count > 1 {
+ return representation
+ }
+ }
+ return nil
+}
+
public func parseMediaData(data: Data) -> Media? {
let buffer = BufferReader(Buffer(data: data))
var parseBuffer: Buffer?
diff --git a/submodules/TelegramCore/Sources/MessageStatistics.swift b/submodules/TelegramCore/Sources/MessageStatistics.swift
new file mode 100644
index 0000000000..7e2cb5bf61
--- /dev/null
+++ b/submodules/TelegramCore/Sources/MessageStatistics.swift
@@ -0,0 +1,188 @@
+import Foundation
+import SwiftSignalKit
+import Postbox
+import TelegramApi
+import MtProtoKit
+import SyncCore
+
+public struct MessageStats: Equatable {
+ public let views: Int
+ public let forwards: Int
+ public let interactionsGraph: StatsGraph
+
+ init(views: Int, forwards: Int, interactionsGraph: StatsGraph) {
+ self.views = views
+ self.forwards = forwards
+ self.interactionsGraph = interactionsGraph
+ }
+
+ public static func == (lhs: MessageStats, rhs: MessageStats) -> Bool {
+ if lhs.views != rhs.views {
+ return false
+ }
+ if lhs.forwards != rhs.forwards {
+ return false
+ }
+ if lhs.interactionsGraph != rhs.interactionsGraph {
+ return false
+ }
+ return true
+ }
+
+ public func withUpdatedInteractionsGraph(_ interactionsGraph: StatsGraph) -> MessageStats {
+ return MessageStats(views: self.views, forwards: self.forwards, interactionsGraph: self.interactionsGraph)
+ }
+}
+
+public struct MessageStatsContextState: Equatable {
+ public var stats: MessageStats?
+}
+
+private func requestMessageStats(postbox: Postbox, network: Network, datacenterId: Int32, messageId: MessageId, dark: Bool = false) -> Signal {
+ return .single(nil)
+ /*return postbox.transaction { transaction -> (Peer, Message)? in
+ if let peer = transaction.getPeer(messageId.peerId), let message = transaction.getMessage(messageId) {
+ return (peer, message)
+ } else {
+ return nil
+ }
+ } |> mapToSignal { peerAndMessage -> Signal in
+ guard let (peer, message) = peerAndMessage, let inputChannel = apiInputChannel(peer) else {
+ return .never()
+ }
+
+ var flags: Int32 = 0
+ if dark {
+ flags |= (1 << 1)
+ }
+
+ let request = Api.functions.stats.getMessageStats(flags: flags, channel: inputChannel, msgId: messageId.id)
+ let signal: Signal
+ if network.datacenterId != datacenterId {
+ signal = network.download(datacenterId: Int(datacenterId), isMedia: false, tag: nil)
+ |> castError(MTRpcError.self)
+ |> mapToSignal { worker in
+ return worker.request(request)
+ }
+ } else {
+ signal = network.request(request)
+ }
+
+ var views: Int = 0
+ var forwards: Int = 0
+ for attribute in message.attributes {
+ if let viewsAttribute = attribute as? ViewCountMessageAttribute {
+ views = viewsAttribute.count
+ } else if let forwardsAttribute = attribute as? ForwardCountMessageAttribute {
+ forwards = forwardsAttribute.count
+ }
+ }
+
+ return signal
+ |> map { result -> MessageStats? in
+ if case let .messageStats(apiViewsGraph) = result {
+ return MessageStats(views: views, forwards: forwards, interactionsGraph: StatsGraph(apiStatsGraph: apiViewsGraph))
+ } else {
+ return nil
+ }
+ }
+ |> retryRequest
+ }*/
+}
+
+private final class MessageStatsContextImpl {
+ private let postbox: Postbox
+ private let network: Network
+ private let datacenterId: Int32
+ private let messageId: MessageId
+
+ private var _state: MessageStatsContextState {
+ didSet {
+ if self._state != oldValue {
+ self._statePromise.set(.single(self._state))
+ }
+ }
+ }
+ private let _statePromise = Promise()
+ var state: Signal {
+ return self._statePromise.get()
+ }
+
+ private let disposable = MetaDisposable()
+ private let disposables = DisposableDict()
+
+ init(postbox: Postbox, network: Network, datacenterId: Int32, messageId: MessageId) {
+ assert(Queue.mainQueue().isCurrent())
+
+ self.postbox = postbox
+ self.network = network
+ self.datacenterId = datacenterId
+ self.messageId = messageId
+ self._state = MessageStatsContextState(stats: nil)
+ self._statePromise.set(.single(self._state))
+
+ self.load()
+ }
+
+ deinit {
+ assert(Queue.mainQueue().isCurrent())
+ self.disposable.dispose()
+ self.disposables.dispose()
+ }
+
+ private func load() {
+ assert(Queue.mainQueue().isCurrent())
+
+ self.disposable.set((requestMessageStats(postbox: self.postbox, network: self.network, datacenterId: self.datacenterId, messageId: self.messageId)
+ |> deliverOnMainQueue).start(next: { [weak self] stats in
+ if let strongSelf = self {
+ strongSelf._state = MessageStatsContextState(stats: stats)
+ strongSelf._statePromise.set(.single(strongSelf._state))
+ }
+ }))
+ }
+
+ func loadDetailedGraph(_ graph: StatsGraph, x: Int64) -> Signal {
+ if let token = graph.token {
+ return requestGraph(network: self.network, datacenterId: self.datacenterId, token: token, x: x)
+ } else {
+ return .single(nil)
+ }
+ }
+}
+
+public final class MessageStatsContext {
+ private let impl: QueueLocalObject
+
+ public var state: Signal {
+ return Signal { subscriber in
+ let disposable = MetaDisposable()
+ self.impl.with { impl in
+ disposable.set(impl.state.start(next: { value in
+ subscriber.putNext(value)
+ }))
+ }
+ return disposable
+ }
+ }
+
+ public init(postbox: Postbox, network: Network, datacenterId: Int32, messageId: MessageId) {
+ self.impl = QueueLocalObject(queue: Queue.mainQueue(), generate: {
+ return MessageStatsContextImpl(postbox: postbox, network: network, datacenterId: datacenterId, messageId: messageId)
+ })
+ }
+
+ public func loadDetailedGraph(_ graph: StatsGraph, x: Int64) -> Signal {
+ return Signal { subscriber in
+ let disposable = MetaDisposable()
+ self.impl.with { impl in
+ disposable.set(impl.loadDetailedGraph(graph, x: x).start(next: { value in
+ subscriber.putNext(value)
+ subscriber.putCompletion()
+ }))
+ }
+ return disposable
+ }
+ }
+}
+
diff --git a/submodules/TelegramCore/Sources/MultipartFetch.swift b/submodules/TelegramCore/Sources/MultipartFetch.swift
index 73304b8fa6..3b777d69e1 100644
--- a/submodules/TelegramCore/Sources/MultipartFetch.swift
+++ b/submodules/TelegramCore/Sources/MultipartFetch.swift
@@ -400,7 +400,7 @@ private enum FetchResourceReference {
private final class MultipartFetchManager {
let parallelParts: Int
- let defaultPartSize = 128 * 1024
+ let defaultPartSize: Int
var partAlignment = 4 * 1024
var resource: TelegramMediaResource
@@ -448,10 +448,17 @@ private final class MultipartFetchManager {
self.consumerId = arc4random64()
self.completeSize = size
- if let _ = size {
- self.parallelParts = 4
+ if let size = size {
+ if size <= 512 * 1024 {
+ self.defaultPartSize = 16 * 1024
+ self.parallelParts = 4 * 4
+ } else {
+ self.defaultPartSize = 128 * 1024
+ self.parallelParts = 4
+ }
} else {
self.parallelParts = 1
+ self.defaultPartSize = 128 * 1024
}
if let info = parameters?.info as? TelegramCloudMediaResourceFetchInfo {
diff --git a/submodules/TelegramCore/Sources/Network.swift b/submodules/TelegramCore/Sources/Network.swift
index 441b660060..7d0313466e 100644
--- a/submodules/TelegramCore/Sources/Network.swift
+++ b/submodules/TelegramCore/Sources/Network.swift
@@ -403,12 +403,12 @@ public struct NetworkInitializationArguments {
public let languagesCategory: String
public let appVersion: String
public let voipMaxLayer: Int32
- public let voipVersions: [String]
+ public let voipVersions: [CallSessionManagerImplementationVersion]
public let appData: Signal
public let autolockDeadine: Signal
public let encryptionProvider: EncryptionProvider
- public init(apiId: Int32, apiHash: String, languagesCategory: String, appVersion: String, voipMaxLayer: Int32, voipVersions: [String], appData: Signal, autolockDeadine: Signal, encryptionProvider: EncryptionProvider) {
+ public init(apiId: Int32, apiHash: String, languagesCategory: String, appVersion: String, voipMaxLayer: Int32, voipVersions: [CallSessionManagerImplementationVersion], appData: Signal, autolockDeadine: Signal, encryptionProvider: EncryptionProvider) {
self.apiId = apiId
self.apiHash = apiHash
self.languagesCategory = languagesCategory
diff --git a/submodules/TelegramCore/Sources/OutgoingMessageWithChatContextResult.swift b/submodules/TelegramCore/Sources/OutgoingMessageWithChatContextResult.swift
index d60704b297..c800fb9793 100644
--- a/submodules/TelegramCore/Sources/OutgoingMessageWithChatContextResult.swift
+++ b/submodules/TelegramCore/Sources/OutgoingMessageWithChatContextResult.swift
@@ -55,7 +55,7 @@ public func outgoingMessageWithChatContextResult(to peerId: PeerId, results: Cha
arc4random_buf(&randomId, 8)
let thumbnailResource = thumbnail.resource
let imageDimensions = thumbnail.dimensions ?? PixelDimensions(width: 128, height: 128)
- let tmpImage = TelegramMediaImage(imageId: MediaId(namespace: Namespaces.Media.LocalImage, id: randomId), representations: [TelegramMediaImageRepresentation(dimensions: imageDimensions, resource: thumbnailResource)], immediateThumbnailData: nil, reference: nil, partialReference: nil, flags: [])
+ let tmpImage = TelegramMediaImage(imageId: MediaId(namespace: Namespaces.Media.LocalImage, id: randomId), representations: [TelegramMediaImageRepresentation(dimensions: imageDimensions, resource: thumbnailResource, progressiveSizes: [])], immediateThumbnailData: nil, reference: nil, partialReference: nil, flags: [])
return .message(text: caption, attributes: attributes, mediaReference: .standalone(media: tmpImage), replyToMessageId: nil, localGroupingKey: nil)
} else {
return .message(text: caption, attributes: attributes, mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil)
@@ -71,7 +71,7 @@ public func outgoingMessageWithChatContextResult(to peerId: PeerId, results: Cha
if thumbnail.mimeType.hasPrefix("video/") {
videoThumbnails.append(TelegramMediaFile.VideoThumbnail(dimensions: thumbnail.dimensions ?? PixelDimensions(width: 128, height: 128), resource: thumbnailResource))
} else {
- previewRepresentations.append(TelegramMediaImageRepresentation(dimensions: thumbnail.dimensions ?? PixelDimensions(width: 128, height: 128), resource: thumbnailResource))
+ previewRepresentations.append(TelegramMediaImageRepresentation(dimensions: thumbnail.dimensions ?? PixelDimensions(width: 128, height: 128), resource: thumbnailResource, progressiveSizes: []))
}
}
var fileName = "file"
diff --git a/submodules/TelegramCore/Sources/PeerPhotoUpdater.swift b/submodules/TelegramCore/Sources/PeerPhotoUpdater.swift
index 9aac08971b..b7ecbc0047 100644
--- a/submodules/TelegramCore/Sources/PeerPhotoUpdater.swift
+++ b/submodules/TelegramCore/Sources/PeerPhotoUpdater.swift
@@ -161,7 +161,12 @@ public func updatePeerPhotoInternal(postbox: Postbox, network: Network, stateMan
case let .photoSize(_, location, w, h, _):
switch location {
case let .fileLocationToBeDeprecated(volumeId, localId):
- representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: w, height: h), resource: CloudPeerPhotoSizeMediaResource(datacenterId: dcId, sizeSpec: w <= 200 ? .small : .fullSize, volumeId: volumeId, localId: localId)))
+ representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: w, height: h), resource: CloudPeerPhotoSizeMediaResource(datacenterId: dcId, sizeSpec: w <= 200 ? .small : .fullSize, volumeId: volumeId, localId: localId), progressiveSizes: []))
+ }
+ case let .photoSizeProgressive(_, location, w, h, sizes):
+ switch location {
+ case let .fileLocationToBeDeprecated(volumeId, localId):
+ representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: w, height: h), resource: CloudPeerPhotoSizeMediaResource(datacenterId: dcId, sizeSpec: w <= 200 ? .small : .fullSize, volumeId: volumeId, localId: localId), progressiveSizes: sizes))
}
default:
break
@@ -294,10 +299,12 @@ public func updatePeerPhotoInternal(postbox: Postbox, network: Network, stateMan
}
} else {
if let _ = peer as? TelegramUser {
- return network.request(Api.functions.photos.updateProfilePhoto(id: Api.InputPhoto.inputPhotoEmpty))
- |> `catch` { _ -> Signal in
- return .fail(.generic)
+ let signal: Signal = network.request(Api.functions.photos.updateProfilePhoto(id: Api.InputPhoto.inputPhotoEmpty))
+ |> mapError { _ -> UploadPeerPhotoError in
+ return .generic
}
+
+ return signal
|> mapToSignal { _ -> Signal in
return .single(.complete([]))
}
@@ -337,15 +344,19 @@ public func updatePeerPhotoInternal(postbox: Postbox, network: Network, stateMan
}
}
-public func updatePeerPhotoExisting(network: Network, reference: TelegramMediaImageReference) -> Signal {
+public func updatePeerPhotoExisting(network: Network, reference: TelegramMediaImageReference) -> Signal {
switch reference {
case let .cloud(imageId, accessHash, fileReference):
return network.request(Api.functions.photos.updateProfilePhoto(id: .inputPhoto(id: imageId, accessHash: accessHash, fileReference: Buffer(data: fileReference))))
- |> `catch` { _ -> Signal in
+ |> `catch` { _ -> Signal in
return .complete()
}
- |> mapToSignal { _ -> Signal in
- return .complete()
+ |> mapToSignal { photo -> Signal in
+ if case let .photo(photo, _) = photo {
+ return .single(telegramMediaImageFromApiPhoto(photo))
+ } else {
+ return .complete()
+ }
}
}
}
diff --git a/submodules/TelegramCore/Sources/PeerStatistics.swift b/submodules/TelegramCore/Sources/PeerStatistics.swift
index a81408a37b..83f9383e9a 100644
--- a/submodules/TelegramCore/Sources/PeerStatistics.swift
+++ b/submodules/TelegramCore/Sources/PeerStatistics.swift
@@ -30,8 +30,8 @@ public enum StatsGraph: Equatable {
switch self {
case .Empty:
return true
- case let .Failed(error):
- return error.lowercased().contains("not enough data")
+ case .Failed:
+ return true
default:
return false
}
@@ -193,15 +193,16 @@ private func requestChannelStats(postbox: Postbox, network: Network, datacenterI
flags |= (1 << 1)
}
+ let request = Api.functions.stats.getBroadcastStats(flags: flags, channel: inputChannel)
let signal: Signal
if network.datacenterId != datacenterId {
signal = network.download(datacenterId: Int(datacenterId), isMedia: false, tag: nil)
|> castError(MTRpcError.self)
|> mapToSignal { worker in
- return worker.request(Api.functions.stats.getBroadcastStats(flags: flags, channel: inputChannel))
+ return worker.request(request)
}
} else {
- signal = network.request(Api.functions.stats.getBroadcastStats(flags: flags, channel: inputChannel))
+ signal = network.request(request)
}
return signal
@@ -212,7 +213,7 @@ private func requestChannelStats(postbox: Postbox, network: Network, datacenterI
}
}
-private func requestGraph(network: Network, datacenterId: Int32, token: String, x: Int64? = nil) -> Signal {
+func requestGraph(network: Network, datacenterId: Int32, token: String, x: Int64? = nil) -> Signal {
var flags: Int32 = 0
if let _ = x {
flags |= (1 << 0)
diff --git a/submodules/TelegramCore/Sources/ProcessSecretChatIncomingDecryptedOperations.swift b/submodules/TelegramCore/Sources/ProcessSecretChatIncomingDecryptedOperations.swift
index 6d6c753f8f..8dd5af8087 100644
--- a/submodules/TelegramCore/Sources/ProcessSecretChatIncomingDecryptedOperations.swift
+++ b/submodules/TelegramCore/Sources/ProcessSecretChatIncomingDecryptedOperations.swift
@@ -708,10 +708,10 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32
var representations: [TelegramMediaImageRepresentation] = []
if thumb.size != 0 {
let resource = LocalFileMediaResource(fileId: arc4random64())
- representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: thumbW, height: thumbH), resource: resource))
+ representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: thumbW, height: thumbH), resource: resource, progressiveSizes: []))
resources.append((resource, thumb.makeData()))
}
- representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: w, height: h), resource: file.resource(key: SecretFileEncryptionKey(aesKey: key.makeData(), aesIv: iv.makeData()), decryptedSize: size)))
+ representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: w, height: h), resource: file.resource(key: SecretFileEncryptionKey(aesKey: key.makeData(), aesIv: iv.makeData()), decryptedSize: size), progressiveSizes: []))
let image = TelegramMediaImage(imageId: MediaId(namespace: Namespaces.Media.CloudSecretImage, id: file.id), representations: representations, immediateThumbnailData: nil, reference: nil, partialReference: nil, flags: [])
parsedMedia.append(image)
}
@@ -734,7 +734,7 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32
var previewRepresentations: [TelegramMediaImageRepresentation] = []
if thumb.size != 0 {
let resource = LocalFileMediaResource(fileId: arc4random64())
- previewRepresentations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: thumbW, height: thumbH), resource: resource))
+ previewRepresentations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: thumbW, height: thumbH), resource: resource, progressiveSizes: []))
resources.append((resource, thumb.makeData()))
}
let fileMedia = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.CloudSecretFile, id: file.id), partialReference: nil, resource: file.resource(key: SecretFileEncryptionKey(aesKey: key.makeData(), aesIv: iv.makeData()), decryptedSize: size), previewRepresentations: previewRepresentations, videoThumbnails: [], immediateThumbnailData: nil, mimeType: mimeType, size: Int(size), attributes: parsedAttributes)
@@ -749,7 +749,7 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32
var previewRepresentations: [TelegramMediaImageRepresentation] = []
if thumb.size != 0 {
let resource = LocalFileMediaResource(fileId: arc4random64())
- previewRepresentations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: thumbW, height: thumbH), resource: resource))
+ previewRepresentations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: thumbW, height: thumbH), resource: resource, progressiveSizes: []))
resources.append((resource, thumb.makeData()))
}
let fileMedia = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.CloudSecretFile, id: file.id), partialReference: nil, resource: file.resource(key: SecretFileEncryptionKey(aesKey: key.makeData(), aesIv: iv.makeData()), decryptedSize: size), previewRepresentations: previewRepresentations, videoThumbnails: [], immediateThumbnailData: nil, mimeType: mimeType, size: Int(size), attributes: parsedAttributes)
@@ -767,7 +767,7 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32
case let .photoSize(_, location, w, h, size):
switch location {
case let .fileLocation(dcId, volumeId, localId, secret):
- previewRepresentations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: w, height: h), resource: CloudFileMediaResource(datacenterId: Int(dcId), volumeId: volumeId, localId: localId, secret: secret, size: size == 0 ? nil : Int(size), fileReference: nil)))
+ previewRepresentations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: w, height: h), resource: CloudFileMediaResource(datacenterId: Int(dcId), volumeId: volumeId, localId: localId, secret: secret, size: size == 0 ? nil : Int(size), fileReference: nil), progressiveSizes: []))
case .fileLocationUnavailable:
break
}
@@ -777,7 +777,7 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32
case let .fileLocation(dcId, volumeId, localId, secret):
let resource = CloudFileMediaResource(datacenterId: Int(dcId), volumeId: volumeId, localId: localId, secret: secret, size: bytes.size, fileReference: nil)
resources.append((resource, bytes.makeData()))
- previewRepresentations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: w, height: h), resource: resource))
+ previewRepresentations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: w, height: h), resource: resource, progressiveSizes: []))
case .fileLocationUnavailable:
break
}
@@ -906,10 +906,10 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32
var representations: [TelegramMediaImageRepresentation] = []
if thumb.size != 0 {
let resource = LocalFileMediaResource(fileId: arc4random64())
- representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: thumbW, height: thumbH), resource: resource))
+ representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: thumbW, height: thumbH), resource: resource, progressiveSizes: []))
resources.append((resource, thumb.makeData()))
}
- representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: w, height: h), resource: file.resource(key: SecretFileEncryptionKey(aesKey: key.makeData(), aesIv: iv.makeData()), decryptedSize: size)))
+ representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: w, height: h), resource: file.resource(key: SecretFileEncryptionKey(aesKey: key.makeData(), aesIv: iv.makeData()), decryptedSize: size), progressiveSizes: []))
let image = TelegramMediaImage(imageId: MediaId(namespace: Namespaces.Media.CloudSecretImage, id: file.id), representations: representations, immediateThumbnailData: nil, reference: nil, partialReference: nil, flags: [])
parsedMedia.append(image)
}
@@ -933,7 +933,7 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32
var previewRepresentations: [TelegramMediaImageRepresentation] = []
if thumb.size != 0 {
let resource = LocalFileMediaResource(fileId: arc4random64())
- previewRepresentations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: thumbW, height: thumbH), resource: resource))
+ previewRepresentations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: thumbW, height: thumbH), resource: resource, progressiveSizes: []))
resources.append((resource, thumb.makeData()))
}
let fileMedia = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.CloudSecretFile, id: file.id), partialReference: nil, resource: file.resource(key: SecretFileEncryptionKey(aesKey: key.makeData(), aesIv: iv.makeData()), decryptedSize: size), previewRepresentations: previewRepresentations, videoThumbnails: [], immediateThumbnailData: nil, mimeType: mimeType, size: Int(size), attributes: parsedAttributes)
@@ -964,7 +964,7 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32
var previewRepresentations: [TelegramMediaImageRepresentation] = []
if thumb.size != 0 {
let resource = LocalFileMediaResource(fileId: arc4random64())
- previewRepresentations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: thumbW, height: thumbH), resource: resource))
+ previewRepresentations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: thumbW, height: thumbH), resource: resource, progressiveSizes: []))
resources.append((resource, thumb.makeData()))
}
let fileMedia = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.CloudSecretFile, id: file.id), partialReference: nil, resource: file.resource(key: SecretFileEncryptionKey(aesKey: key.makeData(), aesIv: iv.makeData()), decryptedSize: size), previewRepresentations: previewRepresentations, videoThumbnails: [], immediateThumbnailData: nil, mimeType: mimeType, size: Int(size), attributes: parsedAttributes)
@@ -982,7 +982,7 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32
case let .photoSize(_, location, w, h, size):
switch location {
case let .fileLocation(dcId, volumeId, localId, secret):
- previewRepresentations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: w, height: h), resource: CloudFileMediaResource(datacenterId: Int(dcId), volumeId: volumeId, localId: localId, secret: secret, size: size == 0 ? nil : Int(size), fileReference: nil)))
+ previewRepresentations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: w, height: h), resource: CloudFileMediaResource(datacenterId: Int(dcId), volumeId: volumeId, localId: localId, secret: secret, size: size == 0 ? nil : Int(size), fileReference: nil), progressiveSizes: []))
case .fileLocationUnavailable:
break
}
@@ -992,7 +992,7 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32
case let .fileLocation(dcId, volumeId, localId, secret):
let resource = CloudFileMediaResource(datacenterId: Int(dcId), volumeId: volumeId, localId: localId, secret: secret, size: bytes.size, fileReference: nil)
resources.append((resource, bytes.makeData()))
- previewRepresentations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: w, height: h), resource: resource))
+ previewRepresentations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: w, height: h), resource: resource, progressiveSizes: []))
case .fileLocationUnavailable:
break
}
@@ -1140,10 +1140,10 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32
var representations: [TelegramMediaImageRepresentation] = []
if thumb.size != 0 {
let resource = LocalFileMediaResource(fileId: arc4random64())
- representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: thumbW, height: thumbH), resource: resource))
+ representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: thumbW, height: thumbH), resource: resource, progressiveSizes: []))
resources.append((resource, thumb.makeData()))
}
- representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: w, height: h), resource: file.resource(key: SecretFileEncryptionKey(aesKey: key.makeData(), aesIv: iv.makeData()), decryptedSize: size)))
+ representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: w, height: h), resource: file.resource(key: SecretFileEncryptionKey(aesKey: key.makeData(), aesIv: iv.makeData()), decryptedSize: size), progressiveSizes: []))
let image = TelegramMediaImage(imageId: MediaId(namespace: Namespaces.Media.CloudSecretImage, id: file.id), representations: representations, immediateThumbnailData: nil, reference: nil, partialReference: nil, flags: [])
parsedMedia.append(image)
}
@@ -1167,7 +1167,7 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32
var previewRepresentations: [TelegramMediaImageRepresentation] = []
if thumb.size != 0 {
let resource = LocalFileMediaResource(fileId: arc4random64())
- previewRepresentations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: thumbW, height: thumbH), resource: resource))
+ previewRepresentations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: thumbW, height: thumbH), resource: resource, progressiveSizes: []))
resources.append((resource, thumb.makeData()))
}
let fileMedia = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.CloudSecretFile, id: file.id), partialReference: nil, resource: file.resource(key: SecretFileEncryptionKey(aesKey: key.makeData(), aesIv: iv.makeData()), decryptedSize: size), previewRepresentations: previewRepresentations, videoThumbnails: [], immediateThumbnailData: nil, mimeType: mimeType, size: Int(size), attributes: parsedAttributes)
@@ -1198,7 +1198,7 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32
var previewRepresentations: [TelegramMediaImageRepresentation] = []
if thumb.size != 0 {
let resource = LocalFileMediaResource(fileId: arc4random64())
- previewRepresentations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: thumbW, height: thumbH), resource: resource))
+ previewRepresentations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: thumbW, height: thumbH), resource: resource, progressiveSizes: []))
resources.append((resource, thumb.makeData()))
}
let fileMedia = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.CloudSecretFile, id: file.id), partialReference: nil, resource: file.resource(key: SecretFileEncryptionKey(aesKey: key.makeData(), aesIv: iv.makeData()), decryptedSize: size), previewRepresentations: previewRepresentations, videoThumbnails: [], immediateThumbnailData: nil, mimeType: mimeType, size: Int(size), attributes: parsedAttributes)
@@ -1216,7 +1216,7 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32
case let .photoSize(_, location, w, h, size):
switch location {
case let .fileLocation(dcId, volumeId, localId, secret):
- previewRepresentations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: w, height: h), resource: CloudFileMediaResource(datacenterId: Int(dcId), volumeId: volumeId, localId: localId, secret: secret, size: size == 0 ? nil : Int(size), fileReference: nil)))
+ previewRepresentations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: w, height: h), resource: CloudFileMediaResource(datacenterId: Int(dcId), volumeId: volumeId, localId: localId, secret: secret, size: size == 0 ? nil : Int(size), fileReference: nil), progressiveSizes: []))
case .fileLocationUnavailable:
break
}
@@ -1226,7 +1226,7 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32
case let .fileLocation(dcId, volumeId, localId, secret):
let resource = CloudFileMediaResource(datacenterId: Int(dcId), volumeId: volumeId, localId: localId, secret: secret, size: bytes.size, fileReference: nil)
resources.append((resource, bytes.makeData()))
- previewRepresentations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: w, height: h), resource: resource))
+ previewRepresentations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: w, height: h), resource: resource, progressiveSizes: []))
case .fileLocationUnavailable:
break
}
diff --git a/submodules/TelegramCore/Sources/RateCall.swift b/submodules/TelegramCore/Sources/RateCall.swift
index 96967d9271..bac6cd7258 100644
--- a/submodules/TelegramCore/Sources/RateCall.swift
+++ b/submodules/TelegramCore/Sources/RateCall.swift
@@ -15,6 +15,9 @@ public func rateCall(account: Account, callId: CallId, starsCount: Int32, commen
}
public func saveCallDebugLog(network: Network, callId: CallId, log: String) -> Signal {
+ if log.count > 1024 * 16 {
+ return .complete()
+ }
return network.request(Api.functions.phone.saveCallDebug(peer: Api.InputPhoneCall.inputPhoneCall(id: callId.id, accessHash: callId.accessHash), debug: .dataJSON(data: log)))
|> `catch` { _ -> Signal in
return .single(.boolFalse)
diff --git a/submodules/TelegramCore/Sources/SearchMessages.swift b/submodules/TelegramCore/Sources/SearchMessages.swift
index 3d63b79ef7..c7710ab77e 100644
--- a/submodules/TelegramCore/Sources/SearchMessages.swift
+++ b/submodules/TelegramCore/Sources/SearchMessages.swift
@@ -10,6 +10,7 @@ public enum SearchMessagesLocation: Equatable {
case general
case group(PeerGroupId)
case peer(peerId: PeerId, fromId: PeerId?, tags: MessageTags?)
+ case publicForwards(messageId: MessageId, datacenterId: Int?)
}
private struct SearchMessagesPeerState: Equatable {
@@ -282,7 +283,7 @@ public func searchMessages(account: Account, location: SearchMessagesLocation, q
}
}
|> mapToSignal { (nextRate, lowerBound, inputPeer) in
- account.network.request(Api.functions.messages.searchGlobal(flags: 0, folderId: nil, q: query, offsetRate: nextRate, offsetPeer: inputPeer, offsetId: lowerBound?.id.id ?? 0, limit: limit), automaticFloodWait: false)
+ return account.network.request(Api.functions.messages.searchGlobal(flags: 0, folderId: nil, q: query, offsetRate: nextRate, offsetPeer: inputPeer, offsetId: lowerBound?.id.id ?? 0, limit: limit), automaticFloodWait: false)
|> map { result -> (Api.messages.Messages?, Api.messages.Messages?) in
return (result, nil)
}
@@ -290,6 +291,46 @@ public func searchMessages(account: Account, location: SearchMessagesLocation, q
return .single((nil, nil))
}
}
+ case let .publicForwards(messageId, datacenterId):
+ remoteSearchResult = .single((nil, nil))
+ /*remoteSearchResult = account.postbox.transaction { transaction -> (Api.InputChannel?, Int32, MessageIndex?, Api.InputPeer) in
+ let sourcePeer = transaction.getPeer(messageId.peerId)
+ let inputChannel = sourcePeer.flatMap { apiInputChannel($0) }
+
+ var lowerBound: MessageIndex?
+ if let state = state, let message = state.main.messages.last {
+ lowerBound = message.index
+ }
+ if let lowerBound = lowerBound, let peer = transaction.getPeer(lowerBound.id.peerId), let inputPeer = apiInputPeer(peer) {
+ return (inputChannel, state?.main.nextRate ?? 0, lowerBound, inputPeer)
+ } else {
+ return (inputChannel, 0, lowerBound, .inputPeerEmpty)
+ }
+ }
+ |> mapToSignal { (inputChannel, nextRate, lowerBound, inputPeer) in
+ guard let inputChannel = inputChannel else {
+ return .complete()
+ }
+
+ let request = Api.functions.stats.getMessagePublicForwards(channel: inputChannel, msgId: messageId.id, offsetRate: nextRate, offsetPeer: inputPeer, offsetId: lowerBound?.id.id ?? 0, limit: limit)
+ let signal: Signal
+ if let datacenterId = datacenterId, account.network.datacenterId != datacenterId {
+ signal = account.network.download(datacenterId: datacenterId, isMedia: false, tag: nil)
+ |> castError(MTRpcError.self)
+ |> mapToSignal { worker in
+ return worker.request(request)
+ }
+ } else {
+ signal = account.network.request(request, automaticFloodWait: false)
+ }
+ return signal
+ |> map { result -> (Api.messages.Messages?, Api.messages.Messages?) in
+ return (result, nil)
+ }
+ |> `catch` { _ -> Signal<(Api.messages.Messages?, Api.messages.Messages?), NoError> in
+ return .single((nil, nil))
+ }
+ }*/
}
return remoteSearchResult
@@ -572,11 +613,11 @@ public func searchMessageIdByTimestamp(account: Account, peerId: PeerId, timesta
} |> switchToLatest
}
-enum UpdatedRemotePeerError {
+public enum UpdatedRemotePeerError {
case generic
}
-func updatedRemotePeer(postbox: Postbox, network: Network, peer: PeerReference) -> Signal {
+public func updatedRemotePeer(postbox: Postbox, network: Network, peer: PeerReference) -> Signal {
if let inputUser = peer.inputUser {
return network.request(Api.functions.users.getUsers(id: [inputUser]))
|> mapError { _ -> UpdatedRemotePeerError in
diff --git a/submodules/TelegramCore/Sources/Serialization.swift b/submodules/TelegramCore/Sources/Serialization.swift
index c5fcaed904..eee8616a22 100644
--- a/submodules/TelegramCore/Sources/Serialization.swift
+++ b/submodules/TelegramCore/Sources/Serialization.swift
@@ -210,7 +210,7 @@ public class BoxedMessage: NSObject {
public class Serialization: NSObject, MTSerialization {
public func currentLayer() -> UInt {
- return 116
+ return 117
}
public func parseMessage(_ data: Data!) -> Any! {
diff --git a/submodules/TelegramCore/Sources/StandaloneUploadedMedia.swift b/submodules/TelegramCore/Sources/StandaloneUploadedMedia.swift
index 80e856b16d..90b5062a61 100644
--- a/submodules/TelegramCore/Sources/StandaloneUploadedMedia.swift
+++ b/submodules/TelegramCore/Sources/StandaloneUploadedMedia.swift
@@ -102,7 +102,7 @@ public func standaloneUploadedImage(account: Account, peerId: PeerId, text: Stri
|> mapToSignal { result -> Signal in
switch result {
case let .encryptedFile(id, accessHash, size, dcId, _):
- return .single(.result(.media(.standalone(media: TelegramMediaImage(imageId: MediaId(namespace: Namespaces.Media.LocalImage, id: arc4random64()), representations: [TelegramMediaImageRepresentation(dimensions: dimensions, resource: SecretFileMediaResource(fileId: id, accessHash: accessHash, containerSize: size, decryptedSize: Int32(data.count), datacenterId: Int(dcId), key: key))], immediateThumbnailData: nil, reference: nil, partialReference: nil, flags: [])))))
+ return .single(.result(.media(.standalone(media: TelegramMediaImage(imageId: MediaId(namespace: Namespaces.Media.LocalImage, id: arc4random64()), representations: [TelegramMediaImageRepresentation(dimensions: dimensions, resource: SecretFileMediaResource(fileId: id, accessHash: accessHash, containerSize: size, decryptedSize: Int32(data.count), datacenterId: Int(dcId), key: key), progressiveSizes: [])], immediateThumbnailData: nil, reference: nil, partialReference: nil, flags: [])))))
case .encryptedFileEmpty:
return .fail(.generic)
}
diff --git a/submodules/TelegramCore/Sources/StickerPack.swift b/submodules/TelegramCore/Sources/StickerPack.swift
index 2dd169dba3..97f3245721 100644
--- a/submodules/TelegramCore/Sources/StickerPack.swift
+++ b/submodules/TelegramCore/Sources/StickerPack.swift
@@ -11,13 +11,19 @@ func telegramStickerPachThumbnailRepresentationFromApiSize(datacenterId: Int32,
switch location {
case let .fileLocationToBeDeprecated(volumeId, localId):
let resource = CloudStickerPackThumbnailMediaResource(datacenterId: datacenterId, volumeId: volumeId, localId: localId)
- return TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: w, height: h), resource: resource)
+ return TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: w, height: h), resource: resource, progressiveSizes: [])
}
case let .photoSize(_, location, w, h, _):
switch location {
case let .fileLocationToBeDeprecated(volumeId, localId):
let resource = CloudStickerPackThumbnailMediaResource(datacenterId: datacenterId, volumeId: volumeId, localId: localId)
- return TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: w, height: h), resource: resource)
+ return TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: w, height: h), resource: resource, progressiveSizes: [])
+ }
+ case let .photoSizeProgressive(_, location, w, h, sizes):
+ switch location {
+ case let .fileLocationToBeDeprecated(volumeId, localId):
+ let resource = CloudStickerPackThumbnailMediaResource(datacenterId: datacenterId, volumeId: volumeId, localId: localId)
+ return TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: w, height: h), resource: resource, progressiveSizes: sizes)
}
case .photoStrippedSize:
return nil
diff --git a/submodules/TelegramCore/Sources/StoreMessage_Telegram.swift b/submodules/TelegramCore/Sources/StoreMessage_Telegram.swift
index bf81ce4d04..aa1ec9bd2e 100644
--- a/submodules/TelegramCore/Sources/StoreMessage_Telegram.swift
+++ b/submodules/TelegramCore/Sources/StoreMessage_Telegram.swift
@@ -521,6 +521,10 @@ extension StoreMessage {
attributes.append(ViewCountMessageAttribute(count: Int(views)))
}
+ /*if let forwards = forwards, namespace != Namespaces.Message.ScheduledCloud {
+ attributes.append(ForwardCountMessageAttribute(count: Int(forwards)))
+ }*/
+
if let editDate = editDate {
attributes.append(EditedMessageAttribute(date: editDate, isHidden: (flags & (1 << 21)) != 0))
}
diff --git a/submodules/TelegramCore/Sources/TelegramMediaFile.swift b/submodules/TelegramCore/Sources/TelegramMediaFile.swift
index 7d052274cf..8a44e61601 100644
--- a/submodules/TelegramCore/Sources/TelegramMediaFile.swift
+++ b/submodules/TelegramCore/Sources/TelegramMediaFile.swift
@@ -129,13 +129,19 @@ func telegramMediaFileThumbnailRepresentationsFromApiSizes(datacenterId: Int32,
switch location {
case let .fileLocationToBeDeprecated(volumeId, localId):
let resource = CloudDocumentSizeMediaResource(datacenterId: datacenterId, documentId: documentId, accessHash: accessHash, sizeSpec: type, volumeId: volumeId, localId: localId, fileReference: fileReference)
- representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: w, height: h), resource: resource))
+ representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: w, height: h), resource: resource, progressiveSizes: []))
}
case let .photoSize(type, location, w, h, _):
switch location {
case let .fileLocationToBeDeprecated(volumeId, localId):
let resource = CloudDocumentSizeMediaResource(datacenterId: datacenterId, documentId: documentId, accessHash: accessHash, sizeSpec: type, volumeId: volumeId, localId: localId, fileReference: fileReference)
- representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: w, height: h), resource: resource))
+ representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: w, height: h), resource: resource, progressiveSizes: []))
+ }
+ case let .photoSizeProgressive(type, location, w, h, sizes):
+ switch location {
+ case let .fileLocationToBeDeprecated(volumeId, localId):
+ let resource = CloudDocumentSizeMediaResource(datacenterId: datacenterId, documentId: documentId, accessHash: accessHash, sizeSpec: type, volumeId: volumeId, localId: localId, fileReference: fileReference)
+ representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: w, height: h), resource: resource, progressiveSizes: sizes))
}
case let .photoStrippedSize(_, data):
immediateThumbnailData = data.makeData()
diff --git a/submodules/TelegramCore/Sources/TelegramMediaImage.swift b/submodules/TelegramCore/Sources/TelegramMediaImage.swift
index bd84b93b2e..674c5afd12 100644
--- a/submodules/TelegramCore/Sources/TelegramMediaImage.swift
+++ b/submodules/TelegramCore/Sources/TelegramMediaImage.swift
@@ -13,13 +13,21 @@ func telegramMediaImageRepresentationsFromApiSizes(datacenterId: Int32, photoId:
switch location {
case let .fileLocationToBeDeprecated(volumeId, localId):
let resource = CloudPhotoSizeMediaResource(datacenterId: datacenterId, photoId: photoId, accessHash: accessHash, sizeSpec: type, volumeId: volumeId, localId: localId, size: nil, fileReference: fileReference)
- representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: w, height: h), resource: resource))
+ representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: w, height: h), resource: resource, progressiveSizes: []))
}
case let .photoSize(type, location, w, h, size):
switch location {
case let .fileLocationToBeDeprecated(volumeId, localId):
let resource = CloudPhotoSizeMediaResource(datacenterId: datacenterId, photoId: photoId, accessHash: accessHash, sizeSpec: type, volumeId: volumeId, localId: localId, size: Int(size), fileReference: fileReference)
- representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: w, height: h), resource: resource))
+ representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: w, height: h), resource: resource, progressiveSizes: []))
+ }
+ case let .photoSizeProgressive(type, location, w, h, sizes):
+ switch location {
+ case let .fileLocationToBeDeprecated(volumeId, localId):
+ if !sizes.isEmpty {
+ let resource = CloudPhotoSizeMediaResource(datacenterId: datacenterId, photoId: photoId, accessHash: accessHash, sizeSpec: type, volumeId: volumeId, localId: localId, size: Int(sizes[sizes.count - 1]), fileReference: fileReference)
+ representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: w, height: h), resource: resource, progressiveSizes: sizes))
+ }
}
case let .photoStrippedSize(_, data):
immediateThumbnailData = data.makeData()
diff --git a/submodules/TelegramCore/Sources/TelegramUser.swift b/submodules/TelegramCore/Sources/TelegramUser.swift
index e89b221736..c4e893972a 100644
--- a/submodules/TelegramCore/Sources/TelegramUser.swift
+++ b/submodules/TelegramCore/Sources/TelegramUser.swift
@@ -20,8 +20,8 @@ func parsedTelegramProfilePhoto(_ photo: Api.UserProfilePhoto) -> [TelegramMedia
case let .fileLocationToBeDeprecated(volumeId, localId):
fullSizeResource = CloudPeerPhotoSizeMediaResource(datacenterId: dcId, sizeSpec: .fullSize, volumeId: volumeId, localId: localId)
}
- representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 80, height: 80), resource: smallResource))
- representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 640, height: 640), resource: fullSizeResource))
+ representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 80, height: 80), resource: smallResource, progressiveSizes: []))
+ representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 640, height: 640), resource: fullSizeResource, progressiveSizes: []))
case .userProfilePhotoEmpty:
break
}
diff --git a/submodules/TelegramCore/Sources/UpdateCachedPeerData.swift b/submodules/TelegramCore/Sources/UpdateCachedPeerData.swift
index 55726f9f60..f61217fabb 100644
--- a/submodules/TelegramCore/Sources/UpdateCachedPeerData.swift
+++ b/submodules/TelegramCore/Sources/UpdateCachedPeerData.swift
@@ -193,7 +193,11 @@ func fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPeerId: PeerI
case let .userFull(userFull):
let botInfo = userFull.botInfo.flatMap(BotInfo.init(apiBotInfo:))
let isBlocked = (userFull.flags & (1 << 0)) != 0
- let callsAvailable = (userFull.flags & (1 << 4)) != 0
+ let voiceCallsAvailable = (userFull.flags & (1 << 4)) != 0
+ var videoCallsAvailable = (userFull.flags & (1 << 13)) != 0
+ #if DEBUG
+ videoCallsAvailable = true
+ #endif
let callsPrivate = (userFull.flags & (1 << 5)) != 0
let canPinMessages = (userFull.flags & (1 << 7)) != 0
let pinnedMessageId = userFull.pinnedMsgId.flatMap({ MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: $0) })
@@ -205,7 +209,7 @@ func fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPeerId: PeerI
hasScheduledMessages = true
}
- return previous.withUpdatedAbout(userFull.about).withUpdatedBotInfo(botInfo).withUpdatedCommonGroupCount(userFull.commonChatsCount).withUpdatedIsBlocked(isBlocked).withUpdatedCallsAvailable(callsAvailable).withUpdatedCallsPrivate(callsPrivate).withUpdatedCanPinMessages(canPinMessages).withUpdatedPeerStatusSettings(peerStatusSettings).withUpdatedPinnedMessageId(pinnedMessageId).withUpdatedHasScheduledMessages(hasScheduledMessages)
+ return previous.withUpdatedAbout(userFull.about).withUpdatedBotInfo(botInfo).withUpdatedCommonGroupCount(userFull.commonChatsCount).withUpdatedIsBlocked(isBlocked).withUpdatedVoiceCallsAvailable(voiceCallsAvailable).withUpdatedVideoCallsAvailable(videoCallsAvailable).withUpdatedCallsPrivate(callsPrivate).withUpdatedCanPinMessages(canPinMessages).withUpdatedPeerStatusSettings(peerStatusSettings).withUpdatedPinnedMessageId(pinnedMessageId).withUpdatedHasScheduledMessages(hasScheduledMessages)
}
})
return true
diff --git a/submodules/TelegramPresentationData/Sources/PresentationStrings.swift b/submodules/TelegramPresentationData/Sources/PresentationStrings.swift
index 0c6bfddfed..d9c947cbc1 100644
--- a/submodules/TelegramPresentationData/Sources/PresentationStrings.swift
+++ b/submodules/TelegramPresentationData/Sources/PresentationStrings.swift
@@ -223,5435 +223,5474 @@ public final class PresentationStrings: Equatable {
}
public var AutoDownloadSettings_Files: String { return self._s[31]! }
public var TextFormat_AddLinkPlaceholder: String { return self._s[32]! }
- public var ChatList_GenericPsaLabel: String { return self._s[37]! }
- public var LastSeen_Lately: String { return self._s[38]! }
+ public var Stats_MessagePublicForwardsTitle: String { return self._s[33]! }
+ public var ChatList_GenericPsaLabel: String { return self._s[38]! }
+ public var LastSeen_Lately: String { return self._s[39]! }
public func PUSH_CHANNEL_MESSAGE_VIDEOS(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[39]!, self._r[39]!, [_1, _2])
+ return formatWithArgumentRanges(self._s[40]!, self._r[40]!, [_1, _2])
}
- public var Camera_Discard: String { return self._s[40]! }
- public var Channel_EditAdmin_PermissinAddAdminOff: String { return self._s[41]! }
- public var Login_InvalidPhoneError: String { return self._s[43]! }
- public var SettingsSearch_Synonyms_Privacy_AuthSessions: String { return self._s[44]! }
- public var GroupInfo_LabelOwner: String { return self._s[45]! }
- public var Conversation_Moderate_Delete: String { return self._s[46]! }
- public var ClearCache_ClearCache: String { return self._s[47]! }
- public var Conversation_DeleteMessagesForEveryone: String { return self._s[48]! }
- public var WatchRemote_AlertOpen: String { return self._s[49]! }
+ public var Camera_Discard: String { return self._s[41]! }
+ public var Channel_EditAdmin_PermissinAddAdminOff: String { return self._s[42]! }
+ public var Login_InvalidPhoneError: String { return self._s[44]! }
+ public var SettingsSearch_Synonyms_Privacy_AuthSessions: String { return self._s[45]! }
+ public var GroupInfo_LabelOwner: String { return self._s[46]! }
+ public var Conversation_Moderate_Delete: String { return self._s[47]! }
+ public var ClearCache_ClearCache: String { return self._s[48]! }
+ public var Conversation_DeleteMessagesForEveryone: String { return self._s[49]! }
+ public var WatchRemote_AlertOpen: String { return self._s[50]! }
public func MediaPicker_Nof(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[50]!, self._r[50]!, [_0])
+ return formatWithArgumentRanges(self._s[51]!, self._r[51]!, [_0])
}
- public var ChatState_ConnectingToProxy: String { return self._s[51]! }
- public var EditTheme_Expand_Preview_IncomingReplyName: String { return self._s[52]! }
- public var AutoDownloadSettings_MediaTypes: String { return self._s[54]! }
- public var Watch_GroupInfo_Title: String { return self._s[55]! }
- public var Passport_Identity_AddPersonalDetails: String { return self._s[56]! }
- public var Channel_Info_Members: String { return self._s[57]! }
- public var LoginPassword_InvalidPasswordError: String { return self._s[59]! }
- public var Conversation_LiveLocation: String { return self._s[60]! }
- public var Wallet_Month_ShortNovember: String { return self._s[61]! }
- public var PrivacyLastSeenSettings_CustomShareSettingsHelp: String { return self._s[62]! }
- public var NetworkUsageSettings_BytesReceived: String { return self._s[65]! }
- public var Stickers_Search: String { return self._s[67]! }
- public var NotificationsSound_Synth: String { return self._s[68]! }
- public var LogoutOptions_LogOutInfo: String { return self._s[69]! }
+ public var ChatState_ConnectingToProxy: String { return self._s[52]! }
+ public var EditTheme_Expand_Preview_IncomingReplyName: String { return self._s[53]! }
+ public var AutoDownloadSettings_MediaTypes: String { return self._s[55]! }
+ public var Watch_GroupInfo_Title: String { return self._s[56]! }
+ public var Passport_Identity_AddPersonalDetails: String { return self._s[57]! }
+ public var Channel_Info_Members: String { return self._s[58]! }
+ public var LoginPassword_InvalidPasswordError: String { return self._s[60]! }
+ public var Conversation_LiveLocation: String { return self._s[61]! }
+ public var Wallet_Month_ShortNovember: String { return self._s[62]! }
+ public var PrivacyLastSeenSettings_CustomShareSettingsHelp: String { return self._s[63]! }
+ public var NetworkUsageSettings_BytesReceived: String { return self._s[66]! }
+ public var Stickers_Search: String { return self._s[68]! }
+ public var NotificationsSound_Synth: String { return self._s[69]! }
+ public var LogoutOptions_LogOutInfo: String { return self._s[70]! }
public func VoiceOver_Chat_ForwardedFrom(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[71]!, self._r[71]!, [_0])
+ return formatWithArgumentRanges(self._s[72]!, self._r[72]!, [_0])
}
- public var NetworkUsageSettings_MediaAudioDataSection: String { return self._s[72]! }
- public var ChatListFolder_NameBots: String { return self._s[73]! }
- public var ChatList_EmptyChatListFilterText: String { return self._s[75]! }
- public var Call_IncomingVoiceCall: String { return self._s[77]! }
- public var ChatList_Context_HideArchive: String { return self._s[78]! }
- public var AutoNightTheme_UseSunsetSunrise: String { return self._s[79]! }
- public var FastTwoStepSetup_Title: String { return self._s[80]! }
- public var EditTheme_Create_Preview_IncomingReplyText: String { return self._s[81]! }
- public var Channel_Info_BlackList: String { return self._s[82]! }
- public var Channel_AdminLog_InfoPanelTitle: String { return self._s[83]! }
- public var Conversation_OpenFile: String { return self._s[85]! }
- public var SecretTimer_ImageDescription: String { return self._s[86]! }
- public var PrivacySettings_AutoArchive: String { return self._s[87]! }
- public var StickerSettings_ContextInfo: String { return self._s[88]! }
- public var TwoStepAuth_GenericHelp: String { return self._s[90]! }
- public var AutoDownloadSettings_Unlimited: String { return self._s[91]! }
- public var PrivacyLastSeenSettings_NeverShareWith_Title: String { return self._s[92]! }
- public var AutoDownloadSettings_DataUsageHigh: String { return self._s[93]! }
+ public var NetworkUsageSettings_MediaAudioDataSection: String { return self._s[73]! }
+ public var ChatListFolder_NameBots: String { return self._s[74]! }
+ public var ChatList_EmptyChatListFilterText: String { return self._s[76]! }
+ public var Call_IncomingVoiceCall: String { return self._s[78]! }
+ public var ChatList_Context_HideArchive: String { return self._s[79]! }
+ public var AutoNightTheme_UseSunsetSunrise: String { return self._s[80]! }
+ public var FastTwoStepSetup_Title: String { return self._s[81]! }
+ public var EditTheme_Create_Preview_IncomingReplyText: String { return self._s[82]! }
+ public var Channel_Info_BlackList: String { return self._s[83]! }
+ public var Channel_AdminLog_InfoPanelTitle: String { return self._s[84]! }
+ public var Conversation_OpenFile: String { return self._s[86]! }
+ public var SecretTimer_ImageDescription: String { return self._s[87]! }
+ public var PrivacySettings_AutoArchive: String { return self._s[88]! }
+ public var StickerSettings_ContextInfo: String { return self._s[89]! }
+ public var TwoStepAuth_GenericHelp: String { return self._s[91]! }
+ public var AutoDownloadSettings_Unlimited: String { return self._s[92]! }
+ public var PrivacyLastSeenSettings_NeverShareWith_Title: String { return self._s[93]! }
+ public var AutoDownloadSettings_DataUsageHigh: String { return self._s[94]! }
public func PUSH_CHAT_MESSAGE_VIDEO(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[94]!, self._r[94]!, [_1, _2])
+ return formatWithArgumentRanges(self._s[95]!, self._r[95]!, [_1, _2])
}
- public var AuthSessions_AddDevice_ScanInfo: String { return self._s[95]! }
- public var Notifications_AddExceptionTitle: String { return self._s[96]! }
- public var Watch_MessageView_Reply: String { return self._s[97]! }
- public var Tour_Text6: String { return self._s[98]! }
- public var TwoStepAuth_SetupPasswordEnterPasswordChange: String { return self._s[99]! }
+ public var AuthSessions_AddDevice_ScanInfo: String { return self._s[96]! }
+ public var Notifications_AddExceptionTitle: String { return self._s[97]! }
+ public var Watch_MessageView_Reply: String { return self._s[98]! }
+ public var Tour_Text6: String { return self._s[99]! }
+ public var TwoStepAuth_SetupPasswordEnterPasswordChange: String { return self._s[100]! }
public func Notification_PinnedAnimationMessage(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[100]!, self._r[100]!, [_0])
- }
- public func ShareFileTip_Text(_ _0: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[101]!, self._r[101]!, [_0])
}
- public var Wallet_Configuration_BlockchainIdPlaceholder: String { return self._s[102]! }
- public var AccessDenied_LocationDenied: String { return self._s[103]! }
- public var CallSettings_RecentCalls: String { return self._s[104]! }
- public var ConversationProfile_LeaveDeleteAndExit: String { return self._s[105]! }
- public var Conversation_Dice_u1F3C0: String { return self._s[106]! }
- public var Channel_Members_AddAdminErrorBlacklisted: String { return self._s[108]! }
- public var Passport_Authorize: String { return self._s[109]! }
- public var StickerPacksSettings_ArchivedMasks_Info: String { return self._s[110]! }
- public var AutoDownloadSettings_Videos: String { return self._s[111]! }
- public var TwoStepAuth_ReEnterPasswordTitle: String { return self._s[112]! }
- public var Wallet_Info_Send: String { return self._s[113]! }
- public var AuthSessions_AddDevice_UrlLoginHint: String { return self._s[114]! }
- public var Wallet_TransactionInfo_SendGrams: String { return self._s[115]! }
- public var Tour_StartButton: String { return self._s[116]! }
- public var Watch_AppName: String { return self._s[118]! }
- public var Settings_AddAnotherAccount: String { return self._s[119]! }
- public var StickerPack_ErrorNotFound: String { return self._s[120]! }
- public var Channel_Info_Subscribers: String { return self._s[121]! }
- public func Channel_AdminLog_MessageGroupPreHistoryVisible(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[122]!, self._r[122]!, [_0])
+ public func ShareFileTip_Text(_ _0: String) -> (String, [(Int, NSRange)]) {
+ return formatWithArgumentRanges(self._s[102]!, self._r[102]!, [_0])
}
- public func DialogList_PinLimitError(_ _0: String) -> (String, [(Int, NSRange)]) {
+ public var Wallet_Configuration_BlockchainIdPlaceholder: String { return self._s[103]! }
+ public var AccessDenied_LocationDenied: String { return self._s[104]! }
+ public var CallSettings_RecentCalls: String { return self._s[105]! }
+ public var ConversationProfile_LeaveDeleteAndExit: String { return self._s[106]! }
+ public var Conversation_Dice_u1F3C0: String { return self._s[107]! }
+ public var Channel_Members_AddAdminErrorBlacklisted: String { return self._s[109]! }
+ public var Passport_Authorize: String { return self._s[110]! }
+ public var StickerPacksSettings_ArchivedMasks_Info: String { return self._s[111]! }
+ public var AutoDownloadSettings_Videos: String { return self._s[112]! }
+ public var TwoStepAuth_ReEnterPasswordTitle: String { return self._s[113]! }
+ public var Wallet_Info_Send: String { return self._s[114]! }
+ public var AuthSessions_AddDevice_UrlLoginHint: String { return self._s[115]! }
+ public var Wallet_TransactionInfo_SendGrams: String { return self._s[116]! }
+ public var Tour_StartButton: String { return self._s[117]! }
+ public var Watch_AppName: String { return self._s[119]! }
+ public var Settings_AddAnotherAccount: String { return self._s[120]! }
+ public var StickerPack_ErrorNotFound: String { return self._s[121]! }
+ public var Channel_Info_Subscribers: String { return self._s[122]! }
+ public func Channel_AdminLog_MessageGroupPreHistoryVisible(_ _0: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[123]!, self._r[123]!, [_0])
}
- public var Appearance_RemoveTheme: String { return self._s[124]! }
+ public func DialogList_PinLimitError(_ _0: String) -> (String, [(Int, NSRange)]) {
+ return formatWithArgumentRanges(self._s[124]!, self._r[124]!, [_0])
+ }
+ public var Appearance_RemoveTheme: String { return self._s[125]! }
public func Wallet_Info_TransactionBlockchainFee(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[125]!, self._r[125]!, [_0])
+ return formatWithArgumentRanges(self._s[126]!, self._r[126]!, [_0])
}
- public var Conversation_StopLiveLocation: String { return self._s[128]! }
- public var Channel_AdminLogFilter_EventsAll: String { return self._s[129]! }
- public var GroupInfo_InviteLink_CopyAlert_Success: String { return self._s[131]! }
- public var Username_LinkCopied: String { return self._s[133]! }
- public var GroupRemoved_Title: String { return self._s[134]! }
- public var SecretVideo_Title: String { return self._s[135]! }
+ public var Conversation_StopLiveLocation: String { return self._s[129]! }
+ public var Channel_AdminLogFilter_EventsAll: String { return self._s[130]! }
+ public var GroupInfo_InviteLink_CopyAlert_Success: String { return self._s[132]! }
+ public var Username_LinkCopied: String { return self._s[134]! }
+ public var GroupRemoved_Title: String { return self._s[135]! }
+ public var SecretVideo_Title: String { return self._s[136]! }
public func PUSH_PINNED_VIDEO(_ _1: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[136]!, self._r[136]!, [_1])
+ return formatWithArgumentRanges(self._s[137]!, self._r[137]!, [_1])
}
- public var AccessDenied_PhotosAndVideos: String { return self._s[137]! }
- public var Appearance_ThemePreview_Chat_1_Text: String { return self._s[138]! }
+ public var AccessDenied_PhotosAndVideos: String { return self._s[138]! }
+ public var Appearance_ThemePreview_Chat_1_Text: String { return self._s[139]! }
public func PUSH_CHANNEL_MESSAGE_GEOLIVE(_ _1: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[140]!, self._r[140]!, [_1])
+ return formatWithArgumentRanges(self._s[141]!, self._r[141]!, [_1])
}
- public var Map_OpenInGoogleMaps: String { return self._s[142]! }
- public var Conversation_Dice_u26BD: String { return self._s[143]! }
+ public var Map_OpenInGoogleMaps: String { return self._s[143]! }
+ public var Conversation_Dice_u26BD: String { return self._s[144]! }
public func Time_PreciseDate_m12(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[144]!, self._r[144]!, [_1, _2, _3])
+ return formatWithArgumentRanges(self._s[145]!, self._r[145]!, [_1, _2, _3])
}
public func Channel_AdminLog_MessageKickedNameUsername(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[145]!, self._r[145]!, [_1, _2])
+ return formatWithArgumentRanges(self._s[146]!, self._r[146]!, [_1, _2])
}
- public var Call_StatusRinging: String { return self._s[146]! }
- public var SettingsSearch_Synonyms_EditProfile_Username: String { return self._s[147]! }
- public var Group_Username_InvalidStartsWithNumber: String { return self._s[148]! }
- public var UserInfo_NotificationsEnabled: String { return self._s[149]! }
- public var PeopleNearby_MakeVisibleDescription: String { return self._s[150]! }
- public var Settings_RemoveConfirmation: String { return self._s[151]! }
- public var ChatListFolder_CategoryRead: String { return self._s[152]! }
- public var Map_Search: String { return self._s[153]! }
- public var ClearCache_StorageFree: String { return self._s[155]! }
- public var Login_TermsOfServiceHeader: String { return self._s[156]! }
+ public var Call_StatusRinging: String { return self._s[147]! }
+ public var SettingsSearch_Synonyms_EditProfile_Username: String { return self._s[148]! }
+ public var Group_Username_InvalidStartsWithNumber: String { return self._s[149]! }
+ public var UserInfo_NotificationsEnabled: String { return self._s[150]! }
+ public var PeopleNearby_MakeVisibleDescription: String { return self._s[151]! }
+ public var Settings_RemoveConfirmation: String { return self._s[152]! }
+ public var ChatListFolder_CategoryRead: String { return self._s[153]! }
+ public var Map_Search: String { return self._s[154]! }
+ public var ClearCache_StorageFree: String { return self._s[156]! }
+ public var Login_TermsOfServiceHeader: String { return self._s[157]! }
public func Notification_PinnedVideoMessage(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[157]!, self._r[157]!, [_0])
+ return formatWithArgumentRanges(self._s[158]!, self._r[158]!, [_0])
}
public func Channel_AdminLog_MessageToggleSignaturesOn(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[159]!, self._r[159]!, [_0])
+ return formatWithArgumentRanges(self._s[160]!, self._r[160]!, [_0])
}
- public var ChatList_GenericPsaAlert: String { return self._s[160]! }
- public var Wallet_Sent_Title: String { return self._s[161]! }
- public var TwoStepAuth_SetupPasswordConfirmPassword: String { return self._s[162]! }
- public var Weekday_Today: String { return self._s[163]! }
- public var Stats_InstantViewInteractionsTitle: String { return self._s[164]! }
+ public var ChatList_GenericPsaAlert: String { return self._s[161]! }
+ public var Wallet_Sent_Title: String { return self._s[162]! }
+ public var TwoStepAuth_SetupPasswordConfirmPassword: String { return self._s[163]! }
+ public var Weekday_Today: String { return self._s[164]! }
+ public var Stats_InstantViewInteractionsTitle: String { return self._s[165]! }
public func InstantPage_AuthorAndDateTitle(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[166]!, self._r[166]!, [_1, _2])
+ return formatWithArgumentRanges(self._s[167]!, self._r[167]!, [_1, _2])
}
public func Conversation_MessageDialogRetryAll(_ _1: Int) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[167]!, self._r[167]!, ["\(_1)"])
+ return formatWithArgumentRanges(self._s[168]!, self._r[168]!, ["\(_1)"])
}
- public var Notification_PassportValuePersonalDetails: String { return self._s[169]! }
- public var ProfilePhoto_SearchWeb: String { return self._s[170]! }
- public var Channel_AdminLog_MessagePreviousLink: String { return self._s[171]! }
- public var ChangePhoneNumberNumber_NewNumber: String { return self._s[172]! }
- public var ApplyLanguage_LanguageNotSupportedError: String { return self._s[173]! }
- public var TwoStepAuth_ChangePasswordDescription: String { return self._s[174]! }
- public var PhotoEditor_BlurToolLinear: String { return self._s[175]! }
- public var Contacts_PermissionsAllowInSettings: String { return self._s[176]! }
- public var Weekday_ShortMonday: String { return self._s[177]! }
- public var Cache_KeepMedia: String { return self._s[178]! }
- public var Passport_FieldIdentitySelfieHelp: String { return self._s[179]! }
+ public var Notification_PassportValuePersonalDetails: String { return self._s[170]! }
+ public var ProfilePhoto_SearchWeb: String { return self._s[171]! }
+ public var Channel_AdminLog_MessagePreviousLink: String { return self._s[172]! }
+ public var ChangePhoneNumberNumber_NewNumber: String { return self._s[173]! }
+ public var ApplyLanguage_LanguageNotSupportedError: String { return self._s[174]! }
+ public var TwoStepAuth_ChangePasswordDescription: String { return self._s[175]! }
+ public var PhotoEditor_BlurToolLinear: String { return self._s[176]! }
+ public var Contacts_PermissionsAllowInSettings: String { return self._s[177]! }
+ public var Weekday_ShortMonday: String { return self._s[178]! }
+ public var Cache_KeepMedia: String { return self._s[179]! }
+ public var Passport_FieldIdentitySelfieHelp: String { return self._s[180]! }
public func PUSH_PINNED_STICKER(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[180]!, self._r[180]!, [_1, _2])
+ return formatWithArgumentRanges(self._s[181]!, self._r[181]!, [_1, _2])
}
public func Chat_SlowmodeTooltip(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[181]!, self._r[181]!, [_0])
+ return formatWithArgumentRanges(self._s[182]!, self._r[182]!, [_0])
}
- public var Wallet_Receive_ShareUrlInfo: String { return self._s[182]! }
- public var Conversation_ClousStorageInfo_Description4: String { return self._s[183]! }
- public var Wallet_RestoreFailed_Title: String { return self._s[184]! }
- public var Passport_Language_ru: String { return self._s[185]! }
+ public var Wallet_Receive_ShareUrlInfo: String { return self._s[183]! }
+ public var Conversation_ClousStorageInfo_Description4: String { return self._s[184]! }
+ public var Wallet_RestoreFailed_Title: String { return self._s[185]! }
+ public var Passport_Language_ru: String { return self._s[186]! }
public func Notification_CreatedChatWithTitle(_ _0: String, _ _1: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[186]!, self._r[186]!, [_0, _1])
+ return formatWithArgumentRanges(self._s[187]!, self._r[187]!, [_0, _1])
}
- public var WallpaperPreview_PatternIntensity: String { return self._s[187]! }
- public var ChatList_EditFolder: String { return self._s[190]! }
- public var ChatList_AutoarchiveSuggestion_Title: String { return self._s[191]! }
- public var WebBrowser_InAppSafari: String { return self._s[192]! }
- public var TwoStepAuth_RecoveryUnavailable: String { return self._s[193]! }
- public var EnterPasscode_TouchId: String { return self._s[194]! }
- public var PhotoEditor_QualityVeryHigh: String { return self._s[197]! }
- public var Checkout_NewCard_SaveInfo: String { return self._s[199]! }
- public var Gif_NoGifsPlaceholder: String { return self._s[201]! }
+ public var WallpaperPreview_PatternIntensity: String { return self._s[188]! }
+ public var ChatList_EditFolder: String { return self._s[191]! }
+ public var ChatList_AutoarchiveSuggestion_Title: String { return self._s[192]! }
+ public var WebBrowser_InAppSafari: String { return self._s[193]! }
+ public var TwoStepAuth_RecoveryUnavailable: String { return self._s[194]! }
+ public var EnterPasscode_TouchId: String { return self._s[195]! }
+ public var PhotoEditor_QualityVeryHigh: String { return self._s[198]! }
+ public var Checkout_NewCard_SaveInfo: String { return self._s[200]! }
+ public var Gif_NoGifsPlaceholder: String { return self._s[202]! }
public func Notification_InvitedMultiple(_ _0: String, _ _1: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[203]!, self._r[203]!, [_0, _1])
+ return formatWithArgumentRanges(self._s[204]!, self._r[204]!, [_0, _1])
}
- public var ChatSettings_AutoDownloadEnabled: String { return self._s[204]! }
- public var NetworkUsageSettings_BytesSent: String { return self._s[205]! }
- public var Checkout_PasswordEntry_Pay: String { return self._s[206]! }
- public var AuthSessions_TerminateSession: String { return self._s[207]! }
- public var Message_File: String { return self._s[208]! }
- public var MediaPicker_VideoMuteDescription: String { return self._s[209]! }
- public var SocksProxySetup_ProxyStatusConnected: String { return self._s[210]! }
- public var TwoStepAuth_RecoveryCode: String { return self._s[211]! }
- public var EnterPasscode_EnterCurrentPasscode: String { return self._s[212]! }
+ public var ChatSettings_AutoDownloadEnabled: String { return self._s[205]! }
+ public var NetworkUsageSettings_BytesSent: String { return self._s[206]! }
+ public var Checkout_PasswordEntry_Pay: String { return self._s[207]! }
+ public var AuthSessions_TerminateSession: String { return self._s[208]! }
+ public var Message_File: String { return self._s[209]! }
+ public var MediaPicker_VideoMuteDescription: String { return self._s[210]! }
+ public var SocksProxySetup_ProxyStatusConnected: String { return self._s[211]! }
+ public var TwoStepAuth_RecoveryCode: String { return self._s[212]! }
+ public var EnterPasscode_EnterCurrentPasscode: String { return self._s[213]! }
public func TwoStepAuth_EnterPasswordHint(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[213]!, self._r[213]!, [_0])
+ return formatWithArgumentRanges(self._s[214]!, self._r[214]!, [_0])
}
- public var Conversation_Moderate_Report: String { return self._s[215]! }
- public var TwoStepAuth_EmailInvalid: String { return self._s[216]! }
- public var Passport_Language_ms: String { return self._s[217]! }
- public var Channel_Edit_AboutItem: String { return self._s[219]! }
- public var DialogList_SearchSectionGlobal: String { return self._s[223]! }
- public var AttachmentMenu_WebSearch: String { return self._s[224]! }
- public var ChatState_WaitingForNetwork: String { return self._s[225]! }
- public var Channel_BanUser_Title: String { return self._s[226]! }
- public var PasscodeSettings_TurnPasscodeOn: String { return self._s[227]! }
- public var WallpaperPreview_SwipeTopText: String { return self._s[228]! }
- public var ChatList_DeleteSavedMessagesConfirmationText: String { return self._s[229]! }
- public var ArchivedChats_IntroText2: String { return self._s[230]! }
- public var ChatSearch_SearchPlaceholder: String { return self._s[232]! }
- public var Conversation_OpenBotLinkTitle: String { return self._s[233]! }
- public var Passport_FieldAddressTranslationHelp: String { return self._s[234]! }
- public var NotificationsSound_Aurora: String { return self._s[235]! }
- public var Notification_Exceptions_DeleteAll: String { return self._s[236]! }
+ public var Conversation_Moderate_Report: String { return self._s[216]! }
+ public var TwoStepAuth_EmailInvalid: String { return self._s[217]! }
+ public var Passport_Language_ms: String { return self._s[218]! }
+ public var Channel_Edit_AboutItem: String { return self._s[220]! }
+ public var DialogList_SearchSectionGlobal: String { return self._s[224]! }
+ public var AttachmentMenu_WebSearch: String { return self._s[225]! }
+ public var ChatState_WaitingForNetwork: String { return self._s[226]! }
+ public var Channel_BanUser_Title: String { return self._s[227]! }
+ public var PasscodeSettings_TurnPasscodeOn: String { return self._s[228]! }
+ public var WallpaperPreview_SwipeTopText: String { return self._s[229]! }
+ public var ChatList_DeleteSavedMessagesConfirmationText: String { return self._s[230]! }
+ public var ArchivedChats_IntroText2: String { return self._s[231]! }
+ public var ChatSearch_SearchPlaceholder: String { return self._s[233]! }
+ public var Conversation_OpenBotLinkTitle: String { return self._s[234]! }
+ public var Passport_FieldAddressTranslationHelp: String { return self._s[235]! }
+ public var NotificationsSound_Aurora: String { return self._s[236]! }
+ public var Notification_Exceptions_DeleteAll: String { return self._s[237]! }
public func FileSize_GB(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[237]!, self._r[237]!, [_0])
+ return formatWithArgumentRanges(self._s[238]!, self._r[238]!, [_0])
}
- public var AuthSessions_LoggedInWithTelegram: String { return self._s[240]! }
+ public var AuthSessions_LoggedInWithTelegram: String { return self._s[241]! }
public func Privacy_GroupsAndChannels_InviteToGroupError(_ _0: String, _ _1: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[241]!, self._r[241]!, [_0, _1])
+ return formatWithArgumentRanges(self._s[242]!, self._r[242]!, [_0, _1])
}
- public var Passport_PasswordNext: String { return self._s[242]! }
- public var Bot_GroupStatusReadsHistory: String { return self._s[243]! }
- public var EmptyGroupInfo_Line2: String { return self._s[244]! }
+ public var Passport_PasswordNext: String { return self._s[243]! }
+ public var Bot_GroupStatusReadsHistory: String { return self._s[244]! }
+ public var EmptyGroupInfo_Line2: String { return self._s[245]! }
public func Channel_AdminLog_MessageTransferedNameUsername(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[245]!, self._r[245]!, [_1, _2])
+ return formatWithArgumentRanges(self._s[246]!, self._r[246]!, [_1, _2])
}
- public var VoiceOver_Chat_SeenByRecipients: String { return self._s[246]! }
- public var Settings_FAQ_Intro: String { return self._s[249]! }
- public var PrivacySettings_PasscodeAndTouchId: String { return self._s[251]! }
- public var FeaturedStickerPacks_Title: String { return self._s[252]! }
- public var TwoStepAuth_PasswordRemoveConfirmation: String { return self._s[254]! }
- public var Username_Title: String { return self._s[255]! }
+ public var VoiceOver_Chat_SeenByRecipients: String { return self._s[247]! }
+ public var Settings_FAQ_Intro: String { return self._s[250]! }
+ public var PrivacySettings_PasscodeAndTouchId: String { return self._s[252]! }
+ public var FeaturedStickerPacks_Title: String { return self._s[253]! }
+ public var TwoStepAuth_PasswordRemoveConfirmation: String { return self._s[255]! }
+ public var Username_Title: String { return self._s[256]! }
public func Message_StickerText(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[256]!, self._r[256]!, [_0])
+ return formatWithArgumentRanges(self._s[257]!, self._r[257]!, [_0])
}
- public var PeerInfo_PaneFiles: String { return self._s[257]! }
- public var PasscodeSettings_AlphanumericCode: String { return self._s[258]! }
- public var Localization_LanguageOther: String { return self._s[259]! }
- public var Stickers_SuggestStickers: String { return self._s[260]! }
+ public var PeerInfo_PaneFiles: String { return self._s[258]! }
+ public var PasscodeSettings_AlphanumericCode: String { return self._s[259]! }
+ public var Localization_LanguageOther: String { return self._s[260]! }
+ public var Stickers_SuggestStickers: String { return self._s[261]! }
public func Channel_AdminLog_MessageRemovedGroupUsername(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[261]!, self._r[261]!, [_0])
+ return formatWithArgumentRanges(self._s[262]!, self._r[262]!, [_0])
}
- public var NotificationSettings_ShowNotificationsFromAccountsSection: String { return self._s[262]! }
- public var Channel_AdminLogFilter_EventsAdmins: String { return self._s[263]! }
- public var Conversation_DefaultRestrictedStickers: String { return self._s[264]! }
+ public var NotificationSettings_ShowNotificationsFromAccountsSection: String { return self._s[263]! }
+ public var Channel_AdminLogFilter_EventsAdmins: String { return self._s[264]! }
+ public var Conversation_DefaultRestrictedStickers: String { return self._s[265]! }
public func Notification_PinnedDeletedMessage(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[265]!, self._r[265]!, [_0])
+ return formatWithArgumentRanges(self._s[266]!, self._r[266]!, [_0])
}
- public var Wallet_TransactionInfo_CopyAddress: String { return self._s[267]! }
- public var Group_UpgradeConfirmation: String { return self._s[269]! }
- public var DialogList_Unpin: String { return self._s[270]! }
- public var Passport_Identity_DateOfBirth: String { return self._s[272]! }
- public var Month_ShortOctober: String { return self._s[273]! }
- public var SettingsSearch_Synonyms_Privacy_Data_ContactsSync: String { return self._s[274]! }
- public var TwoFactorSetup_Done_Text: String { return self._s[275]! }
- public var Notification_CallCanceledShort: String { return self._s[276]! }
- public var Conversation_StopQuiz: String { return self._s[277]! }
- public var Passport_Phone_Help: String { return self._s[278]! }
- public var Passport_Language_az: String { return self._s[280]! }
- public var CreatePoll_TextPlaceholder: String { return self._s[282]! }
- public var VoiceOver_Chat_AnonymousPoll: String { return self._s[283]! }
- public var Passport_Identity_DocumentNumber: String { return self._s[284]! }
- public var PhotoEditor_CurvesRed: String { return self._s[286]! }
- public var PhoneNumberHelp_Alert: String { return self._s[288]! }
- public var Stats_GroupTopPostersTitle: String { return self._s[289]! }
- public var SocksProxySetup_Port: String { return self._s[290]! }
- public var Checkout_PayNone: String { return self._s[291]! }
- public var AutoDownloadSettings_WiFi: String { return self._s[292]! }
- public var GroupInfo_GroupType: String { return self._s[293]! }
- public var StickerSettings_ContextHide: String { return self._s[294]! }
- public var Passport_Address_OneOfTypeTemporaryRegistration: String { return self._s[295]! }
- public var Group_Setup_HistoryTitle: String { return self._s[297]! }
- public var Passport_Identity_FilesUploadNew: String { return self._s[298]! }
- public var PasscodeSettings_AutoLock: String { return self._s[299]! }
- public var Passport_Title: String { return self._s[300]! }
- public var VoiceOver_Chat_ContactPhoneNumber: String { return self._s[301]! }
- public var Channel_AdminLogFilter_EventsNewSubscribers: String { return self._s[302]! }
- public var GroupPermission_NoSendGifs: String { return self._s[303]! }
- public var PrivacySettings_PasscodeOn: String { return self._s[304]! }
+ public var Wallet_TransactionInfo_CopyAddress: String { return self._s[268]! }
+ public var Group_UpgradeConfirmation: String { return self._s[270]! }
+ public var DialogList_Unpin: String { return self._s[271]! }
+ public var Passport_Identity_DateOfBirth: String { return self._s[273]! }
+ public var Month_ShortOctober: String { return self._s[274]! }
+ public var SettingsSearch_Synonyms_Privacy_Data_ContactsSync: String { return self._s[275]! }
+ public var TwoFactorSetup_Done_Text: String { return self._s[276]! }
+ public var Notification_CallCanceledShort: String { return self._s[277]! }
+ public var Conversation_StopQuiz: String { return self._s[278]! }
+ public var Passport_Phone_Help: String { return self._s[279]! }
+ public var Passport_Language_az: String { return self._s[281]! }
+ public var CreatePoll_TextPlaceholder: String { return self._s[283]! }
+ public var VoiceOver_Chat_AnonymousPoll: String { return self._s[284]! }
+ public var Passport_Identity_DocumentNumber: String { return self._s[285]! }
+ public var PhotoEditor_CurvesRed: String { return self._s[287]! }
+ public var PhoneNumberHelp_Alert: String { return self._s[289]! }
+ public var Stats_GroupTopPostersTitle: String { return self._s[290]! }
+ public var SocksProxySetup_Port: String { return self._s[291]! }
+ public var Checkout_PayNone: String { return self._s[292]! }
+ public var AutoDownloadSettings_WiFi: String { return self._s[293]! }
+ public var GroupInfo_GroupType: String { return self._s[294]! }
+ public var StickerSettings_ContextHide: String { return self._s[295]! }
+ public var Passport_Address_OneOfTypeTemporaryRegistration: String { return self._s[296]! }
+ public var Group_Setup_HistoryTitle: String { return self._s[298]! }
+ public var Passport_Identity_FilesUploadNew: String { return self._s[299]! }
+ public var PasscodeSettings_AutoLock: String { return self._s[300]! }
+ public var Passport_Title: String { return self._s[301]! }
+ public var VoiceOver_Chat_ContactPhoneNumber: String { return self._s[302]! }
+ public var Channel_AdminLogFilter_EventsNewSubscribers: String { return self._s[303]! }
+ public var GroupPermission_NoSendGifs: String { return self._s[304]! }
+ public var PrivacySettings_PasscodeOn: String { return self._s[305]! }
public func Conversation_ScheduleMessage_SendTomorrow(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[305]!, self._r[305]!, [_0])
+ return formatWithArgumentRanges(self._s[306]!, self._r[306]!, [_0])
}
- public var ChatList_PeerTypeNonContact: String { return self._s[308]! }
- public var State_WaitingForNetwork: String { return self._s[309]! }
+ public var ChatList_PeerTypeNonContact: String { return self._s[309]! }
+ public var State_WaitingForNetwork: String { return self._s[310]! }
public func Notification_Invited(_ _0: String, _ _1: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[310]!, self._r[310]!, [_0, _1])
+ return formatWithArgumentRanges(self._s[311]!, self._r[311]!, [_0, _1])
}
- public var Calls_NotNow: String { return self._s[312]! }
+ public var Calls_NotNow: String { return self._s[313]! }
public func Channel_DiscussionGroup_HeaderSet(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[313]!, self._r[313]!, [_0])
+ return formatWithArgumentRanges(self._s[314]!, self._r[314]!, [_0])
}
- public var UserInfo_SendMessage: String { return self._s[314]! }
- public var PhotoEditor_SelectCoverFrame: String { return self._s[315]! }
- public var ChatList_AutoarchiveSuggestion_Text: String { return self._s[316]! }
- public var TwoStepAuth_PasswordSet: String { return self._s[317]! }
- public var Passport_DeleteDocument: String { return self._s[318]! }
- public var SocksProxySetup_AddProxyTitle: String { return self._s[319]! }
+ public var UserInfo_SendMessage: String { return self._s[315]! }
+ public var PhotoEditor_SelectCoverFrame: String { return self._s[316]! }
+ public var ChatList_AutoarchiveSuggestion_Text: String { return self._s[317]! }
+ public var TwoStepAuth_PasswordSet: String { return self._s[318]! }
+ public var Passport_DeleteDocument: String { return self._s[319]! }
+ public var SocksProxySetup_AddProxyTitle: String { return self._s[320]! }
public func PUSH_MESSAGE_VIDEO(_ _1: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[320]!, self._r[320]!, [_1])
+ return formatWithArgumentRanges(self._s[321]!, self._r[321]!, [_1])
}
- public var AuthSessions_AddedDeviceTitle: String { return self._s[321]! }
- public var GroupRemoved_Remove: String { return self._s[322]! }
- public var Passport_FieldIdentity: String { return self._s[323]! }
- public var Group_Setup_TypePrivateHelp: String { return self._s[324]! }
- public var Conversation_Processing: String { return self._s[327]! }
- public var Wallet_Settings_BackupWallet: String { return self._s[329]! }
- public var ChatListFolder_NameNonMuted: String { return self._s[330]! }
- public var ChatSettings_AutoPlayAnimations: String { return self._s[331]! }
- public var AuthSessions_LogOutApplicationsHelp: String { return self._s[334]! }
- public var Forward_ErrorPublicQuizDisabledInChannels: String { return self._s[335]! }
- public var Month_GenFebruary: String { return self._s[336]! }
- public var ChatListFilter_AddChatsTitle: String { return self._s[337]! }
- public var Wallet_Send_NetworkErrorTitle: String { return self._s[338]! }
- public var Stats_GroupTopPoster_History: String { return self._s[340]! }
+ public var AuthSessions_AddedDeviceTitle: String { return self._s[322]! }
+ public var GroupRemoved_Remove: String { return self._s[323]! }
+ public var Passport_FieldIdentity: String { return self._s[324]! }
+ public var Group_Setup_TypePrivateHelp: String { return self._s[325]! }
+ public var Conversation_Processing: String { return self._s[328]! }
+ public var Wallet_Settings_BackupWallet: String { return self._s[330]! }
+ public var ChatListFolder_NameNonMuted: String { return self._s[331]! }
+ public var ChatSettings_AutoPlayAnimations: String { return self._s[332]! }
+ public var AuthSessions_LogOutApplicationsHelp: String { return self._s[335]! }
+ public var Forward_ErrorPublicQuizDisabledInChannels: String { return self._s[336]! }
+ public var Month_GenFebruary: String { return self._s[337]! }
+ public var ChatListFilter_AddChatsTitle: String { return self._s[338]! }
+ public var Wallet_Send_NetworkErrorTitle: String { return self._s[339]! }
+ public var Stats_GroupTopPoster_History: String { return self._s[341]! }
public func Login_InvalidPhoneEmailBody(_ _1: String, _ _2: String, _ _3: String, _ _4: String, _ _5: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[341]!, self._r[341]!, [_1, _2, _3, _4, _5])
+ return formatWithArgumentRanges(self._s[342]!, self._r[342]!, [_1, _2, _3, _4, _5])
}
- public var Passport_Identity_TypeIdentityCard: String { return self._s[342]! }
- public var Wallet_Month_ShortJune: String { return self._s[344]! }
- public var AutoDownloadSettings_DataUsageMedium: String { return self._s[345]! }
- public var GroupInfo_AddParticipant: String { return self._s[346]! }
- public var KeyCommand_SendMessage: String { return self._s[347]! }
- public var VoiceOver_Chat_YourContact: String { return self._s[349]! }
- public var Map_LiveLocationShowAll: String { return self._s[350]! }
- public var WallpaperSearch_ColorOrange: String { return self._s[352]! }
- public var Appearance_AppIconDefaultX: String { return self._s[353]! }
- public var Checkout_Receipt_Title: String { return self._s[354]! }
- public var Group_OwnershipTransfer_ErrorPrivacyRestricted: String { return self._s[355]! }
- public var WallpaperPreview_PreviewTopText: String { return self._s[356]! }
- public var Message_Contact: String { return self._s[358]! }
- public var Call_StatusIncoming: String { return self._s[359]! }
- public var Wallet_TransactionInfo_StorageFeeInfo: String { return self._s[360]! }
+ public var Passport_Identity_TypeIdentityCard: String { return self._s[343]! }
+ public var Wallet_Month_ShortJune: String { return self._s[345]! }
+ public var AutoDownloadSettings_DataUsageMedium: String { return self._s[346]! }
+ public var GroupInfo_AddParticipant: String { return self._s[347]! }
+ public var KeyCommand_SendMessage: String { return self._s[348]! }
+ public var VoiceOver_Chat_YourContact: String { return self._s[350]! }
+ public var Map_LiveLocationShowAll: String { return self._s[351]! }
+ public var WallpaperSearch_ColorOrange: String { return self._s[353]! }
+ public var Appearance_AppIconDefaultX: String { return self._s[354]! }
+ public var Checkout_Receipt_Title: String { return self._s[355]! }
+ public var Group_OwnershipTransfer_ErrorPrivacyRestricted: String { return self._s[356]! }
+ public var WallpaperPreview_PreviewTopText: String { return self._s[357]! }
+ public var Message_Contact: String { return self._s[359]! }
+ public var Call_StatusIncoming: String { return self._s[360]! }
+ public var Wallet_TransactionInfo_StorageFeeInfo: String { return self._s[361]! }
public func Channel_AdminLog_MessageKickedName(_ _1: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[361]!, self._r[361]!, [_1])
+ return formatWithArgumentRanges(self._s[362]!, self._r[362]!, [_1])
}
public func PUSH_ENCRYPTED_MESSAGE(_ _1: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[363]!, self._r[363]!, [_1])
+ return formatWithArgumentRanges(self._s[364]!, self._r[364]!, [_1])
}
- public var VoiceOver_Media_PlaybackRate: String { return self._s[364]! }
- public var Passport_FieldIdentityDetailsHelp: String { return self._s[365]! }
- public var Conversation_ViewChannel: String { return self._s[366]! }
+ public var VoiceOver_Media_PlaybackRate: String { return self._s[365]! }
+ public var Passport_FieldIdentityDetailsHelp: String { return self._s[366]! }
+ public var Conversation_ViewChannel: String { return self._s[367]! }
public func Time_TodayAt(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[367]!, self._r[367]!, [_0])
+ return formatWithArgumentRanges(self._s[368]!, self._r[368]!, [_0])
}
- public var Theme_Colors_Accent: String { return self._s[368]! }
- public var Paint_Arrow: String { return self._s[369]! }
- public var Passport_Language_nl: String { return self._s[371]! }
- public var Camera_Retake: String { return self._s[372]! }
+ public var Theme_Colors_Accent: String { return self._s[369]! }
+ public var Paint_Arrow: String { return self._s[370]! }
+ public var Passport_Language_nl: String { return self._s[372]! }
+ public var Camera_Retake: String { return self._s[373]! }
public func UserInfo_BlockActionTitle(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[373]!, self._r[373]!, [_0])
+ return formatWithArgumentRanges(self._s[374]!, self._r[374]!, [_0])
}
- public var AuthSessions_LogOutApplications: String { return self._s[374]! }
- public var ApplyLanguage_ApplySuccess: String { return self._s[375]! }
- public var Tour_Title6: String { return self._s[376]! }
- public var Map_ChooseAPlace: String { return self._s[377]! }
- public var CallSettings_Never: String { return self._s[379]! }
+ public var AuthSessions_LogOutApplications: String { return self._s[375]! }
+ public var ApplyLanguage_ApplySuccess: String { return self._s[376]! }
+ public var Tour_Title6: String { return self._s[377]! }
+ public var Map_ChooseAPlace: String { return self._s[378]! }
+ public var CallSettings_Never: String { return self._s[380]! }
public func Notification_ChangedGroupPhoto(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[380]!, self._r[380]!, [_0])
+ return formatWithArgumentRanges(self._s[381]!, self._r[381]!, [_0])
}
- public var ChannelRemoved_RemoveInfo: String { return self._s[381]! }
+ public var ChannelRemoved_RemoveInfo: String { return self._s[382]! }
public func AutoDownloadSettings_PreloadVideoInfo(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[382]!, self._r[382]!, [_0])
+ return formatWithArgumentRanges(self._s[383]!, self._r[383]!, [_0])
}
- public var SettingsSearch_Synonyms_Notifications_MessageNotificationsExceptions: String { return self._s[383]! }
+ public var SettingsSearch_Synonyms_Notifications_MessageNotificationsExceptions: String { return self._s[384]! }
public func Conversation_ClearChatConfirmation(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[384]!, self._r[384]!, [_0])
+ return formatWithArgumentRanges(self._s[385]!, self._r[385]!, [_0])
}
- public var GroupInfo_InviteLink_Title: String { return self._s[385]! }
+ public var GroupInfo_InviteLink_Title: String { return self._s[386]! }
public func Channel_AdminLog_MessageUnkickedNameUsername(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[386]!, self._r[386]!, [_1, _2])
+ return formatWithArgumentRanges(self._s[387]!, self._r[387]!, [_1, _2])
}
- public var KeyCommand_ScrollUp: String { return self._s[387]! }
- public var ContactInfo_URLLabelHomepage: String { return self._s[388]! }
- public var Channel_OwnershipTransfer_ChangeOwner: String { return self._s[389]! }
+ public var KeyCommand_ScrollUp: String { return self._s[388]! }
+ public var ContactInfo_URLLabelHomepage: String { return self._s[389]! }
+ public var Channel_OwnershipTransfer_ChangeOwner: String { return self._s[390]! }
public func Channel_AdminLog_DisabledSlowmode(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[390]!, self._r[390]!, [_0])
+ return formatWithArgumentRanges(self._s[391]!, self._r[391]!, [_0])
}
- public var TwoFactorSetup_Done_Title: String { return self._s[391]! }
+ public var TwoFactorSetup_Done_Title: String { return self._s[392]! }
public func Conversation_EncryptedPlaceholderTitleOutgoing(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[392]!, self._r[392]!, [_0])
+ return formatWithArgumentRanges(self._s[393]!, self._r[393]!, [_0])
}
- public var CallFeedback_ReasonDistortedSpeech: String { return self._s[393]! }
- public var Watch_LastSeen_WithinAWeek: String { return self._s[394]! }
- public var ContactList_Context_SendMessage: String { return self._s[396]! }
- public var Weekday_Tuesday: String { return self._s[397]! }
- public var Wallet_Created_Title: String { return self._s[399]! }
- public var ScheduledMessages_Delete: String { return self._s[400]! }
- public var UserInfo_StartSecretChat: String { return self._s[401]! }
- public var Passport_Identity_FilesTitle: String { return self._s[402]! }
- public var Permissions_NotificationsAllow_v0: String { return self._s[403]! }
- public var DialogList_DeleteConversationConfirmation: String { return self._s[405]! }
- public var ChatList_UndoArchiveRevealedTitle: String { return self._s[406]! }
+ public var CallFeedback_ReasonDistortedSpeech: String { return self._s[394]! }
+ public var Watch_LastSeen_WithinAWeek: String { return self._s[395]! }
+ public var ContactList_Context_SendMessage: String { return self._s[397]! }
+ public var Weekday_Tuesday: String { return self._s[398]! }
+ public var Wallet_Created_Title: String { return self._s[400]! }
+ public var ScheduledMessages_Delete: String { return self._s[401]! }
+ public var UserInfo_StartSecretChat: String { return self._s[402]! }
+ public var Passport_Identity_FilesTitle: String { return self._s[403]! }
+ public var Permissions_NotificationsAllow_v0: String { return self._s[404]! }
+ public var DialogList_DeleteConversationConfirmation: String { return self._s[406]! }
+ public var ChatList_UndoArchiveRevealedTitle: String { return self._s[407]! }
public func Wallet_Configuration_ApplyErrorTextURLUnreachable(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[407]!, self._r[407]!, [_0])
+ return formatWithArgumentRanges(self._s[408]!, self._r[408]!, [_0])
}
- public var AuthSessions_Sessions: String { return self._s[408]! }
- public var Conversation_PeerNearbyText: String { return self._s[409]! }
+ public var AuthSessions_Sessions: String { return self._s[409]! }
+ public var Conversation_PeerNearbyText: String { return self._s[410]! }
public func Settings_KeepPhoneNumber(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[412]!, self._r[412]!, [_0])
+ return formatWithArgumentRanges(self._s[413]!, self._r[413]!, [_0])
}
- public var TwoStepAuth_RecoveryEmailChangeDescription: String { return self._s[413]! }
- public var Call_StatusWaiting: String { return self._s[414]! }
- public var CreateGroup_SoftUserLimitAlert: String { return self._s[415]! }
- public var FastTwoStepSetup_HintHelp: String { return self._s[416]! }
- public var WallpaperPreview_CustomColorBottomText: String { return self._s[417]! }
- public var EditTheme_Expand_Preview_OutgoingText: String { return self._s[418]! }
- public var LogoutOptions_AddAccountText: String { return self._s[419]! }
- public var PasscodeSettings_6DigitCode: String { return self._s[420]! }
- public var Settings_LogoutConfirmationText: String { return self._s[421]! }
- public var ProfilePhoto_OpenGallery: String { return self._s[422]! }
- public var Passport_Identity_TypePassport: String { return self._s[424]! }
- public var Map_Work: String { return self._s[427]! }
+ public var TwoStepAuth_RecoveryEmailChangeDescription: String { return self._s[414]! }
+ public var Call_StatusWaiting: String { return self._s[415]! }
+ public var CreateGroup_SoftUserLimitAlert: String { return self._s[416]! }
+ public var FastTwoStepSetup_HintHelp: String { return self._s[417]! }
+ public var WallpaperPreview_CustomColorBottomText: String { return self._s[418]! }
+ public var EditTheme_Expand_Preview_OutgoingText: String { return self._s[419]! }
+ public var LogoutOptions_AddAccountText: String { return self._s[420]! }
+ public var PasscodeSettings_6DigitCode: String { return self._s[421]! }
+ public var Settings_LogoutConfirmationText: String { return self._s[422]! }
+ public var ProfilePhoto_OpenGallery: String { return self._s[423]! }
+ public var Passport_Identity_TypePassport: String { return self._s[425]! }
+ public var Map_Work: String { return self._s[428]! }
public func PUSH_MESSAGE_VIDEOS(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[428]!, self._r[428]!, [_1, _2])
+ return formatWithArgumentRanges(self._s[429]!, self._r[429]!, [_1, _2])
}
- public var SocksProxySetup_SaveProxy: String { return self._s[429]! }
- public var AccessDenied_SaveMedia: String { return self._s[430]! }
- public var Checkout_ErrorInvoiceAlreadyPaid: String { return self._s[432]! }
- public var CreatePoll_MultipleChoice: String { return self._s[433]! }
- public var Settings_Title: String { return self._s[435]! }
- public var VoiceOver_Chat_RecordModeVideoMessageInfo: String { return self._s[436]! }
- public var Contacts_InviteSearchLabel: String { return self._s[438]! }
- public var PrivacySettings_WebSessions: String { return self._s[439]! }
- public var ConvertToSupergroup_Title: String { return self._s[440]! }
+ public var SocksProxySetup_SaveProxy: String { return self._s[430]! }
+ public var AccessDenied_SaveMedia: String { return self._s[431]! }
+ public var Checkout_ErrorInvoiceAlreadyPaid: String { return self._s[433]! }
+ public var CreatePoll_MultipleChoice: String { return self._s[434]! }
+ public var Settings_Title: String { return self._s[436]! }
+ public var VoiceOver_Chat_RecordModeVideoMessageInfo: String { return self._s[437]! }
+ public var Contacts_InviteSearchLabel: String { return self._s[439]! }
+ public var PrivacySettings_WebSessions: String { return self._s[440]! }
+ public var ConvertToSupergroup_Title: String { return self._s[441]! }
public func Channel_AdminLog_CaptionEdited(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[441]!, self._r[441]!, [_0])
+ return formatWithArgumentRanges(self._s[442]!, self._r[442]!, [_0])
}
- public var TwoFactorSetup_Hint_Text: String { return self._s[442]! }
- public var InfoPlist_NSSiriUsageDescription: String { return self._s[443]! }
+ public var TwoFactorSetup_Hint_Text: String { return self._s[443]! }
+ public var InfoPlist_NSSiriUsageDescription: String { return self._s[444]! }
public func PUSH_MESSAGE_CHANNEL_MESSAGE_GAME_SCORE(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[444]!, self._r[444]!, [_1, _2, _3])
+ return formatWithArgumentRanges(self._s[445]!, self._r[445]!, [_1, _2, _3])
}
- public var ChatSettings_AutomaticPhotoDownload: String { return self._s[445]! }
- public var UserInfo_BotHelp: String { return self._s[446]! }
- public var PrivacySettings_LastSeenEverybody: String { return self._s[447]! }
- public var Checkout_Name: String { return self._s[448]! }
- public var AutoDownloadSettings_DataUsage: String { return self._s[449]! }
- public var Channel_BanUser_BlockFor: String { return self._s[450]! }
- public var Checkout_ShippingAddress: String { return self._s[451]! }
- public var AutoDownloadSettings_MaxVideoSize: String { return self._s[452]! }
- public var Privacy_PaymentsClearInfoDoneHelp: String { return self._s[453]! }
- public var Privacy_Forwards: String { return self._s[454]! }
- public var Channel_BanUser_PermissionSendPolls: String { return self._s[455]! }
- public var Appearance_ThemeCarouselNewNight: String { return self._s[456]! }
+ public var ChatSettings_AutomaticPhotoDownload: String { return self._s[446]! }
+ public var UserInfo_BotHelp: String { return self._s[447]! }
+ public var PrivacySettings_LastSeenEverybody: String { return self._s[448]! }
+ public var Checkout_Name: String { return self._s[449]! }
+ public var AutoDownloadSettings_DataUsage: String { return self._s[450]! }
+ public var Channel_BanUser_BlockFor: String { return self._s[451]! }
+ public var Checkout_ShippingAddress: String { return self._s[452]! }
+ public var AutoDownloadSettings_MaxVideoSize: String { return self._s[453]! }
+ public var Privacy_PaymentsClearInfoDoneHelp: String { return self._s[454]! }
+ public var Privacy_Forwards: String { return self._s[455]! }
+ public var Channel_BanUser_PermissionSendPolls: String { return self._s[456]! }
+ public var Appearance_ThemeCarouselNewNight: String { return self._s[457]! }
public func SecretVideo_NotViewedYet(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[459]!, self._r[459]!, [_0])
+ return formatWithArgumentRanges(self._s[460]!, self._r[460]!, [_0])
}
- public var Contacts_SortedByName: String { return self._s[460]! }
- public var Group_OwnershipTransfer_Title: String { return self._s[461]! }
- public var PeerInfo_BioExpand: String { return self._s[463]! }
- public var VoiceOver_Chat_OpenHint: String { return self._s[464]! }
- public var Group_LeaveGroup: String { return self._s[465]! }
- public var Settings_UsernameEmpty: String { return self._s[466]! }
+ public var Contacts_SortedByName: String { return self._s[461]! }
+ public var Group_OwnershipTransfer_Title: String { return self._s[462]! }
+ public var PeerInfo_BioExpand: String { return self._s[464]! }
+ public var VoiceOver_Chat_OpenHint: String { return self._s[465]! }
+ public var Group_LeaveGroup: String { return self._s[466]! }
+ public var Settings_UsernameEmpty: String { return self._s[467]! }
public func Notification_PinnedPollMessage(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[467]!, self._r[467]!, [_0])
+ return formatWithArgumentRanges(self._s[468]!, self._r[468]!, [_0])
}
public func TwoStepAuth_ConfirmEmailDescription(_ _1: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[468]!, self._r[468]!, [_1])
+ return formatWithArgumentRanges(self._s[469]!, self._r[469]!, [_1])
}
public func Channel_OwnershipTransfer_DescriptionInfo(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[469]!, self._r[469]!, [_1, _2])
+ return formatWithArgumentRanges(self._s[470]!, self._r[470]!, [_1, _2])
}
- public var Message_ImageExpired: String { return self._s[470]! }
- public var TwoStepAuth_RecoveryFailed: String { return self._s[472]! }
- public var EditTheme_Edit_Preview_OutgoingText: String { return self._s[473]! }
- public var UserInfo_AddToExisting: String { return self._s[474]! }
- public var TwoStepAuth_EnabledSuccess: String { return self._s[475]! }
- public var Wallet_Send_SyncInProgress: String { return self._s[476]! }
- public var ChatListFolderSettings_RecommendedFoldersSection: String { return self._s[477]! }
- public var ChatListFolder_IncludeSectionInfo: String { return self._s[478]! }
- public var SettingsSearch_Synonyms_Appearance_ChatBackground_SetColor: String { return self._s[479]! }
+ public var Message_ImageExpired: String { return self._s[471]! }
+ public var TwoStepAuth_RecoveryFailed: String { return self._s[473]! }
+ public var EditTheme_Edit_Preview_OutgoingText: String { return self._s[474]! }
+ public var UserInfo_AddToExisting: String { return self._s[475]! }
+ public var TwoStepAuth_EnabledSuccess: String { return self._s[476]! }
+ public var Wallet_Send_SyncInProgress: String { return self._s[477]! }
+ public var ChatListFolderSettings_RecommendedFoldersSection: String { return self._s[478]! }
+ public var ChatListFolder_IncludeSectionInfo: String { return self._s[479]! }
+ public var SettingsSearch_Synonyms_Appearance_ChatBackground_SetColor: String { return self._s[480]! }
public func PUSH_CHANNEL_MESSAGE(_ _1: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[480]!, self._r[480]!, [_1])
+ return formatWithArgumentRanges(self._s[481]!, self._r[481]!, [_1])
}
- public var Notifications_GroupNotificationsAlert: String { return self._s[481]! }
- public var Passport_Language_km: String { return self._s[482]! }
- public var SocksProxySetup_AdNoticeHelp: String { return self._s[484]! }
- public var VoiceOver_Media_PlaybackPlay: String { return self._s[485]! }
- public var Notification_CallMissedShort: String { return self._s[486]! }
- public var Wallet_Info_YourBalance: String { return self._s[487]! }
- public var ReportPeer_ReasonOther_Send: String { return self._s[489]! }
- public var Watch_Compose_Send: String { return self._s[490]! }
- public var Passport_Identity_TypeInternalPassportUploadScan: String { return self._s[493]! }
- public var TwoFactorSetup_Email_Action: String { return self._s[494]! }
- public var Conversation_HoldForVideo: String { return self._s[495]! }
- public var Wallet_Configuration_ApplyErrorTextURLInvalidData: String { return self._s[496]! }
- public var AuthSessions_OtherDevices: String { return self._s[497]! }
- public var Wallet_TransactionInfo_CommentHeader: String { return self._s[498]! }
- public var CheckoutInfo_ErrorCityInvalid: String { return self._s[500]! }
- public var Appearance_AutoNightThemeDisabled: String { return self._s[502]! }
- public var Channel_LinkItem: String { return self._s[503]! }
+ public var Notifications_GroupNotificationsAlert: String { return self._s[482]! }
+ public var Passport_Language_km: String { return self._s[483]! }
+ public var SocksProxySetup_AdNoticeHelp: String { return self._s[485]! }
+ public var VoiceOver_Media_PlaybackPlay: String { return self._s[486]! }
+ public var Notification_CallMissedShort: String { return self._s[487]! }
+ public var Wallet_Info_YourBalance: String { return self._s[488]! }
+ public var ReportPeer_ReasonOther_Send: String { return self._s[490]! }
+ public var Watch_Compose_Send: String { return self._s[491]! }
+ public var Passport_Identity_TypeInternalPassportUploadScan: String { return self._s[494]! }
+ public var TwoFactorSetup_Email_Action: String { return self._s[495]! }
+ public var Conversation_HoldForVideo: String { return self._s[496]! }
+ public var Wallet_Configuration_ApplyErrorTextURLInvalidData: String { return self._s[497]! }
+ public var AuthSessions_OtherDevices: String { return self._s[498]! }
+ public var Wallet_TransactionInfo_CommentHeader: String { return self._s[499]! }
+ public var CheckoutInfo_ErrorCityInvalid: String { return self._s[501]! }
+ public var Appearance_AutoNightThemeDisabled: String { return self._s[503]! }
+ public var Channel_LinkItem: String { return self._s[504]! }
public func PrivacySettings_LastSeenContactsMinusPlus(_ _0: String, _ _1: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[504]!, self._r[504]!, [_0, _1])
+ return formatWithArgumentRanges(self._s[505]!, self._r[505]!, [_0, _1])
}
public func Passport_Identity_NativeNameTitle(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[507]!, self._r[507]!, [_0])
+ return formatWithArgumentRanges(self._s[508]!, self._r[508]!, [_0])
}
- public var VoiceOver_Recording_StopAndPreview: String { return self._s[508]! }
- public var Passport_Language_dv: String { return self._s[509]! }
- public var Undo_LeftChannel: String { return self._s[510]! }
- public var Notifications_ExceptionsMuted: String { return self._s[511]! }
- public var ChatList_UnhideAction: String { return self._s[512]! }
- public var Conversation_ContextMenuShare: String { return self._s[514]! }
- public var Conversation_ContextMenuStickerPackInfo: String { return self._s[515]! }
- public var ShareFileTip_Title: String { return self._s[516]! }
- public var NotificationsSound_Chord: String { return self._s[517]! }
- public var Wallet_TransactionInfo_OtherFeeHeader: String { return self._s[518]! }
+ public var VoiceOver_Recording_StopAndPreview: String { return self._s[509]! }
+ public var Passport_Language_dv: String { return self._s[510]! }
+ public var Undo_LeftChannel: String { return self._s[511]! }
+ public var Notifications_ExceptionsMuted: String { return self._s[512]! }
+ public var ChatList_UnhideAction: String { return self._s[513]! }
+ public var Conversation_ContextMenuShare: String { return self._s[515]! }
+ public var Conversation_ContextMenuStickerPackInfo: String { return self._s[516]! }
+ public var ShareFileTip_Title: String { return self._s[517]! }
+ public var NotificationsSound_Chord: String { return self._s[518]! }
+ public var Wallet_TransactionInfo_OtherFeeHeader: String { return self._s[519]! }
public func PUSH_CHAT_RETURNED(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[519]!, self._r[519]!, [_1, _2])
+ return formatWithArgumentRanges(self._s[520]!, self._r[520]!, [_1, _2])
}
- public var PeerInfo_ButtonVideoCall: String { return self._s[520]! }
- public var Passport_Address_EditTemporaryRegistration: String { return self._s[521]! }
+ public var PeerInfo_ButtonVideoCall: String { return self._s[521]! }
+ public var Passport_Address_EditTemporaryRegistration: String { return self._s[522]! }
public func Notification_Joined(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[522]!, self._r[522]!, [_0])
+ return formatWithArgumentRanges(self._s[523]!, self._r[523]!, [_0])
}
public func Wallet_Time_PreciseDate_m3(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[523]!, self._r[523]!, [_1, _2, _3])
+ return formatWithArgumentRanges(self._s[524]!, self._r[524]!, [_1, _2, _3])
}
- public var Wallet_Settings_ConfigurationInfo: String { return self._s[524]! }
- public var Wallpaper_ErrorNotFound: String { return self._s[525]! }
- public var Notification_CallOutgoingShort: String { return self._s[527]! }
- public var Wallet_WordImport_IncorrectText: String { return self._s[528]! }
+ public var Wallet_Settings_ConfigurationInfo: String { return self._s[525]! }
+ public var Wallpaper_ErrorNotFound: String { return self._s[526]! }
+ public var Notification_CallOutgoingShort: String { return self._s[528]! }
+ public var Wallet_WordImport_IncorrectText: String { return self._s[529]! }
public func Watch_Time_ShortFullAt(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[529]!, self._r[529]!, [_1, _2])
+ return formatWithArgumentRanges(self._s[530]!, self._r[530]!, [_1, _2])
}
- public var Passport_Address_TypeUtilityBill: String { return self._s[530]! }
- public var Privacy_Forwards_LinkIfAllowed: String { return self._s[531]! }
- public var ReportPeer_Report: String { return self._s[532]! }
- public var SettingsSearch_Synonyms_Proxy_Title: String { return self._s[533]! }
- public var GroupInfo_DeactivatedStatus: String { return self._s[534]! }
+ public var Passport_Address_TypeUtilityBill: String { return self._s[531]! }
+ public var Privacy_Forwards_LinkIfAllowed: String { return self._s[532]! }
+ public var ReportPeer_Report: String { return self._s[533]! }
+ public var SettingsSearch_Synonyms_Proxy_Title: String { return self._s[534]! }
+ public var GroupInfo_DeactivatedStatus: String { return self._s[535]! }
public func VoiceOver_Chat_MusicTitle(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[535]!, self._r[535]!, [_1, _2])
+ return formatWithArgumentRanges(self._s[536]!, self._r[536]!, [_1, _2])
}
- public var StickerPack_Send: String { return self._s[536]! }
- public var Login_CodeSentInternal: String { return self._s[537]! }
- public var Wallet_Month_GenJanuary: String { return self._s[538]! }
- public var GroupInfo_InviteLink_LinkSection: String { return self._s[540]! }
+ public var StickerPack_Send: String { return self._s[537]! }
+ public var Login_CodeSentInternal: String { return self._s[538]! }
+ public var Wallet_Month_GenJanuary: String { return self._s[539]! }
+ public var GroupInfo_InviteLink_LinkSection: String { return self._s[541]! }
public func Channel_AdminLog_MessageDeleted(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[541]!, self._r[541]!, [_0])
+ return formatWithArgumentRanges(self._s[542]!, self._r[542]!, [_0])
}
public func Conversation_EncryptionWaiting(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[543]!, self._r[543]!, [_0])
+ return formatWithArgumentRanges(self._s[544]!, self._r[544]!, [_0])
}
- public var Channel_BanUser_PermissionSendStickersAndGifs: String { return self._s[544]! }
+ public var Channel_BanUser_PermissionSendStickersAndGifs: String { return self._s[545]! }
public func PUSH_PINNED_GAME(_ _1: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[545]!, self._r[545]!, [_1])
+ return formatWithArgumentRanges(self._s[546]!, self._r[546]!, [_1])
}
- public var ReportPeer_ReasonViolence: String { return self._s[547]! }
- public var Appearance_ShareThemeColor: String { return self._s[548]! }
- public var Map_Locating: String { return self._s[549]! }
+ public var ReportPeer_ReasonViolence: String { return self._s[548]! }
+ public var Appearance_ShareThemeColor: String { return self._s[549]! }
+ public var Map_Locating: String { return self._s[550]! }
public func VoiceOver_Chat_VideoFrom(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[550]!, self._r[550]!, [_0])
+ return formatWithArgumentRanges(self._s[551]!, self._r[551]!, [_0])
}
public func PUSH_ALBUM(_ _1: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[551]!, self._r[551]!, [_1])
+ return formatWithArgumentRanges(self._s[552]!, self._r[552]!, [_1])
}
- public var ChatListFolderSettings_FoldersSection: String { return self._s[552]! }
- public var AutoDownloadSettings_GroupChats: String { return self._s[554]! }
- public var CheckoutInfo_SaveInfo: String { return self._s[555]! }
- public var ChatList_ChatTypesSection: String { return self._s[556]! }
- public var SharedMedia_EmptyLinksText: String { return self._s[558]! }
- public var Passport_Address_CityPlaceholder: String { return self._s[559]! }
- public var CheckoutInfo_ErrorStateInvalid: String { return self._s[560]! }
- public var Privacy_ProfilePhoto_CustomHelp: String { return self._s[561]! }
- public var Wallet_Send_OwnAddressAlertTitle: String { return self._s[563]! }
- public var Channel_AdminLog_CanAddAdmins: String { return self._s[564]! }
+ public var ChatListFolderSettings_FoldersSection: String { return self._s[553]! }
+ public var AutoDownloadSettings_GroupChats: String { return self._s[555]! }
+ public var CheckoutInfo_SaveInfo: String { return self._s[556]! }
+ public var ChatList_ChatTypesSection: String { return self._s[557]! }
+ public var SharedMedia_EmptyLinksText: String { return self._s[559]! }
+ public var Passport_Address_CityPlaceholder: String { return self._s[560]! }
+ public var CheckoutInfo_ErrorStateInvalid: String { return self._s[561]! }
+ public var Privacy_ProfilePhoto_CustomHelp: String { return self._s[562]! }
+ public var Wallet_Send_OwnAddressAlertTitle: String { return self._s[564]! }
+ public var Channel_AdminLog_CanAddAdmins: String { return self._s[565]! }
public func PUSH_CHANNEL_MESSAGE_FWD(_ _1: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[565]!, self._r[565]!, [_1])
+ return formatWithArgumentRanges(self._s[566]!, self._r[566]!, [_1])
}
public func Time_MonthOfYear_m8(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[566]!, self._r[566]!, [_0])
+ return formatWithArgumentRanges(self._s[567]!, self._r[567]!, [_0])
}
- public var InfoPlist_NSLocationWhenInUseUsageDescription: String { return self._s[567]! }
- public var GroupInfo_InviteLink_RevokeAlert_Success: String { return self._s[568]! }
- public var ChangePhoneNumberCode_Code: String { return self._s[569]! }
- public var Appearance_CreateTheme: String { return self._s[570]! }
+ public var InfoPlist_NSLocationWhenInUseUsageDescription: String { return self._s[568]! }
+ public var GroupInfo_InviteLink_RevokeAlert_Success: String { return self._s[569]! }
+ public var ChangePhoneNumberCode_Code: String { return self._s[570]! }
+ public var Appearance_CreateTheme: String { return self._s[571]! }
public func UserInfo_NotificationsDefaultSound(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[571]!, self._r[571]!, [_0])
+ return formatWithArgumentRanges(self._s[572]!, self._r[572]!, [_0])
}
- public var TwoStepAuth_SetupEmail: String { return self._s[572]! }
- public var HashtagSearch_AllChats: String { return self._s[573]! }
- public var MediaPlayer_UnknownTrack: String { return self._s[574]! }
- public var SettingsSearch_Synonyms_Data_AutoDownloadUsingCellular: String { return self._s[576]! }
+ public var TwoStepAuth_SetupEmail: String { return self._s[573]! }
+ public var HashtagSearch_AllChats: String { return self._s[574]! }
+ public var MediaPlayer_UnknownTrack: String { return self._s[575]! }
+ public var SettingsSearch_Synonyms_Data_AutoDownloadUsingCellular: String { return self._s[577]! }
public func ChatList_DeleteForEveryone(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[577]!, self._r[577]!, [_0])
+ return formatWithArgumentRanges(self._s[578]!, self._r[578]!, [_0])
}
- public var Chat_Gifs_SavedSectionHeader: String { return self._s[578]! }
- public var PhotoEditor_QualityHigh: String { return self._s[580]! }
+ public var Chat_Gifs_SavedSectionHeader: String { return self._s[579]! }
+ public var PhotoEditor_QualityHigh: String { return self._s[581]! }
public func Passport_Phone_UseTelegramNumber(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[581]!, self._r[581]!, [_0])
+ return formatWithArgumentRanges(self._s[582]!, self._r[582]!, [_0])
}
- public var ApplyLanguage_ApplyLanguageAction: String { return self._s[582]! }
- public var SettingsSearch_Synonyms_Notifications_ChannelNotificationsPreview: String { return self._s[583]! }
- public var Message_LiveLocation: String { return self._s[584]! }
- public var Cache_LowDiskSpaceText: String { return self._s[585]! }
- public var Wallet_Receive_ShareAddress: String { return self._s[586]! }
- public var EditTheme_ErrorLinkTaken: String { return self._s[588]! }
- public var Conversation_SendMessage: String { return self._s[589]! }
- public var AuthSessions_EmptyTitle: String { return self._s[590]! }
- public var Privacy_PhoneNumber: String { return self._s[591]! }
- public var PeopleNearby_CreateGroup: String { return self._s[592]! }
- public var Stats_SharesPerPost: String { return self._s[594]! }
- public var CallSettings_UseLessData: String { return self._s[595]! }
- public var NetworkUsageSettings_MediaDocumentDataSection: String { return self._s[596]! }
- public var Stickers_AddToFavorites: String { return self._s[597]! }
- public var Wallet_WordImport_Title: String { return self._s[598]! }
- public var PhotoEditor_QualityLow: String { return self._s[599]! }
- public var Watch_UserInfo_Unblock: String { return self._s[600]! }
- public var Settings_Logout: String { return self._s[601]! }
+ public var ApplyLanguage_ApplyLanguageAction: String { return self._s[583]! }
+ public var SettingsSearch_Synonyms_Notifications_ChannelNotificationsPreview: String { return self._s[584]! }
+ public var Message_LiveLocation: String { return self._s[585]! }
+ public var Cache_LowDiskSpaceText: String { return self._s[586]! }
+ public var Wallet_Receive_ShareAddress: String { return self._s[587]! }
+ public var EditTheme_ErrorLinkTaken: String { return self._s[589]! }
+ public var Conversation_SendMessage: String { return self._s[590]! }
+ public var AuthSessions_EmptyTitle: String { return self._s[591]! }
+ public var Privacy_PhoneNumber: String { return self._s[592]! }
+ public var PeopleNearby_CreateGroup: String { return self._s[593]! }
+ public var Stats_SharesPerPost: String { return self._s[595]! }
+ public var CallSettings_UseLessData: String { return self._s[596]! }
+ public var NetworkUsageSettings_MediaDocumentDataSection: String { return self._s[597]! }
+ public var Stickers_AddToFavorites: String { return self._s[598]! }
+ public var Wallet_WordImport_Title: String { return self._s[599]! }
+ public var PhotoEditor_QualityLow: String { return self._s[600]! }
+ public var Watch_UserInfo_Unblock: String { return self._s[601]! }
+ public var Settings_Logout: String { return self._s[602]! }
public func PUSH_MESSAGE_ROUND(_ _1: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[602]!, self._r[602]!, [_1])
+ return formatWithArgumentRanges(self._s[603]!, self._r[603]!, [_1])
}
- public var ContactInfo_PhoneLabelWork: String { return self._s[603]! }
- public var ChannelInfo_Stats: String { return self._s[604]! }
- public var TextFormat_Link: String { return self._s[605]! }
+ public var ContactInfo_PhoneLabelWork: String { return self._s[604]! }
+ public var ChannelInfo_Stats: String { return self._s[605]! }
+ public var TextFormat_Link: String { return self._s[606]! }
public func Date_ChatDateHeader(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[606]!, self._r[606]!, [_1, _2])
+ return formatWithArgumentRanges(self._s[607]!, self._r[607]!, [_1, _2])
}
- public var Paint_Framed: String { return self._s[607]! }
- public var Wallet_TransactionInfo_Title: String { return self._s[608]! }
+ public var Paint_Framed: String { return self._s[608]! }
+ public var Wallet_TransactionInfo_Title: String { return self._s[609]! }
public func Message_ForwardedMessage(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[609]!, self._r[609]!, [_0])
+ return formatWithArgumentRanges(self._s[610]!, self._r[610]!, [_0])
}
- public var Watch_Notification_Joined: String { return self._s[610]! }
- public var Group_Setup_TypePublicHelp: String { return self._s[611]! }
- public var Passport_Scans_UploadNew: String { return self._s[612]! }
- public var Checkout_LiabilityAlertTitle: String { return self._s[613]! }
- public var DialogList_Title: String { return self._s[616]! }
- public var NotificationSettings_ContactJoined: String { return self._s[617]! }
- public var GroupInfo_LabelAdmin: String { return self._s[618]! }
- public var KeyCommand_ChatInfo: String { return self._s[619]! }
- public var Conversation_EditingCaptionPanelTitle: String { return self._s[620]! }
- public var Call_ReportIncludeLog: String { return self._s[621]! }
+ public var Watch_Notification_Joined: String { return self._s[611]! }
+ public var Group_Setup_TypePublicHelp: String { return self._s[612]! }
+ public var Passport_Scans_UploadNew: String { return self._s[613]! }
+ public var Checkout_LiabilityAlertTitle: String { return self._s[614]! }
+ public var DialogList_Title: String { return self._s[617]! }
+ public var NotificationSettings_ContactJoined: String { return self._s[618]! }
+ public var GroupInfo_LabelAdmin: String { return self._s[619]! }
+ public var KeyCommand_ChatInfo: String { return self._s[620]! }
+ public var Conversation_EditingCaptionPanelTitle: String { return self._s[621]! }
+ public var Call_ReportIncludeLog: String { return self._s[622]! }
public func Notifications_ExceptionsChangeSound(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[624]!, self._r[624]!, [_0])
+ return formatWithArgumentRanges(self._s[625]!, self._r[625]!, [_0])
}
- public var Stats_Followers: String { return self._s[625]! }
- public var Stats_GroupLanguagesTitle: String { return self._s[626]! }
- public var Channel_AdminLog_InfoPanelChannelAlertText: String { return self._s[627]! }
- public var ChatAdmins_AllMembersAreAdmins: String { return self._s[628]! }
- public var LocalGroup_IrrelevantWarning: String { return self._s[629]! }
- public var Conversation_DefaultRestrictedInline: String { return self._s[630]! }
- public var Message_Sticker: String { return self._s[631]! }
- public var LastSeen_JustNow: String { return self._s[633]! }
- public var Passport_Email_EmailPlaceholder: String { return self._s[635]! }
- public var SettingsSearch_Synonyms_AppLanguage: String { return self._s[636]! }
- public var Channel_AdminLogFilter_EventsEditedMessages: String { return self._s[638]! }
- public var Channel_EditAdmin_PermissionsHeader: String { return self._s[639]! }
- public var TwoStepAuth_Email: String { return self._s[640]! }
- public var SettingsSearch_Synonyms_Notifications_ChannelNotificationsSound: String { return self._s[641]! }
- public var PhotoEditor_BlurToolOff: String { return self._s[642]! }
- public var Message_PinnedStickerMessage: String { return self._s[643]! }
- public var ContactInfo_PhoneLabelPager: String { return self._s[644]! }
- public var SettingsSearch_Synonyms_Appearance_TextSize: String { return self._s[645]! }
- public var Passport_DiscardMessageTitle: String { return self._s[646]! }
- public var Privacy_PaymentsTitle: String { return self._s[647]! }
- public var EditTheme_Edit_Preview_IncomingReplyName: String { return self._s[648]! }
- public var ClearCache_StorageCache: String { return self._s[649]! }
- public var Appearance_TextSizeSetting: String { return self._s[650]! }
- public var Channel_DiscussionGroup_Header: String { return self._s[652]! }
- public var VoiceOver_Chat_OptionSelected: String { return self._s[653]! }
- public var Appearance_ColorTheme: String { return self._s[654]! }
- public var UserInfo_ShareContact: String { return self._s[655]! }
- public var Passport_Address_TypePassportRegistration: String { return self._s[656]! }
- public var Common_More: String { return self._s[657]! }
- public var Watch_Message_Call: String { return self._s[658]! }
- public var Profile_EncryptionKey: String { return self._s[661]! }
- public var Privacy_TopPeers: String { return self._s[662]! }
- public var Conversation_StopPollConfirmation: String { return self._s[663]! }
- public var Wallet_Words_NotDoneText: String { return self._s[665]! }
- public var Privacy_TopPeersWarning: String { return self._s[667]! }
- public var SettingsSearch_Synonyms_Data_DownloadInBackground: String { return self._s[668]! }
- public var SettingsSearch_Synonyms_Data_Storage_KeepMedia: String { return self._s[669]! }
- public var Media_SendWithTimer: String { return self._s[672]! }
- public var Wallet_RestoreFailed_EnterWords: String { return self._s[673]! }
- public var DialogList_SearchSectionMessages: String { return self._s[674]! }
- public var ChatList_Context_AddToFolder: String { return self._s[675]! }
- public var Notifications_ChannelNotifications: String { return self._s[676]! }
- public var CheckoutInfo_ShippingInfoAddress1Placeholder: String { return self._s[677]! }
- public var Passport_Language_sk: String { return self._s[678]! }
- public var Notification_MessageLifetime1h: String { return self._s[679]! }
- public var Wallpaper_ResetWallpapersInfo: String { return self._s[680]! }
- public var Appearance_ThemePreview_Chat_5_Text: String { return self._s[681]! }
- public var PeerInfo_PaneGifs: String { return self._s[682]! }
- public var Call_ReportSkip: String { return self._s[684]! }
- public var Cache_ServiceFiles: String { return self._s[685]! }
- public var Group_ErrorAddTooMuchAdmins: String { return self._s[686]! }
- public var VoiceOver_Chat_YourFile: String { return self._s[687]! }
- public var Map_Hybrid: String { return self._s[688]! }
- public var Contacts_SearchUsersAndGroupsLabel: String { return self._s[690]! }
+ public var Stats_Followers: String { return self._s[626]! }
+ public var Stats_GroupLanguagesTitle: String { return self._s[627]! }
+ public var Cache_NoLimit: String { return self._s[628]! }
+ public var Channel_AdminLog_InfoPanelChannelAlertText: String { return self._s[629]! }
+ public var ChatAdmins_AllMembersAreAdmins: String { return self._s[630]! }
+ public var LocalGroup_IrrelevantWarning: String { return self._s[631]! }
+ public var Conversation_DefaultRestrictedInline: String { return self._s[632]! }
+ public var Message_Sticker: String { return self._s[633]! }
+ public var LastSeen_JustNow: String { return self._s[635]! }
+ public var Passport_Email_EmailPlaceholder: String { return self._s[637]! }
+ public var SettingsSearch_Synonyms_AppLanguage: String { return self._s[638]! }
+ public var Channel_AdminLogFilter_EventsEditedMessages: String { return self._s[640]! }
+ public var Channel_EditAdmin_PermissionsHeader: String { return self._s[641]! }
+ public var TwoStepAuth_Email: String { return self._s[642]! }
+ public var SettingsSearch_Synonyms_Notifications_ChannelNotificationsSound: String { return self._s[643]! }
+ public var PhotoEditor_BlurToolOff: String { return self._s[644]! }
+ public var Message_PinnedStickerMessage: String { return self._s[645]! }
+ public var ContactInfo_PhoneLabelPager: String { return self._s[646]! }
+ public var SettingsSearch_Synonyms_Appearance_TextSize: String { return self._s[647]! }
+ public var Passport_DiscardMessageTitle: String { return self._s[648]! }
+ public var Privacy_PaymentsTitle: String { return self._s[649]! }
+ public var EditTheme_Edit_Preview_IncomingReplyName: String { return self._s[650]! }
+ public var ClearCache_StorageCache: String { return self._s[651]! }
+ public var Cache_KeepMediaHelp: String { return self._s[652]! }
+ public var Appearance_TextSizeSetting: String { return self._s[653]! }
+ public var Channel_DiscussionGroup_Header: String { return self._s[655]! }
+ public var VoiceOver_Chat_OptionSelected: String { return self._s[656]! }
+ public var Appearance_ColorTheme: String { return self._s[657]! }
+ public var UserInfo_ShareContact: String { return self._s[658]! }
+ public var Passport_Address_TypePassportRegistration: String { return self._s[659]! }
+ public var Common_More: String { return self._s[660]! }
+ public var Watch_Message_Call: String { return self._s[661]! }
+ public var Profile_EncryptionKey: String { return self._s[664]! }
+ public var Privacy_TopPeers: String { return self._s[665]! }
+ public var Conversation_StopPollConfirmation: String { return self._s[666]! }
+ public var Wallet_Words_NotDoneText: String { return self._s[668]! }
+ public var Privacy_TopPeersWarning: String { return self._s[670]! }
+ public var SettingsSearch_Synonyms_Data_DownloadInBackground: String { return self._s[671]! }
+ public var SettingsSearch_Synonyms_Data_Storage_KeepMedia: String { return self._s[672]! }
+ public var Media_SendWithTimer: String { return self._s[675]! }
+ public var Wallet_RestoreFailed_EnterWords: String { return self._s[676]! }
+ public var DialogList_SearchSectionMessages: String { return self._s[677]! }
+ public var ChatList_Context_AddToFolder: String { return self._s[678]! }
+ public var Notifications_ChannelNotifications: String { return self._s[679]! }
+ public var CheckoutInfo_ShippingInfoAddress1Placeholder: String { return self._s[680]! }
+ public var Notification_MessageLifetime1h: String { return self._s[681]! }
+ public var Passport_Language_sk: String { return self._s[682]! }
+ public var Wallpaper_ResetWallpapersInfo: String { return self._s[683]! }
+ public var Appearance_ThemePreview_Chat_5_Text: String { return self._s[684]! }
+ public var PeerInfo_PaneGifs: String { return self._s[685]! }
+ public var Call_ReportSkip: String { return self._s[687]! }
+ public var Cache_ServiceFiles: String { return self._s[688]! }
+ public var Group_ErrorAddTooMuchAdmins: String { return self._s[689]! }
+ public var VoiceOver_Chat_YourFile: String { return self._s[690]! }
+ public var Map_Hybrid: String { return self._s[691]! }
+ public var Contacts_SearchUsersAndGroupsLabel: String { return self._s[693]! }
public func PUSH_MESSAGE_QUIZ(_ _1: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[691]!, self._r[691]!, [_1])
+ return formatWithArgumentRanges(self._s[694]!, self._r[694]!, [_1])
}
- public var ChatSettings_AutoDownloadVideos: String { return self._s[693]! }
- public var Channel_BanUser_PermissionEmbedLinks: String { return self._s[694]! }
- public var InfoPlist_NSLocationAlwaysAndWhenInUseUsageDescription: String { return self._s[695]! }
- public var SocksProxySetup_ProxyTelegram: String { return self._s[698]! }
+ public var ChatSettings_AutoDownloadVideos: String { return self._s[696]! }
+ public var Channel_BanUser_PermissionEmbedLinks: String { return self._s[697]! }
+ public var InfoPlist_NSLocationAlwaysAndWhenInUseUsageDescription: String { return self._s[698]! }
+ public var SocksProxySetup_ProxyTelegram: String { return self._s[701]! }
public func PUSH_MESSAGE_AUDIO(_ _1: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[699]!, self._r[699]!, [_1])
+ return formatWithArgumentRanges(self._s[702]!, self._r[702]!, [_1])
}
- public var Channel_Username_CreatePrivateLinkHelp: String { return self._s[701]! }
- public var ScheduledMessages_ScheduledToday: String { return self._s[702]! }
+ public var Channel_Username_CreatePrivateLinkHelp: String { return self._s[704]! }
+ public var ScheduledMessages_ScheduledToday: String { return self._s[705]! }
public func PUSH_CHAT_TITLE_EDITED(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[703]!, self._r[703]!, [_1, _2])
+ return formatWithArgumentRanges(self._s[706]!, self._r[706]!, [_1, _2])
}
- public var Conversation_LiveLocationYou: String { return self._s[704]! }
- public var SettingsSearch_Synonyms_Privacy_Calls: String { return self._s[705]! }
- public var SettingsSearch_Synonyms_Notifications_MessageNotificationsPreview: String { return self._s[706]! }
- public var UserInfo_ShareBot: String { return self._s[709]! }
+ public var Conversation_LiveLocationYou: String { return self._s[707]! }
+ public var SettingsSearch_Synonyms_Privacy_Calls: String { return self._s[708]! }
+ public var SettingsSearch_Synonyms_Notifications_MessageNotificationsPreview: String { return self._s[709]! }
+ public var UserInfo_ShareBot: String { return self._s[712]! }
public func PUSH_AUTH_REGION(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[710]!, self._r[710]!, [_1, _2])
+ return formatWithArgumentRanges(self._s[713]!, self._r[713]!, [_1, _2])
}
- public var Conversation_ClearCache: String { return self._s[711]! }
- public var PhotoEditor_ShadowsTint: String { return self._s[712]! }
- public var ChatListFolderSettings_EditFoldersInfo: String { return self._s[713]! }
- public var Message_Audio: String { return self._s[714]! }
- public var Passport_Language_lt: String { return self._s[715]! }
+ public var Conversation_ClearCache: String { return self._s[714]! }
+ public var PhotoEditor_ShadowsTint: String { return self._s[715]! }
+ public var ChatListFolderSettings_EditFoldersInfo: String { return self._s[716]! }
+ public var Message_Audio: String { return self._s[717]! }
+ public var Passport_Language_lt: String { return self._s[718]! }
public func Message_PinnedTextMessage(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[716]!, self._r[716]!, [_0])
+ return formatWithArgumentRanges(self._s[719]!, self._r[719]!, [_0])
}
- public var Permissions_SiriText_v0: String { return self._s[717]! }
- public var Conversation_FileICloudDrive: String { return self._s[718]! }
- public var ChatList_DeleteForEveryoneConfirmationTitle: String { return self._s[719]! }
- public var Notifications_Badge_IncludeMutedChats: String { return self._s[720]! }
+ public var Permissions_SiriText_v0: String { return self._s[720]! }
+ public var Conversation_FileICloudDrive: String { return self._s[721]! }
+ public var ChatList_DeleteForEveryoneConfirmationTitle: String { return self._s[722]! }
+ public var Notifications_Badge_IncludeMutedChats: String { return self._s[723]! }
public func Notification_NewAuthDetected(_ _1: String, _ _2: String, _ _3: String, _ _4: String, _ _5: String, _ _6: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[721]!, self._r[721]!, [_1, _2, _3, _4, _5, _6])
+ return formatWithArgumentRanges(self._s[724]!, self._r[724]!, [_1, _2, _3, _4, _5, _6])
}
- public var DialogList_ProxyConnectionIssuesTooltip: String { return self._s[722]! }
+ public var DialogList_ProxyConnectionIssuesTooltip: String { return self._s[725]! }
public func Time_MonthOfYear_m5(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[723]!, self._r[723]!, [_0])
+ return formatWithArgumentRanges(self._s[726]!, self._r[726]!, [_0])
}
- public var Channel_SignMessages: String { return self._s[724]! }
+ public var Channel_SignMessages: String { return self._s[727]! }
public func PUSH_MESSAGE_NOTEXT(_ _1: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[725]!, self._r[725]!, [_1])
+ return formatWithArgumentRanges(self._s[728]!, self._r[728]!, [_1])
}
- public var Compose_ChannelTokenListPlaceholder: String { return self._s[726]! }
- public var Passport_ScanPassport: String { return self._s[727]! }
- public var Watch_Suggestion_Thanks: String { return self._s[728]! }
- public var BlockedUsers_AddNew: String { return self._s[729]! }
+ public var Compose_ChannelTokenListPlaceholder: String { return self._s[729]! }
+ public var Passport_ScanPassport: String { return self._s[730]! }
+ public var Watch_Suggestion_Thanks: String { return self._s[731]! }
+ public var BlockedUsers_AddNew: String { return self._s[732]! }
public func PUSH_CHAT_MESSAGE(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[730]!, self._r[730]!, [_1, _2])
+ return formatWithArgumentRanges(self._s[733]!, self._r[733]!, [_1, _2])
}
- public var Watch_Message_Invoice: String { return self._s[731]! }
- public var SettingsSearch_Synonyms_Privacy_LastSeen: String { return self._s[732]! }
- public var Month_GenJuly: String { return self._s[733]! }
- public var CreatePoll_QuizInfo: String { return self._s[734]! }
- public var UserInfo_StartSecretChatStart: String { return self._s[735]! }
- public var SocksProxySetup_ProxySocks5: String { return self._s[736]! }
- public var IntentsSettings_SuggestByShare: String { return self._s[738]! }
- public var Notification_Exceptions_DeleteAllConfirmation: String { return self._s[739]! }
- public var Notification_ChannelInviterSelf: String { return self._s[740]! }
- public var CheckoutInfo_ReceiverInfoEmail: String { return self._s[741]! }
+ public var Watch_Message_Invoice: String { return self._s[734]! }
+ public var SettingsSearch_Synonyms_Privacy_LastSeen: String { return self._s[735]! }
+ public var Month_GenJuly: String { return self._s[736]! }
+ public var CreatePoll_QuizInfo: String { return self._s[737]! }
+ public var UserInfo_StartSecretChatStart: String { return self._s[738]! }
+ public var SocksProxySetup_ProxySocks5: String { return self._s[739]! }
+ public var IntentsSettings_SuggestByShare: String { return self._s[741]! }
+ public var Notification_Exceptions_DeleteAllConfirmation: String { return self._s[742]! }
+ public var Notification_ChannelInviterSelf: String { return self._s[743]! }
+ public var CheckoutInfo_ReceiverInfoEmail: String { return self._s[744]! }
public func ApplyLanguage_ChangeLanguageUnofficialText(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[742]!, self._r[742]!, [_1, _2])
+ return formatWithArgumentRanges(self._s[745]!, self._r[745]!, [_1, _2])
}
- public var Stats_FollowersTitle: String { return self._s[743]! }
- public var CheckoutInfo_Title: String { return self._s[744]! }
- public var Watch_Stickers_RecentPlaceholder: String { return self._s[745]! }
+ public var Stats_FollowersTitle: String { return self._s[746]! }
+ public var CheckoutInfo_Title: String { return self._s[747]! }
+ public var Watch_Stickers_RecentPlaceholder: String { return self._s[748]! }
public func Map_DistanceAway(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[746]!, self._r[746]!, [_0])
+ return formatWithArgumentRanges(self._s[749]!, self._r[749]!, [_0])
}
- public var Passport_Identity_MainPage: String { return self._s[747]! }
- public var TwoStepAuth_ConfirmEmailResendCode: String { return self._s[748]! }
- public var Passport_Language_de: String { return self._s[749]! }
- public var Update_Title: String { return self._s[750]! }
- public var ContactInfo_PhoneLabelWorkFax: String { return self._s[751]! }
- public var Channel_AdminLog_BanEmbedLinks: String { return self._s[752]! }
- public var Passport_Email_UseTelegramEmailHelp: String { return self._s[753]! }
- public var Notifications_ChannelNotificationsPreview: String { return self._s[754]! }
- public var NotificationsSound_Telegraph: String { return self._s[755]! }
- public var Watch_LastSeen_ALongTimeAgo: String { return self._s[756]! }
- public var ChannelMembers_WhoCanAddMembers: String { return self._s[757]! }
+ public var Passport_Identity_MainPage: String { return self._s[750]! }
+ public var TwoStepAuth_ConfirmEmailResendCode: String { return self._s[751]! }
+ public var Passport_Language_de: String { return self._s[752]! }
+ public var Update_Title: String { return self._s[753]! }
+ public var ContactInfo_PhoneLabelWorkFax: String { return self._s[754]! }
+ public var Channel_AdminLog_BanEmbedLinks: String { return self._s[755]! }
+ public var Passport_Email_UseTelegramEmailHelp: String { return self._s[756]! }
+ public var Notifications_ChannelNotificationsPreview: String { return self._s[757]! }
+ public var NotificationsSound_Telegraph: String { return self._s[758]! }
+ public var Watch_LastSeen_ALongTimeAgo: String { return self._s[759]! }
+ public var ChannelMembers_WhoCanAddMembers: String { return self._s[760]! }
public func AutoDownloadSettings_UpTo(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[758]!, self._r[758]!, [_0])
+ return formatWithArgumentRanges(self._s[761]!, self._r[761]!, [_0])
}
- public var ClearCache_Description: String { return self._s[759]! }
- public var Stickers_SuggestAll: String { return self._s[760]! }
- public var Conversation_ForwardTitle: String { return self._s[761]! }
- public var Appearance_ThemePreview_ChatList_7_Name: String { return self._s[762]! }
+ public var ClearCache_Description: String { return self._s[762]! }
+ public var Stickers_SuggestAll: String { return self._s[763]! }
+ public var Conversation_ForwardTitle: String { return self._s[764]! }
+ public var Appearance_ThemePreview_ChatList_7_Name: String { return self._s[765]! }
public func Notification_JoinedChannel(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[763]!, self._r[763]!, [_0])
+ return formatWithArgumentRanges(self._s[766]!, self._r[766]!, [_0])
}
- public var Calls_NewCall: String { return self._s[764]! }
- public var Call_StatusEnded: String { return self._s[765]! }
- public var AutoDownloadSettings_DataUsageLow: String { return self._s[767]! }
- public var Settings_ProxyConnected: String { return self._s[768]! }
- public var Channel_AdminLogFilter_EventsPinned: String { return self._s[769]! }
- public var PhotoEditor_QualityVeryLow: String { return self._s[770]! }
- public var Channel_AdminLogFilter_EventsDeletedMessages: String { return self._s[771]! }
- public var Passport_PasswordPlaceholder: String { return self._s[772]! }
- public var Message_PinnedInvoice: String { return self._s[773]! }
- public var Passport_Identity_IssueDate: String { return self._s[774]! }
- public var Stats_GroupTopHoursTitle: String { return self._s[775]! }
- public var Passport_Language_pl: String { return self._s[776]! }
+ public var Calls_NewCall: String { return self._s[767]! }
+ public var Call_StatusEnded: String { return self._s[768]! }
+ public var AutoDownloadSettings_DataUsageLow: String { return self._s[770]! }
+ public var Settings_ProxyConnected: String { return self._s[771]! }
+ public var Channel_AdminLogFilter_EventsPinned: String { return self._s[772]! }
+ public var PhotoEditor_QualityVeryLow: String { return self._s[773]! }
+ public var Channel_AdminLogFilter_EventsDeletedMessages: String { return self._s[774]! }
+ public var Passport_PasswordPlaceholder: String { return self._s[775]! }
+ public var Message_PinnedInvoice: String { return self._s[776]! }
+ public var Passport_Identity_IssueDate: String { return self._s[777]! }
+ public var Stats_GroupTopHoursTitle: String { return self._s[778]! }
+ public var Passport_Language_pl: String { return self._s[779]! }
public func ChannelInfo_ChannelForbidden(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[777]!, self._r[777]!, [_0])
- }
- public var Call_StatusConnecting: String { return self._s[778]! }
- public var SocksProxySetup_PasteFromClipboard: String { return self._s[779]! }
- public func Username_UsernameIsAvailable(_ _0: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[780]!, self._r[780]!, [_0])
}
- public var ChatSettings_ConnectionType_UseProxy: String { return self._s[782]! }
- public var Common_Edit: String { return self._s[783]! }
- public var PrivacySettings_LastSeenNobody: String { return self._s[784]! }
+ public var Call_StatusConnecting: String { return self._s[781]! }
+ public var SocksProxySetup_PasteFromClipboard: String { return self._s[782]! }
+ public func Username_UsernameIsAvailable(_ _0: String) -> (String, [(Int, NSRange)]) {
+ return formatWithArgumentRanges(self._s[783]!, self._r[783]!, [_0])
+ }
+ public var ChatSettings_ConnectionType_UseProxy: String { return self._s[785]! }
+ public var Common_Edit: String { return self._s[786]! }
+ public var PrivacySettings_LastSeenNobody: String { return self._s[787]! }
public func Notification_LeftChat(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[785]!, self._r[785]!, [_0])
+ return formatWithArgumentRanges(self._s[788]!, self._r[788]!, [_0])
}
- public var GroupInfo_ChatAdmins: String { return self._s[786]! }
- public var PrivateDataSettings_Title: String { return self._s[787]! }
- public var Login_CancelPhoneVerificationStop: String { return self._s[788]! }
- public var ChatList_Read: String { return self._s[789]! }
- public var Wallet_WordImport_Text: String { return self._s[790]! }
- public var Undo_ChatClearedForBothSides: String { return self._s[791]! }
- public var ChatListFolder_AddChats: String { return self._s[792]! }
- public var GroupPermission_SectionTitle: String { return self._s[793]! }
- public var Settings_ViewVideo: String { return self._s[794]! }
- public var TwoFactorSetup_Intro_Title: String { return self._s[796]! }
+ public var GroupInfo_ChatAdmins: String { return self._s[789]! }
+ public var PrivateDataSettings_Title: String { return self._s[790]! }
+ public var Login_CancelPhoneVerificationStop: String { return self._s[791]! }
+ public var ChatList_Read: String { return self._s[792]! }
+ public var Wallet_WordImport_Text: String { return self._s[793]! }
+ public var Undo_ChatClearedForBothSides: String { return self._s[794]! }
+ public var ChatListFolder_AddChats: String { return self._s[795]! }
+ public var GroupPermission_SectionTitle: String { return self._s[796]! }
+ public var Settings_ViewVideo: String { return self._s[797]! }
+ public var TwoFactorSetup_Intro_Title: String { return self._s[799]! }
public func PUSH_CHAT_LEFT(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[797]!, self._r[797]!, [_1, _2])
+ return formatWithArgumentRanges(self._s[800]!, self._r[800]!, [_1, _2])
}
- public var Checkout_ErrorPaymentFailed: String { return self._s[798]! }
- public var Update_UpdateApp: String { return self._s[800]! }
- public var Group_Username_RevokeExistingUsernamesInfo: String { return self._s[801]! }
- public var Settings_Appearance: String { return self._s[802]! }
- public var SettingsSearch_Synonyms_Stickers_SuggestStickers: String { return self._s[806]! }
- public var Watch_Location_Access: String { return self._s[807]! }
- public var ShareMenu_CopyShareLink: String { return self._s[809]! }
- public var TwoStepAuth_SetupHintTitle: String { return self._s[810]! }
- public var Conversation_Theme: String { return self._s[812]! }
+ public var Checkout_ErrorPaymentFailed: String { return self._s[801]! }
+ public var Update_UpdateApp: String { return self._s[803]! }
+ public var Group_Username_RevokeExistingUsernamesInfo: String { return self._s[804]! }
+ public var Settings_Appearance: String { return self._s[805]! }
+ public var SettingsSearch_Synonyms_Stickers_SuggestStickers: String { return self._s[809]! }
+ public var Watch_Location_Access: String { return self._s[810]! }
+ public var ShareMenu_CopyShareLink: String { return self._s[812]! }
+ public var TwoStepAuth_SetupHintTitle: String { return self._s[813]! }
+ public var Conversation_Theme: String { return self._s[815]! }
public func DialogList_SingleRecordingVideoMessageSuffix(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[813]!, self._r[813]!, [_0])
+ return formatWithArgumentRanges(self._s[816]!, self._r[816]!, [_0])
}
- public var Notifications_ClassicTones: String { return self._s[814]! }
- public var Weekday_ShortWednesday: String { return self._s[815]! }
- public var WallpaperPreview_SwipeColorsBottomText: String { return self._s[816]! }
- public var Undo_LeftGroup: String { return self._s[819]! }
- public var ChatListFolder_DiscardCancel: String { return self._s[820]! }
- public var Wallet_RestoreFailed_Text: String { return self._s[821]! }
- public var Conversation_LinkDialogCopy: String { return self._s[822]! }
- public var Wallet_TransactionInfo_NoAddress: String { return self._s[824]! }
- public var Wallet_Navigation_Back: String { return self._s[825]! }
- public var KeyCommand_FocusOnInputField: String { return self._s[826]! }
- public var Contacts_SelectAll: String { return self._s[827]! }
- public var Preview_SaveToCameraRoll: String { return self._s[828]! }
- public var PrivacySettings_PasscodeOff: String { return self._s[829]! }
- public var Appearance_ThemePreview_ChatList_6_Name: String { return self._s[830]! }
+ public var Notifications_ClassicTones: String { return self._s[817]! }
+ public var Weekday_ShortWednesday: String { return self._s[818]! }
+ public var WallpaperPreview_SwipeColorsBottomText: String { return self._s[819]! }
+ public var Undo_LeftGroup: String { return self._s[822]! }
+ public var ChatListFolder_DiscardCancel: String { return self._s[823]! }
+ public var Wallet_RestoreFailed_Text: String { return self._s[824]! }
+ public var Conversation_LinkDialogCopy: String { return self._s[825]! }
+ public var Wallet_TransactionInfo_NoAddress: String { return self._s[827]! }
+ public var Wallet_Navigation_Back: String { return self._s[828]! }
+ public var KeyCommand_FocusOnInputField: String { return self._s[829]! }
+ public var Contacts_SelectAll: String { return self._s[830]! }
+ public var Preview_SaveToCameraRoll: String { return self._s[831]! }
+ public var PrivacySettings_PasscodeOff: String { return self._s[832]! }
+ public var Appearance_ThemePreview_ChatList_6_Name: String { return self._s[833]! }
public func PUSH_CHANNEL_MESSAGE_QUIZ(_ _1: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[831]!, self._r[831]!, [_1])
+ return formatWithArgumentRanges(self._s[834]!, self._r[834]!, [_1])
}
- public var Wallpaper_Title: String { return self._s[832]! }
- public var Conversation_FilePhotoOrVideo: String { return self._s[833]! }
- public var AccessDenied_Camera: String { return self._s[834]! }
- public var Watch_Compose_CurrentLocation: String { return self._s[835]! }
- public var PeerInfo_ButtonMessage: String { return self._s[837]! }
- public var Channel_DiscussionGroup_MakeHistoryPublicProceed: String { return self._s[838]! }
+ public var Wallpaper_Title: String { return self._s[835]! }
+ public var Conversation_FilePhotoOrVideo: String { return self._s[836]! }
+ public var AccessDenied_Camera: String { return self._s[837]! }
+ public var Watch_Compose_CurrentLocation: String { return self._s[838]! }
+ public var PeerInfo_ButtonMessage: String { return self._s[840]! }
+ public var Channel_DiscussionGroup_MakeHistoryPublicProceed: String { return self._s[841]! }
public func SecretImage_NotViewedYet(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[839]!, self._r[839]!, [_0])
+ return formatWithArgumentRanges(self._s[842]!, self._r[842]!, [_0])
}
- public var GroupInfo_InvitationLinkDoesNotExist: String { return self._s[840]! }
- public var Passport_Language_ro: String { return self._s[841]! }
- public var EditTheme_UploadNewTheme: String { return self._s[842]! }
- public var CheckoutInfo_SaveInfoHelp: String { return self._s[843]! }
- public var Wallet_Intro_Terms: String { return self._s[844]! }
+ public var GroupInfo_InvitationLinkDoesNotExist: String { return self._s[843]! }
+ public var Passport_Language_ro: String { return self._s[844]! }
+ public var EditTheme_UploadNewTheme: String { return self._s[845]! }
+ public var CheckoutInfo_SaveInfoHelp: String { return self._s[846]! }
+ public var Wallet_Intro_Terms: String { return self._s[847]! }
public func Notification_SecretChatMessageScreenshot(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[845]!, self._r[845]!, [_0])
+ return formatWithArgumentRanges(self._s[848]!, self._r[848]!, [_0])
}
- public var Login_CancelPhoneVerification: String { return self._s[846]! }
- public var State_ConnectingToProxy: String { return self._s[847]! }
- public var Calls_RatingTitle: String { return self._s[848]! }
- public var Generic_ErrorMoreInfo: String { return self._s[849]! }
- public var ChatList_Search_ShowMore: String { return self._s[850]! }
- public var Appearance_PreviewReplyText: String { return self._s[851]! }
- public var CheckoutInfo_ShippingInfoPostcodePlaceholder: String { return self._s[852]! }
+ public var Login_CancelPhoneVerification: String { return self._s[849]! }
+ public var State_ConnectingToProxy: String { return self._s[850]! }
+ public var Calls_RatingTitle: String { return self._s[851]! }
+ public var Generic_ErrorMoreInfo: String { return self._s[852]! }
+ public var ChatList_Search_ShowMore: String { return self._s[853]! }
+ public var Appearance_PreviewReplyText: String { return self._s[854]! }
+ public var CheckoutInfo_ShippingInfoPostcodePlaceholder: String { return self._s[855]! }
public func Wallet_Send_Balance(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[853]!, self._r[853]!, [_0])
+ return formatWithArgumentRanges(self._s[856]!, self._r[856]!, [_0])
}
- public var IntentsSettings_SuggestedChatsContacts: String { return self._s[854]! }
- public var SharedMedia_CategoryLinks: String { return self._s[855]! }
- public var Calls_Missed: String { return self._s[856]! }
- public var Settings_AddAnotherAccount_Help: String { return self._s[860]! }
- public var Cache_Photos: String { return self._s[861]! }
- public var GroupPermission_NoAddMembers: String { return self._s[862]! }
- public var ScheduledMessages_Title: String { return self._s[863]! }
+ public var IntentsSettings_SuggestedChatsContacts: String { return self._s[857]! }
+ public var SharedMedia_CategoryLinks: String { return self._s[858]! }
+ public var Calls_Missed: String { return self._s[859]! }
+ public var Settings_AddAnotherAccount_Help: String { return self._s[863]! }
+ public var Cache_Photos: String { return self._s[864]! }
+ public var GroupPermission_NoAddMembers: String { return self._s[865]! }
+ public var ScheduledMessages_Title: String { return self._s[866]! }
public func Channel_AdminLog_MessageUnpinned(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[864]!, self._r[864]!, [_0])
+ return formatWithArgumentRanges(self._s[867]!, self._r[867]!, [_0])
}
- public var Conversation_ShareBotLocationConfirmationTitle: String { return self._s[865]! }
- public var Settings_ProxyDisabled: String { return self._s[866]! }
+ public var Conversation_ShareBotLocationConfirmationTitle: String { return self._s[868]! }
+ public var Settings_ProxyDisabled: String { return self._s[869]! }
public func Settings_ApplyProxyAlertCredentials(_ _1: String, _ _2: String, _ _3: String, _ _4: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[867]!, self._r[867]!, [_1, _2, _3, _4])
+ return formatWithArgumentRanges(self._s[870]!, self._r[870]!, [_1, _2, _3, _4])
}
public func Conversation_RestrictedMediaTimed(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[868]!, self._r[868]!, [_0])
+ return formatWithArgumentRanges(self._s[871]!, self._r[871]!, [_0])
}
- public var Stats_ViewsPerPost: String { return self._s[870]! }
- public var ChatList_AutoarchiveSuggestion_OpenSettings: String { return self._s[871]! }
- public var ChatList_Context_RemoveFromRecents: String { return self._s[872]! }
- public var Appearance_Title: String { return self._s[873]! }
+ public var Stats_ViewsPerPost: String { return self._s[873]! }
+ public var ChatList_AutoarchiveSuggestion_OpenSettings: String { return self._s[874]! }
+ public var ChatList_Context_RemoveFromRecents: String { return self._s[875]! }
+ public var Appearance_Title: String { return self._s[876]! }
public func Time_MonthOfYear_m2(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[875]!, self._r[875]!, [_0])
+ return formatWithArgumentRanges(self._s[878]!, self._r[878]!, [_0])
}
- public var Conversation_WalletRequiredText: String { return self._s[876]! }
- public var StickerPacksSettings_ShowStickersButtonHelp: String { return self._s[877]! }
- public var OldChannels_NoticeCreateText: String { return self._s[878]! }
- public var Channel_EditMessageErrorGeneric: String { return self._s[879]! }
- public var Privacy_Calls_IntegrationHelp: String { return self._s[880]! }
- public var Preview_DeletePhoto: String { return self._s[881]! }
- public var Appearance_AppIconFilledX: String { return self._s[882]! }
- public var PrivacySettings_PrivacyTitle: String { return self._s[883]! }
+ public func Call_CameraOff(_ _0: String) -> (String, [(Int, NSRange)]) {
+ return formatWithArgumentRanges(self._s[879]!, self._r[879]!, [_0])
+ }
+ public var Conversation_WalletRequiredText: String { return self._s[880]! }
+ public var StickerPacksSettings_ShowStickersButtonHelp: String { return self._s[881]! }
+ public var OldChannels_NoticeCreateText: String { return self._s[882]! }
+ public var Channel_EditMessageErrorGeneric: String { return self._s[883]! }
+ public var Privacy_Calls_IntegrationHelp: String { return self._s[884]! }
+ public var Preview_DeletePhoto: String { return self._s[885]! }
+ public var Appearance_AppIconFilledX: String { return self._s[886]! }
+ public var PrivacySettings_PrivacyTitle: String { return self._s[887]! }
public func Conversation_BotInteractiveUrlAlert(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[884]!, self._r[884]!, [_0])
+ return formatWithArgumentRanges(self._s[888]!, self._r[888]!, [_0])
}
- public var ChatListFolder_TitleEdit: String { return self._s[887]! }
- public var MuteFor_Forever: String { return self._s[888]! }
- public var Coub_TapForSound: String { return self._s[889]! }
- public var Map_LocatingError: String { return self._s[890]! }
- public var TwoStepAuth_EmailChangeSuccess: String { return self._s[892]! }
- public var Conversation_SendMessage_SendSilently: String { return self._s[893]! }
- public var VoiceOver_MessageContextOpenMessageMenu: String { return self._s[894]! }
+ public var ChatListFolder_TitleEdit: String { return self._s[891]! }
+ public var MuteFor_Forever: String { return self._s[892]! }
+ public var Coub_TapForSound: String { return self._s[893]! }
+ public var Map_LocatingError: String { return self._s[894]! }
+ public var TwoStepAuth_EmailChangeSuccess: String { return self._s[896]! }
+ public var Conversation_SendMessage_SendSilently: String { return self._s[897]! }
+ public var VoiceOver_MessageContextOpenMessageMenu: String { return self._s[898]! }
public func Wallet_Time_PreciseDate_m8(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[895]!, self._r[895]!, [_1, _2, _3])
+ return formatWithArgumentRanges(self._s[899]!, self._r[899]!, [_1, _2, _3])
}
- public var Passport_ForgottenPassword: String { return self._s[896]! }
- public var GroupInfo_InviteLink_RevokeLink: String { return self._s[897]! }
- public var StickerPacksSettings_ArchivedPacks: String { return self._s[898]! }
- public var Login_TermsOfServiceSignupDecline: String { return self._s[900]! }
- public var Channel_Moderator_AccessLevelRevoke: String { return self._s[901]! }
- public var Message_Location: String { return self._s[902]! }
- public var Passport_Identity_NamePlaceholder: String { return self._s[903]! }
- public var Channel_Management_Title: String { return self._s[904]! }
- public var DialogList_SearchSectionDialogs: String { return self._s[906]! }
- public var Compose_NewChannel_Members: String { return self._s[907]! }
+ public var Passport_ForgottenPassword: String { return self._s[900]! }
+ public var GroupInfo_InviteLink_RevokeLink: String { return self._s[901]! }
+ public var StickerPacksSettings_ArchivedPacks: String { return self._s[902]! }
+ public var Login_TermsOfServiceSignupDecline: String { return self._s[904]! }
+ public var Channel_Moderator_AccessLevelRevoke: String { return self._s[905]! }
+ public var Message_Location: String { return self._s[906]! }
+ public var Passport_Identity_NamePlaceholder: String { return self._s[907]! }
+ public var Channel_Management_Title: String { return self._s[908]! }
+ public var DialogList_SearchSectionDialogs: String { return self._s[910]! }
+ public var Compose_NewChannel_Members: String { return self._s[911]! }
public func DialogList_SingleUploadingFileSuffix(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[908]!, self._r[908]!, [_0])
+ return formatWithArgumentRanges(self._s[912]!, self._r[912]!, [_0])
}
- public var GroupInfo_Location: String { return self._s[909]! }
- public var Appearance_ThemePreview_ChatList_5_Name: String { return self._s[910]! }
- public var ClearCache_Clear: String { return self._s[911]! }
- public var InstantPage_FeedbackButtonShort: String { return self._s[912]! }
- public var AutoNightTheme_ScheduledFrom: String { return self._s[913]! }
- public var PhotoEditor_WarmthTool: String { return self._s[914]! }
+ public var GroupInfo_Location: String { return self._s[913]! }
+ public var Appearance_ThemePreview_ChatList_5_Name: String { return self._s[914]! }
+ public var ClearCache_Clear: String { return self._s[915]! }
+ public var InstantPage_FeedbackButtonShort: String { return self._s[916]! }
+ public var AutoNightTheme_ScheduledFrom: String { return self._s[917]! }
+ public var PhotoEditor_WarmthTool: String { return self._s[918]! }
public func PUSH_MESSAGE_GAME_SCORE(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[915]!, self._r[915]!, [_1, _2, _3])
+ return formatWithArgumentRanges(self._s[919]!, self._r[919]!, [_1, _2, _3])
}
- public var Passport_Language_tr: String { return self._s[916]! }
- public var OldChannels_NoticeUpgradeText: String { return self._s[917]! }
- public var Login_ResetAccountProtected_Reset: String { return self._s[919]! }
- public var Watch_PhotoView_Title: String { return self._s[920]! }
- public var Passport_Phone_Delete: String { return self._s[921]! }
- public var Undo_ChatDeletedForBothSides: String { return self._s[922]! }
- public var Conversation_EditingMessageMediaEditCurrentPhoto: String { return self._s[923]! }
- public var GroupInfo_Permissions: String { return self._s[924]! }
- public var PasscodeSettings_TurnPasscodeOff: String { return self._s[925]! }
- public var Profile_ShareContactButton: String { return self._s[926]! }
- public var ChatSettings_Other: String { return self._s[927]! }
- public var UserInfo_NotificationsDisabled: String { return self._s[928]! }
- public var CheckoutInfo_ShippingInfoCity: String { return self._s[929]! }
- public var LastSeen_WithinAMonth: String { return self._s[930]! }
- public var VoiceOver_Chat_PlayHint: String { return self._s[931]! }
- public var Conversation_ReportGroupLocation: String { return self._s[932]! }
- public var Conversation_EncryptionCanceled: String { return self._s[933]! }
- public var MediaPicker_GroupDescription: String { return self._s[934]! }
- public var WebSearch_Images: String { return self._s[935]! }
+ public var Passport_Language_tr: String { return self._s[920]! }
+ public var OldChannels_NoticeUpgradeText: String { return self._s[921]! }
+ public var Login_ResetAccountProtected_Reset: String { return self._s[923]! }
+ public var Watch_PhotoView_Title: String { return self._s[924]! }
+ public var Passport_Phone_Delete: String { return self._s[925]! }
+ public var Undo_ChatDeletedForBothSides: String { return self._s[926]! }
+ public var Conversation_EditingMessageMediaEditCurrentPhoto: String { return self._s[927]! }
+ public var GroupInfo_Permissions: String { return self._s[928]! }
+ public var PasscodeSettings_TurnPasscodeOff: String { return self._s[929]! }
+ public var Profile_ShareContactButton: String { return self._s[930]! }
+ public var ChatSettings_Other: String { return self._s[931]! }
+ public var UserInfo_NotificationsDisabled: String { return self._s[932]! }
+ public var CheckoutInfo_ShippingInfoCity: String { return self._s[933]! }
+ public var LastSeen_WithinAMonth: String { return self._s[934]! }
+ public var VoiceOver_Chat_PlayHint: String { return self._s[935]! }
+ public var Conversation_ReportGroupLocation: String { return self._s[936]! }
+ public var Conversation_EncryptionCanceled: String { return self._s[937]! }
+ public var MediaPicker_GroupDescription: String { return self._s[938]! }
+ public var WebSearch_Images: String { return self._s[939]! }
public func Channel_Management_PromotedBy(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[936]!, self._r[936]!, [_0])
+ return formatWithArgumentRanges(self._s[940]!, self._r[940]!, [_0])
}
- public var Message_Photo: String { return self._s[937]! }
- public var PasscodeSettings_HelpBottom: String { return self._s[938]! }
- public var AutoDownloadSettings_VideosTitle: String { return self._s[939]! }
- public var VoiceOver_Media_PlaybackRateChange: String { return self._s[940]! }
- public var Passport_Identity_AddDriversLicense: String { return self._s[941]! }
- public var TwoStepAuth_EnterPasswordPassword: String { return self._s[942]! }
- public var NotificationsSound_Calypso: String { return self._s[943]! }
- public var Map_Map: String { return self._s[944]! }
+ public var Message_Photo: String { return self._s[941]! }
+ public var PasscodeSettings_HelpBottom: String { return self._s[942]! }
+ public var AutoDownloadSettings_VideosTitle: String { return self._s[943]! }
+ public var Conversation_ContextMenuSendMessage: String { return self._s[944]! }
+ public var VoiceOver_Media_PlaybackRateChange: String { return self._s[945]! }
+ public var Passport_Identity_AddDriversLicense: String { return self._s[946]! }
+ public var TwoStepAuth_EnterPasswordPassword: String { return self._s[947]! }
+ public var NotificationsSound_Calypso: String { return self._s[948]! }
+ public var Map_Map: String { return self._s[949]! }
public func Conversation_LiveLocationYouAndOther(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[945]!, self._r[945]!, [_0])
- }
- public var CheckoutInfo_ReceiverInfoTitle: String { return self._s[948]! }
- public var ChatSettings_TextSizeUnits: String { return self._s[949]! }
- public func VoiceOver_Chat_FileFrom(_ _0: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[950]!, self._r[950]!, [_0])
}
- public var Common_of: String { return self._s[951]! }
- public var Conversation_ForwardContacts: String { return self._s[954]! }
- public var IntentsSettings_SuggestByAll: String { return self._s[956]! }
+ public var CheckoutInfo_ReceiverInfoTitle: String { return self._s[953]! }
+ public var ChatSettings_TextSizeUnits: String { return self._s[954]! }
+ public func VoiceOver_Chat_FileFrom(_ _0: String) -> (String, [(Int, NSRange)]) {
+ return formatWithArgumentRanges(self._s[955]!, self._r[955]!, [_0])
+ }
+ public var Common_of: String { return self._s[956]! }
+ public var Conversation_ForwardContacts: String { return self._s[959]! }
+ public var IntentsSettings_SuggestByAll: String { return self._s[961]! }
public func Call_AnsweringWithAccount(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[957]!, self._r[957]!, [_0])
+ return formatWithArgumentRanges(self._s[962]!, self._r[962]!, [_0])
}
- public var Passport_Language_hy: String { return self._s[958]! }
- public var Notifications_MessageNotificationsHelp: String { return self._s[959]! }
- public var AutoDownloadSettings_Reset: String { return self._s[960]! }
- public var Wallet_TransactionInfo_AddressCopied: String { return self._s[961]! }
- public var Paint_ClearConfirm: String { return self._s[962]! }
- public var Camera_VideoMode: String { return self._s[963]! }
+ public var Call_CameraConfirmationText: String { return self._s[963]! }
+ public var Passport_Language_hy: String { return self._s[964]! }
+ public var Notifications_MessageNotificationsHelp: String { return self._s[965]! }
+ public var AutoDownloadSettings_Reset: String { return self._s[966]! }
+ public var Wallet_TransactionInfo_AddressCopied: String { return self._s[967]! }
+ public var Paint_ClearConfirm: String { return self._s[968]! }
+ public var Camera_VideoMode: String { return self._s[969]! }
public func Conversation_RestrictedStickersTimed(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[964]!, self._r[964]!, [_0])
+ return formatWithArgumentRanges(self._s[970]!, self._r[970]!, [_0])
}
- public var Privacy_Calls_AlwaysAllow_Placeholder: String { return self._s[965]! }
- public var Conversation_ViewBackground: String { return self._s[966]! }
+ public var Privacy_Calls_AlwaysAllow_Placeholder: String { return self._s[971]! }
+ public var Conversation_ViewBackground: String { return self._s[972]! }
public func Wallet_Info_TransactionDateHeaderYear(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[967]!, self._r[967]!, [_1, _2, _3])
+ return formatWithArgumentRanges(self._s[973]!, self._r[973]!, [_1, _2, _3])
}
- public var Passport_Language_el: String { return self._s[968]! }
- public var PhotoEditor_Original: String { return self._s[969]! }
- public var Settings_FAQ_Button: String { return self._s[972]! }
- public var Channel_Setup_PublicNoLink: String { return self._s[974]! }
- public var Conversation_UnsupportedMedia: String { return self._s[975]! }
- public var Conversation_SlideToCancel: String { return self._s[976]! }
- public var Appearance_ThemePreview_ChatList_4_Name: String { return self._s[977]! }
- public var Passport_Identity_OneOfTypeInternalPassport: String { return self._s[978]! }
- public var CheckoutInfo_ShippingInfoPostcode: String { return self._s[979]! }
- public var Conversation_ReportSpamChannelConfirmation: String { return self._s[980]! }
- public var Stats_GroupViewers: String { return self._s[981]! }
- public var AutoNightTheme_NotAvailable: String { return self._s[982]! }
- public var Conversation_Owner: String { return self._s[983]! }
- public var Common_Create: String { return self._s[984]! }
- public var Settings_ApplyProxyAlertEnable: String { return self._s[985]! }
- public var ContactList_Context_Call: String { return self._s[986]! }
- public var Localization_ChooseLanguage: String { return self._s[988]! }
- public var ChatList_Context_AddToContacts: String { return self._s[990]! }
- public var OldChannels_NoticeTitle: String { return self._s[991]! }
- public var Settings_Proxy: String { return self._s[993]! }
- public var Privacy_TopPeersHelp: String { return self._s[994]! }
- public var CheckoutInfo_ShippingInfoCountryPlaceholder: String { return self._s[995]! }
- public var Chat_UnsendMyMessages: String { return self._s[996]! }
+ public var Passport_Language_el: String { return self._s[974]! }
+ public var PhotoEditor_Original: String { return self._s[975]! }
+ public var Settings_FAQ_Button: String { return self._s[978]! }
+ public var Channel_Setup_PublicNoLink: String { return self._s[980]! }
+ public var Conversation_UnsupportedMedia: String { return self._s[981]! }
+ public var Conversation_SlideToCancel: String { return self._s[982]! }
+ public var Appearance_ThemePreview_ChatList_4_Name: String { return self._s[983]! }
+ public var Passport_Identity_OneOfTypeInternalPassport: String { return self._s[984]! }
+ public var CheckoutInfo_ShippingInfoPostcode: String { return self._s[985]! }
+ public var Conversation_ReportSpamChannelConfirmation: String { return self._s[986]! }
+ public var Stats_GroupViewers: String { return self._s[987]! }
+ public var AutoNightTheme_NotAvailable: String { return self._s[988]! }
+ public var Conversation_Owner: String { return self._s[989]! }
+ public var Common_Create: String { return self._s[990]! }
+ public var Settings_ApplyProxyAlertEnable: String { return self._s[991]! }
+ public var ContactList_Context_Call: String { return self._s[992]! }
+ public var Localization_ChooseLanguage: String { return self._s[994]! }
+ public var ChatList_Context_AddToContacts: String { return self._s[996]! }
+ public var OldChannels_NoticeTitle: String { return self._s[997]! }
+ public var Settings_Proxy: String { return self._s[999]! }
+ public var Privacy_TopPeersHelp: String { return self._s[1000]! }
+ public var CheckoutInfo_ShippingInfoCountryPlaceholder: String { return self._s[1001]! }
+ public var Chat_UnsendMyMessages: String { return self._s[1002]! }
public func VoiceOver_Chat_Duration(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[997]!, self._r[997]!, [_0])
+ return formatWithArgumentRanges(self._s[1003]!, self._r[1003]!, [_0])
}
- public var TwoStepAuth_ConfirmationAbort: String { return self._s[998]! }
+ public var TwoStepAuth_ConfirmationAbort: String { return self._s[1004]! }
public func Contacts_AccessDeniedHelpPortrait(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1000]!, self._r[1000]!, [_0])
+ return formatWithArgumentRanges(self._s[1006]!, self._r[1006]!, [_0])
}
- public var Contacts_SortedByPresence: String { return self._s[1001]! }
- public var Passport_Identity_SurnamePlaceholder: String { return self._s[1002]! }
- public var Cache_Title: String { return self._s[1003]! }
+ public var Contacts_SortedByPresence: String { return self._s[1007]! }
+ public var Passport_Identity_SurnamePlaceholder: String { return self._s[1008]! }
+ public var Cache_Title: String { return self._s[1009]! }
public func Login_PhoneBannedEmailSubject(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1004]!, self._r[1004]!, [_0])
+ return formatWithArgumentRanges(self._s[1010]!, self._r[1010]!, [_0])
}
- public var TwoStepAuth_EmailCodeExpired: String { return self._s[1005]! }
- public var Channel_Moderator_Title: String { return self._s[1006]! }
- public var InstantPage_AutoNightTheme: String { return self._s[1008]! }
+ public var TwoStepAuth_EmailCodeExpired: String { return self._s[1011]! }
+ public var Channel_Moderator_Title: String { return self._s[1012]! }
+ public var InstantPage_AutoNightTheme: String { return self._s[1014]! }
public func PUSH_MESSAGE_POLL(_ _1: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1011]!, self._r[1011]!, [_1])
+ return formatWithArgumentRanges(self._s[1017]!, self._r[1017]!, [_1])
}
- public var Passport_Scans_Upload: String { return self._s[1012]! }
- public var Undo_Undo: String { return self._s[1014]! }
- public var Contacts_AccessDeniedHelpON: String { return self._s[1015]! }
- public var TwoStepAuth_RemovePassword: String { return self._s[1016]! }
- public var Common_Delete: String { return self._s[1017]! }
- public var Contacts_AddPeopleNearby: String { return self._s[1019]! }
- public var Conversation_ContextMenuDelete: String { return self._s[1020]! }
- public var SocksProxySetup_Credentials: String { return self._s[1021]! }
- public var Appearance_EditTheme: String { return self._s[1023]! }
- public var ClearCache_StorageOtherApps: String { return self._s[1024]! }
+ public var Passport_Scans_Upload: String { return self._s[1018]! }
+ public var Undo_Undo: String { return self._s[1020]! }
+ public var Contacts_AccessDeniedHelpON: String { return self._s[1021]! }
+ public var TwoStepAuth_RemovePassword: String { return self._s[1022]! }
+ public var Common_Delete: String { return self._s[1023]! }
+ public var Contacts_AddPeopleNearby: String { return self._s[1025]! }
+ public var Conversation_ContextMenuDelete: String { return self._s[1026]! }
+ public var SocksProxySetup_Credentials: String { return self._s[1027]! }
+ public var Appearance_EditTheme: String { return self._s[1029]! }
+ public var ClearCache_StorageOtherApps: String { return self._s[1030]! }
public func Conversation_PeerNearbyTitle(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1026]!, self._r[1026]!, [_1, _2])
+ return formatWithArgumentRanges(self._s[1032]!, self._r[1032]!, [_1, _2])
}
- public var Settings_EditPhoto: String { return self._s[1027]! }
- public var PasscodeSettings_AutoLock_Disabled: String { return self._s[1028]! }
- public var Wallet_Send_NetworkErrorText: String { return self._s[1029]! }
- public var AuthSessions_DevicesTitle: String { return self._s[1031]! }
- public var Passport_Address_OneOfTypeRentalAgreement: String { return self._s[1033]! }
- public var Conversation_ShareBotContactConfirmationTitle: String { return self._s[1034]! }
- public var Passport_Language_id: String { return self._s[1036]! }
- public var Chat_Gifs_TrendingSectionHeader: String { return self._s[1037]! }
- public var WallpaperSearch_ColorTeal: String { return self._s[1038]! }
- public var ChannelIntro_Title: String { return self._s[1039]! }
+ public var Settings_EditPhoto: String { return self._s[1033]! }
+ public var PasscodeSettings_AutoLock_Disabled: String { return self._s[1034]! }
+ public var Wallet_Send_NetworkErrorText: String { return self._s[1035]! }
+ public var AuthSessions_DevicesTitle: String { return self._s[1037]! }
+ public var Passport_Address_OneOfTypeRentalAgreement: String { return self._s[1039]! }
+ public var Conversation_ShareBotContactConfirmationTitle: String { return self._s[1040]! }
+ public var Passport_Language_id: String { return self._s[1042]! }
+ public var Chat_Gifs_TrendingSectionHeader: String { return self._s[1043]! }
+ public var WallpaperSearch_ColorTeal: String { return self._s[1044]! }
+ public var ChannelIntro_Title: String { return self._s[1045]! }
public func Channel_AdminLog_MessageToggleSignaturesOff(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1040]!, self._r[1040]!, [_0])
+ return formatWithArgumentRanges(self._s[1046]!, self._r[1046]!, [_0])
}
- public var VoiceOver_Chat_OpenLinkHint: String { return self._s[1042]! }
- public var VoiceOver_Chat_Reply: String { return self._s[1043]! }
- public var ScheduledMessages_BotActionUnavailable: String { return self._s[1044]! }
- public var Channel_Info_Description: String { return self._s[1045]! }
- public var Stickers_FavoriteStickers: String { return self._s[1046]! }
- public var Channel_BanUser_PermissionAddMembers: String { return self._s[1047]! }
- public var Notifications_DisplayNamesOnLockScreen: String { return self._s[1048]! }
- public var ChatSearch_ResultsTooltip: String { return self._s[1049]! }
- public var Wallet_VoiceOver_Editing_ClearText: String { return self._s[1050]! }
- public var Calls_NoMissedCallsPlacehoder: String { return self._s[1051]! }
- public var Group_PublicLink_Placeholder: String { return self._s[1052]! }
- public var Notifications_ExceptionsDefaultSound: String { return self._s[1053]! }
+ public var VoiceOver_Chat_OpenLinkHint: String { return self._s[1048]! }
+ public var VoiceOver_Chat_Reply: String { return self._s[1049]! }
+ public var ScheduledMessages_BotActionUnavailable: String { return self._s[1050]! }
+ public var Channel_Info_Description: String { return self._s[1051]! }
+ public var Stickers_FavoriteStickers: String { return self._s[1052]! }
+ public var Channel_BanUser_PermissionAddMembers: String { return self._s[1053]! }
+ public var Notifications_DisplayNamesOnLockScreen: String { return self._s[1054]! }
+ public var ChatSearch_ResultsTooltip: String { return self._s[1055]! }
+ public var Wallet_VoiceOver_Editing_ClearText: String { return self._s[1056]! }
+ public var Calls_NoMissedCallsPlacehoder: String { return self._s[1057]! }
+ public var Group_PublicLink_Placeholder: String { return self._s[1058]! }
+ public var Notifications_ExceptionsDefaultSound: String { return self._s[1059]! }
public func PUSH_CHANNEL_MESSAGE_POLL(_ _1: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1054]!, self._r[1054]!, [_1])
+ return formatWithArgumentRanges(self._s[1060]!, self._r[1060]!, [_1])
}
- public var TextFormat_Underline: String { return self._s[1055]! }
+ public var TextFormat_Underline: String { return self._s[1061]! }
public func DialogList_SearchSubtitleFormat(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1057]!, self._r[1057]!, [_1, _2])
+ return formatWithArgumentRanges(self._s[1063]!, self._r[1063]!, [_1, _2])
}
public func Channel_AdminLog_MessageRemovedGroupStickerPack(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1058]!, self._r[1058]!, [_0])
+ return formatWithArgumentRanges(self._s[1064]!, self._r[1064]!, [_0])
}
- public var Appearance_ThemePreview_ChatList_3_Name: String { return self._s[1059]! }
+ public var Appearance_ThemePreview_ChatList_3_Name: String { return self._s[1065]! }
public func Channel_OwnershipTransfer_TransferCompleted(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1060]!, self._r[1060]!, [_1, _2])
+ return formatWithArgumentRanges(self._s[1066]!, self._r[1066]!, [_1, _2])
}
- public var Wallet_Intro_ImportExisting: String { return self._s[1061]! }
- public var GroupPermission_Delete: String { return self._s[1062]! }
- public var Passport_Language_uk: String { return self._s[1063]! }
- public var StickerPack_HideStickers: String { return self._s[1065]! }
- public var ChangePhoneNumberNumber_NumberPlaceholder: String { return self._s[1066]! }
+ public var Wallet_Intro_ImportExisting: String { return self._s[1067]! }
+ public var GroupPermission_Delete: String { return self._s[1068]! }
+ public var Passport_Language_uk: String { return self._s[1069]! }
+ public var StickerPack_HideStickers: String { return self._s[1071]! }
+ public var ChangePhoneNumberNumber_NumberPlaceholder: String { return self._s[1072]! }
public func PUSH_CHAT_MESSAGE_PHOTO(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1067]!, self._r[1067]!, [_1, _2])
+ return formatWithArgumentRanges(self._s[1073]!, self._r[1073]!, [_1, _2])
}
- public var Activity_UploadingVideoMessage: String { return self._s[1068]! }
+ public var Activity_UploadingVideoMessage: String { return self._s[1074]! }
public func GroupPermission_ApplyAlertText(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1069]!, self._r[1069]!, [_0])
+ return formatWithArgumentRanges(self._s[1075]!, self._r[1075]!, [_0])
}
- public var Channel_TitleInfo: String { return self._s[1070]! }
- public var StickerPacksSettings_ArchivedPacks_Info: String { return self._s[1071]! }
- public var Settings_CallSettings: String { return self._s[1072]! }
- public var Camera_SquareMode: String { return self._s[1073]! }
- public var Conversation_SendMessage_ScheduleMessage: String { return self._s[1074]! }
- public var GroupInfo_SharedMediaNone: String { return self._s[1075]! }
+ public var Channel_TitleInfo: String { return self._s[1076]! }
+ public var StickerPacksSettings_ArchivedPacks_Info: String { return self._s[1077]! }
+ public var Settings_CallSettings: String { return self._s[1078]! }
+ public var Camera_SquareMode: String { return self._s[1079]! }
+ public var Conversation_SendMessage_ScheduleMessage: String { return self._s[1080]! }
+ public var GroupInfo_SharedMediaNone: String { return self._s[1081]! }
public func PUSH_MESSAGE_VIDEO_SECRET(_ _1: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1076]!, self._r[1076]!, [_1])
+ return formatWithArgumentRanges(self._s[1082]!, self._r[1082]!, [_1])
}
- public var Bot_GenericBotStatus: String { return self._s[1077]! }
- public var Application_Update: String { return self._s[1079]! }
- public var Month_ShortJanuary: String { return self._s[1080]! }
- public var Contacts_PermissionsKeepDisabled: String { return self._s[1081]! }
- public var Channel_AdminLog_BanReadMessages: String { return self._s[1082]! }
- public var Settings_AppLanguage_Unofficial: String { return self._s[1083]! }
- public var Passport_Address_Street2Placeholder: String { return self._s[1084]! }
+ public var Bot_GenericBotStatus: String { return self._s[1083]! }
+ public var Application_Update: String { return self._s[1085]! }
+ public var Month_ShortJanuary: String { return self._s[1086]! }
+ public var Contacts_PermissionsKeepDisabled: String { return self._s[1087]! }
+ public var Channel_AdminLog_BanReadMessages: String { return self._s[1088]! }
+ public var Settings_AppLanguage_Unofficial: String { return self._s[1089]! }
+ public var Passport_Address_Street2Placeholder: String { return self._s[1090]! }
public func Map_LiveLocationShortHour(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1085]!, self._r[1085]!, [_0])
+ return formatWithArgumentRanges(self._s[1091]!, self._r[1091]!, [_0])
}
- public var NetworkUsageSettings_Cellular: String { return self._s[1086]! }
- public var Appearance_PreviewOutgoingText: String { return self._s[1087]! }
+ public var NetworkUsageSettings_Cellular: String { return self._s[1092]! }
+ public var Appearance_PreviewOutgoingText: String { return self._s[1093]! }
public func StickerPackActionInfo_RemovedText(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1088]!, self._r[1088]!, [_0])
+ return formatWithArgumentRanges(self._s[1094]!, self._r[1094]!, [_0])
}
- public var Notifications_PermissionsAllowInSettings: String { return self._s[1089]! }
- public var AutoDownloadSettings_OnForAll: String { return self._s[1092]! }
- public var Map_Directions: String { return self._s[1093]! }
- public var Passport_FieldIdentityTranslationHelp: String { return self._s[1095]! }
- public var Appearance_ThemeDay: String { return self._s[1096]! }
- public var LogoutOptions_LogOut: String { return self._s[1097]! }
- public var Group_PublicLink_Title: String { return self._s[1099]! }
- public var Channel_AddBotErrorNoRights: String { return self._s[1100]! }
- public var ChatList_Search_ShowLess: String { return self._s[1103]! }
- public var Passport_Identity_AddPassport: String { return self._s[1104]! }
- public var LocalGroup_ButtonTitle: String { return self._s[1105]! }
- public var Stats_InteractionsTitle: String { return self._s[1106]! }
- public var Stats_GroupActionsTitle: String { return self._s[1107]! }
- public var Call_Message: String { return self._s[1108]! }
- public var PhotoEditor_ExposureTool: String { return self._s[1109]! }
- public var Wallet_Receive_CommentInfo: String { return self._s[1111]! }
- public var Passport_FieldOneOf_Delimeter: String { return self._s[1112]! }
- public var Channel_AdminLog_CanBanUsers: String { return self._s[1114]! }
- public var Appearance_ThemePreview_ChatList_2_Name: String { return self._s[1115]! }
- public var Appearance_Preview: String { return self._s[1116]! }
- public var Compose_ChannelMembers: String { return self._s[1117]! }
- public var Conversation_DeleteManyMessages: String { return self._s[1118]! }
- public var ReportPeer_ReasonOther_Title: String { return self._s[1119]! }
- public var Checkout_ErrorProviderAccountTimeout: String { return self._s[1120]! }
- public var TwoStepAuth_ResetAccountConfirmation: String { return self._s[1121]! }
- public var Channel_Stickers_CreateYourOwn: String { return self._s[1124]! }
- public var Conversation_UpdateTelegram: String { return self._s[1125]! }
- public var EditTheme_Create_TopInfo: String { return self._s[1126]! }
+ public var Notifications_PermissionsAllowInSettings: String { return self._s[1095]! }
+ public var AutoDownloadSettings_OnForAll: String { return self._s[1098]! }
+ public var Map_Directions: String { return self._s[1099]! }
+ public var Passport_FieldIdentityTranslationHelp: String { return self._s[1101]! }
+ public var Appearance_ThemeDay: String { return self._s[1102]! }
+ public var LogoutOptions_LogOut: String { return self._s[1103]! }
+ public var Group_PublicLink_Title: String { return self._s[1105]! }
+ public var Channel_AddBotErrorNoRights: String { return self._s[1106]! }
+ public var ChatList_Search_ShowLess: String { return self._s[1109]! }
+ public var Passport_Identity_AddPassport: String { return self._s[1110]! }
+ public var LocalGroup_ButtonTitle: String { return self._s[1111]! }
+ public var Stats_InteractionsTitle: String { return self._s[1112]! }
+ public var Stats_GroupActionsTitle: String { return self._s[1113]! }
+ public var Call_Message: String { return self._s[1114]! }
+ public var PhotoEditor_ExposureTool: String { return self._s[1115]! }
+ public var Wallet_Receive_CommentInfo: String { return self._s[1117]! }
+ public var Passport_FieldOneOf_Delimeter: String { return self._s[1118]! }
+ public var Channel_AdminLog_CanBanUsers: String { return self._s[1120]! }
+ public var Appearance_ThemePreview_ChatList_2_Name: String { return self._s[1121]! }
+ public var Appearance_Preview: String { return self._s[1122]! }
+ public var Compose_ChannelMembers: String { return self._s[1123]! }
+ public var Conversation_DeleteManyMessages: String { return self._s[1124]! }
+ public var ReportPeer_ReasonOther_Title: String { return self._s[1125]! }
+ public var Checkout_ErrorProviderAccountTimeout: String { return self._s[1126]! }
+ public var TwoStepAuth_ResetAccountConfirmation: String { return self._s[1127]! }
+ public var Channel_Stickers_CreateYourOwn: String { return self._s[1130]! }
+ public var Conversation_UpdateTelegram: String { return self._s[1131]! }
+ public var EditTheme_Create_TopInfo: String { return self._s[1132]! }
public func Notification_PinnedPhotoMessage(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1127]!, self._r[1127]!, [_0])
+ return formatWithArgumentRanges(self._s[1133]!, self._r[1133]!, [_0])
}
- public var Wallet_WordCheck_Continue: String { return self._s[1128]! }
- public var TwoFactorSetup_Hint_Action: String { return self._s[1129]! }
- public var IntentsSettings_ResetAll: String { return self._s[1130]! }
+ public var Wallet_WordCheck_Continue: String { return self._s[1134]! }
+ public var TwoFactorSetup_Hint_Action: String { return self._s[1135]! }
+ public var IntentsSettings_ResetAll: String { return self._s[1136]! }
public func PUSH_PINNED_GIF(_ _1: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1131]!, self._r[1131]!, [_1])
+ return formatWithArgumentRanges(self._s[1137]!, self._r[1137]!, [_1])
}
- public var ChatList_RemoveFolder: String { return self._s[1132]! }
- public var GroupInfo_Administrators_Title: String { return self._s[1133]! }
- public var Stats_GroupPosters: String { return self._s[1134]! }
- public var Privacy_Forwards_PreviewMessageText: String { return self._s[1135]! }
+ public var ChatList_RemoveFolder: String { return self._s[1138]! }
+ public var GroupInfo_Administrators_Title: String { return self._s[1139]! }
+ public var Stats_GroupPosters: String { return self._s[1140]! }
+ public var Stats_MessageTitle: String { return self._s[1141]! }
+ public var Privacy_Forwards_PreviewMessageText: String { return self._s[1142]! }
public func PrivacySettings_LastSeenNobodyPlus(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1136]!, self._r[1136]!, [_0])
+ return formatWithArgumentRanges(self._s[1143]!, self._r[1143]!, [_0])
}
- public var Tour_Title3: String { return self._s[1137]! }
- public var Channel_EditAdmin_PermissionInviteSubscribers: String { return self._s[1138]! }
- public var Settings_RemoveVideo: String { return self._s[1141]! }
- public var Clipboard_SendPhoto: String { return self._s[1143]! }
- public var MediaPicker_Videos: String { return self._s[1144]! }
- public var Passport_Email_Title: String { return self._s[1145]! }
+ public var Tour_Title3: String { return self._s[1144]! }
+ public var Channel_EditAdmin_PermissionInviteSubscribers: String { return self._s[1145]! }
+ public var Settings_RemoveVideo: String { return self._s[1148]! }
+ public var Clipboard_SendPhoto: String { return self._s[1150]! }
+ public var MediaPicker_Videos: String { return self._s[1151]! }
+ public var Passport_Email_Title: String { return self._s[1152]! }
public func PrivacySettings_LastSeenEverybodyMinus(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1146]!, self._r[1146]!, [_0])
+ return formatWithArgumentRanges(self._s[1153]!, self._r[1153]!, [_0])
}
- public var StickerPacksSettings_Title: String { return self._s[1147]! }
- public var Conversation_MessageDialogDelete: String { return self._s[1148]! }
- public var Privacy_Calls_CustomHelp: String { return self._s[1150]! }
- public var Message_Wallpaper: String { return self._s[1151]! }
- public var MemberSearch_BotSection: String { return self._s[1152]! }
- public var GroupInfo_SetSound: String { return self._s[1153]! }
- public var Wallet_Send_EncryptComment: String { return self._s[1154]! }
+ public var StickerPacksSettings_Title: String { return self._s[1154]! }
+ public var Conversation_MessageDialogDelete: String { return self._s[1155]! }
+ public var Privacy_Calls_CustomHelp: String { return self._s[1157]! }
+ public var Message_Wallpaper: String { return self._s[1158]! }
+ public var MemberSearch_BotSection: String { return self._s[1159]! }
+ public var GroupInfo_SetSound: String { return self._s[1160]! }
+ public var Wallet_Send_EncryptComment: String { return self._s[1161]! }
public func Time_TomorrowAt(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1155]!, self._r[1155]!, [_0])
+ return formatWithArgumentRanges(self._s[1162]!, self._r[1162]!, [_0])
}
- public var Core_ServiceUserStatus: String { return self._s[1156]! }
- public var LiveLocationUpdated_JustNow: String { return self._s[1157]! }
- public var Call_StatusFailed: String { return self._s[1158]! }
- public var TwoFactorSetup_Email_Placeholder: String { return self._s[1159]! }
- public var TwoStepAuth_SetupPasswordDescription: String { return self._s[1160]! }
- public var TwoStepAuth_SetPassword: String { return self._s[1161]! }
- public var Permissions_PeopleNearbyText_v0: String { return self._s[1162]! }
+ public var Core_ServiceUserStatus: String { return self._s[1163]! }
+ public var LiveLocationUpdated_JustNow: String { return self._s[1164]! }
+ public var Call_StatusFailed: String { return self._s[1165]! }
+ public var TwoFactorSetup_Email_Placeholder: String { return self._s[1166]! }
+ public var TwoStepAuth_SetupPasswordDescription: String { return self._s[1167]! }
+ public var TwoStepAuth_SetPassword: String { return self._s[1168]! }
+ public var Permissions_PeopleNearbyText_v0: String { return self._s[1169]! }
public func SocksProxySetup_ProxyStatusPing(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1164]!, self._r[1164]!, [_0])
+ return formatWithArgumentRanges(self._s[1171]!, self._r[1171]!, [_0])
}
- public var Calls_SubmitRating: String { return self._s[1165]! }
- public var Map_NoPlacesNearby: String { return self._s[1166]! }
- public var Profile_Username: String { return self._s[1167]! }
- public var Bot_DescriptionTitle: String { return self._s[1168]! }
- public var MaskStickerSettings_Title: String { return self._s[1169]! }
- public var SharedMedia_CategoryOther: String { return self._s[1170]! }
- public var GroupInfo_SetGroupPhoto: String { return self._s[1171]! }
- public var Common_NotNow: String { return self._s[1172]! }
- public var CallFeedback_IncludeLogsInfo: String { return self._s[1173]! }
- public var Conversation_ShareMyPhoneNumber: String { return self._s[1174]! }
- public var Map_Location: String { return self._s[1175]! }
- public var Invitation_JoinGroup: String { return self._s[1176]! }
- public var AutoDownloadSettings_Title: String { return self._s[1178]! }
- public var Conversation_DiscardVoiceMessageDescription: String { return self._s[1179]! }
- public var Channel_ErrorAddBlocked: String { return self._s[1180]! }
- public var ChatList_AddChatsToFolder: String { return self._s[1181]! }
- public var Conversation_UnblockUser: String { return self._s[1182]! }
- public var EditTheme_Edit_TopInfo: String { return self._s[1183]! }
- public var Watch_Bot_Restart: String { return self._s[1184]! }
- public var TwoStepAuth_Title: String { return self._s[1185]! }
- public var Channel_AdminLog_BanSendMessages: String { return self._s[1186]! }
- public var Checkout_ShippingMethod: String { return self._s[1187]! }
- public var Passport_Identity_OneOfTypeIdentityCard: String { return self._s[1188]! }
+ public var Calls_SubmitRating: String { return self._s[1172]! }
+ public var Map_NoPlacesNearby: String { return self._s[1173]! }
+ public var Profile_Username: String { return self._s[1174]! }
+ public var Bot_DescriptionTitle: String { return self._s[1175]! }
+ public var MaskStickerSettings_Title: String { return self._s[1176]! }
+ public var SharedMedia_CategoryOther: String { return self._s[1177]! }
+ public var GroupInfo_SetGroupPhoto: String { return self._s[1178]! }
+ public var Common_NotNow: String { return self._s[1179]! }
+ public var CallFeedback_IncludeLogsInfo: String { return self._s[1180]! }
+ public var Conversation_ShareMyPhoneNumber: String { return self._s[1181]! }
+ public var Map_Location: String { return self._s[1182]! }
+ public var Invitation_JoinGroup: String { return self._s[1183]! }
+ public var AutoDownloadSettings_Title: String { return self._s[1185]! }
+ public var Conversation_DiscardVoiceMessageDescription: String { return self._s[1186]! }
+ public var Channel_ErrorAddBlocked: String { return self._s[1187]! }
+ public var ChatList_AddChatsToFolder: String { return self._s[1188]! }
+ public var Conversation_UnblockUser: String { return self._s[1189]! }
+ public var EditTheme_Edit_TopInfo: String { return self._s[1190]! }
+ public var Watch_Bot_Restart: String { return self._s[1191]! }
+ public var TwoStepAuth_Title: String { return self._s[1192]! }
+ public var Channel_AdminLog_BanSendMessages: String { return self._s[1193]! }
+ public var Checkout_ShippingMethod: String { return self._s[1194]! }
+ public var Passport_Identity_OneOfTypeIdentityCard: String { return self._s[1195]! }
public func PUSH_CHAT_MESSAGE_STICKER(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1189]!, self._r[1189]!, [_1, _2, _3])
+ return formatWithArgumentRanges(self._s[1196]!, self._r[1196]!, [_1, _2, _3])
}
- public var PeerInfo_ButtonDiscuss: String { return self._s[1190]! }
- public var EditTheme_ChangeColors: String { return self._s[1192]! }
+ public var PeerInfo_ButtonDiscuss: String { return self._s[1197]! }
+ public var EditTheme_ChangeColors: String { return self._s[1199]! }
public func Chat_UnsendMyMessagesAlertTitle(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1193]!, self._r[1193]!, [_0])
+ return formatWithArgumentRanges(self._s[1200]!, self._r[1200]!, [_0])
}
public func Channel_Username_LinkHint(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1194]!, self._r[1194]!, [_0])
+ return formatWithArgumentRanges(self._s[1201]!, self._r[1201]!, [_0])
}
- public var Appearance_ThemePreview_ChatList_1_Name: String { return self._s[1195]! }
- public var SettingsSearch_Synonyms_Data_AutoplayGifs: String { return self._s[1197]! }
- public var AuthSessions_TerminateOtherSessions: String { return self._s[1199]! }
- public var Contacts_FailedToSendInvitesMessage: String { return self._s[1200]! }
- public var PrivacySettings_TwoStepAuth: String { return self._s[1201]! }
- public var Notification_Exceptions_PreviewAlwaysOn: String { return self._s[1202]! }
- public var SettingsSearch_Synonyms_Privacy_Passcode: String { return self._s[1203]! }
- public var Conversation_EditingMessagePanelMedia: String { return self._s[1204]! }
- public var Checkout_PaymentMethod_Title: String { return self._s[1205]! }
- public var SocksProxySetup_Connection: String { return self._s[1206]! }
- public var Group_MessagePhotoRemoved: String { return self._s[1207]! }
- public var PeopleNearby_MakeInvisible: String { return self._s[1209]! }
- public var Channel_Stickers_NotFound: String { return self._s[1211]! }
- public var Group_About_Help: String { return self._s[1212]! }
- public var Notification_PassportValueProofOfIdentity: String { return self._s[1213]! }
- public var PeopleNearby_Title: String { return self._s[1215]! }
+ public var Appearance_ThemePreview_ChatList_1_Name: String { return self._s[1202]! }
+ public var Notification_VideoCallMissed: String { return self._s[1204]! }
+ public var SettingsSearch_Synonyms_Data_AutoplayGifs: String { return self._s[1205]! }
+ public var AuthSessions_TerminateOtherSessions: String { return self._s[1207]! }
+ public var Contacts_FailedToSendInvitesMessage: String { return self._s[1208]! }
+ public var PrivacySettings_TwoStepAuth: String { return self._s[1209]! }
+ public var Notification_Exceptions_PreviewAlwaysOn: String { return self._s[1210]! }
+ public var SettingsSearch_Synonyms_Privacy_Passcode: String { return self._s[1211]! }
+ public var Conversation_EditingMessagePanelMedia: String { return self._s[1212]! }
+ public var Checkout_PaymentMethod_Title: String { return self._s[1213]! }
+ public var SocksProxySetup_Connection: String { return self._s[1214]! }
+ public var Group_MessagePhotoRemoved: String { return self._s[1215]! }
+ public var PeopleNearby_MakeInvisible: String { return self._s[1217]! }
+ public var Channel_Stickers_NotFound: String { return self._s[1219]! }
+ public var Group_About_Help: String { return self._s[1220]! }
+ public var Notification_PassportValueProofOfIdentity: String { return self._s[1221]! }
+ public var PeopleNearby_Title: String { return self._s[1223]! }
public func ApplyLanguage_ChangeLanguageOfficialText(_ _1: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1216]!, self._r[1216]!, [_1])
+ return formatWithArgumentRanges(self._s[1224]!, self._r[1224]!, [_1])
}
- public var Map_Home: String { return self._s[1217]! }
- public var Stats_ZoomOut: String { return self._s[1218]! }
- public var CheckoutInfo_ShippingInfoStatePlaceholder: String { return self._s[1220]! }
- public var Notifications_GroupNotificationsExceptionsHelp: String { return self._s[1221]! }
- public var SocksProxySetup_Password: String { return self._s[1222]! }
- public var Notifications_PermissionsEnable: String { return self._s[1223]! }
- public var TwoStepAuth_ChangeEmail: String { return self._s[1225]! }
+ public var Map_Home: String { return self._s[1225]! }
+ public var Stats_ZoomOut: String { return self._s[1226]! }
+ public var CheckoutInfo_ShippingInfoStatePlaceholder: String { return self._s[1228]! }
+ public var Notifications_GroupNotificationsExceptionsHelp: String { return self._s[1229]! }
+ public var SocksProxySetup_Password: String { return self._s[1230]! }
+ public var Notifications_PermissionsEnable: String { return self._s[1231]! }
+ public var TwoStepAuth_ChangeEmail: String { return self._s[1233]! }
public func Channel_AdminLog_MessageInvitedName(_ _1: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1226]!, self._r[1226]!, [_1])
+ return formatWithArgumentRanges(self._s[1234]!, self._r[1234]!, [_1])
}
public func Time_MonthOfYear_m10(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1228]!, self._r[1228]!, [_0])
+ return formatWithArgumentRanges(self._s[1236]!, self._r[1236]!, [_0])
}
- public var Passport_Identity_TypeDriversLicense: String { return self._s[1229]! }
- public var ArchivedPacksAlert_Title: String { return self._s[1230]! }
- public var Wallet_Receive_InvoiceUrlCopied: String { return self._s[1231]! }
- public var Map_PlacesNearby: String { return self._s[1232]! }
+ public var Passport_Identity_TypeDriversLicense: String { return self._s[1237]! }
+ public var ArchivedPacksAlert_Title: String { return self._s[1238]! }
+ public var Wallet_Receive_InvoiceUrlCopied: String { return self._s[1239]! }
+ public var Map_PlacesNearby: String { return self._s[1240]! }
public func Time_PreciseDate_m7(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1233]!, self._r[1233]!, [_1, _2, _3])
+ return formatWithArgumentRanges(self._s[1241]!, self._r[1241]!, [_1, _2, _3])
}
- public var PrivacyLastSeenSettings_GroupsAndChannelsHelp: String { return self._s[1234]! }
- public var Privacy_Calls_NeverAllow_Placeholder: String { return self._s[1237]! }
- public var Conversation_StatusTyping: String { return self._s[1238]! }
- public var Widget_ApplicationStartRequired: String { return self._s[1239]! }
- public var Broadcast_AdminLog_EmptyText: String { return self._s[1240]! }
- public var Notification_PassportValueProofOfAddress: String { return self._s[1241]! }
- public var UserInfo_CreateNewContact: String { return self._s[1242]! }
- public var Passport_Identity_FrontSide: String { return self._s[1243]! }
- public var Login_PhoneNumberAlreadyAuthorizedSwitch: String { return self._s[1244]! }
- public var Calls_CallTabTitle: String { return self._s[1245]! }
- public var Channel_AdminLog_ChannelEmptyText: String { return self._s[1246]! }
+ public var PrivacyLastSeenSettings_GroupsAndChannelsHelp: String { return self._s[1242]! }
+ public var Privacy_Calls_NeverAllow_Placeholder: String { return self._s[1245]! }
+ public var Conversation_StatusTyping: String { return self._s[1246]! }
+ public var Widget_ApplicationStartRequired: String { return self._s[1247]! }
+ public var Broadcast_AdminLog_EmptyText: String { return self._s[1248]! }
+ public var Notification_PassportValueProofOfAddress: String { return self._s[1249]! }
+ public var UserInfo_CreateNewContact: String { return self._s[1250]! }
+ public var Passport_Identity_FrontSide: String { return self._s[1251]! }
+ public var Login_PhoneNumberAlreadyAuthorizedSwitch: String { return self._s[1252]! }
+ public var Calls_CallTabTitle: String { return self._s[1253]! }
+ public var Channel_AdminLog_ChannelEmptyText: String { return self._s[1254]! }
public func Login_BannedPhoneBody(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1248]!, self._r[1248]!, [_0])
+ return formatWithArgumentRanges(self._s[1256]!, self._r[1256]!, [_0])
}
- public var Watch_UserInfo_MuteTitle: String { return self._s[1249]! }
- public var Group_EditAdmin_RankAdminPlaceholder: String { return self._s[1250]! }
- public var SharedMedia_EmptyMusicText: String { return self._s[1251]! }
- public var Wallet_Completed_Text: String { return self._s[1252]! }
- public var PasscodeSettings_AutoLock_IfAwayFor_1minute: String { return self._s[1253]! }
- public var Paint_Stickers: String { return self._s[1254]! }
- public var Privacy_GroupsAndChannels: String { return self._s[1255]! }
- public var ChatList_Context_Delete: String { return self._s[1257]! }
- public var UserInfo_AddContact: String { return self._s[1258]! }
+ public var Watch_UserInfo_MuteTitle: String { return self._s[1257]! }
+ public var Group_EditAdmin_RankAdminPlaceholder: String { return self._s[1258]! }
+ public var SharedMedia_EmptyMusicText: String { return self._s[1259]! }
+ public var Wallet_Completed_Text: String { return self._s[1260]! }
+ public var PasscodeSettings_AutoLock_IfAwayFor_1minute: String { return self._s[1261]! }
+ public var Paint_Stickers: String { return self._s[1262]! }
+ public var Privacy_GroupsAndChannels: String { return self._s[1263]! }
+ public var ChatList_Context_Delete: String { return self._s[1265]! }
+ public var UserInfo_AddContact: String { return self._s[1266]! }
public func Conversation_MessageViaUser(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1259]!, self._r[1259]!, [_0])
+ return formatWithArgumentRanges(self._s[1267]!, self._r[1267]!, [_0])
}
- public var PhoneNumberHelp_ChangeNumber: String { return self._s[1261]! }
+ public var PhoneNumberHelp_ChangeNumber: String { return self._s[1269]! }
public func ChatList_ClearChatConfirmation(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1263]!, self._r[1263]!, [_0])
+ return formatWithArgumentRanges(self._s[1271]!, self._r[1271]!, [_0])
}
- public var DialogList_NoMessagesTitle: String { return self._s[1264]! }
- public var EditProfile_NameAndPhotoHelp: String { return self._s[1265]! }
- public var BlockedUsers_BlockUser: String { return self._s[1266]! }
- public var Notifications_PermissionsOpenSettings: String { return self._s[1267]! }
- public var MediaPicker_UngroupDescription: String { return self._s[1270]! }
- public var Watch_NoConnection: String { return self._s[1271]! }
- public var Month_GenSeptember: String { return self._s[1272]! }
- public var Conversation_ViewGroup: String { return self._s[1274]! }
- public var Channel_AdminLogFilter_EventsLeavingSubscribers: String { return self._s[1277]! }
- public var Privacy_Forwards_AlwaysLink: String { return self._s[1278]! }
- public var Channel_OwnershipTransfer_ErrorAdminsTooMuch: String { return self._s[1279]! }
- public var Passport_FieldOneOf_FinalDelimeter: String { return self._s[1280]! }
- public var Wallet_WordCheck_IncorrectHeader: String { return self._s[1281]! }
- public var MediaPicker_CameraRoll: String { return self._s[1283]! }
- public var Month_GenAugust: String { return self._s[1284]! }
- public var Wallet_Configuration_SourceHeader: String { return self._s[1285]! }
- public var AccessDenied_VideoMessageMicrophone: String { return self._s[1286]! }
- public var SharedMedia_EmptyText: String { return self._s[1287]! }
- public var Map_ShareLiveLocation: String { return self._s[1288]! }
- public var Calls_All: String { return self._s[1289]! }
- public var Map_SendThisPlace: String { return self._s[1291]! }
- public var Appearance_ThemeNight: String { return self._s[1293]! }
- public var Conversation_HoldForAudio: String { return self._s[1294]! }
- public var SettingsSearch_Synonyms_Support: String { return self._s[1297]! }
- public var GroupInfo_GroupHistoryHidden: String { return self._s[1298]! }
- public var SocksProxySetup_Secret: String { return self._s[1299]! }
+ public var DialogList_NoMessagesTitle: String { return self._s[1272]! }
+ public var EditProfile_NameAndPhotoHelp: String { return self._s[1273]! }
+ public var BlockedUsers_BlockUser: String { return self._s[1274]! }
+ public var Notifications_PermissionsOpenSettings: String { return self._s[1275]! }
+ public var MediaPicker_UngroupDescription: String { return self._s[1278]! }
+ public var Watch_NoConnection: String { return self._s[1279]! }
+ public var Month_GenSeptember: String { return self._s[1280]! }
+ public var Conversation_ViewGroup: String { return self._s[1282]! }
+ public var Channel_AdminLogFilter_EventsLeavingSubscribers: String { return self._s[1285]! }
+ public var Privacy_Forwards_AlwaysLink: String { return self._s[1286]! }
+ public var Channel_OwnershipTransfer_ErrorAdminsTooMuch: String { return self._s[1287]! }
+ public var Passport_FieldOneOf_FinalDelimeter: String { return self._s[1288]! }
+ public var Wallet_WordCheck_IncorrectHeader: String { return self._s[1289]! }
+ public var MediaPicker_CameraRoll: String { return self._s[1291]! }
+ public var Month_GenAugust: String { return self._s[1292]! }
+ public var Wallet_Configuration_SourceHeader: String { return self._s[1293]! }
+ public var AccessDenied_VideoMessageMicrophone: String { return self._s[1294]! }
+ public var SharedMedia_EmptyText: String { return self._s[1295]! }
+ public var Map_ShareLiveLocation: String { return self._s[1296]! }
+ public var Calls_All: String { return self._s[1297]! }
+ public var Map_SendThisPlace: String { return self._s[1299]! }
+ public var Appearance_ThemeNight: String { return self._s[1301]! }
+ public var Conversation_HoldForAudio: String { return self._s[1302]! }
+ public var SettingsSearch_Synonyms_Support: String { return self._s[1305]! }
+ public var GroupInfo_GroupHistoryHidden: String { return self._s[1306]! }
+ public var SocksProxySetup_Secret: String { return self._s[1307]! }
public func Activity_RemindAboutChannel(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1300]!, self._r[1300]!, [_0])
+ return formatWithArgumentRanges(self._s[1308]!, self._r[1308]!, [_0])
}
- public var Channel_BanList_RestrictedTitle: String { return self._s[1302]! }
- public var Conversation_Location: String { return self._s[1303]! }
+ public var Channel_BanList_RestrictedTitle: String { return self._s[1310]! }
+ public var Conversation_Location: String { return self._s[1311]! }
public func AutoDownloadSettings_UpToFor(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1304]!, self._r[1304]!, [_1, _2])
+ return formatWithArgumentRanges(self._s[1312]!, self._r[1312]!, [_1, _2])
}
- public var ChatSettings_AutoDownloadPhotos: String { return self._s[1306]! }
- public var SettingsSearch_Synonyms_Privacy_Title: String { return self._s[1307]! }
- public var Notifications_PermissionsText: String { return self._s[1308]! }
- public var SettingsSearch_Synonyms_Data_SaveIncomingPhotos: String { return self._s[1309]! }
- public var Call_Flip: String { return self._s[1310]! }
- public var Channel_AdminLog_CanDeleteMessagesOfOthers: String { return self._s[1312]! }
- public var SocksProxySetup_ProxyStatusConnecting: String { return self._s[1313]! }
- public var Wallet_TransactionInfo_StorageFeeInfoUrl: String { return self._s[1314]! }
- public var PrivacyPhoneNumberSettings_DiscoveryHeader: String { return self._s[1315]! }
- public var Stats_GroupTopAdmin_Promote: String { return self._s[1317]! }
- public var Channel_EditAdmin_PermissionPinMessages: String { return self._s[1318]! }
- public var TwoStepAuth_ReEnterPasswordDescription: String { return self._s[1320]! }
- public var ChatList_EditFolders: String { return self._s[1322]! }
- public var Channel_TooMuchBots: String { return self._s[1323]! }
- public var Passport_DeletePassportConfirmation: String { return self._s[1324]! }
- public var Login_InvalidCodeError: String { return self._s[1325]! }
- public var StickerPacksSettings_FeaturedPacks: String { return self._s[1326]! }
+ public var ChatSettings_AutoDownloadPhotos: String { return self._s[1314]! }
+ public var SettingsSearch_Synonyms_Privacy_Title: String { return self._s[1315]! }
+ public var Notifications_PermissionsText: String { return self._s[1316]! }
+ public var SettingsSearch_Synonyms_Data_SaveIncomingPhotos: String { return self._s[1317]! }
+ public var Call_Flip: String { return self._s[1318]! }
+ public var Channel_AdminLog_CanDeleteMessagesOfOthers: String { return self._s[1320]! }
+ public var SocksProxySetup_ProxyStatusConnecting: String { return self._s[1321]! }
+ public var Wallet_TransactionInfo_StorageFeeInfoUrl: String { return self._s[1322]! }
+ public var PrivacyPhoneNumberSettings_DiscoveryHeader: String { return self._s[1323]! }
+ public var Stats_GroupTopAdmin_Promote: String { return self._s[1325]! }
+ public var Channel_EditAdmin_PermissionPinMessages: String { return self._s[1326]! }
+ public var TwoStepAuth_ReEnterPasswordDescription: String { return self._s[1328]! }
+ public var ChatList_EditFolders: String { return self._s[1330]! }
+ public var Channel_TooMuchBots: String { return self._s[1331]! }
+ public var Passport_DeletePassportConfirmation: String { return self._s[1332]! }
+ public var Login_InvalidCodeError: String { return self._s[1333]! }
+ public var StickerPacksSettings_FeaturedPacks: String { return self._s[1334]! }
public func ChatList_DeleteSecretChatConfirmation(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1327]!, self._r[1327]!, [_0])
+ return formatWithArgumentRanges(self._s[1335]!, self._r[1335]!, [_0])
}
public func GroupInfo_InvitationLinkAcceptChannel(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1328]!, self._r[1328]!, [_0])
+ return formatWithArgumentRanges(self._s[1336]!, self._r[1336]!, [_0])
}
- public var VoiceOver_Navigation_ProxySettings: String { return self._s[1329]! }
- public var Call_CallInProgressTitle: String { return self._s[1330]! }
- public var Month_ShortSeptember: String { return self._s[1331]! }
- public var Watch_ChannelInfo_Title: String { return self._s[1332]! }
- public var ChatList_DeleteSavedMessagesConfirmation: String { return self._s[1335]! }
- public var DialogList_PasscodeLockHelp: String { return self._s[1336]! }
- public var Chat_MultipleTextMessagesDisabled: String { return self._s[1337]! }
- public var Wallet_Receive_Title: String { return self._s[1338]! }
- public var Notifications_Badge_IncludePublicGroups: String { return self._s[1339]! }
- public var EditProfile_NameAndPhotoOrVideoHelp: String { return self._s[1340]! }
- public var Channel_AdminLogFilter_EventsTitle: String { return self._s[1341]! }
- public var PhotoEditor_CropReset: String { return self._s[1342]! }
- public var Group_Username_CreatePrivateLinkHelp: String { return self._s[1344]! }
- public var Channel_Management_LabelEditor: String { return self._s[1345]! }
- public var Passport_Identity_LatinNameHelp: String { return self._s[1347]! }
- public var PhotoEditor_HighlightsTool: String { return self._s[1348]! }
- public var Wallet_Info_WalletCreated: String { return self._s[1349]! }
- public var UserInfo_Title: String { return self._s[1350]! }
- public var ChatList_HideAction: String { return self._s[1351]! }
- public var AccessDenied_Title: String { return self._s[1352]! }
- public var DialogList_SearchLabel: String { return self._s[1353]! }
- public var Group_Setup_HistoryHidden: String { return self._s[1354]! }
- public var TwoStepAuth_PasswordChangeSuccess: String { return self._s[1355]! }
- public var State_Updating: String { return self._s[1357]! }
- public var Contacts_TabTitle: String { return self._s[1358]! }
- public var Notifications_Badge_CountUnreadMessages: String { return self._s[1360]! }
- public var GroupInfo_GroupHistory: String { return self._s[1361]! }
- public var Conversation_UnsupportedMediaPlaceholder: String { return self._s[1362]! }
- public var Wallpaper_SetColor: String { return self._s[1363]! }
- public var CheckoutInfo_ShippingInfoCountry: String { return self._s[1364]! }
- public var SettingsSearch_Synonyms_SavedMessages: String { return self._s[1365]! }
- public var ChatList_ReorderTabs: String { return self._s[1366]! }
- public var ChatListFolder_IncludeChatsTitle: String { return self._s[1367]! }
- public var Chat_AttachmentLimitReached: String { return self._s[1368]! }
- public var Passport_Identity_OneOfTypeDriversLicense: String { return self._s[1369]! }
- public var Contacts_NotRegisteredSection: String { return self._s[1370]! }
+ public var VoiceOver_Navigation_ProxySettings: String { return self._s[1337]! }
+ public var Call_CallInProgressTitle: String { return self._s[1338]! }
+ public var Month_ShortSeptember: String { return self._s[1339]! }
+ public var Watch_ChannelInfo_Title: String { return self._s[1340]! }
+ public var ChatList_DeleteSavedMessagesConfirmation: String { return self._s[1343]! }
+ public var DialogList_PasscodeLockHelp: String { return self._s[1344]! }
+ public var Chat_MultipleTextMessagesDisabled: String { return self._s[1345]! }
+ public var Wallet_Receive_Title: String { return self._s[1346]! }
+ public var Notifications_Badge_IncludePublicGroups: String { return self._s[1347]! }
+ public var EditProfile_NameAndPhotoOrVideoHelp: String { return self._s[1348]! }
+ public var Channel_AdminLogFilter_EventsTitle: String { return self._s[1349]! }
+ public var PhotoEditor_CropReset: String { return self._s[1350]! }
+ public var Group_Username_CreatePrivateLinkHelp: String { return self._s[1352]! }
+ public var Channel_Management_LabelEditor: String { return self._s[1353]! }
+ public var Passport_Identity_LatinNameHelp: String { return self._s[1355]! }
+ public var PhotoEditor_HighlightsTool: String { return self._s[1356]! }
+ public var Wallet_Info_WalletCreated: String { return self._s[1357]! }
+ public var UserInfo_Title: String { return self._s[1358]! }
+ public var ChatList_HideAction: String { return self._s[1359]! }
+ public var AccessDenied_Title: String { return self._s[1360]! }
+ public var DialogList_SearchLabel: String { return self._s[1361]! }
+ public var Group_Setup_HistoryHidden: String { return self._s[1362]! }
+ public var TwoStepAuth_PasswordChangeSuccess: String { return self._s[1363]! }
+ public var State_Updating: String { return self._s[1365]! }
+ public var Contacts_TabTitle: String { return self._s[1366]! }
+ public var Notifications_Badge_CountUnreadMessages: String { return self._s[1368]! }
+ public var GroupInfo_GroupHistory: String { return self._s[1369]! }
+ public var Conversation_UnsupportedMediaPlaceholder: String { return self._s[1370]! }
+ public var Wallpaper_SetColor: String { return self._s[1371]! }
+ public var CheckoutInfo_ShippingInfoCountry: String { return self._s[1372]! }
+ public var SettingsSearch_Synonyms_SavedMessages: String { return self._s[1373]! }
+ public var ChatList_ReorderTabs: String { return self._s[1374]! }
+ public var ChatListFolder_IncludeChatsTitle: String { return self._s[1375]! }
+ public var Chat_AttachmentLimitReached: String { return self._s[1376]! }
+ public var Passport_Identity_OneOfTypeDriversLicense: String { return self._s[1377]! }
+ public var Contacts_NotRegisteredSection: String { return self._s[1378]! }
public func Time_PreciseDate_m4(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1371]!, self._r[1371]!, [_1, _2, _3])
+ return formatWithArgumentRanges(self._s[1379]!, self._r[1379]!, [_1, _2, _3])
}
- public var Paint_Clear: String { return self._s[1372]! }
- public var StickerPacksSettings_ArchivedMasks: String { return self._s[1373]! }
- public var SocksProxySetup_Connecting: String { return self._s[1374]! }
- public var ExplicitContent_AlertChannel: String { return self._s[1375]! }
- public var CreatePoll_AllOptionsAdded: String { return self._s[1376]! }
- public var Conversation_Contact: String { return self._s[1377]! }
- public var Login_CodeExpired: String { return self._s[1378]! }
- public var Passport_DiscardMessageAction: String { return self._s[1379]! }
- public var ChatList_Context_Unpin: String { return self._s[1380]! }
- public var Channel_AdminLog_MessagePreviousDescription: String { return self._s[1381]! }
+ public var Paint_Clear: String { return self._s[1380]! }
+ public var Call_Audio: String { return self._s[1381]! }
+ public var StickerPacksSettings_ArchivedMasks: String { return self._s[1382]! }
+ public var SocksProxySetup_Connecting: String { return self._s[1383]! }
+ public var ExplicitContent_AlertChannel: String { return self._s[1384]! }
+ public var CreatePoll_AllOptionsAdded: String { return self._s[1385]! }
+ public var Conversation_Contact: String { return self._s[1386]! }
+ public var Login_CodeExpired: String { return self._s[1387]! }
+ public var Passport_DiscardMessageAction: String { return self._s[1388]! }
+ public var ChatList_Context_Unpin: String { return self._s[1389]! }
+ public var Channel_AdminLog_MessagePreviousDescription: String { return self._s[1390]! }
public func VoiceOver_Chat_MusicFrom(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1382]!, self._r[1382]!, [_0])
+ return formatWithArgumentRanges(self._s[1391]!, self._r[1391]!, [_0])
}
- public var Channel_AdminLog_EmptyMessageText: String { return self._s[1383]! }
- public var SettingsSearch_Synonyms_Data_NetworkUsage: String { return self._s[1384]! }
+ public var Channel_AdminLog_EmptyMessageText: String { return self._s[1392]! }
+ public var SettingsSearch_Synonyms_Data_NetworkUsage: String { return self._s[1393]! }
public func Group_EditAdmin_RankInfo(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1385]!, self._r[1385]!, [_0])
+ return formatWithArgumentRanges(self._s[1394]!, self._r[1394]!, [_0])
}
- public var Month_ShortApril: String { return self._s[1386]! }
- public var AuthSessions_CurrentSession: String { return self._s[1387]! }
- public var Chat_AttachmentMultipleFilesDisabled: String { return self._s[1390]! }
- public var Wallet_Navigation_Cancel: String { return self._s[1392]! }
- public var WallpaperPreview_CropTopText: String { return self._s[1393]! }
- public var PrivacySettings_DeleteAccountIfAwayFor: String { return self._s[1394]! }
- public var CheckoutInfo_ShippingInfoTitle: String { return self._s[1396]! }
+ public var Month_ShortApril: String { return self._s[1395]! }
+ public var AuthSessions_CurrentSession: String { return self._s[1396]! }
+ public var Chat_AttachmentMultipleFilesDisabled: String { return self._s[1399]! }
+ public var Wallet_Navigation_Cancel: String { return self._s[1401]! }
+ public var WallpaperPreview_CropTopText: String { return self._s[1402]! }
+ public var PrivacySettings_DeleteAccountIfAwayFor: String { return self._s[1403]! }
+ public var CheckoutInfo_ShippingInfoTitle: String { return self._s[1405]! }
public func Conversation_ScheduleMessage_SendOn(_ _0: String, _ _1: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1397]!, self._r[1397]!, [_0, _1])
+ return formatWithArgumentRanges(self._s[1406]!, self._r[1406]!, [_0, _1])
}
- public var Appearance_ThemePreview_Chat_2_Text: String { return self._s[1398]! }
- public var Channel_Setup_TypePrivate: String { return self._s[1400]! }
- public var Forward_ChannelReadOnly: String { return self._s[1403]! }
- public var PhotoEditor_CurvesBlue: String { return self._s[1404]! }
- public var AddContact_SharedContactException: String { return self._s[1405]! }
- public var UserInfo_BotPrivacy: String { return self._s[1407]! }
- public var Wallet_CreateInvoice_Title: String { return self._s[1408]! }
- public var Notification_PassportValueEmail: String { return self._s[1409]! }
- public var EmptyGroupInfo_Subtitle: String { return self._s[1410]! }
- public var GroupPermission_NewTitle: String { return self._s[1411]! }
- public var CallFeedback_ReasonDropped: String { return self._s[1412]! }
- public var GroupInfo_Permissions_AddException: String { return self._s[1413]! }
- public var Channel_SignMessages_Help: String { return self._s[1416]! }
- public var Undo_ChatDeleted: String { return self._s[1418]! }
- public var Conversation_ChatBackground: String { return self._s[1419]! }
+ public var Appearance_ThemePreview_Chat_2_Text: String { return self._s[1407]! }
+ public var Channel_Setup_TypePrivate: String { return self._s[1409]! }
+ public var Forward_ChannelReadOnly: String { return self._s[1412]! }
+ public var PhotoEditor_CurvesBlue: String { return self._s[1413]! }
+ public var AddContact_SharedContactException: String { return self._s[1414]! }
+ public var UserInfo_BotPrivacy: String { return self._s[1416]! }
+ public var Wallet_CreateInvoice_Title: String { return self._s[1417]! }
+ public var Notification_PassportValueEmail: String { return self._s[1418]! }
+ public var EmptyGroupInfo_Subtitle: String { return self._s[1419]! }
+ public var GroupPermission_NewTitle: String { return self._s[1420]! }
+ public var CallFeedback_ReasonDropped: String { return self._s[1421]! }
+ public var GroupInfo_Permissions_AddException: String { return self._s[1422]! }
+ public var Channel_SignMessages_Help: String { return self._s[1425]! }
+ public var Undo_ChatDeleted: String { return self._s[1427]! }
+ public var Conversation_ChatBackground: String { return self._s[1428]! }
public func Wallet_WordCheck_Text(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1420]!, self._r[1420]!, [_1, _2, _3])
+ return formatWithArgumentRanges(self._s[1429]!, self._r[1429]!, [_1, _2, _3])
}
public func PUSH_CHAT_MESSAGE_QUIZ(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1423]!, self._r[1423]!, [_1, _2, _3])
+ return formatWithArgumentRanges(self._s[1432]!, self._r[1432]!, [_1, _2, _3])
}
- public var ChannelMembers_WhoCanAddMembers_Admins: String { return self._s[1424]! }
- public var FastTwoStepSetup_EmailPlaceholder: String { return self._s[1425]! }
- public var Passport_Language_pt: String { return self._s[1426]! }
- public var VoiceOver_Chat_YourVoiceMessage: String { return self._s[1427]! }
- public var NotificationsSound_Popcorn: String { return self._s[1430]! }
- public var AutoNightTheme_Disabled: String { return self._s[1431]! }
- public var BlockedUsers_LeavePrefix: String { return self._s[1432]! }
- public var WallpaperPreview_CustomColorTopText: String { return self._s[1433]! }
- public var Contacts_PermissionsSuppressWarningText: String { return self._s[1434]! }
- public var WallpaperSearch_ColorBlue: String { return self._s[1435]! }
+ public var ChannelMembers_WhoCanAddMembers_Admins: String { return self._s[1433]! }
+ public var FastTwoStepSetup_EmailPlaceholder: String { return self._s[1434]! }
+ public var Passport_Language_pt: String { return self._s[1435]! }
+ public var VoiceOver_Chat_YourVoiceMessage: String { return self._s[1436]! }
+ public var NotificationsSound_Popcorn: String { return self._s[1439]! }
+ public var AutoNightTheme_Disabled: String { return self._s[1440]! }
+ public var BlockedUsers_LeavePrefix: String { return self._s[1441]! }
+ public var WallpaperPreview_CustomColorTopText: String { return self._s[1442]! }
+ public var Contacts_PermissionsSuppressWarningText: String { return self._s[1443]! }
+ public var WallpaperSearch_ColorBlue: String { return self._s[1444]! }
public func CancelResetAccount_TextSMS(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1436]!, self._r[1436]!, [_0])
+ return formatWithArgumentRanges(self._s[1445]!, self._r[1445]!, [_0])
}
- public var ChatListFolder_TitleCreate: String { return self._s[1437]! }
- public var CheckoutInfo_ErrorNameInvalid: String { return self._s[1438]! }
- public var SocksProxySetup_UseForCalls: String { return self._s[1439]! }
- public var Passport_DeleteDocumentConfirmation: String { return self._s[1441]! }
- public var PeerInfo_PaneGroups: String { return self._s[1442]! }
+ public var ChatListFolder_TitleCreate: String { return self._s[1446]! }
+ public var CheckoutInfo_ErrorNameInvalid: String { return self._s[1447]! }
+ public var SocksProxySetup_UseForCalls: String { return self._s[1448]! }
+ public var Passport_DeleteDocumentConfirmation: String { return self._s[1450]! }
+ public var PeerInfo_PaneGroups: String { return self._s[1451]! }
public func Conversation_Megabytes(_ _0: Float) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1443]!, self._r[1443]!, ["\(_0)"])
+ return formatWithArgumentRanges(self._s[1452]!, self._r[1452]!, ["\(_0)"])
}
- public var SocksProxySetup_Hostname: String { return self._s[1446]! }
- public var ChatSettings_AutoDownloadSettings_OffForAll: String { return self._s[1447]! }
- public var Compose_NewEncryptedChat: String { return self._s[1448]! }
- public var Login_CodeFloodError: String { return self._s[1449]! }
- public var Calls_TabTitle: String { return self._s[1450]! }
- public var Privacy_ProfilePhoto: String { return self._s[1451]! }
- public var Passport_Language_he: String { return self._s[1452]! }
+ public var SocksProxySetup_Hostname: String { return self._s[1455]! }
+ public var ChatSettings_AutoDownloadSettings_OffForAll: String { return self._s[1456]! }
+ public var Compose_NewEncryptedChat: String { return self._s[1457]! }
+ public var Login_CodeFloodError: String { return self._s[1458]! }
+ public var Calls_TabTitle: String { return self._s[1459]! }
+ public var Privacy_ProfilePhoto: String { return self._s[1460]! }
+ public var Passport_Language_he: String { return self._s[1461]! }
public func Conversation_SetReminder_RemindToday(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1453]!, self._r[1453]!, [_0])
+ return formatWithArgumentRanges(self._s[1462]!, self._r[1462]!, [_0])
}
- public var ChatList_TabIconFoldersTooltipNonEmptyFolders: String { return self._s[1454]! }
- public var GroupPermission_Title: String { return self._s[1455]! }
+ public var ChatList_TabIconFoldersTooltipNonEmptyFolders: String { return self._s[1463]! }
+ public var GroupPermission_Title: String { return self._s[1464]! }
public func Channel_AdminLog_MessageGroupPreHistoryHidden(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1456]!, self._r[1456]!, [_0])
+ return formatWithArgumentRanges(self._s[1465]!, self._r[1465]!, [_0])
}
- public var Wallet_TransactionInfo_SenderHeader: String { return self._s[1457]! }
- public var GroupPermission_NoChangeInfo: String { return self._s[1458]! }
- public var ChatList_DeleteForCurrentUser: String { return self._s[1459]! }
- public var Tour_Text1: String { return self._s[1460]! }
- public var Channel_EditAdmin_TransferOwnership: String { return self._s[1461]! }
- public var Month_ShortFebruary: String { return self._s[1462]! }
- public var TwoStepAuth_EmailSkip: String { return self._s[1463]! }
+ public var Wallet_TransactionInfo_SenderHeader: String { return self._s[1466]! }
+ public var GroupPermission_NoChangeInfo: String { return self._s[1467]! }
+ public var ChatList_DeleteForCurrentUser: String { return self._s[1468]! }
+ public var Tour_Text1: String { return self._s[1469]! }
+ public var Channel_EditAdmin_TransferOwnership: String { return self._s[1470]! }
+ public var Month_ShortFebruary: String { return self._s[1471]! }
+ public var Call_ExternalCallInProgressMessage: String { return self._s[1472]! }
+ public var TwoStepAuth_EmailSkip: String { return self._s[1473]! }
+ public var ContactList_Context_VideoCall: String { return self._s[1474]! }
public func Wallet_Time_PreciseDate_m4(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1464]!, self._r[1464]!, [_1, _2, _3])
+ return formatWithArgumentRanges(self._s[1475]!, self._r[1475]!, [_1, _2, _3])
}
- public var Stats_LanguagesTitle: String { return self._s[1465]! }
- public var NotificationsSound_Glass: String { return self._s[1466]! }
- public var Appearance_ThemeNightBlue: String { return self._s[1467]! }
- public var CheckoutInfo_Pay: String { return self._s[1468]! }
- public var PeerInfo_ButtonLeave: String { return self._s[1470]! }
- public var SettingsSearch_Synonyms_ChatFolders: String { return self._s[1471]! }
- public var Invite_LargeRecipientsCountWarning: String { return self._s[1472]! }
- public var Call_CallAgain: String { return self._s[1474]! }
- public var AttachmentMenu_SendAsFile: String { return self._s[1475]! }
- public var AccessDenied_MicrophoneRestricted: String { return self._s[1476]! }
- public var Passport_InvalidPasswordError: String { return self._s[1477]! }
- public var Watch_Message_Game: String { return self._s[1478]! }
- public var Stickers_Install: String { return self._s[1479]! }
- public var VoiceOver_Chat_Message: String { return self._s[1480]! }
- public var PrivacyLastSeenSettings_NeverShareWith: String { return self._s[1481]! }
- public var Passport_Identity_ResidenceCountry: String { return self._s[1483]! }
- public var Notifications_GroupNotificationsHelp: String { return self._s[1484]! }
- public var AuthSessions_OtherSessions: String { return self._s[1485]! }
- public var Channel_Username_Help: String { return self._s[1486]! }
- public var Camera_Title: String { return self._s[1487]! }
- public var IntentsSettings_Title: String { return self._s[1489]! }
- public var GroupInfo_SetGroupPhotoDelete: String { return self._s[1491]! }
- public var Privacy_ProfilePhoto_NeverShareWith_Title: String { return self._s[1492]! }
- public var Channel_AdminLog_SendPolls: String { return self._s[1493]! }
- public var Channel_AdminLog_TitleAllEvents: String { return self._s[1494]! }
- public var Channel_EditAdmin_PermissionInviteMembers: String { return self._s[1495]! }
- public var Contacts_MemberSearchSectionTitleGroup: String { return self._s[1496]! }
- public var ScheduledMessages_DeleteMany: String { return self._s[1497]! }
- public var Conversation_RestrictedStickers: String { return self._s[1498]! }
- public var Notifications_ExceptionsResetToDefaults: String { return self._s[1500]! }
- public var UserInfo_TelegramCall: String { return self._s[1502]! }
- public var TwoStepAuth_SetupResendEmailCode: String { return self._s[1503]! }
- public var CreatePoll_OptionsHeader: String { return self._s[1504]! }
- public var SettingsSearch_Synonyms_Data_CallsUseLessData: String { return self._s[1505]! }
- public var ArchivedChats_IntroTitle1: String { return self._s[1506]! }
- public var Privacy_GroupsAndChannels_AlwaysAllow_Title: String { return self._s[1507]! }
- public var Theme_Colors_Proceed: String { return self._s[1508]! }
- public var Passport_Identity_EditPersonalDetails: String { return self._s[1509]! }
+ public var NotificationsSound_Glass: String { return self._s[1476]! }
+ public var Appearance_ThemeNightBlue: String { return self._s[1477]! }
+ public var CheckoutInfo_Pay: String { return self._s[1478]! }
+ public var Stats_LanguagesTitle: String { return self._s[1480]! }
+ public var PeerInfo_ButtonLeave: String { return self._s[1481]! }
+ public var SettingsSearch_Synonyms_ChatFolders: String { return self._s[1482]! }
+ public var Invite_LargeRecipientsCountWarning: String { return self._s[1483]! }
+ public var Call_CallAgain: String { return self._s[1485]! }
+ public var AttachmentMenu_SendAsFile: String { return self._s[1486]! }
+ public var AccessDenied_MicrophoneRestricted: String { return self._s[1487]! }
+ public var Passport_InvalidPasswordError: String { return self._s[1488]! }
+ public var Watch_Message_Game: String { return self._s[1489]! }
+ public var Stickers_Install: String { return self._s[1490]! }
+ public var VoiceOver_Chat_Message: String { return self._s[1491]! }
+ public var PrivacyLastSeenSettings_NeverShareWith: String { return self._s[1492]! }
+ public var Passport_Identity_ResidenceCountry: String { return self._s[1494]! }
+ public var Notifications_GroupNotificationsHelp: String { return self._s[1495]! }
+ public var AuthSessions_OtherSessions: String { return self._s[1496]! }
+ public var Channel_Username_Help: String { return self._s[1497]! }
+ public var Camera_Title: String { return self._s[1498]! }
+ public var IntentsSettings_Title: String { return self._s[1500]! }
+ public var GroupInfo_SetGroupPhotoDelete: String { return self._s[1502]! }
+ public var Privacy_ProfilePhoto_NeverShareWith_Title: String { return self._s[1503]! }
+ public var Channel_AdminLog_SendPolls: String { return self._s[1504]! }
+ public var Channel_AdminLog_TitleAllEvents: String { return self._s[1505]! }
+ public var Channel_EditAdmin_PermissionInviteMembers: String { return self._s[1506]! }
+ public var Contacts_MemberSearchSectionTitleGroup: String { return self._s[1507]! }
+ public var ScheduledMessages_DeleteMany: String { return self._s[1508]! }
+ public var Conversation_RestrictedStickers: String { return self._s[1509]! }
+ public var Notifications_ExceptionsResetToDefaults: String { return self._s[1511]! }
+ public var UserInfo_TelegramCall: String { return self._s[1513]! }
+ public var TwoStepAuth_SetupResendEmailCode: String { return self._s[1514]! }
+ public var CreatePoll_OptionsHeader: String { return self._s[1515]! }
+ public var SettingsSearch_Synonyms_Data_CallsUseLessData: String { return self._s[1516]! }
+ public var ArchivedChats_IntroTitle1: String { return self._s[1517]! }
+ public var Privacy_GroupsAndChannels_AlwaysAllow_Title: String { return self._s[1518]! }
+ public var Theme_Colors_Proceed: String { return self._s[1519]! }
+ public var Passport_Identity_EditPersonalDetails: String { return self._s[1520]! }
public func Time_PreciseDate_m1(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1510]!, self._r[1510]!, [_1, _2, _3])
+ return formatWithArgumentRanges(self._s[1521]!, self._r[1521]!, [_1, _2, _3])
}
- public var Wallet_Month_GenAugust: String { return self._s[1511]! }
- public var Settings_SaveEditedPhotos: String { return self._s[1512]! }
- public var Stats_FollowersBySourceTitle: String { return self._s[1513]! }
- public var TwoStepAuth_ConfirmationTitle: String { return self._s[1514]! }
- public var Privacy_GroupsAndChannels_NeverAllow_Title: String { return self._s[1515]! }
- public var Conversation_MessageDialogRetry: String { return self._s[1516]! }
- public var ChatList_Context_MarkAsUnread: String { return self._s[1517]! }
- public var MessagePoll_SubmitVote: String { return self._s[1518]! }
- public var Conversation_DiscardVoiceMessageAction: String { return self._s[1519]! }
- public var Permissions_PeopleNearbyTitle_v0: String { return self._s[1520]! }
- public var ChatList_Context_Back: String { return self._s[1521]! }
- public var Group_Setup_TypeHeader: String { return self._s[1522]! }
- public var Paint_RecentStickers: String { return self._s[1523]! }
- public var PhotoEditor_GrainTool: String { return self._s[1524]! }
- public var CheckoutInfo_ShippingInfoState: String { return self._s[1525]! }
- public var EmptyGroupInfo_Line4: String { return self._s[1526]! }
- public var Watch_AuthRequired: String { return self._s[1528]! }
+ public var Wallet_Month_GenAugust: String { return self._s[1522]! }
+ public var Settings_SaveEditedPhotos: String { return self._s[1523]! }
+ public var Stats_FollowersBySourceTitle: String { return self._s[1524]! }
+ public var TwoStepAuth_ConfirmationTitle: String { return self._s[1525]! }
+ public var Privacy_GroupsAndChannels_NeverAllow_Title: String { return self._s[1526]! }
+ public var Conversation_MessageDialogRetry: String { return self._s[1527]! }
+ public var ChatList_Context_MarkAsUnread: String { return self._s[1528]! }
+ public var MessagePoll_SubmitVote: String { return self._s[1529]! }
+ public var Conversation_DiscardVoiceMessageAction: String { return self._s[1530]! }
+ public var Permissions_PeopleNearbyTitle_v0: String { return self._s[1531]! }
+ public var ChatList_Context_Back: String { return self._s[1532]! }
+ public var Group_Setup_TypeHeader: String { return self._s[1533]! }
+ public var Paint_RecentStickers: String { return self._s[1534]! }
+ public var PhotoEditor_GrainTool: String { return self._s[1535]! }
+ public var CheckoutInfo_ShippingInfoState: String { return self._s[1536]! }
+ public var EmptyGroupInfo_Line4: String { return self._s[1537]! }
+ public var Watch_AuthRequired: String { return self._s[1539]! }
public func Passport_Email_UseTelegramEmail(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1529]!, self._r[1529]!, [_0])
+ return formatWithArgumentRanges(self._s[1540]!, self._r[1540]!, [_0])
}
- public var Conversation_EncryptedDescriptionTitle: String { return self._s[1530]! }
- public var ChannelIntro_Text: String { return self._s[1531]! }
- public var DialogList_DeleteBotConfirmation: String { return self._s[1532]! }
- public var GroupPermission_NoSendMedia: String { return self._s[1533]! }
- public var Calls_AddTab: String { return self._s[1534]! }
- public var Message_ReplyActionButtonShowReceipt: String { return self._s[1535]! }
- public var Channel_AdminLog_EmptyFilterText: String { return self._s[1536]! }
- public var Conversation_WalletRequiredSetup: String { return self._s[1537]! }
- public var Notification_MessageLifetime1d: String { return self._s[1538]! }
- public var Notifications_ChannelNotificationsExceptionsHelp: String { return self._s[1539]! }
- public var Channel_BanUser_PermissionsHeader: String { return self._s[1540]! }
- public var Passport_Identity_GenderFemale: String { return self._s[1541]! }
- public var BlockedUsers_BlockTitle: String { return self._s[1542]! }
+ public var Conversation_EncryptedDescriptionTitle: String { return self._s[1541]! }
+ public var ChannelIntro_Text: String { return self._s[1542]! }
+ public var DialogList_DeleteBotConfirmation: String { return self._s[1543]! }
+ public var GroupPermission_NoSendMedia: String { return self._s[1544]! }
+ public var Calls_AddTab: String { return self._s[1545]! }
+ public var Message_ReplyActionButtonShowReceipt: String { return self._s[1546]! }
+ public var Channel_AdminLog_EmptyFilterText: String { return self._s[1547]! }
+ public var Conversation_WalletRequiredSetup: String { return self._s[1548]! }
+ public var Notification_MessageLifetime1d: String { return self._s[1549]! }
+ public var Notifications_ChannelNotificationsExceptionsHelp: String { return self._s[1550]! }
+ public var Channel_BanUser_PermissionsHeader: String { return self._s[1551]! }
+ public var Passport_Identity_GenderFemale: String { return self._s[1552]! }
+ public var BlockedUsers_BlockTitle: String { return self._s[1553]! }
public func PUSH_CHANNEL_MESSAGE_GIF(_ _1: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1543]!, self._r[1543]!, [_1])
+ return formatWithArgumentRanges(self._s[1554]!, self._r[1554]!, [_1])
}
- public var Weekday_Yesterday: String { return self._s[1544]! }
- public var WallpaperSearch_ColorBlack: String { return self._s[1545]! }
- public var Settings_Context_Logout: String { return self._s[1546]! }
- public var Wallet_Info_UnknownTransaction: String { return self._s[1547]! }
- public var ChatList_ArchiveAction: String { return self._s[1548]! }
- public var AutoNightTheme_Scheduled: String { return self._s[1549]! }
- public var TwoFactorSetup_Email_SkipAction: String { return self._s[1550]! }
- public var Settings_Devices: String { return self._s[1551]! }
- public var ContactInfo_Note: String { return self._s[1552]! }
+ public var Weekday_Yesterday: String { return self._s[1555]! }
+ public var WallpaperSearch_ColorBlack: String { return self._s[1556]! }
+ public var Settings_Context_Logout: String { return self._s[1557]! }
+ public var Wallet_Info_UnknownTransaction: String { return self._s[1558]! }
+ public var ChatList_ArchiveAction: String { return self._s[1559]! }
+ public var AutoNightTheme_Scheduled: String { return self._s[1560]! }
+ public var TwoFactorSetup_Email_SkipAction: String { return self._s[1561]! }
+ public var Settings_Devices: String { return self._s[1562]! }
+ public var ContactInfo_Note: String { return self._s[1563]! }
public func Login_PhoneGenericEmailBody(_ _1: String, _ _2: String, _ _3: String, _ _4: String, _ _5: String, _ _6: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1553]!, self._r[1553]!, [_1, _2, _3, _4, _5, _6])
+ return formatWithArgumentRanges(self._s[1564]!, self._r[1564]!, [_1, _2, _3, _4, _5, _6])
}
- public var EditTheme_ThemeTemplateAlertTitle: String { return self._s[1554]! }
- public var Wallet_Receive_CreateInvoice: String { return self._s[1555]! }
- public var PrivacyPolicy_DeclineDeleteNow: String { return self._s[1556]! }
- public var Theme_Colors_ColorWallpaperWarningProceed: String { return self._s[1557]! }
+ public var EditTheme_ThemeTemplateAlertTitle: String { return self._s[1565]! }
+ public var Wallet_Receive_CreateInvoice: String { return self._s[1566]! }
+ public var PrivacyPolicy_DeclineDeleteNow: String { return self._s[1567]! }
+ public var Theme_Colors_ColorWallpaperWarningProceed: String { return self._s[1568]! }
public func PUSH_CHAT_JOINED(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1558]!, self._r[1558]!, [_1, _2])
+ return formatWithArgumentRanges(self._s[1569]!, self._r[1569]!, [_1, _2])
}
- public var CreatePoll_Create: String { return self._s[1559]! }
- public var Channel_Members_AddBannedErrorAdmin: String { return self._s[1560]! }
+ public var CreatePoll_Create: String { return self._s[1570]! }
+ public var Channel_Members_AddBannedErrorAdmin: String { return self._s[1571]! }
public func Notification_CallFormat(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1561]!, self._r[1561]!, [_1, _2])
+ return formatWithArgumentRanges(self._s[1572]!, self._r[1572]!, [_1, _2])
}
- public var ScheduledMessages_ClearAllConfirmation: String { return self._s[1562]! }
- public var Checkout_ErrorProviderAccountInvalid: String { return self._s[1563]! }
- public var Notifications_InAppNotificationsSounds: String { return self._s[1565]! }
+ public var ScheduledMessages_ClearAllConfirmation: String { return self._s[1573]! }
+ public var Checkout_ErrorProviderAccountInvalid: String { return self._s[1574]! }
+ public var Notifications_InAppNotificationsSounds: String { return self._s[1576]! }
public func PUSH_PINNED_GAME_SCORE(_ _1: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1566]!, self._r[1566]!, [_1])
+ return formatWithArgumentRanges(self._s[1577]!, self._r[1577]!, [_1])
}
- public var Preview_OpenInInstagram: String { return self._s[1567]! }
- public var Notification_MessageLifetimeRemovedOutgoing: String { return self._s[1568]! }
+ public var Preview_OpenInInstagram: String { return self._s[1578]! }
+ public var Notification_MessageLifetimeRemovedOutgoing: String { return self._s[1579]! }
public func PUSH_CHAT_ADD_MEMBER(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1569]!, self._r[1569]!, [_1, _2, _3])
+ return formatWithArgumentRanges(self._s[1580]!, self._r[1580]!, [_1, _2, _3])
}
public func Passport_PrivacyPolicy(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1570]!, self._r[1570]!, [_1, _2])
+ return formatWithArgumentRanges(self._s[1581]!, self._r[1581]!, [_1, _2])
}
- public var Channel_AdminLog_InfoPanelAlertTitle: String { return self._s[1571]! }
- public var ArchivedChats_IntroText3: String { return self._s[1572]! }
- public var ChatList_UndoArchiveHiddenText: String { return self._s[1573]! }
- public var NetworkUsageSettings_TotalSection: String { return self._s[1574]! }
- public var Wallet_Month_GenSeptember: String { return self._s[1575]! }
- public var Channel_Setup_TypePrivateHelp: String { return self._s[1576]! }
+ public var Channel_AdminLog_InfoPanelAlertTitle: String { return self._s[1582]! }
+ public var ArchivedChats_IntroText3: String { return self._s[1583]! }
+ public var ChatList_UndoArchiveHiddenText: String { return self._s[1584]! }
+ public var NetworkUsageSettings_TotalSection: String { return self._s[1585]! }
+ public var Wallet_Month_GenSeptember: String { return self._s[1586]! }
+ public var Channel_Setup_TypePrivateHelp: String { return self._s[1587]! }
public func PUSH_CHAT_MESSAGE_POLL(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1577]!, self._r[1577]!, [_1, _2, _3])
+ return formatWithArgumentRanges(self._s[1588]!, self._r[1588]!, [_1, _2, _3])
}
- public var Privacy_GroupsAndChannels_NeverAllow_Placeholder: String { return self._s[1579]! }
- public var FastTwoStepSetup_HintSection: String { return self._s[1580]! }
- public var Wallpaper_PhotoLibrary: String { return self._s[1581]! }
- public var TwoStepAuth_SetupResendEmailCodeAlert: String { return self._s[1582]! }
- public var Gif_NoGifsFound: String { return self._s[1583]! }
- public var Watch_LastSeen_WithinAMonth: String { return self._s[1584]! }
- public var VoiceOver_MessageContextDelete: String { return self._s[1585]! }
- public var EditTheme_Preview: String { return self._s[1586]! }
+ public var Privacy_GroupsAndChannels_NeverAllow_Placeholder: String { return self._s[1590]! }
+ public var FastTwoStepSetup_HintSection: String { return self._s[1591]! }
+ public var Wallpaper_PhotoLibrary: String { return self._s[1592]! }
+ public var TwoStepAuth_SetupResendEmailCodeAlert: String { return self._s[1593]! }
+ public var Gif_NoGifsFound: String { return self._s[1594]! }
+ public var Watch_LastSeen_WithinAMonth: String { return self._s[1595]! }
+ public var VoiceOver_MessageContextDelete: String { return self._s[1596]! }
+ public var EditTheme_Preview: String { return self._s[1597]! }
public func ClearCache_StorageTitle(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1587]!, self._r[1587]!, [_0])
- }
- public var GroupInfo_ActionPromote: String { return self._s[1588]! }
- public var PasscodeSettings_SimplePasscode: String { return self._s[1589]! }
- public var GroupInfo_Permissions_Title: String { return self._s[1590]! }
- public var Permissions_ContactsText_v0: String { return self._s[1591]! }
- public var PrivacyPhoneNumberSettings_CustomDisabledHelp: String { return self._s[1592]! }
- public var SettingsSearch_Synonyms_Notifications_BadgeIncludeMutedPublicGroups: String { return self._s[1593]! }
- public var PrivacySettings_DataSettingsHelp: String { return self._s[1596]! }
- public var Passport_FieldEmailHelp: String { return self._s[1597]! }
- public func Activity_RemindAboutUser(_ _0: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[1598]!, self._r[1598]!, [_0])
}
- public var Passport_Identity_GenderPlaceholder: String { return self._s[1599]! }
- public var Weekday_ShortSaturday: String { return self._s[1600]! }
- public var ContactInfo_PhoneLabelMain: String { return self._s[1601]! }
- public var Watch_Conversation_UserInfo: String { return self._s[1602]! }
- public var CheckoutInfo_ShippingInfoCityPlaceholder: String { return self._s[1603]! }
- public var GroupPermission_PermissionDisabledByDefault: String { return self._s[1604]! }
- public var PrivacyLastSeenSettings_Title: String { return self._s[1605]! }
- public var Conversation_ShareBotLocationConfirmation: String { return self._s[1607]! }
- public var PhotoEditor_VignetteTool: String { return self._s[1608]! }
- public var Conversation_ContextMenuDiscuss: String { return self._s[1609]! }
- public var Passport_Address_Street1Placeholder: String { return self._s[1610]! }
- public var Passport_Language_et: String { return self._s[1611]! }
- public var AppUpgrade_Running: String { return self._s[1612]! }
- public var Channel_DiscussionGroup_Info: String { return self._s[1614]! }
- public var EditTheme_Create_Preview_IncomingReplyName: String { return self._s[1615]! }
- public var Passport_Language_bg: String { return self._s[1616]! }
- public var Stickers_NoStickersFound: String { return self._s[1618]! }
+ public var GroupInfo_ActionPromote: String { return self._s[1599]! }
+ public var PasscodeSettings_SimplePasscode: String { return self._s[1600]! }
+ public var GroupInfo_Permissions_Title: String { return self._s[1601]! }
+ public var Permissions_ContactsText_v0: String { return self._s[1602]! }
+ public var PrivacyPhoneNumberSettings_CustomDisabledHelp: String { return self._s[1603]! }
+ public var SettingsSearch_Synonyms_Notifications_BadgeIncludeMutedPublicGroups: String { return self._s[1604]! }
+ public var PrivacySettings_DataSettingsHelp: String { return self._s[1607]! }
+ public var Passport_FieldEmailHelp: String { return self._s[1608]! }
+ public func Activity_RemindAboutUser(_ _0: String) -> (String, [(Int, NSRange)]) {
+ return formatWithArgumentRanges(self._s[1609]!, self._r[1609]!, [_0])
+ }
+ public var Passport_Identity_GenderPlaceholder: String { return self._s[1610]! }
+ public var Weekday_ShortSaturday: String { return self._s[1611]! }
+ public var ContactInfo_PhoneLabelMain: String { return self._s[1612]! }
+ public var Watch_Conversation_UserInfo: String { return self._s[1613]! }
+ public var CheckoutInfo_ShippingInfoCityPlaceholder: String { return self._s[1614]! }
+ public var GroupPermission_PermissionDisabledByDefault: String { return self._s[1615]! }
+ public var PrivacyLastSeenSettings_Title: String { return self._s[1616]! }
+ public var Conversation_ShareBotLocationConfirmation: String { return self._s[1618]! }
+ public var PhotoEditor_VignetteTool: String { return self._s[1619]! }
+ public var Conversation_ContextMenuDiscuss: String { return self._s[1620]! }
+ public var Passport_Address_Street1Placeholder: String { return self._s[1621]! }
+ public var Passport_Language_et: String { return self._s[1622]! }
+ public var AppUpgrade_Running: String { return self._s[1623]! }
+ public var Channel_DiscussionGroup_Info: String { return self._s[1625]! }
+ public var EditTheme_Create_Preview_IncomingReplyName: String { return self._s[1626]! }
+ public var Passport_Language_bg: String { return self._s[1627]! }
+ public var Stickers_NoStickersFound: String { return self._s[1629]! }
public func PUSH_CHANNEL_MESSAGE_TEXT(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1620]!, self._r[1620]!, [_1, _2])
+ return formatWithArgumentRanges(self._s[1631]!, self._r[1631]!, [_1, _2])
}
public func VoiceOver_Chat_ContactFrom(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1621]!, self._r[1621]!, [_0])
- }
- public var Wallet_Month_GenJuly: String { return self._s[1622]! }
- public var Wallet_Receive_AddressHeader: String { return self._s[1624]! }
- public var Wallet_Send_AmountText: String { return self._s[1625]! }
- public var Settings_About: String { return self._s[1626]! }
- public func Channel_AdminLog_MessageRestricted(_ _0: String, _ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1627]!, self._r[1627]!, [_0, _1, _2])
- }
- public var ChatList_Context_MarkAsRead: String { return self._s[1629]! }
- public var KeyCommand_NewMessage: String { return self._s[1630]! }
- public var Group_ErrorAddBlocked: String { return self._s[1631]! }
- public func Message_PaymentSent(_ _0: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[1632]!, self._r[1632]!, [_0])
}
- public var Map_LocationTitle: String { return self._s[1633]! }
- public var ReportGroupLocation_Title: String { return self._s[1634]! }
- public var CallSettings_UseLessDataLongDescription: String { return self._s[1635]! }
- public var Cache_ClearProgress: String { return self._s[1636]! }
+ public var Wallet_Month_GenJuly: String { return self._s[1633]! }
+ public var Wallet_Receive_AddressHeader: String { return self._s[1635]! }
+ public var Wallet_Send_AmountText: String { return self._s[1636]! }
+ public var Settings_About: String { return self._s[1637]! }
+ public func Channel_AdminLog_MessageRestricted(_ _0: String, _ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
+ return formatWithArgumentRanges(self._s[1638]!, self._r[1638]!, [_0, _1, _2])
+ }
+ public var ChatList_Context_MarkAsRead: String { return self._s[1640]! }
+ public var KeyCommand_NewMessage: String { return self._s[1641]! }
+ public var Group_ErrorAddBlocked: String { return self._s[1642]! }
+ public func Message_PaymentSent(_ _0: String) -> (String, [(Int, NSRange)]) {
+ return formatWithArgumentRanges(self._s[1643]!, self._r[1643]!, [_0])
+ }
+ public var Map_LocationTitle: String { return self._s[1644]! }
+ public var ReportGroupLocation_Title: String { return self._s[1645]! }
+ public var CallSettings_UseLessDataLongDescription: String { return self._s[1646]! }
+ public var Cache_ClearProgress: String { return self._s[1647]! }
public func Channel_Management_ErrorNotMember(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1637]!, self._r[1637]!, [_0])
+ return formatWithArgumentRanges(self._s[1648]!, self._r[1648]!, [_0])
}
- public var GroupRemoved_AddToGroup: String { return self._s[1638]! }
+ public var GroupRemoved_AddToGroup: String { return self._s[1649]! }
public func External_OpenIn(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1639]!, self._r[1639]!, [_0])
+ return formatWithArgumentRanges(self._s[1650]!, self._r[1650]!, [_0])
}
- public var Passport_UpdateRequiredError: String { return self._s[1640]! }
- public var Wallet_SecureStorageNotAvailable_Text: String { return self._s[1641]! }
+ public var Passport_UpdateRequiredError: String { return self._s[1651]! }
+ public var Wallet_SecureStorageNotAvailable_Text: String { return self._s[1652]! }
public func PUSH_MESSAGE_DOC(_ _1: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1642]!, self._r[1642]!, [_1])
+ return formatWithArgumentRanges(self._s[1653]!, self._r[1653]!, [_1])
}
- public var Notifications_PermissionsSuppressWarningText: String { return self._s[1644]! }
- public var Passport_Identity_MainPageHelp: String { return self._s[1645]! }
- public var PeerInfo_ButtonSearch: String { return self._s[1646]! }
- public var Conversation_StatusKickedFromGroup: String { return self._s[1647]! }
- public var Passport_Language_ka: String { return self._s[1648]! }
+ public var Notifications_PermissionsSuppressWarningText: String { return self._s[1655]! }
+ public var Passport_Identity_MainPageHelp: String { return self._s[1656]! }
+ public var PeerInfo_ButtonSearch: String { return self._s[1657]! }
+ public var Conversation_StatusKickedFromGroup: String { return self._s[1658]! }
+ public var Passport_Language_ka: String { return self._s[1659]! }
public func Wallet_Time_PreciseDate_m12(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1649]!, self._r[1649]!, [_1, _2, _3])
+ return formatWithArgumentRanges(self._s[1660]!, self._r[1660]!, [_1, _2, _3])
}
- public var Call_Decline: String { return self._s[1650]! }
- public var SocksProxySetup_ProxyEnabled: String { return self._s[1651]! }
- public var TwoFactorSetup_Email_SkipConfirmationText: String { return self._s[1654]! }
+ public var Call_Decline: String { return self._s[1661]! }
+ public var SocksProxySetup_ProxyEnabled: String { return self._s[1662]! }
+ public var TwoFactorSetup_Email_SkipConfirmationText: String { return self._s[1665]! }
public func AuthCode_Alert(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1655]!, self._r[1655]!, [_0])
+ return formatWithArgumentRanges(self._s[1666]!, self._r[1666]!, [_0])
}
- public var CallFeedback_Send: String { return self._s[1656]! }
- public var EditTheme_EditTitle: String { return self._s[1657]! }
+ public var CallFeedback_Send: String { return self._s[1667]! }
+ public var EditTheme_EditTitle: String { return self._s[1668]! }
public func Channel_AdminLog_MessagePromotedNameUsername(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1658]!, self._r[1658]!, [_1, _2])
+ return formatWithArgumentRanges(self._s[1669]!, self._r[1669]!, [_1, _2])
}
- public var Passport_Phone_UseTelegramNumberHelp: String { return self._s[1659]! }
+ public var Passport_Phone_UseTelegramNumberHelp: String { return self._s[1670]! }
public func Wallet_Updated_TodayAt(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1661]!, self._r[1661]!, [_0])
+ return formatWithArgumentRanges(self._s[1672]!, self._r[1672]!, [_0])
}
- public var Media_SendingOptionsTooltip: String { return self._s[1662]! }
- public var SettingsSearch_Synonyms_Data_Title: String { return self._s[1663]! }
- public var Passport_DeletePassport: String { return self._s[1664]! }
- public var Appearance_AppIconFilled: String { return self._s[1665]! }
- public var Privacy_Calls_P2PAlways: String { return self._s[1666]! }
- public var Month_ShortDecember: String { return self._s[1667]! }
- public var Channel_AdminLog_CanEditMessages: String { return self._s[1669]! }
+ public var Media_SendingOptionsTooltip: String { return self._s[1673]! }
+ public var Call_YourMicrophoneOff: String { return self._s[1674]! }
+ public var SettingsSearch_Synonyms_Data_Title: String { return self._s[1675]! }
+ public var Passport_DeletePassport: String { return self._s[1676]! }
+ public var Appearance_AppIconFilled: String { return self._s[1677]! }
+ public var Privacy_Calls_P2PAlways: String { return self._s[1678]! }
+ public var Month_ShortDecember: String { return self._s[1679]! }
+ public var Channel_AdminLog_CanEditMessages: String { return self._s[1681]! }
public func Contacts_AccessDeniedHelpLandscape(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1670]!, self._r[1670]!, [_0])
+ return formatWithArgumentRanges(self._s[1682]!, self._r[1682]!, [_0])
}
- public var Channel_Stickers_Searching: String { return self._s[1671]! }
- public var Conversation_EncryptedDescription1: String { return self._s[1672]! }
- public var Conversation_EncryptedDescription2: String { return self._s[1673]! }
- public var PasscodeSettings_PasscodeOptions: String { return self._s[1674]! }
- public var ChatListFolder_NameUnread: String { return self._s[1676]! }
- public var Conversation_EncryptedDescription3: String { return self._s[1677]! }
- public var PhotoEditor_SharpenTool: String { return self._s[1678]! }
- public var Wallet_Configuration_Title: String { return self._s[1679]! }
+ public var Channel_Stickers_Searching: String { return self._s[1683]! }
+ public var Conversation_EncryptedDescription1: String { return self._s[1684]! }
+ public var Conversation_EncryptedDescription2: String { return self._s[1685]! }
+ public var PasscodeSettings_PasscodeOptions: String { return self._s[1686]! }
+ public var ChatListFolder_NameUnread: String { return self._s[1688]! }
+ public var Conversation_EncryptedDescription3: String { return self._s[1689]! }
+ public var PhotoEditor_SharpenTool: String { return self._s[1690]! }
+ public var Wallet_Configuration_Title: String { return self._s[1691]! }
public func Conversation_AddNameToContacts(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1680]!, self._r[1680]!, [_0])
- }
- public var Conversation_EncryptedDescription4: String { return self._s[1683]! }
- public var Channel_Members_AddMembers: String { return self._s[1684]! }
- public var Wallpaper_Search: String { return self._s[1685]! }
- public func Message_GenericForwardedPsa(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1687]!, self._r[1687]!, [_0])
- }
- public var Weekday_Friday: String { return self._s[1688]! }
- public var Privacy_ContactsSync: String { return self._s[1689]! }
- public var SettingsSearch_Synonyms_Privacy_Data_ContactsReset: String { return self._s[1690]! }
- public var ApplyLanguage_ChangeLanguageAction: String { return self._s[1691]! }
- public func Channel_Management_RestrictedBy(_ _0: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[1692]!, self._r[1692]!, [_0])
}
- public var Wallet_Configuration_BlockchainIdHeader: String { return self._s[1693]! }
- public var GroupInfo_Permissions_Removed: String { return self._s[1694]! }
- public var ScheduledMessages_ScheduledOnline: String { return self._s[1695]! }
- public var Passport_Identity_GenderMale: String { return self._s[1696]! }
+ public var Conversation_EncryptedDescription4: String { return self._s[1695]! }
+ public var Channel_Members_AddMembers: String { return self._s[1696]! }
+ public var Wallpaper_Search: String { return self._s[1697]! }
+ public func Message_GenericForwardedPsa(_ _0: String) -> (String, [(Int, NSRange)]) {
+ return formatWithArgumentRanges(self._s[1699]!, self._r[1699]!, [_0])
+ }
+ public var Weekday_Friday: String { return self._s[1700]! }
+ public var Privacy_ContactsSync: String { return self._s[1701]! }
+ public var SettingsSearch_Synonyms_Privacy_Data_ContactsReset: String { return self._s[1702]! }
+ public var ApplyLanguage_ChangeLanguageAction: String { return self._s[1703]! }
+ public func Channel_Management_RestrictedBy(_ _0: String) -> (String, [(Int, NSRange)]) {
+ return formatWithArgumentRanges(self._s[1704]!, self._r[1704]!, [_0])
+ }
+ public var Wallet_Configuration_BlockchainIdHeader: String { return self._s[1705]! }
+ public var GroupInfo_Permissions_Removed: String { return self._s[1706]! }
+ public var ScheduledMessages_ScheduledOnline: String { return self._s[1707]! }
+ public var Passport_Identity_GenderMale: String { return self._s[1708]! }
public func Call_StatusBar(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1697]!, self._r[1697]!, [_0])
+ return formatWithArgumentRanges(self._s[1709]!, self._r[1709]!, [_0])
}
- public var Notifications_PermissionsKeepDisabled: String { return self._s[1698]! }
- public var Conversation_JumpToDate: String { return self._s[1699]! }
- public var Contacts_GlobalSearch: String { return self._s[1700]! }
- public var AutoDownloadSettings_ResetHelp: String { return self._s[1701]! }
- public var SettingsSearch_Synonyms_FAQ: String { return self._s[1702]! }
- public var ChatListFolderSettings_NewFolder: String { return self._s[1703]! }
- public var Profile_MessageLifetime1d: String { return self._s[1704]! }
+ public var Notifications_PermissionsKeepDisabled: String { return self._s[1710]! }
+ public var Conversation_JumpToDate: String { return self._s[1711]! }
+ public var Contacts_GlobalSearch: String { return self._s[1712]! }
+ public var AutoDownloadSettings_ResetHelp: String { return self._s[1713]! }
+ public var SettingsSearch_Synonyms_FAQ: String { return self._s[1714]! }
+ public var ChatListFolderSettings_NewFolder: String { return self._s[1715]! }
+ public var Profile_MessageLifetime1d: String { return self._s[1716]! }
public func MESSAGE_INVOICE(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1705]!, self._r[1705]!, [_1, _2])
+ return formatWithArgumentRanges(self._s[1717]!, self._r[1717]!, [_1, _2])
}
- public var StickerPack_BuiltinPackName: String { return self._s[1708]! }
+ public var StickerPack_BuiltinPackName: String { return self._s[1720]! }
public func PUSH_CHAT_MESSAGE_AUDIO(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1709]!, self._r[1709]!, [_1, _2])
+ return formatWithArgumentRanges(self._s[1721]!, self._r[1721]!, [_1, _2])
}
- public var VoiceOver_Chat_RecordModeVoiceMessageInfo: String { return self._s[1710]! }
- public var Passport_InfoTitle: String { return self._s[1712]! }
- public var Notifications_PermissionsUnreachableText: String { return self._s[1713]! }
+ public var VoiceOver_Chat_RecordModeVoiceMessageInfo: String { return self._s[1722]! }
+ public var Passport_InfoTitle: String { return self._s[1724]! }
+ public var Notifications_PermissionsUnreachableText: String { return self._s[1725]! }
public func NetworkUsageSettings_CellularUsageSince(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1717]!, self._r[1717]!, [_0])
+ return formatWithArgumentRanges(self._s[1729]!, self._r[1729]!, [_0])
}
public func PUSH_CHAT_MESSAGE_GEO(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1718]!, self._r[1718]!, [_1, _2])
+ return formatWithArgumentRanges(self._s[1730]!, self._r[1730]!, [_1, _2])
}
- public var Passport_Address_TypePassportRegistrationUploadScan: String { return self._s[1719]! }
- public var Profile_BotInfo: String { return self._s[1720]! }
- public var Watch_Compose_CreateMessage: String { return self._s[1721]! }
- public var AutoDownloadSettings_VoiceMessagesInfo: String { return self._s[1722]! }
- public var Month_ShortNovember: String { return self._s[1723]! }
- public var Conversation_ScamWarning: String { return self._s[1724]! }
- public var Wallpaper_SetCustomBackground: String { return self._s[1725]! }
- public var Appearance_TextSize_Title: String { return self._s[1726]! }
- public var ChatList_EmptyChatListFilterTitle: String { return self._s[1727]! }
- public var Passport_Identity_TranslationsHelp: String { return self._s[1728]! }
- public var NotificationsSound_Chime: String { return self._s[1729]! }
- public var Passport_Language_ko: String { return self._s[1731]! }
- public var InviteText_URL: String { return self._s[1732]! }
- public var TextFormat_Monospace: String { return self._s[1733]! }
+ public var Passport_Address_TypePassportRegistrationUploadScan: String { return self._s[1731]! }
+ public var Profile_BotInfo: String { return self._s[1732]! }
+ public var Watch_Compose_CreateMessage: String { return self._s[1733]! }
+ public var AutoDownloadSettings_VoiceMessagesInfo: String { return self._s[1734]! }
+ public var Month_ShortNovember: String { return self._s[1735]! }
+ public var Conversation_ScamWarning: String { return self._s[1736]! }
+ public var Wallpaper_SetCustomBackground: String { return self._s[1737]! }
+ public var Appearance_TextSize_Title: String { return self._s[1738]! }
+ public var Conversation_ContextMenuOpenProfile: String { return self._s[1739]! }
+ public var ChatList_EmptyChatListFilterTitle: String { return self._s[1740]! }
+ public func Call_BatteryLow(_ _0: String) -> (String, [(Int, NSRange)]) {
+ return formatWithArgumentRanges(self._s[1741]!, self._r[1741]!, [_0])
+ }
+ public var Passport_Identity_TranslationsHelp: String { return self._s[1742]! }
+ public var NotificationsSound_Chime: String { return self._s[1743]! }
+ public var Passport_Language_ko: String { return self._s[1745]! }
+ public var InviteText_URL: String { return self._s[1746]! }
+ public var TextFormat_Monospace: String { return self._s[1747]! }
public func Time_PreciseDate_m11(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1734]!, self._r[1734]!, [_1, _2, _3])
+ return formatWithArgumentRanges(self._s[1748]!, self._r[1748]!, [_1, _2, _3])
}
- public var EditTheme_Edit_BottomInfo: String { return self._s[1735]! }
+ public var EditTheme_Edit_BottomInfo: String { return self._s[1749]! }
public func Login_WillSendSms(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1736]!, self._r[1736]!, [_0])
+ return formatWithArgumentRanges(self._s[1750]!, self._r[1750]!, [_0])
}
public func Watch_Time_ShortWeekdayAt(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1737]!, self._r[1737]!, [_1, _2])
+ return formatWithArgumentRanges(self._s[1751]!, self._r[1751]!, [_1, _2])
}
- public var Wallet_Words_Title: String { return self._s[1738]! }
- public var Wallet_Month_ShortMay: String { return self._s[1739]! }
- public var EditTheme_CreateTitle: String { return self._s[1741]! }
- public var Passport_InfoLearnMore: String { return self._s[1742]! }
- public var TwoStepAuth_EmailPlaceholder: String { return self._s[1743]! }
- public var Passport_Identity_AddIdentityCard: String { return self._s[1744]! }
- public var Your_card_has_expired: String { return self._s[1745]! }
- public var StickerPacksSettings_StickerPacksSection: String { return self._s[1746]! }
- public var GroupInfo_InviteLink_Help: String { return self._s[1747]! }
- public var TwoFactorSetup_EmailVerification_ResendAction: String { return self._s[1751]! }
- public var Conversation_Report: String { return self._s[1753]! }
- public var Notifications_MessageNotificationsSound: String { return self._s[1754]! }
- public var Notification_MessageLifetime1m: String { return self._s[1755]! }
- public var Privacy_ContactsTitle: String { return self._s[1756]! }
- public var Conversation_ShareMyContactInfo: String { return self._s[1757]! }
- public var Wallet_WordCheck_Title: String { return self._s[1758]! }
- public var ChannelMembers_WhoCanAddMembersAdminsHelp: String { return self._s[1759]! }
- public var Channel_Members_Title: String { return self._s[1760]! }
- public var Map_OpenInWaze: String { return self._s[1761]! }
- public var Appearance_RemoveThemeColorConfirmation: String { return self._s[1762]! }
- public var Stats_GroupTopWeekdaysTitle: String { return self._s[1763]! }
- public var Login_PhoneBannedError: String { return self._s[1764]! }
- public var PeerInfo_GroupAboutItem: String { return self._s[1765]! }
+ public var Wallet_Words_Title: String { return self._s[1752]! }
+ public var Wallet_Month_ShortMay: String { return self._s[1753]! }
+ public var EditTheme_CreateTitle: String { return self._s[1755]! }
+ public var Passport_InfoLearnMore: String { return self._s[1756]! }
+ public var TwoStepAuth_EmailPlaceholder: String { return self._s[1757]! }
+ public var Passport_Identity_AddIdentityCard: String { return self._s[1758]! }
+ public var Your_card_has_expired: String { return self._s[1759]! }
+ public var StickerPacksSettings_StickerPacksSection: String { return self._s[1760]! }
+ public var Call_AudioRouteMute: String { return self._s[1761]! }
+ public var GroupInfo_InviteLink_Help: String { return self._s[1762]! }
+ public var TwoFactorSetup_EmailVerification_ResendAction: String { return self._s[1766]! }
+ public var Conversation_Report: String { return self._s[1768]! }
+ public var Notifications_MessageNotificationsSound: String { return self._s[1769]! }
+ public var Notification_MessageLifetime1m: String { return self._s[1770]! }
+ public var Privacy_ContactsTitle: String { return self._s[1771]! }
+ public var Conversation_ShareMyContactInfo: String { return self._s[1772]! }
+ public var Wallet_WordCheck_Title: String { return self._s[1773]! }
+ public var ChannelMembers_WhoCanAddMembersAdminsHelp: String { return self._s[1774]! }
+ public var Channel_Members_Title: String { return self._s[1775]! }
+ public var Map_OpenInWaze: String { return self._s[1776]! }
+ public var Appearance_RemoveThemeColorConfirmation: String { return self._s[1777]! }
+ public var Stats_GroupTopWeekdaysTitle: String { return self._s[1778]! }
+ public var Login_PhoneBannedError: String { return self._s[1779]! }
+ public var PeerInfo_GroupAboutItem: String { return self._s[1780]! }
public func LiveLocationUpdated_YesterdayAt(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1766]!, self._r[1766]!, [_0])
+ return formatWithArgumentRanges(self._s[1781]!, self._r[1781]!, [_0])
}
- public var IntentsSettings_MainAccount: String { return self._s[1767]! }
- public var Group_Management_AddModeratorHelp: String { return self._s[1768]! }
- public var AutoDownloadSettings_WifiTitle: String { return self._s[1769]! }
- public var Common_OK: String { return self._s[1770]! }
- public var Passport_Address_TypeBankStatementUploadScan: String { return self._s[1771]! }
- public var Wallet_Words_NotDoneResponse: String { return self._s[1772]! }
- public var Cache_Music: String { return self._s[1773]! }
- public var Wallet_Configuration_SourceURL: String { return self._s[1774]! }
- public var SettingsSearch_Synonyms_EditProfile_PhoneNumber: String { return self._s[1775]! }
- public var PasscodeSettings_UnlockWithTouchId: String { return self._s[1778]! }
- public var ChatList_EmptyChatListEditFilter: String { return self._s[1779]! }
- public var TwoStepAuth_HintPlaceholder: String { return self._s[1780]! }
+ public var IntentsSettings_MainAccount: String { return self._s[1782]! }
+ public var Group_Management_AddModeratorHelp: String { return self._s[1783]! }
+ public var AutoDownloadSettings_WifiTitle: String { return self._s[1784]! }
+ public var Common_OK: String { return self._s[1785]! }
+ public var Passport_Address_TypeBankStatementUploadScan: String { return self._s[1786]! }
+ public var Wallet_Words_NotDoneResponse: String { return self._s[1787]! }
+ public var Cache_Music: String { return self._s[1788]! }
+ public var Wallet_Configuration_SourceURL: String { return self._s[1789]! }
+ public var SettingsSearch_Synonyms_EditProfile_PhoneNumber: String { return self._s[1790]! }
+ public var PasscodeSettings_UnlockWithTouchId: String { return self._s[1793]! }
+ public var ChatList_EmptyChatListEditFilter: String { return self._s[1794]! }
+ public var TwoStepAuth_HintPlaceholder: String { return self._s[1795]! }
public func PUSH_PINNED_INVOICE(_ _1: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1781]!, self._r[1781]!, [_1])
+ return formatWithArgumentRanges(self._s[1796]!, self._r[1796]!, [_1])
}
public func Passport_RequestHeader(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1782]!, self._r[1782]!, [_0])
+ return formatWithArgumentRanges(self._s[1797]!, self._r[1797]!, [_0])
}
- public var TwoFactorSetup_Done_Action: String { return self._s[1783]! }
+ public var TwoFactorSetup_Done_Action: String { return self._s[1798]! }
public func VoiceOver_Chat_ContactOrganization(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1784]!, self._r[1784]!, [_0])
+ return formatWithArgumentRanges(self._s[1799]!, self._r[1799]!, [_0])
}
- public var Wallet_Send_ErrorNotEnoughFundsText: String { return self._s[1785]! }
- public var Watch_MessageView_ViewOnPhone: String { return self._s[1787]! }
- public var Privacy_Calls_CustomShareHelp: String { return self._s[1788]! }
- public var Wallet_Receive_CreateInvoiceInfo: String { return self._s[1790]! }
- public var ChangePhoneNumberNumber_Title: String { return self._s[1791]! }
- public var State_ConnectingToProxyInfo: String { return self._s[1792]! }
- public var Conversation_SwipeToReplyHintTitle: String { return self._s[1793]! }
- public var Message_VideoMessage: String { return self._s[1795]! }
- public var ChannelInfo_DeleteChannel: String { return self._s[1796]! }
- public var ContactInfo_PhoneLabelOther: String { return self._s[1797]! }
- public var Channel_EditAdmin_CannotEdit: String { return self._s[1798]! }
- public var Passport_DeleteAddressConfirmation: String { return self._s[1799]! }
+ public var Wallet_Send_ErrorNotEnoughFundsText: String { return self._s[1800]! }
+ public var Watch_MessageView_ViewOnPhone: String { return self._s[1802]! }
+ public var Privacy_Calls_CustomShareHelp: String { return self._s[1803]! }
+ public var Wallet_Receive_CreateInvoiceInfo: String { return self._s[1805]! }
+ public var ChangePhoneNumberNumber_Title: String { return self._s[1806]! }
+ public var State_ConnectingToProxyInfo: String { return self._s[1807]! }
+ public var Conversation_SwipeToReplyHintTitle: String { return self._s[1808]! }
+ public var Message_VideoMessage: String { return self._s[1810]! }
+ public var ChannelInfo_DeleteChannel: String { return self._s[1811]! }
+ public var ContactInfo_PhoneLabelOther: String { return self._s[1812]! }
+ public var Channel_EditAdmin_CannotEdit: String { return self._s[1813]! }
+ public var Passport_DeleteAddressConfirmation: String { return self._s[1814]! }
public func Wallet_Time_PreciseDate_m9(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1800]!, self._r[1800]!, [_1, _2, _3])
+ return formatWithArgumentRanges(self._s[1815]!, self._r[1815]!, [_1, _2, _3])
}
- public var WallpaperPreview_SwipeBottomText: String { return self._s[1801]! }
- public var Activity_RecordingAudio: String { return self._s[1802]! }
- public var SettingsSearch_Synonyms_Watch: String { return self._s[1803]! }
- public var PasscodeSettings_TryAgainIn1Minute: String { return self._s[1804]! }
- public var Wallet_Info_Address: String { return self._s[1805]! }
+ public var WallpaperPreview_SwipeBottomText: String { return self._s[1816]! }
+ public var Activity_RecordingAudio: String { return self._s[1817]! }
+ public var SettingsSearch_Synonyms_Watch: String { return self._s[1818]! }
+ public var PasscodeSettings_TryAgainIn1Minute: String { return self._s[1819]! }
+ public var Wallet_Info_Address: String { return self._s[1820]! }
+ public var Notification_VideoCallCanceled: String { return self._s[1821]! }
public func Notification_ChangedGroupName(_ _0: String, _ _1: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1807]!, self._r[1807]!, [_0, _1])
+ return formatWithArgumentRanges(self._s[1823]!, self._r[1823]!, [_0, _1])
}
public func EmptyGroupInfo_Line1(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1811]!, self._r[1811]!, [_0])
+ return formatWithArgumentRanges(self._s[1827]!, self._r[1827]!, [_0])
}
- public var ChatList_RemoveFolderConfirmation: String { return self._s[1812]! }
- public var Conversation_ApplyLocalization: String { return self._s[1813]! }
+ public var ChatList_RemoveFolderConfirmation: String { return self._s[1828]! }
+ public var Conversation_ApplyLocalization: String { return self._s[1829]! }
public func Conversation_PeerNearbyDistance(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1814]!, self._r[1814]!, [_1, _2])
+ return formatWithArgumentRanges(self._s[1830]!, self._r[1830]!, [_1, _2])
}
- public var TwoFactorSetup_Intro_Action: String { return self._s[1815]! }
- public var UserInfo_AddPhone: String { return self._s[1817]! }
- public var Map_ShareLiveLocationHelp: String { return self._s[1818]! }
+ public var TwoFactorSetup_Intro_Action: String { return self._s[1831]! }
+ public var UserInfo_AddPhone: String { return self._s[1833]! }
+ public var Map_ShareLiveLocationHelp: String { return self._s[1834]! }
public func Passport_Identity_NativeNameGenericHelp(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1819]!, self._r[1819]!, [_0])
+ return formatWithArgumentRanges(self._s[1835]!, self._r[1835]!, [_0])
}
- public var ChatListFolder_CategoryArchived: String { return self._s[1821]! }
- public var Call_IncomingVideoCall: String { return self._s[1822]! }
- public var Passport_Scans: String { return self._s[1823]! }
- public var BlockedUsers_Unblock: String { return self._s[1824]! }
+ public var ChatListFolder_CategoryArchived: String { return self._s[1837]! }
+ public var Call_IncomingVideoCall: String { return self._s[1838]! }
+ public var Passport_Scans: String { return self._s[1839]! }
+ public var BlockedUsers_Unblock: String { return self._s[1840]! }
public func PUSH_ENCRYPTION_REQUEST(_ _1: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1825]!, self._r[1825]!, [_1])
+ return formatWithArgumentRanges(self._s[1841]!, self._r[1841]!, [_1])
}
- public var Channel_Management_LabelCreator: String { return self._s[1826]! }
- public var Conversation_ReportSpamAndLeave: String { return self._s[1827]! }
- public var SettingsSearch_Synonyms_EditProfile_Bio: String { return self._s[1828]! }
- public var ChatList_UndoArchiveMultipleTitle: String { return self._s[1829]! }
- public var Passport_Identity_NativeNameGenericTitle: String { return self._s[1830]! }
+ public var Channel_Management_LabelCreator: String { return self._s[1842]! }
+ public var Conversation_ReportSpamAndLeave: String { return self._s[1843]! }
+ public var SettingsSearch_Synonyms_EditProfile_Bio: String { return self._s[1844]! }
+ public var ChatList_UndoArchiveMultipleTitle: String { return self._s[1845]! }
+ public var Passport_Identity_NativeNameGenericTitle: String { return self._s[1846]! }
public func Login_EmailPhoneBody(_ _0: String, _ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1831]!, self._r[1831]!, [_0, _1, _2])
+ return formatWithArgumentRanges(self._s[1847]!, self._r[1847]!, [_0, _1, _2])
+ }
+ public var Login_PhoneNumberHelp: String { return self._s[1848]! }
+ public var LastSeen_ALongTimeAgo: String { return self._s[1849]! }
+ public var Channel_AdminLog_CanPinMessages: String { return self._s[1850]! }
+ public var ChannelIntro_CreateChannel: String { return self._s[1851]! }
+ public var Conversation_UnreadMessages: String { return self._s[1852]! }
+ public var SettingsSearch_Synonyms_Stickers_ArchivedPacks: String { return self._s[1853]! }
+ public var Channel_AdminLog_EmptyText: String { return self._s[1854]! }
+ public var Theme_Context_Apply: String { return self._s[1855]! }
+ public var Notification_GroupActivated: String { return self._s[1856]! }
+ public var NotificationSettings_ContactJoinedInfo: String { return self._s[1857]! }
+ public var Wallet_Intro_CreateWallet: String { return self._s[1858]! }
+ public func Call_MicrophoneOff(_ _0: String) -> (String, [(Int, NSRange)]) {
+ return formatWithArgumentRanges(self._s[1859]!, self._r[1859]!, [_0])
}
- public var Login_PhoneNumberHelp: String { return self._s[1832]! }
- public var LastSeen_ALongTimeAgo: String { return self._s[1833]! }
- public var Channel_AdminLog_CanPinMessages: String { return self._s[1834]! }
- public var ChannelIntro_CreateChannel: String { return self._s[1835]! }
- public var Conversation_UnreadMessages: String { return self._s[1836]! }
- public var SettingsSearch_Synonyms_Stickers_ArchivedPacks: String { return self._s[1837]! }
- public var Channel_AdminLog_EmptyText: String { return self._s[1838]! }
- public var Theme_Context_Apply: String { return self._s[1839]! }
- public var Notification_GroupActivated: String { return self._s[1840]! }
- public var NotificationSettings_ContactJoinedInfo: String { return self._s[1841]! }
- public var Wallet_Intro_CreateWallet: String { return self._s[1842]! }
public func Notification_PinnedContactMessage(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1843]!, self._r[1843]!, [_0])
+ return formatWithArgumentRanges(self._s[1860]!, self._r[1860]!, [_0])
}
public func DownloadingStatus(_ _0: String, _ _1: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1844]!, self._r[1844]!, [_0, _1])
+ return formatWithArgumentRanges(self._s[1861]!, self._r[1861]!, [_0, _1])
}
- public var GroupInfo_ConvertToSupergroup: String { return self._s[1846]! }
+ public var GroupInfo_ConvertToSupergroup: String { return self._s[1863]! }
public func PrivacyPolicy_AgeVerificationMessage(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1847]!, self._r[1847]!, [_0])
+ return formatWithArgumentRanges(self._s[1864]!, self._r[1864]!, [_0])
}
- public var Undo_DeletedChannel: String { return self._s[1848]! }
- public var CallFeedback_AddComment: String { return self._s[1849]! }
+ public var Undo_DeletedChannel: String { return self._s[1865]! }
+ public var CallFeedback_AddComment: String { return self._s[1866]! }
public func Conversation_OpenBotLinkAllowMessages(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1850]!, self._r[1850]!, [_0])
+ return formatWithArgumentRanges(self._s[1867]!, self._r[1867]!, [_0])
}
- public var Document_TargetConfirmationFormat: String { return self._s[1851]! }
+ public var Document_TargetConfirmationFormat: String { return self._s[1868]! }
public func Call_StatusOngoing(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1852]!, self._r[1852]!, [_0])
+ return formatWithArgumentRanges(self._s[1869]!, self._r[1869]!, [_0])
}
- public var LogoutOptions_SetPasscodeTitle: String { return self._s[1853]! }
+ public var LogoutOptions_SetPasscodeTitle: String { return self._s[1870]! }
public func PUSH_CHAT_MESSAGE_GAME_SCORE(_ _1: String, _ _2: String, _ _3: String, _ _4: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1854]!, self._r[1854]!, [_1, _2, _3, _4])
+ return formatWithArgumentRanges(self._s[1871]!, self._r[1871]!, [_1, _2, _3, _4])
}
- public var Wallet_SecureStorageChanged_PasscodeText: String { return self._s[1855]! }
- public var Theme_ErrorNotFound: String { return self._s[1856]! }
- public var Contacts_SortByName: String { return self._s[1857]! }
- public var SettingsSearch_Synonyms_Privacy_Forwards: String { return self._s[1858]! }
+ public var Wallet_SecureStorageChanged_PasscodeText: String { return self._s[1872]! }
+ public var Theme_ErrorNotFound: String { return self._s[1873]! }
+ public var Contacts_SortByName: String { return self._s[1874]! }
+ public var SettingsSearch_Synonyms_Privacy_Forwards: String { return self._s[1875]! }
public func CHAT_MESSAGE_INVOICE(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1861]!, self._r[1861]!, [_1, _2, _3])
+ return formatWithArgumentRanges(self._s[1878]!, self._r[1878]!, [_1, _2, _3])
}
- public var Notification_Exceptions_RemoveFromExceptions: String { return self._s[1862]! }
- public var ScheduledMessages_EditTime: String { return self._s[1863]! }
- public var Conversation_ClearSelfHistory: String { return self._s[1864]! }
- public var Checkout_NewCard_PostcodePlaceholder: String { return self._s[1865]! }
- public var PasscodeSettings_DoNotMatch: String { return self._s[1866]! }
- public var Stickers_SuggestNone: String { return self._s[1867]! }
- public var ChatSettings_Cache: String { return self._s[1868]! }
- public var Settings_SaveIncomingPhotos: String { return self._s[1869]! }
- public var Media_ShareThisPhoto: String { return self._s[1870]! }
- public var Chat_SlowmodeTooltipPending: String { return self._s[1871]! }
- public var InfoPlist_NSContactsUsageDescription: String { return self._s[1872]! }
- public var Conversation_ContextMenuCopyLink: String { return self._s[1873]! }
- public var PrivacyPolicy_AgeVerificationTitle: String { return self._s[1874]! }
- public var SettingsSearch_Synonyms_Stickers_Masks: String { return self._s[1875]! }
- public var TwoStepAuth_SetupPasswordEnterPasswordNew: String { return self._s[1876]! }
- public var Appearance_ThemePreview_Chat_6_Text: String { return self._s[1877]! }
+ public var Notification_Exceptions_RemoveFromExceptions: String { return self._s[1879]! }
+ public var ScheduledMessages_EditTime: String { return self._s[1880]! }
+ public var Conversation_ClearSelfHistory: String { return self._s[1881]! }
+ public var Checkout_NewCard_PostcodePlaceholder: String { return self._s[1882]! }
+ public var PasscodeSettings_DoNotMatch: String { return self._s[1883]! }
+ public var Stickers_SuggestNone: String { return self._s[1884]! }
+ public var ChatSettings_Cache: String { return self._s[1885]! }
+ public var Settings_SaveIncomingPhotos: String { return self._s[1886]! }
+ public var Media_ShareThisPhoto: String { return self._s[1887]! }
+ public var Chat_SlowmodeTooltipPending: String { return self._s[1888]! }
+ public var InfoPlist_NSContactsUsageDescription: String { return self._s[1889]! }
+ public var Conversation_ContextMenuCopyLink: String { return self._s[1890]! }
+ public var PrivacyPolicy_AgeVerificationTitle: String { return self._s[1891]! }
+ public var SettingsSearch_Synonyms_Stickers_Masks: String { return self._s[1892]! }
+ public var TwoStepAuth_SetupPasswordEnterPasswordNew: String { return self._s[1893]! }
+ public var Appearance_ThemePreview_Chat_6_Text: String { return self._s[1894]! }
public func Wallet_SecureStorageReset_BiometryText(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1878]!, self._r[1878]!, [_0])
+ return formatWithArgumentRanges(self._s[1895]!, self._r[1895]!, [_0])
}
- public var PhotoEditor_BlurToolPortrait: String { return self._s[1879]! }
- public var Permissions_CellularDataTitle_v0: String { return self._s[1880]! }
- public var WallpaperSearch_ColorWhite: String { return self._s[1882]! }
- public var Channel_AdminLog_DefaultRestrictionsUpdated: String { return self._s[1883]! }
- public var Conversation_ErrorInaccessibleMessage: String { return self._s[1884]! }
- public var Map_OpenIn: String { return self._s[1885]! }
- public var PeerInfo_ButtonCall: String { return self._s[1886]! }
+ public var PhotoEditor_BlurToolPortrait: String { return self._s[1896]! }
+ public var Permissions_CellularDataTitle_v0: String { return self._s[1897]! }
+ public var WallpaperSearch_ColorWhite: String { return self._s[1899]! }
+ public var Channel_AdminLog_DefaultRestrictionsUpdated: String { return self._s[1900]! }
+ public var Conversation_ErrorInaccessibleMessage: String { return self._s[1901]! }
+ public var Map_OpenIn: String { return self._s[1902]! }
+ public var PeerInfo_ButtonCall: String { return self._s[1903]! }
public func PUSH_PHONE_CALL_MISSED(_ _1: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1890]!, self._r[1890]!, [_1])
+ return formatWithArgumentRanges(self._s[1907]!, self._r[1907]!, [_1])
}
public func ChannelInfo_AddParticipantConfirmation(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1891]!, self._r[1891]!, [_0])
+ return formatWithArgumentRanges(self._s[1908]!, self._r[1908]!, [_0])
}
- public var GroupInfo_Permissions_SlowmodeHeader: String { return self._s[1892]! }
- public var MessagePoll_LabelClosed: String { return self._s[1893]! }
- public var GroupPermission_PermissionGloballyDisabled: String { return self._s[1895]! }
- public var Wallet_Send_SendAnyway: String { return self._s[1896]! }
- public var Passport_Identity_MiddleNamePlaceholder: String { return self._s[1897]! }
- public var UserInfo_FirstNamePlaceholder: String { return self._s[1898]! }
- public var PrivacyLastSeenSettings_WhoCanSeeMyTimestamp: String { return self._s[1899]! }
- public var Map_SetThisPlace: String { return self._s[1900]! }
- public var Stats_GroupTopAdmin_Actions: String { return self._s[1901]! }
- public var Login_SelectCountry_Title: String { return self._s[1902]! }
- public var Channel_EditAdmin_PermissionBanUsers: String { return self._s[1903]! }
+ public var GroupInfo_Permissions_SlowmodeHeader: String { return self._s[1909]! }
+ public var MessagePoll_LabelClosed: String { return self._s[1910]! }
+ public var GroupPermission_PermissionGloballyDisabled: String { return self._s[1912]! }
+ public var Wallet_Send_SendAnyway: String { return self._s[1913]! }
+ public var Passport_Identity_MiddleNamePlaceholder: String { return self._s[1914]! }
+ public var UserInfo_FirstNamePlaceholder: String { return self._s[1915]! }
+ public var PrivacyLastSeenSettings_WhoCanSeeMyTimestamp: String { return self._s[1916]! }
+ public var Map_SetThisPlace: String { return self._s[1917]! }
+ public var Stats_GroupTopAdmin_Actions: String { return self._s[1918]! }
+ public var Login_SelectCountry_Title: String { return self._s[1919]! }
+ public var Channel_EditAdmin_PermissionBanUsers: String { return self._s[1920]! }
public func Conversation_OpenBotLinkLogin(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1904]!, self._r[1904]!, [_1, _2])
+ return formatWithArgumentRanges(self._s[1921]!, self._r[1921]!, [_1, _2])
}
- public var Channel_AdminLog_ChangeInfo: String { return self._s[1905]! }
- public var Watch_Suggestion_BRB: String { return self._s[1906]! }
- public var Passport_Identity_EditIdentityCard: String { return self._s[1907]! }
- public var Contacts_PermissionsTitle: String { return self._s[1908]! }
- public var Conversation_RestrictedInline: String { return self._s[1909]! }
- public var Appearance_RemoveThemeColor: String { return self._s[1911]! }
- public var StickerPack_ViewPack: String { return self._s[1912]! }
- public var Wallet_UnknownError: String { return self._s[1913]! }
+ public var Channel_AdminLog_ChangeInfo: String { return self._s[1922]! }
+ public var Watch_Suggestion_BRB: String { return self._s[1923]! }
+ public var Passport_Identity_EditIdentityCard: String { return self._s[1924]! }
+ public var Contacts_PermissionsTitle: String { return self._s[1925]! }
+ public var Conversation_RestrictedInline: String { return self._s[1926]! }
+ public var Appearance_RemoveThemeColor: String { return self._s[1928]! }
+ public var StickerPack_ViewPack: String { return self._s[1929]! }
+ public var Wallet_UnknownError: String { return self._s[1930]! }
public func Update_AppVersion(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1914]!, self._r[1914]!, [_0])
+ return formatWithArgumentRanges(self._s[1931]!, self._r[1931]!, [_0])
}
- public var Compose_NewChannel: String { return self._s[1916]! }
- public var ChatSettings_AutoDownloadSettings_TypePhoto: String { return self._s[1920]! }
- public var MessagePoll_LabelQuiz: String { return self._s[1922]! }
- public var Conversation_ReportSpamGroupConfirmation: String { return self._s[1923]! }
- public var Channel_Info_Stickers: String { return self._s[1924]! }
- public var AutoNightTheme_PreferredTheme: String { return self._s[1925]! }
- public var PrivacyPolicy_AgeVerificationAgree: String { return self._s[1926]! }
- public var Passport_DeletePersonalDetails: String { return self._s[1927]! }
- public var LogoutOptions_AddAccountTitle: String { return self._s[1928]! }
- public var Channel_DiscussionGroupInfo: String { return self._s[1929]! }
- public var Group_EditAdmin_RankOwnerPlaceholder: String { return self._s[1930]! }
- public var Stats_LoadingText: String { return self._s[1933]! }
- public var Conversation_SearchNoResults: String { return self._s[1934]! }
- public var ChatList_AddFolder: String { return self._s[1935]! }
- public var Wallet_Configuration_ApplyErrorTextURLInvalid: String { return self._s[1936]! }
- public var ChatListFolder_NameNonContacts: String { return self._s[1937]! }
- public var MessagePoll_LabelAnonymous: String { return self._s[1938]! }
- public var Channel_Members_AddAdminErrorNotAMember: String { return self._s[1939]! }
- public var Login_Code: String { return self._s[1940]! }
- public var EditTheme_Create_BottomInfo: String { return self._s[1941]! }
- public var Watch_Suggestion_WhatsUp: String { return self._s[1942]! }
- public var Weekday_ShortThursday: String { return self._s[1943]! }
- public var Notification_VideoCallOutgoing: String { return self._s[1944]! }
- public var Resolve_ErrorNotFound: String { return self._s[1945]! }
- public var LastSeen_Offline: String { return self._s[1947]! }
- public var PeopleNearby_NoMembers: String { return self._s[1948]! }
- public var GroupPermission_AddMembersNotAvailable: String { return self._s[1949]! }
- public var Privacy_Calls_AlwaysAllow_Title: String { return self._s[1950]! }
- public var Conversation_Dice_u1F3AF: String { return self._s[1952]! }
- public var GroupInfo_Title: String { return self._s[1953]! }
- public var NotificationsSound_Note: String { return self._s[1954]! }
- public var Conversation_EditingMessagePanelTitle: String { return self._s[1955]! }
- public var Watch_Message_Poll: String { return self._s[1956]! }
- public var Privacy_Calls: String { return self._s[1957]! }
+ public var Compose_NewChannel: String { return self._s[1933]! }
+ public var ChatSettings_AutoDownloadSettings_TypePhoto: String { return self._s[1937]! }
+ public var MessagePoll_LabelQuiz: String { return self._s[1939]! }
+ public var Conversation_ReportSpamGroupConfirmation: String { return self._s[1940]! }
+ public var Channel_Info_Stickers: String { return self._s[1941]! }
+ public var AutoNightTheme_PreferredTheme: String { return self._s[1942]! }
+ public var PrivacyPolicy_AgeVerificationAgree: String { return self._s[1943]! }
+ public var Passport_DeletePersonalDetails: String { return self._s[1944]! }
+ public var LogoutOptions_AddAccountTitle: String { return self._s[1945]! }
+ public var Channel_DiscussionGroupInfo: String { return self._s[1946]! }
+ public var Group_EditAdmin_RankOwnerPlaceholder: String { return self._s[1947]! }
+ public var Stats_LoadingText: String { return self._s[1950]! }
+ public var Conversation_SearchNoResults: String { return self._s[1951]! }
+ public var ChatList_AddFolder: String { return self._s[1952]! }
+ public var Wallet_Configuration_ApplyErrorTextURLInvalid: String { return self._s[1953]! }
+ public var ChatListFolder_NameNonContacts: String { return self._s[1954]! }
+ public var MessagePoll_LabelAnonymous: String { return self._s[1955]! }
+ public var Channel_Members_AddAdminErrorNotAMember: String { return self._s[1956]! }
+ public var Login_Code: String { return self._s[1957]! }
+ public var EditTheme_Create_BottomInfo: String { return self._s[1958]! }
+ public var Watch_Suggestion_WhatsUp: String { return self._s[1959]! }
+ public var Weekday_ShortThursday: String { return self._s[1960]! }
+ public var Notification_VideoCallOutgoing: String { return self._s[1961]! }
+ public var Resolve_ErrorNotFound: String { return self._s[1962]! }
+ public var LastSeen_Offline: String { return self._s[1964]! }
+ public var PeopleNearby_NoMembers: String { return self._s[1965]! }
+ public var GroupPermission_AddMembersNotAvailable: String { return self._s[1966]! }
+ public var Privacy_Calls_AlwaysAllow_Title: String { return self._s[1967]! }
+ public var Conversation_Dice_u1F3AF: String { return self._s[1969]! }
+ public var GroupInfo_Title: String { return self._s[1970]! }
+ public var NotificationsSound_Note: String { return self._s[1971]! }
+ public var Conversation_EditingMessagePanelTitle: String { return self._s[1972]! }
+ public var Watch_Message_Poll: String { return self._s[1973]! }
+ public var Privacy_Calls: String { return self._s[1974]! }
public func Channel_AdminLog_MessageRankUsername(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1958]!, self._r[1958]!, [_1, _2, _3])
+ return formatWithArgumentRanges(self._s[1975]!, self._r[1975]!, [_1, _2, _3])
}
- public var Month_ShortAugust: String { return self._s[1959]! }
- public var TwoStepAuth_SetPasswordHelp: String { return self._s[1960]! }
- public var Notifications_Reset: String { return self._s[1961]! }
- public var Conversation_Pin: String { return self._s[1962]! }
- public var Passport_Language_lv: String { return self._s[1963]! }
- public var Permissions_PeopleNearbyAllowInSettings_v0: String { return self._s[1964]! }
- public var BlockedUsers_Info: String { return self._s[1965]! }
- public var SettingsSearch_Synonyms_Data_AutoplayVideos: String { return self._s[1967]! }
- public var Watch_Conversation_Unblock: String { return self._s[1969]! }
+ public var Month_ShortAugust: String { return self._s[1976]! }
+ public var TwoStepAuth_SetPasswordHelp: String { return self._s[1977]! }
+ public var Notifications_Reset: String { return self._s[1978]! }
+ public var Conversation_Pin: String { return self._s[1979]! }
+ public var Passport_Language_lv: String { return self._s[1980]! }
+ public var Permissions_PeopleNearbyAllowInSettings_v0: String { return self._s[1981]! }
+ public var BlockedUsers_Info: String { return self._s[1982]! }
+ public var SettingsSearch_Synonyms_Data_AutoplayVideos: String { return self._s[1984]! }
+ public var Watch_Conversation_Unblock: String { return self._s[1986]! }
public func Time_MonthOfYear_m9(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1970]!, self._r[1970]!, [_0])
+ return formatWithArgumentRanges(self._s[1987]!, self._r[1987]!, [_0])
}
- public var CloudStorage_Title: String { return self._s[1971]! }
- public var GroupInfo_DeleteAndExitConfirmation: String { return self._s[1972]! }
+ public var CloudStorage_Title: String { return self._s[1988]! }
+ public var GroupInfo_DeleteAndExitConfirmation: String { return self._s[1989]! }
public func NetworkUsageSettings_WifiUsageSince(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1973]!, self._r[1973]!, [_0])
+ return formatWithArgumentRanges(self._s[1990]!, self._r[1990]!, [_0])
}
- public var Channel_AdminLogFilter_AdminsTitle: String { return self._s[1974]! }
- public var Watch_Suggestion_OnMyWay: String { return self._s[1975]! }
- public var TwoStepAuth_RecoveryEmailTitle: String { return self._s[1976]! }
- public var Passport_Address_EditBankStatement: String { return self._s[1977]! }
+ public var Channel_AdminLogFilter_AdminsTitle: String { return self._s[1991]! }
+ public var Watch_Suggestion_OnMyWay: String { return self._s[1992]! }
+ public var TwoStepAuth_RecoveryEmailTitle: String { return self._s[1993]! }
+ public var Passport_Address_EditBankStatement: String { return self._s[1994]! }
public func Channel_AdminLog_MessageChangedUnlinkedGroup(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[1978]!, self._r[1978]!, [_1, _2])
+ return formatWithArgumentRanges(self._s[1995]!, self._r[1995]!, [_1, _2])
}
- public var ChatSettings_DownloadInBackgroundInfo: String { return self._s[1979]! }
- public var ShareMenu_Comment: String { return self._s[1980]! }
- public var Permissions_ContactsTitle_v0: String { return self._s[1981]! }
- public var Notifications_PermissionsTitle: String { return self._s[1982]! }
- public var GroupPermission_NoSendLinks: String { return self._s[1983]! }
- public var Privacy_Forwards_NeverAllow_Title: String { return self._s[1984]! }
- public var Wallet_SecureStorageChanged_ImportWallet: String { return self._s[1985]! }
- public var PeerInfo_PaneLinks: String { return self._s[1986]! }
- public var Settings_Support: String { return self._s[1987]! }
- public var Notifications_ChannelNotificationsSound: String { return self._s[1988]! }
- public var SettingsSearch_Synonyms_Data_AutoDownloadReset: String { return self._s[1989]! }
- public var Settings_SetNewProfilePhotoOrVideo: String { return self._s[1990]! }
- public var Privacy_Forwards_Preview: String { return self._s[1991]! }
- public var GroupPermission_ApplyAlertAction: String { return self._s[1992]! }
- public var Watch_Stickers_StickerPacks: String { return self._s[1993]! }
- public var Common_Select: String { return self._s[1995]! }
- public var CheckoutInfo_ErrorEmailInvalid: String { return self._s[1996]! }
- public var WallpaperSearch_ColorGray: String { return self._s[1999]! }
- public var TwoFactorSetup_Password_PlaceholderPassword: String { return self._s[2000]! }
- public var TwoFactorSetup_Hint_SkipAction: String { return self._s[2001]! }
- public var ChatAdmins_AllMembersAreAdminsOffHelp: String { return self._s[2002]! }
- public var PollResults_Title: String { return self._s[2003]! }
- public var PasscodeSettings_AutoLock_IfAwayFor_5hours: String { return self._s[2004]! }
- public var Appearance_PreviewReplyAuthor: String { return self._s[2005]! }
- public var TwoStepAuth_RecoveryTitle: String { return self._s[2006]! }
- public var Widget_AuthRequired: String { return self._s[2007]! }
- public var ProfilePhoto_OpenInEditor: String { return self._s[2008]! }
- public var Camera_FlashOn: String { return self._s[2009]! }
- public var Conversation_ContextMenuLookUp: String { return self._s[2010]! }
- public var Channel_Stickers_NotFoundHelp: String { return self._s[2011]! }
- public var Watch_Suggestion_OK: String { return self._s[2012]! }
+ public var ChatSettings_DownloadInBackgroundInfo: String { return self._s[1996]! }
+ public var ShareMenu_Comment: String { return self._s[1997]! }
+ public var Permissions_ContactsTitle_v0: String { return self._s[1998]! }
+ public var Notifications_PermissionsTitle: String { return self._s[1999]! }
+ public var GroupPermission_NoSendLinks: String { return self._s[2000]! }
+ public var Privacy_Forwards_NeverAllow_Title: String { return self._s[2001]! }
+ public var Wallet_SecureStorageChanged_ImportWallet: String { return self._s[2002]! }
+ public var PeerInfo_PaneLinks: String { return self._s[2003]! }
+ public var Settings_Support: String { return self._s[2004]! }
+ public var Notifications_ChannelNotificationsSound: String { return self._s[2005]! }
+ public var SettingsSearch_Synonyms_Data_AutoDownloadReset: String { return self._s[2006]! }
+ public var Settings_SetNewProfilePhotoOrVideo: String { return self._s[2007]! }
+ public var Privacy_Forwards_Preview: String { return self._s[2008]! }
+ public var GroupPermission_ApplyAlertAction: String { return self._s[2009]! }
+ public var Watch_Stickers_StickerPacks: String { return self._s[2010]! }
+ public var Common_Select: String { return self._s[2012]! }
+ public var CheckoutInfo_ErrorEmailInvalid: String { return self._s[2013]! }
+ public var WallpaperSearch_ColorGray: String { return self._s[2016]! }
+ public var TwoFactorSetup_Password_PlaceholderPassword: String { return self._s[2017]! }
+ public var TwoFactorSetup_Hint_SkipAction: String { return self._s[2018]! }
+ public var ChatAdmins_AllMembersAreAdminsOffHelp: String { return self._s[2019]! }
+ public var PollResults_Title: String { return self._s[2020]! }
+ public var PasscodeSettings_AutoLock_IfAwayFor_5hours: String { return self._s[2021]! }
+ public var Appearance_PreviewReplyAuthor: String { return self._s[2022]! }
+ public var TwoStepAuth_RecoveryTitle: String { return self._s[2023]! }
+ public var Widget_AuthRequired: String { return self._s[2024]! }
+ public var ProfilePhoto_OpenInEditor: String { return self._s[2025]! }
+ public var Camera_FlashOn: String { return self._s[2026]! }
+ public var Conversation_ContextMenuLookUp: String { return self._s[2027]! }
+ public var Channel_Stickers_NotFoundHelp: String { return self._s[2028]! }
+ public var Watch_Suggestion_OK: String { return self._s[2029]! }
public func Username_LinkHint(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2014]!, self._r[2014]!, [_0])
+ return formatWithArgumentRanges(self._s[2031]!, self._r[2031]!, [_0])
}
public func Notification_PinnedLiveLocationMessage(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2016]!, self._r[2016]!, [_0])
+ return formatWithArgumentRanges(self._s[2033]!, self._r[2033]!, [_0])
}
- public var TextFormat_Strikethrough: String { return self._s[2017]! }
- public var DialogList_AdLabel: String { return self._s[2018]! }
- public var WatchRemote_NotificationText: String { return self._s[2019]! }
- public var IntentsSettings_SuggestedChatsSavedMessages: String { return self._s[2020]! }
- public var SettingsSearch_Synonyms_Notifications_MessageNotificationsAlert: String { return self._s[2021]! }
- public var Conversation_ReportSpam: String { return self._s[2022]! }
- public var SettingsSearch_Synonyms_Privacy_Data_TopPeers: String { return self._s[2023]! }
- public var Settings_LogoutConfirmationTitle: String { return self._s[2025]! }
- public var PhoneLabel_Title: String { return self._s[2026]! }
- public var Passport_Address_EditRentalAgreement: String { return self._s[2027]! }
- public var Settings_ChangePhoneNumber: String { return self._s[2028]! }
- public var Notifications_ExceptionsTitle: String { return self._s[2029]! }
- public var Notifications_AlertTones: String { return self._s[2030]! }
- public var Call_ReportIncludeLogDescription: String { return self._s[2031]! }
- public var SettingsSearch_Synonyms_Notifications_ResetAllNotifications: String { return self._s[2032]! }
- public var AutoDownloadSettings_PrivateChats: String { return self._s[2033]! }
- public var VoiceOver_Chat_Photo: String { return self._s[2035]! }
- public var TwoStepAuth_AddHintTitle: String { return self._s[2036]! }
- public var Stats_PostsTitle: String { return self._s[2037]! }
- public var ReportPeer_ReasonOther: String { return self._s[2038]! }
- public var ChatList_Context_JoinChannel: String { return self._s[2039]! }
- public var PhotoEditor_SkinTool: String { return self._s[2040]! }
- public var KeyCommand_ScrollDown: String { return self._s[2042]! }
- public var Conversation_ScheduleMessage_Title: String { return self._s[2043]! }
+ public var TextFormat_Strikethrough: String { return self._s[2034]! }
+ public var DialogList_AdLabel: String { return self._s[2035]! }
+ public var WatchRemote_NotificationText: String { return self._s[2036]! }
+ public var IntentsSettings_SuggestedChatsSavedMessages: String { return self._s[2037]! }
+ public var SettingsSearch_Synonyms_Notifications_MessageNotificationsAlert: String { return self._s[2038]! }
+ public var Conversation_ReportSpam: String { return self._s[2039]! }
+ public var SettingsSearch_Synonyms_Privacy_Data_TopPeers: String { return self._s[2040]! }
+ public var Settings_LogoutConfirmationTitle: String { return self._s[2042]! }
+ public var PhoneLabel_Title: String { return self._s[2043]! }
+ public var Passport_Address_EditRentalAgreement: String { return self._s[2044]! }
+ public var Settings_ChangePhoneNumber: String { return self._s[2045]! }
+ public var Notifications_ExceptionsTitle: String { return self._s[2046]! }
+ public var Notifications_AlertTones: String { return self._s[2047]! }
+ public var Call_ReportIncludeLogDescription: String { return self._s[2048]! }
+ public var SettingsSearch_Synonyms_Notifications_ResetAllNotifications: String { return self._s[2049]! }
+ public var AutoDownloadSettings_PrivateChats: String { return self._s[2050]! }
+ public var VoiceOver_Chat_Photo: String { return self._s[2052]! }
+ public var TwoStepAuth_AddHintTitle: String { return self._s[2053]! }
+ public var Stats_PostsTitle: String { return self._s[2054]! }
+ public var ReportPeer_ReasonOther: String { return self._s[2055]! }
+ public var ChatList_Context_JoinChannel: String { return self._s[2056]! }
+ public var PhotoEditor_SkinTool: String { return self._s[2057]! }
+ public var KeyCommand_ScrollDown: String { return self._s[2059]! }
+ public var Conversation_ScheduleMessage_Title: String { return self._s[2060]! }
public func Login_BannedPhoneSubject(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2044]!, self._r[2044]!, [_0])
+ return formatWithArgumentRanges(self._s[2061]!, self._r[2061]!, [_0])
}
- public var NetworkUsageSettings_MediaVideoDataSection: String { return self._s[2046]! }
- public var ChannelInfo_DeleteGroupConfirmation: String { return self._s[2047]! }
- public var AuthSessions_LogOut: String { return self._s[2048]! }
- public var Passport_Identity_TypeInternalPassport: String { return self._s[2049]! }
- public var ChatSettings_AutoDownloadVoiceMessages: String { return self._s[2050]! }
- public var Passport_Phone_Title: String { return self._s[2051]! }
- public var ContactList_Context_StartSecretChat: String { return self._s[2052]! }
- public var Settings_PhoneNumber: String { return self._s[2053]! }
+ public var NetworkUsageSettings_MediaVideoDataSection: String { return self._s[2063]! }
+ public var ChannelInfo_DeleteGroupConfirmation: String { return self._s[2064]! }
+ public var AuthSessions_LogOut: String { return self._s[2065]! }
+ public func PUSH_VIDEO_CALL_MISSED(_ _1: String) -> (String, [(Int, NSRange)]) {
+ return formatWithArgumentRanges(self._s[2066]!, self._r[2066]!, [_1])
+ }
+ public var Passport_Identity_TypeInternalPassport: String { return self._s[2067]! }
+ public var ChatSettings_AutoDownloadVoiceMessages: String { return self._s[2068]! }
+ public var Passport_Phone_Title: String { return self._s[2069]! }
+ public var ContactList_Context_StartSecretChat: String { return self._s[2070]! }
+ public var Settings_PhoneNumber: String { return self._s[2071]! }
public func Conversation_ScheduleMessage_SendToday(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2054]!, self._r[2054]!, [_0])
+ return formatWithArgumentRanges(self._s[2072]!, self._r[2072]!, [_0])
}
- public var NotificationsSound_Alert: String { return self._s[2056]! }
- public var Wallet_SecureStorageChanged_CreateWallet: String { return self._s[2057]! }
- public var WebSearch_SearchNoResults: String { return self._s[2058]! }
- public var Privacy_ProfilePhoto_AlwaysShareWith_Title: String { return self._s[2060]! }
- public var Wallet_Configuration_SourceInfo: String { return self._s[2061]! }
- public var LogoutOptions_AlternativeOptionsSection: String { return self._s[2062]! }
- public var SettingsSearch_Synonyms_Passport: String { return self._s[2063]! }
- public var PhotoEditor_CurvesTool: String { return self._s[2064]! }
- public var Checkout_PaymentMethod: String { return self._s[2066]! }
+ public var NotificationsSound_Alert: String { return self._s[2074]! }
+ public var Wallet_SecureStorageChanged_CreateWallet: String { return self._s[2075]! }
+ public var WebSearch_SearchNoResults: String { return self._s[2076]! }
+ public var Privacy_ProfilePhoto_AlwaysShareWith_Title: String { return self._s[2078]! }
+ public var Wallet_Configuration_SourceInfo: String { return self._s[2079]! }
+ public var LogoutOptions_AlternativeOptionsSection: String { return self._s[2080]! }
+ public var SettingsSearch_Synonyms_Passport: String { return self._s[2081]! }
+ public var PhotoEditor_CurvesTool: String { return self._s[2082]! }
+ public var Checkout_PaymentMethod: String { return self._s[2084]! }
public func PUSH_CHAT_ADD_YOU(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2067]!, self._r[2067]!, [_1, _2])
+ return formatWithArgumentRanges(self._s[2085]!, self._r[2085]!, [_1, _2])
}
- public var Contacts_AccessDeniedError: String { return self._s[2068]! }
- public var Camera_PhotoMode: String { return self._s[2071]! }
- public var EditTheme_Expand_Preview_IncomingText: String { return self._s[2072]! }
- public var Appearance_TextSize_Apply: String { return self._s[2073]! }
- public var Passport_Address_AddUtilityBill: String { return self._s[2075]! }
- public var ChatListFolderSettings_RecommendedNewFolder: String { return self._s[2076]! }
- public var CallSettings_OnMobile: String { return self._s[2077]! }
- public var Tour_Text2: String { return self._s[2078]! }
+ public var Contacts_AccessDeniedError: String { return self._s[2086]! }
+ public var Camera_PhotoMode: String { return self._s[2089]! }
+ public var EditTheme_Expand_Preview_IncomingText: String { return self._s[2090]! }
+ public var Appearance_TextSize_Apply: String { return self._s[2091]! }
+ public var Passport_Address_AddUtilityBill: String { return self._s[2093]! }
+ public var ChatListFolderSettings_RecommendedNewFolder: String { return self._s[2094]! }
+ public var CallSettings_OnMobile: String { return self._s[2095]! }
+ public var Tour_Text2: String { return self._s[2096]! }
public func PUSH_CHAT_MESSAGE_ROUND(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2079]!, self._r[2079]!, [_1, _2])
+ return formatWithArgumentRanges(self._s[2097]!, self._r[2097]!, [_1, _2])
}
- public var DialogList_EncryptionProcessing: String { return self._s[2081]! }
- public var Permissions_Skip: String { return self._s[2082]! }
- public var Wallet_Words_NotDoneOk: String { return self._s[2083]! }
- public var SecretImage_Title: String { return self._s[2084]! }
- public var Watch_MessageView_Title: String { return self._s[2085]! }
- public var Channel_DiscussionGroupAdd: String { return self._s[2086]! }
- public var AttachmentMenu_Poll: String { return self._s[2087]! }
+ public var DialogList_EncryptionProcessing: String { return self._s[2099]! }
+ public var Permissions_Skip: String { return self._s[2100]! }
+ public var Wallet_Words_NotDoneOk: String { return self._s[2101]! }
+ public var SecretImage_Title: String { return self._s[2102]! }
+ public var Watch_MessageView_Title: String { return self._s[2103]! }
+ public var Channel_DiscussionGroupAdd: String { return self._s[2104]! }
+ public var AttachmentMenu_Poll: String { return self._s[2105]! }
public func Notification_GroupInviter(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2088]!, self._r[2088]!, [_0])
+ return formatWithArgumentRanges(self._s[2106]!, self._r[2106]!, [_0])
}
public func Channel_DiscussionGroup_PrivateChannelLink(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2089]!, self._r[2089]!, [_1, _2])
+ return formatWithArgumentRanges(self._s[2107]!, self._r[2107]!, [_1, _2])
}
- public var Notification_CallCanceled: String { return self._s[2090]! }
- public var WallpaperPreview_Title: String { return self._s[2091]! }
- public var Privacy_PaymentsClear_PaymentInfo: String { return self._s[2092]! }
- public var Settings_ProxyConnecting: String { return self._s[2093]! }
- public var Settings_CheckPhoneNumberText: String { return self._s[2095]! }
- public var VoiceOver_Chat_YourVideo: String { return self._s[2096]! }
- public var Wallet_Intro_Title: String { return self._s[2097]! }
- public var TwoFactorSetup_Password_Action: String { return self._s[2098]! }
- public var Profile_MessageLifetime5s: String { return self._s[2099]! }
- public var Username_InvalidCharacters: String { return self._s[2100]! }
- public var VoiceOver_Media_PlaybackRateFast: String { return self._s[2101]! }
- public var ScheduledMessages_ClearAll: String { return self._s[2102]! }
- public var Group_MessageVideoUpdated: String { return self._s[2103]! }
- public var WallpaperPreview_CropBottomText: String { return self._s[2104]! }
- public var AutoDownloadSettings_LimitBySize: String { return self._s[2105]! }
- public var Settings_AddAccount: String { return self._s[2106]! }
- public var Notification_CreatedChannel: String { return self._s[2109]! }
+ public var Notification_CallCanceled: String { return self._s[2108]! }
+ public var WallpaperPreview_Title: String { return self._s[2109]! }
+ public var Privacy_PaymentsClear_PaymentInfo: String { return self._s[2110]! }
+ public var Settings_ProxyConnecting: String { return self._s[2111]! }
+ public var Settings_CheckPhoneNumberText: String { return self._s[2113]! }
+ public var VoiceOver_Chat_YourVideo: String { return self._s[2114]! }
+ public var Wallet_Intro_Title: String { return self._s[2115]! }
+ public var TwoFactorSetup_Password_Action: String { return self._s[2116]! }
+ public var Profile_MessageLifetime5s: String { return self._s[2117]! }
+ public var Username_InvalidCharacters: String { return self._s[2118]! }
+ public var VoiceOver_Media_PlaybackRateFast: String { return self._s[2119]! }
+ public var ScheduledMessages_ClearAll: String { return self._s[2120]! }
+ public var Group_MessageVideoUpdated: String { return self._s[2121]! }
+ public var WallpaperPreview_CropBottomText: String { return self._s[2122]! }
+ public var AutoDownloadSettings_LimitBySize: String { return self._s[2123]! }
+ public var Settings_AddAccount: String { return self._s[2124]! }
+ public var Notification_CreatedChannel: String { return self._s[2127]! }
public func PUSH_CHAT_DELETE_MEMBER(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2110]!, self._r[2110]!, [_1, _2, _3])
+ return formatWithArgumentRanges(self._s[2128]!, self._r[2128]!, [_1, _2, _3])
}
- public var Passcode_AppLockedAlert: String { return self._s[2112]! }
- public var StickerPacksSettings_AnimatedStickersInfo: String { return self._s[2113]! }
- public var VoiceOver_Media_PlaybackStop: String { return self._s[2114]! }
- public var Contacts_TopSection: String { return self._s[2115]! }
- public var ChatList_DeleteForEveryoneConfirmationAction: String { return self._s[2116]! }
+ public var Passcode_AppLockedAlert: String { return self._s[2130]! }
+ public var StickerPacksSettings_AnimatedStickersInfo: String { return self._s[2131]! }
+ public var VoiceOver_Media_PlaybackStop: String { return self._s[2132]! }
+ public var Contacts_TopSection: String { return self._s[2133]! }
+ public var ChatList_DeleteForEveryoneConfirmationAction: String { return self._s[2134]! }
public func Conversation_SetReminder_RemindOn(_ _0: String, _ _1: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2117]!, self._r[2117]!, [_0, _1])
+ return formatWithArgumentRanges(self._s[2135]!, self._r[2135]!, [_0, _1])
}
- public var Wallet_Info_Receive: String { return self._s[2118]! }
- public var Wallet_Completed_ViewWallet: String { return self._s[2120]! }
+ public var Wallet_Info_Receive: String { return self._s[2136]! }
+ public var Wallet_Completed_ViewWallet: String { return self._s[2138]! }
public func Time_MonthOfYear_m6(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2121]!, self._r[2121]!, [_0])
+ return formatWithArgumentRanges(self._s[2139]!, self._r[2139]!, [_0])
}
- public var ReportPeer_ReasonSpam: String { return self._s[2122]! }
- public var UserInfo_TapToCall: String { return self._s[2123]! }
- public var Conversation_ForwardAuthorHiddenTooltip: String { return self._s[2125]! }
- public var AutoDownloadSettings_DataUsageCustom: String { return self._s[2126]! }
- public var Common_Search: String { return self._s[2127]! }
- public var ScheduledMessages_EmptyPlaceholder: String { return self._s[2128]! }
+ public var ReportPeer_ReasonSpam: String { return self._s[2140]! }
+ public var UserInfo_TapToCall: String { return self._s[2141]! }
+ public var Conversation_ForwardAuthorHiddenTooltip: String { return self._s[2143]! }
+ public var AutoDownloadSettings_DataUsageCustom: String { return self._s[2144]! }
+ public var Common_Search: String { return self._s[2145]! }
+ public var ScheduledMessages_EmptyPlaceholder: String { return self._s[2146]! }
public func Channel_AdminLog_MessageChangedGroupGeoLocation(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2129]!, self._r[2129]!, [_0])
+ return formatWithArgumentRanges(self._s[2147]!, self._r[2147]!, [_0])
}
- public var Wallet_Month_ShortJuly: String { return self._s[2130]! }
- public var AuthSessions_IncompleteAttemptsInfo: String { return self._s[2132]! }
- public var Message_InvoiceLabel: String { return self._s[2133]! }
- public var Conversation_InputTextPlaceholder: String { return self._s[2134]! }
- public var NetworkUsageSettings_MediaImageDataSection: String { return self._s[2135]! }
+ public var Wallet_Month_ShortJuly: String { return self._s[2148]! }
+ public var AuthSessions_IncompleteAttemptsInfo: String { return self._s[2150]! }
+ public var Message_InvoiceLabel: String { return self._s[2151]! }
+ public var Conversation_InputTextPlaceholder: String { return self._s[2152]! }
+ public var NetworkUsageSettings_MediaImageDataSection: String { return self._s[2153]! }
public func Passport_Address_UploadOneOfScan(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2136]!, self._r[2136]!, [_0])
+ return formatWithArgumentRanges(self._s[2154]!, self._r[2154]!, [_0])
}
- public var IntentsSettings_Reset: String { return self._s[2137]! }
- public var Conversation_Info: String { return self._s[2138]! }
- public var Login_InfoDeletePhoto: String { return self._s[2139]! }
- public var ChatListFolder_DiscardDiscard: String { return self._s[2141]! }
- public var Passport_Language_vi: String { return self._s[2142]! }
- public var UserInfo_ScamUserWarning: String { return self._s[2143]! }
- public var Conversation_Search: String { return self._s[2144]! }
- public var DialogList_DeleteBotConversationConfirmation: String { return self._s[2146]! }
- public var ChatListFolder_NameGroups: String { return self._s[2147]! }
- public var ReportPeer_ReasonPornography: String { return self._s[2148]! }
- public var AutoDownloadSettings_PhotosTitle: String { return self._s[2149]! }
- public var Conversation_SendMessageErrorGroupRestricted: String { return self._s[2150]! }
- public var Map_LiveLocationGroupDescription: String { return self._s[2151]! }
- public var Channel_Setup_TypeHeader: String { return self._s[2152]! }
- public var AuthSessions_LoggedIn: String { return self._s[2153]! }
- public var Privacy_Forwards_AlwaysAllow_Title: String { return self._s[2154]! }
- public var Login_SmsRequestState3: String { return self._s[2155]! }
- public var Passport_Address_EditUtilityBill: String { return self._s[2156]! }
- public var Appearance_ReduceMotionInfo: String { return self._s[2157]! }
- public var Join_ChannelsTooMuch: String { return self._s[2158]! }
- public var Channel_Edit_LinkItem: String { return self._s[2159]! }
- public var Privacy_Calls_P2PNever: String { return self._s[2160]! }
- public var Conversation_AddToReadingList: String { return self._s[2162]! }
- public var Share_MultipleMessagesDisabled: String { return self._s[2163]! }
- public var Message_Animation: String { return self._s[2164]! }
- public var Conversation_DefaultRestrictedMedia: String { return self._s[2165]! }
- public var Map_Unknown: String { return self._s[2166]! }
- public var AutoDownloadSettings_LastDelimeter: String { return self._s[2167]! }
+ public var IntentsSettings_Reset: String { return self._s[2155]! }
+ public var Conversation_Info: String { return self._s[2156]! }
+ public var Login_InfoDeletePhoto: String { return self._s[2157]! }
+ public var ChatListFolder_DiscardDiscard: String { return self._s[2159]! }
+ public var Passport_Language_vi: String { return self._s[2160]! }
+ public var UserInfo_ScamUserWarning: String { return self._s[2161]! }
+ public var Conversation_Search: String { return self._s[2162]! }
+ public var DialogList_DeleteBotConversationConfirmation: String { return self._s[2164]! }
+ public var ChatListFolder_NameGroups: String { return self._s[2165]! }
+ public var ReportPeer_ReasonPornography: String { return self._s[2166]! }
+ public var AutoDownloadSettings_PhotosTitle: String { return self._s[2167]! }
+ public var Conversation_SendMessageErrorGroupRestricted: String { return self._s[2168]! }
+ public var Map_LiveLocationGroupDescription: String { return self._s[2169]! }
+ public var Channel_Setup_TypeHeader: String { return self._s[2170]! }
+ public var AuthSessions_LoggedIn: String { return self._s[2171]! }
+ public var Privacy_Forwards_AlwaysAllow_Title: String { return self._s[2172]! }
+ public var Login_SmsRequestState3: String { return self._s[2173]! }
+ public var Passport_Address_EditUtilityBill: String { return self._s[2174]! }
+ public var Appearance_ReduceMotionInfo: String { return self._s[2175]! }
+ public var Join_ChannelsTooMuch: String { return self._s[2176]! }
+ public var Channel_Edit_LinkItem: String { return self._s[2177]! }
+ public var Privacy_Calls_P2PNever: String { return self._s[2178]! }
+ public var Conversation_AddToReadingList: String { return self._s[2180]! }
+ public var Share_MultipleMessagesDisabled: String { return self._s[2181]! }
+ public var Message_Animation: String { return self._s[2182]! }
+ public var Conversation_DefaultRestrictedMedia: String { return self._s[2183]! }
+ public var Map_Unknown: String { return self._s[2184]! }
+ public var AutoDownloadSettings_LastDelimeter: String { return self._s[2185]! }
public func PUSH_PINNED_TEXT(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2168]!, self._r[2168]!, [_1, _2])
+ return formatWithArgumentRanges(self._s[2186]!, self._r[2186]!, [_1, _2])
}
public func Passport_FieldOneOf_Or(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2169]!, self._r[2169]!, [_1, _2])
+ return formatWithArgumentRanges(self._s[2187]!, self._r[2187]!, [_1, _2])
}
- public var Call_StatusRequesting: String { return self._s[2170]! }
- public var Conversation_SecretChatContextBotAlert: String { return self._s[2171]! }
- public var SocksProxySetup_ProxyStatusChecking: String { return self._s[2172]! }
+ public var Call_StatusRequesting: String { return self._s[2188]! }
+ public var Conversation_SecretChatContextBotAlert: String { return self._s[2189]! }
+ public var SocksProxySetup_ProxyStatusChecking: String { return self._s[2190]! }
+ public var Stats_MessageInteractionsTitle: String { return self._s[2191]! }
public func PUSH_CHAT_MESSAGE_DOC(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2173]!, self._r[2173]!, [_1, _2])
+ return formatWithArgumentRanges(self._s[2192]!, self._r[2192]!, [_1, _2])
}
public func Notification_PinnedLocationMessage(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2174]!, self._r[2174]!, [_0])
+ return formatWithArgumentRanges(self._s[2193]!, self._r[2193]!, [_0])
}
- public var Update_Skip: String { return self._s[2175]! }
- public var Group_Username_RemoveExistingUsernamesInfo: String { return self._s[2176]! }
- public var BlockedUsers_Title: String { return self._s[2177]! }
- public var Weekday_Monday: String { return self._s[2178]! }
+ public var Update_Skip: String { return self._s[2194]! }
+ public var Group_Username_RemoveExistingUsernamesInfo: String { return self._s[2195]! }
+ public var BlockedUsers_Title: String { return self._s[2196]! }
+ public var Weekday_Monday: String { return self._s[2197]! }
public func PUSH_CHANNEL_MESSAGE_AUDIO(_ _1: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2179]!, self._r[2179]!, [_1])
+ return formatWithArgumentRanges(self._s[2198]!, self._r[2198]!, [_1])
}
- public var Username_CheckingUsername: String { return self._s[2180]! }
- public var NotificationsSound_Bell: String { return self._s[2181]! }
- public var Conversation_SendMessageErrorFlood: String { return self._s[2182]! }
- public var SettingsSearch_Synonyms_Notifications_DisplayNamesOnLockScreen: String { return self._s[2183]! }
- public var ChannelMembers_ChannelAdminsTitle: String { return self._s[2184]! }
+ public var Username_CheckingUsername: String { return self._s[2199]! }
+ public var NotificationsSound_Bell: String { return self._s[2200]! }
+ public var Conversation_SendMessageErrorFlood: String { return self._s[2201]! }
+ public var SettingsSearch_Synonyms_Notifications_DisplayNamesOnLockScreen: String { return self._s[2202]! }
+ public var ChannelMembers_ChannelAdminsTitle: String { return self._s[2203]! }
public func Notification_ChangedGroupVideo(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2185]!, self._r[2185]!, [_0])
+ return formatWithArgumentRanges(self._s[2204]!, self._r[2204]!, [_0])
}
- public var ChatSettings_Groups: String { return self._s[2186]! }
- public var WallpaperPreview_PatternPaternDiscard: String { return self._s[2187]! }
- public var ChatList_PeerTypeContact: String { return self._s[2188]! }
+ public var ChatSettings_Groups: String { return self._s[2205]! }
+ public var WallpaperPreview_PatternPaternDiscard: String { return self._s[2206]! }
+ public var ChatList_PeerTypeContact: String { return self._s[2207]! }
public func Conversation_SetReminder_RemindTomorrow(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2189]!, self._r[2189]!, [_0])
+ return formatWithArgumentRanges(self._s[2208]!, self._r[2208]!, [_0])
}
- public var Your_card_was_declined: String { return self._s[2190]! }
- public var TwoStepAuth_EnterPasswordHelp: String { return self._s[2192]! }
- public var Wallet_Month_ShortApril: String { return self._s[2193]! }
- public var ChatList_Unmute: String { return self._s[2194]! }
- public var AuthSessions_AddDevice_ScanTitle: String { return self._s[2195]! }
- public var PhotoEditor_CurvesAll: String { return self._s[2196]! }
- public var Weekday_ShortTuesday: String { return self._s[2197]! }
- public var DialogList_Read: String { return self._s[2198]! }
- public var Appearance_AppIconClassic: String { return self._s[2199]! }
- public var Conversation_Dice_u1F3B2: String { return self._s[2200]! }
- public var ChannelMembers_WhoCanAddMembers_AllMembers: String { return self._s[2201]! }
- public var Passport_Identity_Gender: String { return self._s[2202]! }
+ public var Your_card_was_declined: String { return self._s[2209]! }
+ public var TwoStepAuth_EnterPasswordHelp: String { return self._s[2211]! }
+ public var Wallet_Month_ShortApril: String { return self._s[2212]! }
+ public var ChatList_Unmute: String { return self._s[2213]! }
+ public var AuthSessions_AddDevice_ScanTitle: String { return self._s[2214]! }
+ public var PhotoEditor_CurvesAll: String { return self._s[2215]! }
+ public var Weekday_ShortTuesday: String { return self._s[2216]! }
+ public var DialogList_Read: String { return self._s[2217]! }
+ public var Appearance_AppIconClassic: String { return self._s[2218]! }
+ public var Conversation_Dice_u1F3B2: String { return self._s[2219]! }
+ public var ChannelMembers_WhoCanAddMembers_AllMembers: String { return self._s[2220]! }
+ public var Passport_Identity_Gender: String { return self._s[2221]! }
public func Target_ShareGameConfirmationPrivate(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2203]!, self._r[2203]!, [_0])
+ return formatWithArgumentRanges(self._s[2222]!, self._r[2222]!, [_0])
}
- public var Target_SelectGroup: String { return self._s[2204]! }
- public var Map_HomeAndWorkInfo: String { return self._s[2206]! }
+ public var Target_SelectGroup: String { return self._s[2223]! }
+ public var Map_HomeAndWorkInfo: String { return self._s[2225]! }
public func DialogList_EncryptedChatStartedIncoming(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2207]!, self._r[2207]!, [_0])
+ return formatWithArgumentRanges(self._s[2226]!, self._r[2226]!, [_0])
}
- public var Passport_Language_en: String { return self._s[2208]! }
- public var AutoDownloadSettings_AutodownloadPhotos: String { return self._s[2209]! }
- public var Channel_Username_CreatePublicLinkHelp: String { return self._s[2210]! }
- public var Login_CancelPhoneVerificationContinue: String { return self._s[2211]! }
- public var ScheduledMessages_SendNow: String { return self._s[2212]! }
- public var Checkout_NewCard_PaymentCard: String { return self._s[2214]! }
- public var Login_InfoHelp: String { return self._s[2215]! }
- public var Appearance_BubbleCorners_AdjustAdjacent: String { return self._s[2216]! }
- public var ProfilePhoto_SetMainPhoto: String { return self._s[2217]! }
- public var Contacts_PermissionsSuppressWarningTitle: String { return self._s[2218]! }
- public var SettingsSearch_Synonyms_Stickers_FeaturedPacks: String { return self._s[2219]! }
+ public var Passport_Language_en: String { return self._s[2227]! }
+ public var AutoDownloadSettings_AutodownloadPhotos: String { return self._s[2228]! }
+ public var Channel_Username_CreatePublicLinkHelp: String { return self._s[2229]! }
+ public var Login_CancelPhoneVerificationContinue: String { return self._s[2230]! }
+ public var ScheduledMessages_SendNow: String { return self._s[2231]! }
+ public var Checkout_NewCard_PaymentCard: String { return self._s[2233]! }
+ public var Login_InfoHelp: String { return self._s[2234]! }
+ public var Appearance_BubbleCorners_AdjustAdjacent: String { return self._s[2235]! }
+ public var ProfilePhoto_SetMainPhoto: String { return self._s[2236]! }
+ public var Contacts_PermissionsSuppressWarningTitle: String { return self._s[2237]! }
+ public var SettingsSearch_Synonyms_Stickers_FeaturedPacks: String { return self._s[2238]! }
public func Channel_AdminLog_MessageChangedLinkedChannel(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2220]!, self._r[2220]!, [_1, _2])
+ return formatWithArgumentRanges(self._s[2239]!, self._r[2239]!, [_1, _2])
}
- public var SocksProxySetup_AddProxy: String { return self._s[2223]! }
- public var CreatePoll_Title: String { return self._s[2224]! }
- public var MessagePoll_QuizNoUsers: String { return self._s[2225]! }
- public var Conversation_ViewTheme: String { return self._s[2226]! }
- public var SettingsSearch_Synonyms_Privacy_Data_SecretChatLinkPreview: String { return self._s[2227]! }
- public var PasscodeSettings_SimplePasscodeHelp: String { return self._s[2228]! }
- public var TwoFactorSetup_Intro_Text: String { return self._s[2229]! }
- public var UserInfo_GroupsInCommon: String { return self._s[2230]! }
- public var TelegramWallet_Intro_TermsUrl: String { return self._s[2231]! }
- public var Stats_ViewsByHoursTitle: String { return self._s[2232]! }
- public var Conversation_PrivateChannelTimeLimitedAlertTitle: String { return self._s[2233]! }
- public var Call_AudioRouteHide: String { return self._s[2234]! }
+ public var SocksProxySetup_AddProxy: String { return self._s[2242]! }
+ public var CreatePoll_Title: String { return self._s[2243]! }
+ public var MessagePoll_QuizNoUsers: String { return self._s[2244]! }
+ public var Conversation_ViewTheme: String { return self._s[2245]! }
+ public var SettingsSearch_Synonyms_Privacy_Data_SecretChatLinkPreview: String { return self._s[2246]! }
+ public var PasscodeSettings_SimplePasscodeHelp: String { return self._s[2247]! }
+ public var TwoFactorSetup_Intro_Text: String { return self._s[2248]! }
+ public var UserInfo_GroupsInCommon: String { return self._s[2249]! }
+ public var TelegramWallet_Intro_TermsUrl: String { return self._s[2250]! }
+ public var Stats_ViewsByHoursTitle: String { return self._s[2251]! }
+ public var Conversation_PrivateChannelTimeLimitedAlertTitle: String { return self._s[2252]! }
+ public var Call_AudioRouteHide: String { return self._s[2253]! }
public func Wallet_Info_TransactionDateHeader(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2236]!, self._r[2236]!, [_1, _2])
+ return formatWithArgumentRanges(self._s[2255]!, self._r[2255]!, [_1, _2])
}
- public var ContactInfo_PhoneLabelMobile: String { return self._s[2237]! }
- public var IntentsSettings_SuggestedChatsInfo: String { return self._s[2238]! }
- public var CreatePoll_QuizOptionsHeader: String { return self._s[2239]! }
+ public var ContactInfo_PhoneLabelMobile: String { return self._s[2256]! }
+ public var IntentsSettings_SuggestedChatsInfo: String { return self._s[2257]! }
+ public var CreatePoll_QuizOptionsHeader: String { return self._s[2258]! }
public func ChatList_LeaveGroupConfirmation(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2240]!, self._r[2240]!, [_0])
+ return formatWithArgumentRanges(self._s[2259]!, self._r[2259]!, [_0])
}
- public var TextFormat_Bold: String { return self._s[2241]! }
- public var CreatePoll_ExplanationInfo: String { return self._s[2242]! }
- public var FastTwoStepSetup_EmailSection: String { return self._s[2243]! }
- public var StickerPackActionInfo_AddedTitle: String { return self._s[2244]! }
- public var Notifications_Title: String { return self._s[2245]! }
- public var Group_Username_InvalidTooShort: String { return self._s[2246]! }
- public var Channel_ErrorAddTooMuch: String { return self._s[2247]! }
+ public var TextFormat_Bold: String { return self._s[2260]! }
+ public var CreatePoll_ExplanationInfo: String { return self._s[2261]! }
+ public var FastTwoStepSetup_EmailSection: String { return self._s[2262]! }
+ public var StickerPackActionInfo_AddedTitle: String { return self._s[2263]! }
+ public var Notifications_Title: String { return self._s[2264]! }
+ public var Group_Username_InvalidTooShort: String { return self._s[2265]! }
+ public var Channel_ErrorAddTooMuch: String { return self._s[2266]! }
public func DialogList_MultipleTypingSuffix(_ _0: Int) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2248]!, self._r[2248]!, ["\(_0)"])
+ return formatWithArgumentRanges(self._s[2267]!, self._r[2267]!, ["\(_0)"])
}
- public var VoiceOver_DiscardPreparedContent: String { return self._s[2250]! }
- public var Stickers_SuggestAdded: String { return self._s[2251]! }
- public var Login_CountryCode: String { return self._s[2252]! }
- public var ChatSettings_AutoPlayVideos: String { return self._s[2253]! }
- public var Map_GetDirections: String { return self._s[2254]! }
- public var Wallet_Receive_ShareInvoiceUrl: String { return self._s[2255]! }
- public var Stats_GroupNewMembersBySourceTitle: String { return self._s[2256]! }
- public var Login_PhoneFloodError: String { return self._s[2257]! }
+ public var VoiceOver_DiscardPreparedContent: String { return self._s[2269]! }
+ public var Stickers_SuggestAdded: String { return self._s[2270]! }
+ public var Login_CountryCode: String { return self._s[2271]! }
+ public var ChatSettings_AutoPlayVideos: String { return self._s[2272]! }
+ public var Map_GetDirections: String { return self._s[2273]! }
+ public var Wallet_Receive_ShareInvoiceUrl: String { return self._s[2274]! }
+ public var Stats_GroupNewMembersBySourceTitle: String { return self._s[2275]! }
+ public var Login_PhoneFloodError: String { return self._s[2276]! }
public func Time_MonthOfYear_m3(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2258]!, self._r[2258]!, [_0])
+ return formatWithArgumentRanges(self._s[2277]!, self._r[2277]!, [_0])
}
public func Wallet_Time_PreciseDate_m10(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2259]!, self._r[2259]!, [_1, _2, _3])
+ return formatWithArgumentRanges(self._s[2278]!, self._r[2278]!, [_1, _2, _3])
}
- public var IntentsSettings_SuggestedChatsPrivateChats: String { return self._s[2260]! }
- public var Settings_SetUsername: String { return self._s[2262]! }
- public var Group_Location_ChangeLocation: String { return self._s[2263]! }
- public var Notification_GroupInviterSelf: String { return self._s[2264]! }
- public var InstantPage_TapToOpenLink: String { return self._s[2265]! }
+ public var IntentsSettings_SuggestedChatsPrivateChats: String { return self._s[2279]! }
+ public var Settings_SetUsername: String { return self._s[2281]! }
+ public var Group_Location_ChangeLocation: String { return self._s[2282]! }
+ public var Notification_GroupInviterSelf: String { return self._s[2283]! }
+ public var InstantPage_TapToOpenLink: String { return self._s[2284]! }
public func Notification_ChannelInviter(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2266]!, self._r[2266]!, [_0])
+ return formatWithArgumentRanges(self._s[2285]!, self._r[2285]!, [_0])
}
- public var PrivacySettings_AutoArchiveInfo: String { return self._s[2267]! }
- public var Watch_Suggestion_TalkLater: String { return self._s[2268]! }
- public var SecretChat_Title: String { return self._s[2269]! }
- public var Group_UpgradeNoticeText1: String { return self._s[2270]! }
- public var AuthSessions_Title: String { return self._s[2271]! }
+ public var PrivacySettings_AutoArchiveInfo: String { return self._s[2286]! }
+ public var Watch_Suggestion_TalkLater: String { return self._s[2287]! }
+ public var SecretChat_Title: String { return self._s[2288]! }
+ public var Group_UpgradeNoticeText1: String { return self._s[2289]! }
+ public var AuthSessions_Title: String { return self._s[2290]! }
public func TextFormat_AddLinkText(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2272]!, self._r[2272]!, [_0])
+ return formatWithArgumentRanges(self._s[2291]!, self._r[2291]!, [_0])
}
- public var PhotoEditor_CropAuto: String { return self._s[2273]! }
- public var Channel_About_Title: String { return self._s[2275]! }
- public var Theme_ThemeChanged: String { return self._s[2276]! }
- public var FastTwoStepSetup_EmailHelp: String { return self._s[2277]! }
+ public var PhotoEditor_CropAuto: String { return self._s[2292]! }
+ public var Channel_About_Title: String { return self._s[2294]! }
+ public var Theme_ThemeChanged: String { return self._s[2295]! }
+ public var FastTwoStepSetup_EmailHelp: String { return self._s[2296]! }
public func Conversation_Bytes(_ _0: Int) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2280]!, self._r[2280]!, ["\(_0)"])
+ return formatWithArgumentRanges(self._s[2299]!, self._r[2299]!, ["\(_0)"])
}
- public var VoiceOver_MessageContextReport: String { return self._s[2281]! }
- public var Conversation_PinMessageAlert_OnlyPin: String { return self._s[2283]! }
- public var Group_Setup_HistoryVisibleHelp: String { return self._s[2284]! }
+ public var VoiceOver_MessageContextReport: String { return self._s[2300]! }
+ public var Conversation_PinMessageAlert_OnlyPin: String { return self._s[2302]! }
+ public var Group_Setup_HistoryVisibleHelp: String { return self._s[2303]! }
public func PUSH_MESSAGE_GIF(_ _1: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2285]!, self._r[2285]!, [_1])
+ return formatWithArgumentRanges(self._s[2304]!, self._r[2304]!, [_1])
}
public func SharedMedia_SearchNoResultsDescription(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2287]!, self._r[2287]!, [_0])
- }
- public func TwoStepAuth_RecoveryEmailUnavailable(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2288]!, self._r[2288]!, [_0])
- }
- public var Privacy_PaymentsClearInfoHelp: String { return self._s[2289]! }
- public var PeopleNearby_DiscoverDescription: String { return self._s[2291]! }
- public var Presence_online: String { return self._s[2293]! }
- public var PasscodeSettings_Title: String { return self._s[2294]! }
- public var Passport_Identity_ExpiryDatePlaceholder: String { return self._s[2295]! }
- public var Web_OpenExternal: String { return self._s[2296]! }
- public var AutoDownloadSettings_AutoDownload: String { return self._s[2298]! }
- public var Channel_OwnershipTransfer_EnterPasswordText: String { return self._s[2299]! }
- public var LocalGroup_Title: String { return self._s[2300]! }
- public func AutoNightTheme_AutomaticHelp(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2301]!, self._r[2301]!, [_0])
- }
- public var FastTwoStepSetup_PasswordConfirmationPlaceholder: String { return self._s[2302]! }
- public var Conversation_StopQuizConfirmation: String { return self._s[2303]! }
- public var Map_YouAreHere: String { return self._s[2304]! }
- public func AuthSessions_Message(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2305]!, self._r[2305]!, [_0])
- }
- public func ChatList_DeleteChatConfirmation(_ _0: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[2306]!, self._r[2306]!, [_0])
}
- public var Theme_Context_ChangeColors: String { return self._s[2307]! }
- public var PrivacyLastSeenSettings_AlwaysShareWith: String { return self._s[2308]! }
- public var Target_InviteToGroupErrorAlreadyInvited: String { return self._s[2309]! }
+ public func TwoStepAuth_RecoveryEmailUnavailable(_ _0: String) -> (String, [(Int, NSRange)]) {
+ return formatWithArgumentRanges(self._s[2307]!, self._r[2307]!, [_0])
+ }
+ public var Privacy_PaymentsClearInfoHelp: String { return self._s[2308]! }
+ public var PeopleNearby_DiscoverDescription: String { return self._s[2310]! }
+ public var Presence_online: String { return self._s[2312]! }
+ public var PasscodeSettings_Title: String { return self._s[2313]! }
+ public var Passport_Identity_ExpiryDatePlaceholder: String { return self._s[2314]! }
+ public var Web_OpenExternal: String { return self._s[2315]! }
+ public var AutoDownloadSettings_AutoDownload: String { return self._s[2317]! }
+ public var Channel_OwnershipTransfer_EnterPasswordText: String { return self._s[2318]! }
+ public var LocalGroup_Title: String { return self._s[2319]! }
+ public func AutoNightTheme_AutomaticHelp(_ _0: String) -> (String, [(Int, NSRange)]) {
+ return formatWithArgumentRanges(self._s[2320]!, self._r[2320]!, [_0])
+ }
+ public var FastTwoStepSetup_PasswordConfirmationPlaceholder: String { return self._s[2321]! }
+ public var Conversation_StopQuizConfirmation: String { return self._s[2322]! }
+ public var Map_YouAreHere: String { return self._s[2323]! }
+ public func AuthSessions_Message(_ _0: String) -> (String, [(Int, NSRange)]) {
+ return formatWithArgumentRanges(self._s[2324]!, self._r[2324]!, [_0])
+ }
+ public func ChatList_DeleteChatConfirmation(_ _0: String) -> (String, [(Int, NSRange)]) {
+ return formatWithArgumentRanges(self._s[2325]!, self._r[2325]!, [_0])
+ }
+ public var Theme_Context_ChangeColors: String { return self._s[2326]! }
+ public var PrivacyLastSeenSettings_AlwaysShareWith: String { return self._s[2327]! }
+ public var Target_InviteToGroupErrorAlreadyInvited: String { return self._s[2328]! }
public func AuthSessions_AppUnofficial(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2310]!, self._r[2310]!, [_0])
+ return formatWithArgumentRanges(self._s[2329]!, self._r[2329]!, [_0])
}
public func DialogList_LiveLocationSharingTo(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2311]!, self._r[2311]!, [_0])
+ return formatWithArgumentRanges(self._s[2330]!, self._r[2330]!, [_0])
}
- public var SocksProxySetup_Username: String { return self._s[2312]! }
- public var Bot_Start: String { return self._s[2313]! }
+ public var SocksProxySetup_Username: String { return self._s[2331]! }
+ public var Bot_Start: String { return self._s[2332]! }
public func Channel_AdminLog_EmptyFilterQueryText(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2314]!, self._r[2314]!, [_0])
+ return formatWithArgumentRanges(self._s[2333]!, self._r[2333]!, [_0])
}
public func Channel_AdminLog_MessagePinned(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2315]!, self._r[2315]!, [_0])
+ return formatWithArgumentRanges(self._s[2334]!, self._r[2334]!, [_0])
}
- public var Contacts_SortByPresence: String { return self._s[2316]! }
- public var AccentColor_Title: String { return self._s[2319]! }
- public var Conversation_DiscardVoiceMessageTitle: String { return self._s[2320]! }
+ public var Contacts_SortByPresence: String { return self._s[2335]! }
+ public var AccentColor_Title: String { return self._s[2338]! }
+ public var Conversation_DiscardVoiceMessageTitle: String { return self._s[2339]! }
public func PUSH_CHAT_CREATED(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2321]!, self._r[2321]!, [_1, _2])
+ return formatWithArgumentRanges(self._s[2340]!, self._r[2340]!, [_1, _2])
}
public func PrivacySettings_LastSeenContactsMinus(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2322]!, self._r[2322]!, [_0])
+ return formatWithArgumentRanges(self._s[2341]!, self._r[2341]!, [_0])
}
public func Channel_AdminLog_MessageChangedLinkedGroup(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2323]!, self._r[2323]!, [_1, _2])
+ return formatWithArgumentRanges(self._s[2342]!, self._r[2342]!, [_1, _2])
}
- public var Stats_GroupOverview: String { return self._s[2324]! }
- public var Passport_Email_EnterOtherEmail: String { return self._s[2325]! }
- public var Login_InfoAvatarPhoto: String { return self._s[2326]! }
+ public var Stats_GroupOverview: String { return self._s[2343]! }
+ public var Passport_Email_EnterOtherEmail: String { return self._s[2344]! }
+ public var Login_InfoAvatarPhoto: String { return self._s[2345]! }
public func ChatList_RemovedFromFolderTooltip(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2327]!, self._r[2327]!, [_1, _2])
+ return formatWithArgumentRanges(self._s[2346]!, self._r[2346]!, [_1, _2])
}
- public var Privacy_PaymentsClear_ShippingInfo: String { return self._s[2328]! }
- public var Tour_Title4: String { return self._s[2329]! }
- public var Passport_Identity_Translation: String { return self._s[2330]! }
- public var SettingsSearch_Synonyms_Notifications_ContactJoined: String { return self._s[2331]! }
- public var Login_TermsOfServiceLabel: String { return self._s[2333]! }
- public var Passport_Language_it: String { return self._s[2334]! }
- public var KeyCommand_JumpToNextUnreadChat: String { return self._s[2335]! }
- public var Passport_Identity_SelfieHelp: String { return self._s[2336]! }
- public var Conversation_ClearAll: String { return self._s[2338]! }
- public var Wallet_Send_UninitializedText: String { return self._s[2340]! }
- public var Channel_OwnershipTransfer_Title: String { return self._s[2341]! }
- public var TwoStepAuth_FloodError: String { return self._s[2342]! }
+ public var Privacy_PaymentsClear_ShippingInfo: String { return self._s[2347]! }
+ public var Tour_Title4: String { return self._s[2348]! }
+ public var Passport_Identity_Translation: String { return self._s[2349]! }
+ public var SettingsSearch_Synonyms_Notifications_ContactJoined: String { return self._s[2350]! }
+ public var Login_TermsOfServiceLabel: String { return self._s[2352]! }
+ public var Passport_Language_it: String { return self._s[2353]! }
+ public var KeyCommand_JumpToNextUnreadChat: String { return self._s[2354]! }
+ public var Passport_Identity_SelfieHelp: String { return self._s[2355]! }
+ public var Conversation_ClearAll: String { return self._s[2357]! }
+ public var Wallet_Send_UninitializedText: String { return self._s[2359]! }
+ public var Channel_OwnershipTransfer_Title: String { return self._s[2360]! }
+ public var TwoStepAuth_FloodError: String { return self._s[2361]! }
public func PUSH_CHANNEL_MESSAGE_GEO(_ _1: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2343]!, self._r[2343]!, [_1])
+ return formatWithArgumentRanges(self._s[2362]!, self._r[2362]!, [_1])
}
- public var Paint_Delete: String { return self._s[2344]! }
+ public var Paint_Delete: String { return self._s[2363]! }
public func Wallet_Sent_Text(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2345]!, self._r[2345]!, [_0])
+ return formatWithArgumentRanges(self._s[2364]!, self._r[2364]!, [_0])
}
- public var Privacy_AddNewPeer: String { return self._s[2346]! }
+ public var Privacy_AddNewPeer: String { return self._s[2365]! }
public func Channel_AdminLog_MessageRank(_ _1: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2347]!, self._r[2347]!, [_1])
+ return formatWithArgumentRanges(self._s[2366]!, self._r[2366]!, [_1])
}
- public var LogoutOptions_SetPasscodeText: String { return self._s[2348]! }
+ public var LogoutOptions_SetPasscodeText: String { return self._s[2367]! }
public func Passport_AcceptHelp(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2349]!, self._r[2349]!, [_1, _2])
+ return formatWithArgumentRanges(self._s[2368]!, self._r[2368]!, [_1, _2])
}
- public var Message_PinnedAudioMessage: String { return self._s[2350]! }
+ public var Message_PinnedAudioMessage: String { return self._s[2369]! }
public func Watch_Time_ShortTodayAt(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2351]!, self._r[2351]!, [_0])
+ return formatWithArgumentRanges(self._s[2370]!, self._r[2370]!, [_0])
}
- public var Notification_Mute1hMin: String { return self._s[2352]! }
- public var Notifications_GroupNotificationsSound: String { return self._s[2353]! }
- public var Wallet_Month_GenNovember: String { return self._s[2354]! }
- public var SocksProxySetup_ShareProxyList: String { return self._s[2356]! }
- public var Conversation_MessageEditedLabel: String { return self._s[2357]! }
+ public var Notification_Mute1hMin: String { return self._s[2371]! }
+ public var Notifications_GroupNotificationsSound: String { return self._s[2372]! }
+ public var Wallet_Month_GenNovember: String { return self._s[2373]! }
+ public var SocksProxySetup_ShareProxyList: String { return self._s[2375]! }
+ public var Conversation_MessageEditedLabel: String { return self._s[2376]! }
public func ClearCache_Success(_ _0: String, _ _1: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2358]!, self._r[2358]!, [_0, _1])
+ return formatWithArgumentRanges(self._s[2377]!, self._r[2377]!, [_0, _1])
}
- public var Notification_Exceptions_AlwaysOff: String { return self._s[2359]! }
- public var Notification_Exceptions_NewException_MessagePreviewHeader: String { return self._s[2360]! }
+ public var Notification_Exceptions_AlwaysOff: String { return self._s[2378]! }
+ public var Conversation_ContextMenuOpenChannelProfile: String { return self._s[2379]! }
+ public var Notification_Exceptions_NewException_MessagePreviewHeader: String { return self._s[2380]! }
public func Channel_AdminLog_MessageAdmin(_ _0: String, _ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2361]!, self._r[2361]!, [_0, _1, _2])
+ return formatWithArgumentRanges(self._s[2381]!, self._r[2381]!, [_0, _1, _2])
}
- public var NetworkUsageSettings_ResetStats: String { return self._s[2362]! }
+ public var NetworkUsageSettings_ResetStats: String { return self._s[2382]! }
public func PUSH_MESSAGE_GEOLIVE(_ _1: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2363]!, self._r[2363]!, [_1])
+ return formatWithArgumentRanges(self._s[2383]!, self._r[2383]!, [_1])
}
- public var AccessDenied_LocationTracking: String { return self._s[2364]! }
- public var Month_GenOctober: String { return self._s[2365]! }
- public var GroupInfo_InviteLink_RevokeAlert_Revoke: String { return self._s[2366]! }
- public var EnterPasscode_EnterPasscode: String { return self._s[2367]! }
- public var MediaPicker_TimerTooltip: String { return self._s[2369]! }
- public var SharedMedia_TitleAll: String { return self._s[2370]! }
- public var SettingsSearch_Synonyms_Notifications_ChannelNotificationsExceptions: String { return self._s[2373]! }
- public var Conversation_RestrictedMedia: String { return self._s[2374]! }
- public var AccessDenied_PhotosRestricted: String { return self._s[2375]! }
- public var Privacy_Forwards_WhoCanForward: String { return self._s[2377]! }
- public var ChangePhoneNumberCode_Called: String { return self._s[2378]! }
+ public var AccessDenied_LocationTracking: String { return self._s[2384]! }
+ public var Month_GenOctober: String { return self._s[2385]! }
+ public var GroupInfo_InviteLink_RevokeAlert_Revoke: String { return self._s[2386]! }
+ public var EnterPasscode_EnterPasscode: String { return self._s[2387]! }
+ public var Call_CameraConfirmationConfirm: String { return self._s[2389]! }
+ public var MediaPicker_TimerTooltip: String { return self._s[2390]! }
+ public var SharedMedia_TitleAll: String { return self._s[2391]! }
+ public var SettingsSearch_Synonyms_Notifications_ChannelNotificationsExceptions: String { return self._s[2394]! }
+ public var Conversation_RestrictedMedia: String { return self._s[2395]! }
+ public var AccessDenied_PhotosRestricted: String { return self._s[2396]! }
+ public var Privacy_Forwards_WhoCanForward: String { return self._s[2398]! }
+ public var ChangePhoneNumberCode_Called: String { return self._s[2399]! }
public func Notification_PinnedDocumentMessage(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2379]!, self._r[2379]!, [_0])
+ return formatWithArgumentRanges(self._s[2400]!, self._r[2400]!, [_0])
}
- public var Conversation_SavedMessages: String { return self._s[2382]! }
- public var Your_cards_expiration_month_is_invalid: String { return self._s[2384]! }
- public var FastTwoStepSetup_PasswordPlaceholder: String { return self._s[2385]! }
+ public var Conversation_SavedMessages: String { return self._s[2403]! }
+ public var Your_cards_expiration_month_is_invalid: String { return self._s[2405]! }
+ public var FastTwoStepSetup_PasswordPlaceholder: String { return self._s[2406]! }
public func Target_ShareGameConfirmationGroup(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2387]!, self._r[2387]!, [_0])
+ return formatWithArgumentRanges(self._s[2408]!, self._r[2408]!, [_0])
}
- public var VoiceOver_Chat_YourMessage: String { return self._s[2388]! }
+ public var VoiceOver_Chat_YourMessage: String { return self._s[2409]! }
public func VoiceOver_Chat_Title(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2389]!, self._r[2389]!, [_0])
+ return formatWithArgumentRanges(self._s[2410]!, self._r[2410]!, [_0])
}
- public var ReportPeer_AlertSuccess: String { return self._s[2390]! }
- public var PhotoEditor_CropAspectRatioOriginal: String { return self._s[2391]! }
+ public var ReportPeer_AlertSuccess: String { return self._s[2411]! }
+ public var PhotoEditor_CropAspectRatioOriginal: String { return self._s[2412]! }
public func InstantPage_RelatedArticleAuthorAndDateTitle(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2392]!, self._r[2392]!, [_1, _2])
+ return formatWithArgumentRanges(self._s[2413]!, self._r[2413]!, [_1, _2])
}
- public var Checkout_PasswordEntry_Title: String { return self._s[2393]! }
- public var PhotoEditor_FadeTool: String { return self._s[2394]! }
- public var Privacy_ContactsReset: String { return self._s[2395]! }
- public var Conversation_PrivateChannelTimeLimitedAlertText: String { return self._s[2396]! }
+ public var Checkout_PasswordEntry_Title: String { return self._s[2414]! }
+ public var PhotoEditor_FadeTool: String { return self._s[2415]! }
+ public var Privacy_ContactsReset: String { return self._s[2416]! }
+ public var Conversation_PrivateChannelTimeLimitedAlertText: String { return self._s[2417]! }
public func Channel_AdminLog_MessageRestrictedUntil(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2398]!, self._r[2398]!, [_0])
+ return formatWithArgumentRanges(self._s[2419]!, self._r[2419]!, [_0])
}
- public var Message_PinnedVideoMessage: String { return self._s[2399]! }
- public var ChatList_Mute: String { return self._s[2400]! }
+ public var Message_PinnedVideoMessage: String { return self._s[2420]! }
+ public var ChatList_Mute: String { return self._s[2421]! }
public func Wallet_Time_PreciseDate_m5(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2401]!, self._r[2401]!, [_1, _2, _3])
+ return formatWithArgumentRanges(self._s[2422]!, self._r[2422]!, [_1, _2, _3])
}
- public var Permissions_CellularDataText_v0: String { return self._s[2402]! }
- public var Conversation_PinnedQuiz: String { return self._s[2404]! }
- public var ShareMenu_SelectChats: String { return self._s[2406]! }
- public var ChatList_Context_Unarchive: String { return self._s[2407]! }
- public var MusicPlayer_VoiceNote: String { return self._s[2408]! }
- public var Conversation_RestrictedText: String { return self._s[2409]! }
- public var SettingsSearch_Synonyms_Privacy_Data_DeleteDrafts: String { return self._s[2410]! }
- public var Wallet_Month_GenApril: String { return self._s[2411]! }
- public var Wallet_Month_ShortMarch: String { return self._s[2412]! }
- public var TwoStepAuth_DisableSuccess: String { return self._s[2413]! }
- public var Chat_PsaTooltip_covid: String { return self._s[2414]! }
- public var Cache_Videos: String { return self._s[2415]! }
- public var PrivacySettings_PhoneNumber: String { return self._s[2416]! }
- public var Wallet_Month_GenFebruary: String { return self._s[2417]! }
- public var FeatureDisabled_Oops: String { return self._s[2419]! }
- public var ChatList_RemoveFolderAction: String { return self._s[2420]! }
- public var Passport_Address_PostcodePlaceholder: String { return self._s[2421]! }
+ public var Permissions_CellularDataText_v0: String { return self._s[2423]! }
+ public var Conversation_PinnedQuiz: String { return self._s[2425]! }
+ public var ShareMenu_SelectChats: String { return self._s[2427]! }
+ public var ChatList_Context_Unarchive: String { return self._s[2428]! }
+ public var MusicPlayer_VoiceNote: String { return self._s[2429]! }
+ public var Conversation_RestrictedText: String { return self._s[2430]! }
+ public var SettingsSearch_Synonyms_Privacy_Data_DeleteDrafts: String { return self._s[2431]! }
+ public var Wallet_Month_GenApril: String { return self._s[2432]! }
+ public var Wallet_Month_ShortMarch: String { return self._s[2433]! }
+ public var TwoStepAuth_DisableSuccess: String { return self._s[2434]! }
+ public var Chat_PsaTooltip_covid: String { return self._s[2435]! }
+ public var Cache_Videos: String { return self._s[2436]! }
+ public var PrivacySettings_PhoneNumber: String { return self._s[2437]! }
+ public var Wallet_Month_GenFebruary: String { return self._s[2438]! }
+ public var FeatureDisabled_Oops: String { return self._s[2440]! }
+ public var ChatList_RemoveFolderAction: String { return self._s[2441]! }
+ public var Passport_Address_PostcodePlaceholder: String { return self._s[2442]! }
public func AddContact_StatusSuccess(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2422]!, self._r[2422]!, [_0])
+ return formatWithArgumentRanges(self._s[2443]!, self._r[2443]!, [_0])
}
- public var Stickers_GroupStickersHelp: String { return self._s[2424]! }
- public var GroupPermission_NoSendPolls: String { return self._s[2425]! }
- public var Wallet_Qr_ScanCode: String { return self._s[2426]! }
- public var Message_VideoExpired: String { return self._s[2428]! }
- public var GroupInfo_GroupHistoryVisible: String { return self._s[2429]! }
- public var Notifications_Badge: String { return self._s[2430]! }
- public var Wallet_Receive_AddressCopied: String { return self._s[2431]! }
- public var CreatePoll_OptionPlaceholder: String { return self._s[2432]! }
- public var Username_InvalidTooShort: String { return self._s[2433]! }
- public var EnterPasscode_EnterNewPasscodeChange: String { return self._s[2434]! }
- public var Channel_AdminLog_PinMessages: String { return self._s[2435]! }
- public var ArchivedChats_IntroTitle3: String { return self._s[2436]! }
+ public var Stickers_GroupStickersHelp: String { return self._s[2445]! }
+ public var GroupPermission_NoSendPolls: String { return self._s[2446]! }
+ public var Wallet_Qr_ScanCode: String { return self._s[2447]! }
+ public var Message_VideoExpired: String { return self._s[2449]! }
+ public var GroupInfo_GroupHistoryVisible: String { return self._s[2450]! }
+ public var Notifications_Badge: String { return self._s[2451]! }
+ public var Wallet_Receive_AddressCopied: String { return self._s[2452]! }
+ public var CreatePoll_OptionPlaceholder: String { return self._s[2453]! }
+ public var Username_InvalidTooShort: String { return self._s[2454]! }
+ public var EnterPasscode_EnterNewPasscodeChange: String { return self._s[2455]! }
+ public var Channel_AdminLog_PinMessages: String { return self._s[2456]! }
+ public var ArchivedChats_IntroTitle3: String { return self._s[2457]! }
public func Notification_MessageLifetimeRemoved(_ _1: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2437]!, self._r[2437]!, [_1])
+ return formatWithArgumentRanges(self._s[2458]!, self._r[2458]!, [_1])
}
- public var Permissions_SiriAllowInSettings_v0: String { return self._s[2438]! }
- public var Conversation_DefaultRestrictedText: String { return self._s[2439]! }
- public var SharedMedia_CategoryDocs: String { return self._s[2442]! }
+ public var Permissions_SiriAllowInSettings_v0: String { return self._s[2459]! }
+ public var Conversation_DefaultRestrictedText: String { return self._s[2460]! }
+ public var SharedMedia_CategoryDocs: String { return self._s[2463]! }
public func PUSH_MESSAGE_CONTACT(_ _1: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2443]!, self._r[2443]!, [_1])
+ return formatWithArgumentRanges(self._s[2464]!, self._r[2464]!, [_1])
}
- public var Wallet_Send_UninitializedTitle: String { return self._s[2444]! }
- public var StickerPackActionInfo_ArchivedTitle: String { return self._s[2445]! }
- public var Privacy_Forwards_NeverLink: String { return self._s[2447]! }
+ public var Wallet_Send_UninitializedTitle: String { return self._s[2465]! }
+ public var StickerPackActionInfo_ArchivedTitle: String { return self._s[2466]! }
+ public var Privacy_Forwards_NeverLink: String { return self._s[2468]! }
public func Notification_MessageLifetimeChangedOutgoing(_ _1: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2448]!, self._r[2448]!, [_1])
+ return formatWithArgumentRanges(self._s[2469]!, self._r[2469]!, [_1])
}
- public var CheckoutInfo_ErrorShippingNotAvailable: String { return self._s[2449]! }
+ public var CheckoutInfo_ErrorShippingNotAvailable: String { return self._s[2470]! }
public func Time_MonthOfYear_m12(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2450]!, self._r[2450]!, [_0])
+ return formatWithArgumentRanges(self._s[2471]!, self._r[2471]!, [_0])
}
- public var ChatSettings_PrivateChats: String { return self._s[2451]! }
- public var SettingsSearch_Synonyms_EditProfile_Logout: String { return self._s[2452]! }
- public var Conversation_PrivateMessageLinkCopied: String { return self._s[2453]! }
- public var Channel_UpdatePhotoItem: String { return self._s[2454]! }
- public var GroupInfo_LeftStatus: String { return self._s[2455]! }
- public var Watch_MessageView_Forward: String { return self._s[2457]! }
- public var ReportPeer_ReasonChildAbuse: String { return self._s[2458]! }
- public var Cache_ClearEmpty: String { return self._s[2460]! }
- public var Localization_LanguageName: String { return self._s[2462]! }
- public var Wallet_AccessDenied_Title: String { return self._s[2463]! }
- public var WebSearch_GIFs: String { return self._s[2464]! }
- public var Notifications_DisplayNamesOnLockScreenInfoWithLink: String { return self._s[2465]! }
- public var Wallet_AccessDenied_Settings: String { return self._s[2466]! }
- public var Username_InvalidStartsWithNumber: String { return self._s[2467]! }
- public var Common_Back: String { return self._s[2468]! }
- public var GroupInfo_Permissions_EditingDisabled: String { return self._s[2469]! }
- public var Passport_Identity_DateOfBirthPlaceholder: String { return self._s[2470]! }
- public var Wallet_Send_Send: String { return self._s[2471]! }
+ public var ChatSettings_PrivateChats: String { return self._s[2472]! }
+ public var SettingsSearch_Synonyms_EditProfile_Logout: String { return self._s[2473]! }
+ public var Conversation_PrivateMessageLinkCopied: String { return self._s[2474]! }
+ public var Channel_UpdatePhotoItem: String { return self._s[2475]! }
+ public var GroupInfo_LeftStatus: String { return self._s[2476]! }
+ public var Watch_MessageView_Forward: String { return self._s[2478]! }
+ public var ReportPeer_ReasonChildAbuse: String { return self._s[2479]! }
+ public var Cache_ClearEmpty: String { return self._s[2481]! }
+ public var Localization_LanguageName: String { return self._s[2483]! }
+ public var Wallet_AccessDenied_Title: String { return self._s[2484]! }
+ public var WebSearch_GIFs: String { return self._s[2485]! }
+ public var Notifications_DisplayNamesOnLockScreenInfoWithLink: String { return self._s[2486]! }
+ public var Wallet_AccessDenied_Settings: String { return self._s[2487]! }
+ public var AccessDenied_VideoCallCamera: String { return self._s[2488]! }
+ public var Username_InvalidStartsWithNumber: String { return self._s[2489]! }
+ public var Common_Back: String { return self._s[2490]! }
+ public var GroupInfo_Permissions_EditingDisabled: String { return self._s[2491]! }
+ public var Passport_Identity_DateOfBirthPlaceholder: String { return self._s[2492]! }
+ public var Wallet_Send_Send: String { return self._s[2493]! }
public func PUSH_CHANNEL_MESSAGE_STICKER(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2473]!, self._r[2473]!, [_1, _2])
+ return formatWithArgumentRanges(self._s[2495]!, self._r[2495]!, [_1, _2])
}
- public var Wallet_Info_RefreshErrorTitle: String { return self._s[2474]! }
- public var ChatList_Tabs_All: String { return self._s[2475]! }
- public var Wallet_Month_GenJune: String { return self._s[2476]! }
- public var Passport_Email_Help: String { return self._s[2477]! }
- public var Watch_Conversation_Reply: String { return self._s[2479]! }
- public var Stats_GroupTopInvitersTitle: String { return self._s[2480]! }
- public var Conversation_EditingMessageMediaChange: String { return self._s[2483]! }
- public var Passport_Identity_IssueDatePlaceholder: String { return self._s[2484]! }
- public var Channel_BanUser_Unban: String { return self._s[2486]! }
- public var Channel_EditAdmin_PermissionPostMessages: String { return self._s[2487]! }
- public var Group_Username_CreatePublicLinkHelp: String { return self._s[2488]! }
- public var TwoStepAuth_ConfirmEmailCodePlaceholder: String { return self._s[2490]! }
- public var Wallet_Send_AddressHeader: String { return self._s[2491]! }
- public var Passport_Identity_Name: String { return self._s[2492]! }
+ public var Wallet_Info_RefreshErrorTitle: String { return self._s[2496]! }
+ public var ChatList_Tabs_All: String { return self._s[2497]! }
+ public var Wallet_Month_GenJune: String { return self._s[2498]! }
+ public var Passport_Email_Help: String { return self._s[2499]! }
+ public var Watch_Conversation_Reply: String { return self._s[2501]! }
+ public var Stats_GroupTopInvitersTitle: String { return self._s[2502]! }
+ public var Conversation_EditingMessageMediaChange: String { return self._s[2505]! }
+ public var Passport_Identity_IssueDatePlaceholder: String { return self._s[2506]! }
+ public var Channel_BanUser_Unban: String { return self._s[2508]! }
+ public var Channel_EditAdmin_PermissionPostMessages: String { return self._s[2509]! }
+ public var Group_Username_CreatePublicLinkHelp: String { return self._s[2510]! }
+ public var TwoStepAuth_ConfirmEmailCodePlaceholder: String { return self._s[2512]! }
+ public var Wallet_Send_AddressHeader: String { return self._s[2513]! }
+ public var Passport_Identity_Name: String { return self._s[2514]! }
public func Channel_DiscussionGroup_HeaderGroupSet(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2493]!, self._r[2493]!, [_0])
+ return formatWithArgumentRanges(self._s[2515]!, self._r[2515]!, [_0])
}
- public var GroupRemoved_ViewUserInfo: String { return self._s[2494]! }
- public var Conversation_BlockUser: String { return self._s[2495]! }
- public var Month_GenJanuary: String { return self._s[2496]! }
- public var ChatSettings_TextSize: String { return self._s[2497]! }
- public var Notification_PassportValuePhone: String { return self._s[2498]! }
- public var MediaPlayer_UnknownArtist: String { return self._s[2499]! }
- public var Passport_Language_ne: String { return self._s[2500]! }
- public var Notification_CallBack: String { return self._s[2501]! }
- public var Wallet_SecureStorageReset_BiometryTouchId: String { return self._s[2502]! }
- public var TwoStepAuth_EmailHelp: String { return self._s[2503]! }
+ public var GroupRemoved_ViewUserInfo: String { return self._s[2516]! }
+ public var Stats_MessageOverview: String { return self._s[2517]! }
+ public var Conversation_BlockUser: String { return self._s[2518]! }
+ public var Month_GenJanuary: String { return self._s[2519]! }
+ public var ChatSettings_TextSize: String { return self._s[2520]! }
+ public var Notification_PassportValuePhone: String { return self._s[2521]! }
+ public var MediaPlayer_UnknownArtist: String { return self._s[2522]! }
+ public var Passport_Language_ne: String { return self._s[2523]! }
+ public var Notification_CallBack: String { return self._s[2524]! }
+ public var Wallet_SecureStorageReset_BiometryTouchId: String { return self._s[2525]! }
+ public var TwoStepAuth_EmailHelp: String { return self._s[2526]! }
public func Time_YesterdayAt(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2504]!, self._r[2504]!, [_0])
+ return formatWithArgumentRanges(self._s[2527]!, self._r[2527]!, [_0])
}
- public var Channel_Info_Management: String { return self._s[2505]! }
- public var Passport_FieldIdentityUploadHelp: String { return self._s[2506]! }
- public var Stickers_FrequentlyUsed: String { return self._s[2508]! }
- public var Channel_BanUser_PermissionSendMessages: String { return self._s[2509]! }
- public var Passport_Address_OneOfTypeUtilityBill: String { return self._s[2511]! }
+ public var Channel_Info_Management: String { return self._s[2528]! }
+ public var Passport_FieldIdentityUploadHelp: String { return self._s[2529]! }
+ public var Stickers_FrequentlyUsed: String { return self._s[2531]! }
+ public var Channel_BanUser_PermissionSendMessages: String { return self._s[2532]! }
+ public var Passport_Address_OneOfTypeUtilityBill: String { return self._s[2534]! }
public func LOCAL_CHANNEL_MESSAGE_FWDS(_ _1: String, _ _2: Int) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2512]!, self._r[2512]!, [_1, "\(_2)"])
+ return formatWithArgumentRanges(self._s[2535]!, self._r[2535]!, [_1, "\(_2)"])
}
- public var TwoFactorSetup_Password_Title: String { return self._s[2513]! }
- public var Passport_Address_EditResidentialAddress: String { return self._s[2514]! }
- public var PrivacyPolicy_DeclineTitle: String { return self._s[2515]! }
- public var CreatePoll_TextHeader: String { return self._s[2516]! }
+ public var TwoFactorSetup_Password_Title: String { return self._s[2536]! }
+ public var Passport_Address_EditResidentialAddress: String { return self._s[2537]! }
+ public var PrivacyPolicy_DeclineTitle: String { return self._s[2538]! }
+ public var CreatePoll_TextHeader: String { return self._s[2539]! }
public func Checkout_SavePasswordTimeoutAndTouchId(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2517]!, self._r[2517]!, [_0])
+ return formatWithArgumentRanges(self._s[2540]!, self._r[2540]!, [_0])
}
- public var PhotoEditor_QualityMedium: String { return self._s[2518]! }
- public var InfoPlist_NSMicrophoneUsageDescription: String { return self._s[2519]! }
- public var Conversation_StatusKickedFromChannel: String { return self._s[2521]! }
- public var CheckoutInfo_ReceiverInfoName: String { return self._s[2522]! }
- public var Group_ErrorSendRestrictedStickers: String { return self._s[2523]! }
+ public var PhotoEditor_QualityMedium: String { return self._s[2541]! }
+ public var InfoPlist_NSMicrophoneUsageDescription: String { return self._s[2542]! }
+ public var Conversation_StatusKickedFromChannel: String { return self._s[2544]! }
+ public var CheckoutInfo_ReceiverInfoName: String { return self._s[2545]! }
+ public var Group_ErrorSendRestrictedStickers: String { return self._s[2546]! }
public func Conversation_RestrictedInlineTimed(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2524]!, self._r[2524]!, [_0])
+ return formatWithArgumentRanges(self._s[2547]!, self._r[2547]!, [_0])
}
public func Channel_AdminLog_MessageTransferedName(_ _1: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2525]!, self._r[2525]!, [_1])
+ return formatWithArgumentRanges(self._s[2548]!, self._r[2548]!, [_1])
}
- public var LogoutOptions_LogOutWalletInfo: String { return self._s[2526]! }
- public var TwoFactorSetup_Email_SkipConfirmationTitle: String { return self._s[2527]! }
- public var Conversation_LinkDialogOpen: String { return self._s[2529]! }
- public var TwoFactorSetup_Hint_Title: String { return self._s[2530]! }
- public var VoiceOver_Chat_PollNoVotes: String { return self._s[2531]! }
- public var Settings_Username: String { return self._s[2533]! }
- public var Conversation_Block: String { return self._s[2535]! }
- public var Wallpaper_Wallpaper: String { return self._s[2536]! }
- public var SocksProxySetup_UseProxy: String { return self._s[2538]! }
- public var Wallet_Send_Confirmation: String { return self._s[2539]! }
- public var EditTheme_UploadEditedTheme: String { return self._s[2540]! }
- public var UserInfo_ShareMyContactInfo: String { return self._s[2541]! }
- public var MessageTimer_Forever: String { return self._s[2542]! }
- public var Privacy_Calls_WhoCanCallMe: String { return self._s[2543]! }
- public var PhotoEditor_DiscardChanges: String { return self._s[2544]! }
- public var AuthSessions_TerminateOtherSessionsHelp: String { return self._s[2545]! }
- public var Passport_Language_da: String { return self._s[2546]! }
- public var Conversation_PrivateChannelTimeLimitedAlertJoin: String { return self._s[2548]! }
- public var SocksProxySetup_PortPlaceholder: String { return self._s[2549]! }
+ public var LogoutOptions_LogOutWalletInfo: String { return self._s[2549]! }
+ public var TwoFactorSetup_Email_SkipConfirmationTitle: String { return self._s[2550]! }
+ public var Conversation_LinkDialogOpen: String { return self._s[2552]! }
+ public var TwoFactorSetup_Hint_Title: String { return self._s[2553]! }
+ public var VoiceOver_Chat_PollNoVotes: String { return self._s[2554]! }
+ public var Settings_Username: String { return self._s[2556]! }
+ public var Conversation_Block: String { return self._s[2558]! }
+ public var Wallpaper_Wallpaper: String { return self._s[2559]! }
+ public var SocksProxySetup_UseProxy: String { return self._s[2561]! }
+ public var Wallet_Send_Confirmation: String { return self._s[2562]! }
+ public var EditTheme_UploadEditedTheme: String { return self._s[2563]! }
+ public var UserInfo_ShareMyContactInfo: String { return self._s[2564]! }
+ public var MessageTimer_Forever: String { return self._s[2565]! }
+ public var Privacy_Calls_WhoCanCallMe: String { return self._s[2566]! }
+ public var PhotoEditor_DiscardChanges: String { return self._s[2567]! }
+ public var AuthSessions_TerminateOtherSessionsHelp: String { return self._s[2568]! }
+ public var Passport_Language_da: String { return self._s[2569]! }
+ public var Conversation_PrivateChannelTimeLimitedAlertJoin: String { return self._s[2571]! }
+ public var SocksProxySetup_PortPlaceholder: String { return self._s[2572]! }
public func SecretGIF_NotViewedYet(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2550]!, self._r[2550]!, [_0])
+ return formatWithArgumentRanges(self._s[2573]!, self._r[2573]!, [_0])
}
- public var Passport_Address_EditPassportRegistration: String { return self._s[2551]! }
+ public var Passport_Address_EditPassportRegistration: String { return self._s[2574]! }
public func Channel_AdminLog_MessageChangedGroupAbout(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2553]!, self._r[2553]!, [_0])
+ return formatWithArgumentRanges(self._s[2576]!, self._r[2576]!, [_0])
}
- public var Settings_AddDevice: String { return self._s[2554]! }
- public var Passport_Identity_ResidenceCountryPlaceholder: String { return self._s[2556]! }
- public var AuthSessions_AddDeviceIntro_Text1: String { return self._s[2557]! }
- public var Conversation_SearchByName_Prefix: String { return self._s[2558]! }
- public var Conversation_PinnedPoll: String { return self._s[2559]! }
- public var AuthSessions_AddDeviceIntro_Text2: String { return self._s[2560]! }
- public var Conversation_EmptyGifPanelPlaceholder: String { return self._s[2561]! }
- public var AuthSessions_AddDeviceIntro_Text3: String { return self._s[2562]! }
+ public var Settings_AddDevice: String { return self._s[2577]! }
+ public var Passport_Identity_ResidenceCountryPlaceholder: String { return self._s[2579]! }
+ public var AuthSessions_AddDeviceIntro_Text1: String { return self._s[2580]! }
+ public var Conversation_SearchByName_Prefix: String { return self._s[2581]! }
+ public var Conversation_PinnedPoll: String { return self._s[2582]! }
+ public var AuthSessions_AddDeviceIntro_Text2: String { return self._s[2583]! }
+ public var Conversation_EmptyGifPanelPlaceholder: String { return self._s[2584]! }
+ public var AuthSessions_AddDeviceIntro_Text3: String { return self._s[2585]! }
public func PUSH_ENCRYPTION_ACCEPT(_ _1: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2563]!, self._r[2563]!, [_1])
+ return formatWithArgumentRanges(self._s[2586]!, self._r[2586]!, [_1])
}
- public var WallpaperSearch_ColorPurple: String { return self._s[2564]! }
- public var Cache_ByPeerHeader: String { return self._s[2565]! }
+ public var WallpaperSearch_ColorPurple: String { return self._s[2587]! }
+ public var Cache_ByPeerHeader: String { return self._s[2588]! }
public func Conversation_EncryptedPlaceholderTitleIncoming(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2566]!, self._r[2566]!, [_0])
+ return formatWithArgumentRanges(self._s[2589]!, self._r[2589]!, [_0])
}
- public var ChatSettings_AutoDownloadDocuments: String { return self._s[2567]! }
- public var Appearance_ThemePreview_Chat_3_Text: String { return self._s[2570]! }
- public var Wallet_Completed_Title: String { return self._s[2571]! }
- public var Notification_PinnedMessage: String { return self._s[2572]! }
- public var TwoFactorSetup_EmailVerification_Placeholder: String { return self._s[2573]! }
- public var VoiceOver_Chat_RecordModeVideoMessage: String { return self._s[2575]! }
- public var Contacts_SortBy: String { return self._s[2576]! }
+ public var ChatSettings_AutoDownloadDocuments: String { return self._s[2590]! }
+ public var Appearance_ThemePreview_Chat_3_Text: String { return self._s[2593]! }
+ public var Wallet_Completed_Title: String { return self._s[2594]! }
+ public var Notification_PinnedMessage: String { return self._s[2595]! }
+ public var TwoFactorSetup_EmailVerification_Placeholder: String { return self._s[2596]! }
+ public var VoiceOver_Chat_RecordModeVideoMessage: String { return self._s[2598]! }
+ public var Contacts_SortBy: String { return self._s[2599]! }
public func PUSH_CHANNEL_MESSAGE_NOTEXT(_ _1: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2577]!, self._r[2577]!, [_1])
+ return formatWithArgumentRanges(self._s[2600]!, self._r[2600]!, [_1])
}
- public var Appearance_ColorThemeNight: String { return self._s[2579]! }
+ public var Appearance_ColorThemeNight: String { return self._s[2602]! }
public func PUSH_MESSAGE_GAME(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2580]!, self._r[2580]!, [_1, _2])
+ return formatWithArgumentRanges(self._s[2603]!, self._r[2603]!, [_1, _2])
}
- public var Call_EncryptionKey_Title: String { return self._s[2581]! }
- public var Watch_UserInfo_Service: String { return self._s[2582]! }
- public var SettingsSearch_Synonyms_Data_SaveEditedPhotos: String { return self._s[2584]! }
- public var Conversation_Unpin: String { return self._s[2586]! }
- public var CancelResetAccount_Title: String { return self._s[2587]! }
- public var Map_LiveLocationFor15Minutes: String { return self._s[2588]! }
+ public var Call_EncryptionKey_Title: String { return self._s[2604]! }
+ public var Watch_UserInfo_Service: String { return self._s[2605]! }
+ public var SettingsSearch_Synonyms_Data_SaveEditedPhotos: String { return self._s[2607]! }
+ public var Conversation_Unpin: String { return self._s[2609]! }
+ public var CancelResetAccount_Title: String { return self._s[2610]! }
+ public var Map_LiveLocationFor15Minutes: String { return self._s[2611]! }
public func Time_PreciseDate_m8(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2590]!, self._r[2590]!, [_1, _2, _3])
+ return formatWithArgumentRanges(self._s[2613]!, self._r[2613]!, [_1, _2, _3])
}
- public var Group_Members_AddMemberBotErrorNotAllowed: String { return self._s[2591]! }
- public var Appearance_BubbleCorners_Title: String { return self._s[2592]! }
- public var CallSettings_Title: String { return self._s[2593]! }
- public var SettingsSearch_Synonyms_Appearance_ChatBackground: String { return self._s[2594]! }
- public var PasscodeSettings_EncryptDataHelp: String { return self._s[2596]! }
- public var AutoDownloadSettings_Contacts: String { return self._s[2597]! }
+ public var Group_Members_AddMemberBotErrorNotAllowed: String { return self._s[2614]! }
+ public var Appearance_BubbleCorners_Title: String { return self._s[2615]! }
+ public var CallSettings_Title: String { return self._s[2616]! }
+ public var SettingsSearch_Synonyms_Appearance_ChatBackground: String { return self._s[2617]! }
+ public var PasscodeSettings_EncryptDataHelp: String { return self._s[2619]! }
+ public var AutoDownloadSettings_Contacts: String { return self._s[2620]! }
public func Channel_AdminLog_MessageRankName(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2598]!, self._r[2598]!, [_1, _2])
+ return formatWithArgumentRanges(self._s[2621]!, self._r[2621]!, [_1, _2])
}
- public var ChatList_Tabs_AllChats: String { return self._s[2599]! }
- public var Passport_Identity_DocumentDetails: String { return self._s[2600]! }
- public var LoginPassword_PasswordHelp: String { return self._s[2601]! }
- public var ChatListFolderSettings_Info: String { return self._s[2602]! }
- public var SettingsSearch_Synonyms_Data_AutoDownloadUsingWifi: String { return self._s[2603]! }
- public var PrivacyLastSeenSettings_CustomShareSettings_Delete: String { return self._s[2604]! }
- public var ChatContextMenu_TextSelectionTip: String { return self._s[2605]! }
- public var ChatListFolder_CategoryGroups: String { return self._s[2606]! }
- public var Checkout_TotalPaidAmount: String { return self._s[2608]! }
+ public var ChatList_Tabs_AllChats: String { return self._s[2622]! }
+ public var Passport_Identity_DocumentDetails: String { return self._s[2623]! }
+ public var LoginPassword_PasswordHelp: String { return self._s[2624]! }
+ public var ChatListFolderSettings_Info: String { return self._s[2625]! }
+ public var SettingsSearch_Synonyms_Data_AutoDownloadUsingWifi: String { return self._s[2626]! }
+ public var PrivacyLastSeenSettings_CustomShareSettings_Delete: String { return self._s[2627]! }
+ public var ChatContextMenu_TextSelectionTip: String { return self._s[2628]! }
+ public var ChatListFolder_CategoryGroups: String { return self._s[2629]! }
+ public var Checkout_TotalPaidAmount: String { return self._s[2631]! }
public func FileSize_KB(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2609]!, self._r[2609]!, [_0])
+ return formatWithArgumentRanges(self._s[2632]!, self._r[2632]!, [_0])
}
- public var ChatState_Updating: String { return self._s[2610]! }
- public var PasscodeSettings_ChangePasscode: String { return self._s[2611]! }
- public var ChatListFolder_ExcludedSectionHeader: String { return self._s[2612]! }
- public var Conversation_SecretLinkPreviewAlert: String { return self._s[2614]! }
- public var Privacy_SecretChatsLinkPreviews: String { return self._s[2615]! }
+ public var ChatState_Updating: String { return self._s[2633]! }
+ public var PasscodeSettings_ChangePasscode: String { return self._s[2634]! }
+ public var ChatListFolder_ExcludedSectionHeader: String { return self._s[2635]! }
+ public var Conversation_SecretLinkPreviewAlert: String { return self._s[2637]! }
+ public var Privacy_SecretChatsLinkPreviews: String { return self._s[2638]! }
public func PUSH_CHANNEL_MESSAGE_DOC(_ _1: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2616]!, self._r[2616]!, [_1])
+ return formatWithArgumentRanges(self._s[2639]!, self._r[2639]!, [_1])
}
- public var VoiceOver_Chat_ReplyToYourMessage: String { return self._s[2617]! }
- public var Contacts_InviteFriends: String { return self._s[2619]! }
- public var Map_ChooseLocationTitle: String { return self._s[2620]! }
- public var Conversation_StopPoll: String { return self._s[2622]! }
+ public var VoiceOver_Chat_ReplyToYourMessage: String { return self._s[2640]! }
+ public var Contacts_InviteFriends: String { return self._s[2642]! }
+ public var Map_ChooseLocationTitle: String { return self._s[2643]! }
+ public var Conversation_StopPoll: String { return self._s[2645]! }
public func WebSearch_SearchNoResultsDescription(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2623]!, self._r[2623]!, [_0])
+ return formatWithArgumentRanges(self._s[2646]!, self._r[2646]!, [_0])
}
- public var Call_Camera: String { return self._s[2624]! }
- public var LogoutOptions_ChangePhoneNumberTitle: String { return self._s[2625]! }
- public var AppWallet_Intro_Text: String { return self._s[2626]! }
- public var Appearance_BubbleCornersSetting: String { return self._s[2627]! }
- public var Calls_RatingFeedback: String { return self._s[2628]! }
+ public var Call_Camera: String { return self._s[2647]! }
+ public var LogoutOptions_ChangePhoneNumberTitle: String { return self._s[2648]! }
+ public var AppWallet_Intro_Text: String { return self._s[2649]! }
+ public var Appearance_BubbleCornersSetting: String { return self._s[2650]! }
+ public var Calls_RatingFeedback: String { return self._s[2651]! }
public func Conversation_NoticeInvitedByInGroup(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2630]!, self._r[2630]!, [_0])
+ return formatWithArgumentRanges(self._s[2653]!, self._r[2653]!, [_0])
}
- public var GroupInfo_BroadcastListNamePlaceholder: String { return self._s[2631]! }
- public var Wallet_Alert_OK: String { return self._s[2632]! }
- public var NotificationsSound_Pulse: String { return self._s[2633]! }
- public var Watch_LastSeen_Lately: String { return self._s[2634]! }
- public var ReportGroupLocation_Report: String { return self._s[2637]! }
- public var Widget_NoUsers: String { return self._s[2638]! }
- public var Conversation_UnvotePoll: String { return self._s[2639]! }
- public var SettingsSearch_Synonyms_Privacy_ProfilePhoto: String { return self._s[2641]! }
- public var Privacy_ProfilePhoto_WhoCanSeeMyPhoto: String { return self._s[2642]! }
- public var NotificationsSound_Circles: String { return self._s[2643]! }
- public var PrivacyLastSeenSettings_AlwaysShareWith_Title: String { return self._s[2646]! }
- public var Wallet_Settings_DeleteWallet: String { return self._s[2647]! }
- public var ChatListFolder_CategoryBots: String { return self._s[2648]! }
- public var TwoStepAuth_RecoveryCodeExpired: String { return self._s[2649]! }
- public var Proxy_TooltipUnavailable: String { return self._s[2650]! }
- public var Passport_Identity_CountryPlaceholder: String { return self._s[2652]! }
- public var GroupInfo_Permissions_SlowmodeInfo: String { return self._s[2654]! }
- public var Conversation_FileDropbox: String { return self._s[2655]! }
- public var Notifications_ExceptionsUnmuted: String { return self._s[2656]! }
- public var Tour_Text3: String { return self._s[2658]! }
- public var Login_ResetAccountProtected_Title: String { return self._s[2661]! }
- public var ChatListFolder_NamePlaceholder: String { return self._s[2662]! }
- public var Settings_FrequentlyAskedQuestions: String { return self._s[2663]! }
- public var GroupPermission_NoSendMessages: String { return self._s[2664]! }
- public var WallpaperSearch_ColorTitle: String { return self._s[2665]! }
- public var ChatAdmins_AllMembersAreAdminsOnHelp: String { return self._s[2666]! }
+ public var GroupInfo_BroadcastListNamePlaceholder: String { return self._s[2654]! }
+ public var Wallet_Alert_OK: String { return self._s[2655]! }
+ public var NotificationsSound_Pulse: String { return self._s[2656]! }
+ public var Watch_LastSeen_Lately: String { return self._s[2657]! }
+ public var ReportGroupLocation_Report: String { return self._s[2660]! }
+ public var Widget_NoUsers: String { return self._s[2661]! }
+ public var Conversation_UnvotePoll: String { return self._s[2662]! }
+ public var SettingsSearch_Synonyms_Privacy_ProfilePhoto: String { return self._s[2664]! }
+ public var Privacy_ProfilePhoto_WhoCanSeeMyPhoto: String { return self._s[2665]! }
+ public var NotificationsSound_Circles: String { return self._s[2666]! }
+ public var PrivacyLastSeenSettings_AlwaysShareWith_Title: String { return self._s[2669]! }
+ public var Wallet_Settings_DeleteWallet: String { return self._s[2670]! }
+ public var ChatListFolder_CategoryBots: String { return self._s[2671]! }
+ public var TwoStepAuth_RecoveryCodeExpired: String { return self._s[2672]! }
+ public var Proxy_TooltipUnavailable: String { return self._s[2673]! }
+ public var Passport_Identity_CountryPlaceholder: String { return self._s[2675]! }
+ public var GroupInfo_Permissions_SlowmodeInfo: String { return self._s[2677]! }
+ public var Conversation_FileDropbox: String { return self._s[2678]! }
+ public var Notifications_ExceptionsUnmuted: String { return self._s[2679]! }
+ public var Tour_Text3: String { return self._s[2681]! }
+ public var Login_ResetAccountProtected_Title: String { return self._s[2684]! }
+ public var ChatListFolder_NamePlaceholder: String { return self._s[2685]! }
+ public var Settings_FrequentlyAskedQuestions: String { return self._s[2686]! }
+ public var GroupPermission_NoSendMessages: String { return self._s[2687]! }
+ public var WallpaperSearch_ColorTitle: String { return self._s[2688]! }
+ public var ChatAdmins_AllMembersAreAdminsOnHelp: String { return self._s[2689]! }
public func Conversation_LiveLocationYouAnd(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2668]!, self._r[2668]!, [_0])
+ return formatWithArgumentRanges(self._s[2691]!, self._r[2691]!, [_0])
}
- public var GroupInfo_AddParticipantTitle: String { return self._s[2669]! }
- public var Checkout_ShippingOption_Title: String { return self._s[2670]! }
- public var ChatSettings_AutoDownloadTitle: String { return self._s[2671]! }
+ public var GroupInfo_AddParticipantTitle: String { return self._s[2692]! }
+ public var Checkout_ShippingOption_Title: String { return self._s[2693]! }
+ public var ChatSettings_AutoDownloadTitle: String { return self._s[2694]! }
public func DialogList_SingleTypingSuffix(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2672]!, self._r[2672]!, [_0])
+ return formatWithArgumentRanges(self._s[2695]!, self._r[2695]!, [_0])
}
public func ChatSettings_AutoDownloadSettings_TypeVideo(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2673]!, self._r[2673]!, [_0])
+ return formatWithArgumentRanges(self._s[2696]!, self._r[2696]!, [_0])
}
- public var Channel_Management_LabelAdministrator: String { return self._s[2674]! }
- public var EditTheme_FileReadError: String { return self._s[2675]! }
- public var OwnershipTransfer_ComeBackLater: String { return self._s[2676]! }
- public var PrivacyLastSeenSettings_NeverShareWith_Placeholder: String { return self._s[2677]! }
- public var AutoDownloadSettings_Photos: String { return self._s[2679]! }
- public var Appearance_PreviewIncomingText: String { return self._s[2680]! }
- public var ChatList_Context_MarkAllAsRead: String { return self._s[2681]! }
- public var ChannelInfo_ConfirmLeave: String { return self._s[2682]! }
- public var ChatListFolder_ExcludeSectionInfo: String { return self._s[2683]! }
- public var MediaPicker_MomentsDateRangeSameMonthYearFormat: String { return self._s[2684]! }
- public var Passport_Identity_DocumentNumberPlaceholder: String { return self._s[2685]! }
- public var Channel_AdminLogFilter_EventsNewMembers: String { return self._s[2686]! }
- public var PasscodeSettings_AutoLock_IfAwayFor_5minutes: String { return self._s[2687]! }
- public var GroupInfo_SetGroupPhotoStop: String { return self._s[2688]! }
- public var Notification_SecretChatScreenshot: String { return self._s[2689]! }
- public var AccessDenied_Wallpapers: String { return self._s[2690]! }
- public var ChatList_Context_Mute: String { return self._s[2692]! }
- public var Passport_Address_City: String { return self._s[2693]! }
- public var Settings_EditVideo: String { return self._s[2694]! }
- public var InfoPlist_NSPhotoLibraryAddUsageDescription: String { return self._s[2695]! }
- public var Appearance_ThemeCarouselClassic: String { return self._s[2696]! }
- public var SocksProxySetup_SecretPlaceholder: String { return self._s[2697]! }
- public var AccessDenied_LocationDisabled: String { return self._s[2698]! }
- public var Group_Location_Title: String { return self._s[2699]! }
- public var SocksProxySetup_HostnamePlaceholder: String { return self._s[2701]! }
- public var GroupInfo_Sound: String { return self._s[2702]! }
- public var SettingsSearch_Synonyms_ChatSettings_OpenLinksIn: String { return self._s[2703]! }
- public var ChannelInfo_ScamChannelWarning: String { return self._s[2704]! }
- public var Stickers_RemoveFromFavorites: String { return self._s[2705]! }
- public var Contacts_Title: String { return self._s[2706]! }
- public var EditTheme_ThemeTemplateAlertText: String { return self._s[2707]! }
- public var Passport_Language_fr: String { return self._s[2708]! }
- public var TwoFactorSetup_EmailVerification_Action: String { return self._s[2709]! }
- public var Notifications_ResetAllNotifications: String { return self._s[2710]! }
- public var IntentsSettings_SuggestedChats: String { return self._s[2712]! }
- public var PrivacySettings_SecurityTitle: String { return self._s[2714]! }
- public var Checkout_NewCard_Title: String { return self._s[2715]! }
- public var Login_HaveNotReceivedCodeInternal: String { return self._s[2716]! }
- public var Conversation_ForwardChats: String { return self._s[2717]! }
- public var Wallet_SecureStorageReset_PasscodeText: String { return self._s[2719]! }
- public var PasscodeSettings_4DigitCode: String { return self._s[2721]! }
- public var Settings_FAQ: String { return self._s[2723]! }
- public var AutoDownloadSettings_DocumentsTitle: String { return self._s[2724]! }
- public var Conversation_ContextMenuForward: String { return self._s[2725]! }
- public var VoiceOver_Chat_YourPhoto: String { return self._s[2728]! }
- public var PrivacyPolicy_Title: String { return self._s[2731]! }
- public var Notifications_TextTone: String { return self._s[2732]! }
- public var Profile_CreateNewContact: String { return self._s[2733]! }
- public var PrivacyPhoneNumberSettings_WhoCanSeeMyPhoneNumber: String { return self._s[2734]! }
- public var TwoFactorSetup_EmailVerification_Title: String { return self._s[2736]! }
- public var Call_Speaker: String { return self._s[2737]! }
- public var AutoNightTheme_AutomaticSection: String { return self._s[2738]! }
- public var Channel_OwnershipTransfer_EnterPassword: String { return self._s[2740]! }
- public var Channel_Username_InvalidCharacters: String { return self._s[2741]! }
+ public var Channel_Management_LabelAdministrator: String { return self._s[2697]! }
+ public var EditTheme_FileReadError: String { return self._s[2698]! }
+ public var OwnershipTransfer_ComeBackLater: String { return self._s[2699]! }
+ public var PrivacyLastSeenSettings_NeverShareWith_Placeholder: String { return self._s[2700]! }
+ public var AutoDownloadSettings_Photos: String { return self._s[2702]! }
+ public var Appearance_PreviewIncomingText: String { return self._s[2703]! }
+ public var ChatList_Context_MarkAllAsRead: String { return self._s[2704]! }
+ public var ChannelInfo_ConfirmLeave: String { return self._s[2705]! }
+ public var ChatListFolder_ExcludeSectionInfo: String { return self._s[2706]! }
+ public var MediaPicker_MomentsDateRangeSameMonthYearFormat: String { return self._s[2707]! }
+ public var Passport_Identity_DocumentNumberPlaceholder: String { return self._s[2708]! }
+ public var Channel_AdminLogFilter_EventsNewMembers: String { return self._s[2709]! }
+ public var PasscodeSettings_AutoLock_IfAwayFor_5minutes: String { return self._s[2710]! }
+ public var GroupInfo_SetGroupPhotoStop: String { return self._s[2711]! }
+ public var Notification_SecretChatScreenshot: String { return self._s[2712]! }
+ public var AccessDenied_Wallpapers: String { return self._s[2713]! }
+ public var ChatList_Context_Mute: String { return self._s[2715]! }
+ public var Passport_Address_City: String { return self._s[2716]! }
+ public var Settings_EditVideo: String { return self._s[2717]! }
+ public var InfoPlist_NSPhotoLibraryAddUsageDescription: String { return self._s[2718]! }
+ public var Appearance_ThemeCarouselClassic: String { return self._s[2719]! }
+ public var SocksProxySetup_SecretPlaceholder: String { return self._s[2720]! }
+ public var AccessDenied_LocationDisabled: String { return self._s[2721]! }
+ public var Group_Location_Title: String { return self._s[2722]! }
+ public var SocksProxySetup_HostnamePlaceholder: String { return self._s[2724]! }
+ public var GroupInfo_Sound: String { return self._s[2725]! }
+ public var SettingsSearch_Synonyms_ChatSettings_OpenLinksIn: String { return self._s[2726]! }
+ public var ChannelInfo_ScamChannelWarning: String { return self._s[2727]! }
+ public var Stickers_RemoveFromFavorites: String { return self._s[2728]! }
+ public var Contacts_Title: String { return self._s[2729]! }
+ public var EditTheme_ThemeTemplateAlertText: String { return self._s[2730]! }
+ public var Passport_Language_fr: String { return self._s[2731]! }
+ public var TwoFactorSetup_EmailVerification_Action: String { return self._s[2732]! }
+ public var Notifications_ResetAllNotifications: String { return self._s[2733]! }
+ public var IntentsSettings_SuggestedChats: String { return self._s[2735]! }
+ public var PrivacySettings_SecurityTitle: String { return self._s[2737]! }
+ public var Checkout_NewCard_Title: String { return self._s[2738]! }
+ public var Login_HaveNotReceivedCodeInternal: String { return self._s[2739]! }
+ public var Conversation_ForwardChats: String { return self._s[2740]! }
+ public var Wallet_SecureStorageReset_PasscodeText: String { return self._s[2742]! }
+ public var PasscodeSettings_4DigitCode: String { return self._s[2744]! }
+ public var Settings_FAQ: String { return self._s[2746]! }
+ public var AutoDownloadSettings_DocumentsTitle: String { return self._s[2747]! }
+ public var Conversation_ContextMenuForward: String { return self._s[2748]! }
+ public var VoiceOver_Chat_YourPhoto: String { return self._s[2751]! }
+ public var PrivacyPolicy_Title: String { return self._s[2754]! }
+ public var Notifications_TextTone: String { return self._s[2755]! }
+ public var Profile_CreateNewContact: String { return self._s[2756]! }
+ public var PrivacyPhoneNumberSettings_WhoCanSeeMyPhoneNumber: String { return self._s[2757]! }
+ public var TwoFactorSetup_EmailVerification_Title: String { return self._s[2759]! }
+ public var Call_Speaker: String { return self._s[2760]! }
+ public var AutoNightTheme_AutomaticSection: String { return self._s[2761]! }
+ public var Channel_OwnershipTransfer_EnterPassword: String { return self._s[2763]! }
+ public var Channel_Username_InvalidCharacters: String { return self._s[2764]! }
public func Channel_AdminLog_MessageChangedChannelUsername(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2742]!, self._r[2742]!, [_0])
+ return formatWithArgumentRanges(self._s[2765]!, self._r[2765]!, [_0])
}
- public var AutoDownloadSettings_AutodownloadFiles: String { return self._s[2743]! }
- public var PrivacySettings_LastSeenTitle: String { return self._s[2744]! }
- public var Channel_AdminLog_CanInviteUsers: String { return self._s[2745]! }
- public var SettingsSearch_Synonyms_Privacy_Data_ClearPaymentsInfo: String { return self._s[2746]! }
- public var OwnershipTransfer_SecurityCheck: String { return self._s[2747]! }
- public var Conversation_MessageDeliveryFailed: String { return self._s[2748]! }
- public var Watch_ChatList_NoConversationsText: String { return self._s[2749]! }
- public var Bot_Unblock: String { return self._s[2750]! }
- public var TextFormat_Italic: String { return self._s[2751]! }
- public var WallpaperSearch_ColorPink: String { return self._s[2752]! }
- public var Settings_About_Help: String { return self._s[2754]! }
- public var SearchImages_Title: String { return self._s[2755]! }
- public var Weekday_Wednesday: String { return self._s[2756]! }
- public var Conversation_ClousStorageInfo_Description1: String { return self._s[2757]! }
- public var ExplicitContent_AlertTitle: String { return self._s[2758]! }
+ public var AutoDownloadSettings_AutodownloadFiles: String { return self._s[2766]! }
+ public var PrivacySettings_LastSeenTitle: String { return self._s[2767]! }
+ public var Channel_AdminLog_CanInviteUsers: String { return self._s[2768]! }
+ public var SettingsSearch_Synonyms_Privacy_Data_ClearPaymentsInfo: String { return self._s[2769]! }
+ public var OwnershipTransfer_SecurityCheck: String { return self._s[2770]! }
+ public var Conversation_MessageDeliveryFailed: String { return self._s[2771]! }
+ public var Watch_ChatList_NoConversationsText: String { return self._s[2772]! }
+ public var Bot_Unblock: String { return self._s[2773]! }
+ public var TextFormat_Italic: String { return self._s[2774]! }
+ public var WallpaperSearch_ColorPink: String { return self._s[2775]! }
+ public var Settings_About_Help: String { return self._s[2777]! }
+ public var SearchImages_Title: String { return self._s[2778]! }
+ public var Weekday_Wednesday: String { return self._s[2779]! }
+ public var Conversation_ClousStorageInfo_Description1: String { return self._s[2780]! }
+ public var ExplicitContent_AlertTitle: String { return self._s[2781]! }
public func Time_PreciseDate_m5(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2759]!, self._r[2759]!, [_1, _2, _3])
+ return formatWithArgumentRanges(self._s[2782]!, self._r[2782]!, [_1, _2, _3])
}
- public var Channel_DiscussionGroup_Create: String { return self._s[2760]! }
- public var Weekday_Thursday: String { return self._s[2761]! }
- public var Channel_BanUser_PermissionChangeGroupInfo: String { return self._s[2762]! }
- public var Channel_Members_AddMembersHelp: String { return self._s[2763]! }
+ public var Channel_DiscussionGroup_Create: String { return self._s[2783]! }
+ public var Weekday_Thursday: String { return self._s[2784]! }
+ public var Channel_BanUser_PermissionChangeGroupInfo: String { return self._s[2785]! }
+ public var Channel_Members_AddMembersHelp: String { return self._s[2786]! }
public func Checkout_SavePasswordTimeout(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2764]!, self._r[2764]!, [_0])
+ return formatWithArgumentRanges(self._s[2787]!, self._r[2787]!, [_0])
}
- public var Channel_DiscussionGroup_LinkGroup: String { return self._s[2765]! }
- public var SettingsSearch_Synonyms_Notifications_InAppNotificationsVibrate: String { return self._s[2766]! }
- public var Passport_RequestedInformation: String { return self._s[2767]! }
- public var Login_PhoneAndCountryHelp: String { return self._s[2768]! }
- public var Conversation_EncryptionProcessing: String { return self._s[2770]! }
- public var Notifications_PermissionsSuppressWarningTitle: String { return self._s[2771]! }
- public var PhotoEditor_EnhanceTool: String { return self._s[2773]! }
- public var Channel_Setup_Title: String { return self._s[2774]! }
- public var PeerInfo_PaneVoiceAndVideo: String { return self._s[2775]! }
- public var Conversation_SearchPlaceholder: String { return self._s[2776]! }
- public var OldChannels_GroupEmptyFormat: String { return self._s[2777]! }
- public var AccessDenied_LocationAlwaysDenied: String { return self._s[2778]! }
- public var Checkout_ErrorGeneric: String { return self._s[2779]! }
- public var Passport_Language_hu: String { return self._s[2780]! }
- public var GroupPermission_EditingDisabled: String { return self._s[2781]! }
- public var Wallet_Month_ShortSeptember: String { return self._s[2783]! }
+ public var Channel_DiscussionGroup_LinkGroup: String { return self._s[2788]! }
+ public var SettingsSearch_Synonyms_Notifications_InAppNotificationsVibrate: String { return self._s[2789]! }
+ public var Passport_RequestedInformation: String { return self._s[2790]! }
+ public var Login_PhoneAndCountryHelp: String { return self._s[2791]! }
+ public var Conversation_EncryptionProcessing: String { return self._s[2793]! }
+ public var Notifications_PermissionsSuppressWarningTitle: String { return self._s[2794]! }
+ public var PhotoEditor_EnhanceTool: String { return self._s[2796]! }
+ public var Channel_Setup_Title: String { return self._s[2797]! }
+ public var PeerInfo_PaneVoiceAndVideo: String { return self._s[2798]! }
+ public var Conversation_SearchPlaceholder: String { return self._s[2799]! }
+ public var OldChannels_GroupEmptyFormat: String { return self._s[2800]! }
+ public var AccessDenied_LocationAlwaysDenied: String { return self._s[2801]! }
+ public var Checkout_ErrorGeneric: String { return self._s[2802]! }
+ public var Passport_Language_hu: String { return self._s[2803]! }
+ public var GroupPermission_EditingDisabled: String { return self._s[2804]! }
+ public var Wallet_Month_ShortSeptember: String { return self._s[2806]! }
public func Passport_Identity_UploadOneOfScan(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2784]!, self._r[2784]!, [_0])
+ return formatWithArgumentRanges(self._s[2807]!, self._r[2807]!, [_0])
}
public func PUSH_MESSAGE(_ _1: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2787]!, self._r[2787]!, [_1])
+ return formatWithArgumentRanges(self._s[2810]!, self._r[2810]!, [_1])
}
- public var ChatList_DeleteSavedMessagesConfirmationTitle: String { return self._s[2788]! }
+ public var ChatList_DeleteSavedMessagesConfirmationTitle: String { return self._s[2811]! }
public func UserInfo_BlockConfirmationTitle(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2789]!, self._r[2789]!, [_0])
+ return formatWithArgumentRanges(self._s[2812]!, self._r[2812]!, [_0])
}
- public var Conversation_CloudStorageInfo_Title: String { return self._s[2790]! }
- public var Group_Location_Info: String { return self._s[2791]! }
- public var PhotoEditor_CropAspectRatioSquare: String { return self._s[2792]! }
- public var Permissions_PeopleNearbyAllow_v0: String { return self._s[2793]! }
+ public var Conversation_CloudStorageInfo_Title: String { return self._s[2813]! }
+ public var Group_Location_Info: String { return self._s[2814]! }
+ public var PhotoEditor_CropAspectRatioSquare: String { return self._s[2815]! }
+ public var Permissions_PeopleNearbyAllow_v0: String { return self._s[2816]! }
public func Notification_Exceptions_MutedUntil(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2795]!, self._r[2795]!, [_0])
+ return formatWithArgumentRanges(self._s[2818]!, self._r[2818]!, [_0])
}
- public var Conversation_ClearPrivateHistory: String { return self._s[2796]! }
- public var ContactInfo_PhoneLabelHome: String { return self._s[2797]! }
- public var Appearance_RemoveThemeConfirmation: String { return self._s[2798]! }
- public var PrivacySettings_LastSeenContacts: String { return self._s[2799]! }
+ public var Conversation_ClearPrivateHistory: String { return self._s[2819]! }
+ public var ContactInfo_PhoneLabelHome: String { return self._s[2820]! }
+ public var Appearance_RemoveThemeConfirmation: String { return self._s[2821]! }
+ public var PrivacySettings_LastSeenContacts: String { return self._s[2822]! }
public func ChangePhone_ErrorOccupied(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2800]!, self._r[2800]!, [_0])
+ return formatWithArgumentRanges(self._s[2823]!, self._r[2823]!, [_0])
}
+ public var Cache_MaximumCacheSizeHelp: String { return self._s[2824]! }
public func Notification_PinnedQuizMessage(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2801]!, self._r[2801]!, [_0])
+ return formatWithArgumentRanges(self._s[2825]!, self._r[2825]!, [_0])
}
- public var Passport_Language_cs: String { return self._s[2802]! }
- public var Message_PinnedAnimationMessage: String { return self._s[2804]! }
- public var Passport_Identity_ReverseSideHelp: String { return self._s[2806]! }
- public var SettingsSearch_Synonyms_Data_Storage_Title: String { return self._s[2807]! }
- public var Wallet_Info_TransactionTo: String { return self._s[2809]! }
- public var Stats_ViewsBySourceTitle: String { return self._s[2810]! }
- public var ChatList_DeleteForEveryoneConfirmationText: String { return self._s[2811]! }
- public var SettingsSearch_Synonyms_Privacy_PasscodeAndTouchId: String { return self._s[2812]! }
- public var Embed_PlayingInPIP: String { return self._s[2813]! }
- public var Appearance_ThemePreview_Chat_3_TextWithLink: String { return self._s[2814]! }
- public var AutoNightTheme_ScheduleSection: String { return self._s[2815]! }
- public var Stats_GroupMessages: String { return self._s[2816]! }
+ public var Passport_Language_cs: String { return self._s[2826]! }
+ public var Message_PinnedAnimationMessage: String { return self._s[2828]! }
+ public var Passport_Identity_ReverseSideHelp: String { return self._s[2830]! }
+ public var SettingsSearch_Synonyms_Data_Storage_Title: String { return self._s[2831]! }
+ public var Wallet_Info_TransactionTo: String { return self._s[2833]! }
+ public var Stats_ViewsBySourceTitle: String { return self._s[2834]! }
+ public var ChatList_DeleteForEveryoneConfirmationText: String { return self._s[2835]! }
+ public var SettingsSearch_Synonyms_Privacy_PasscodeAndTouchId: String { return self._s[2836]! }
+ public var Embed_PlayingInPIP: String { return self._s[2837]! }
+ public var Appearance_ThemePreview_Chat_3_TextWithLink: String { return self._s[2838]! }
+ public var AutoNightTheme_ScheduleSection: String { return self._s[2839]! }
+ public var Stats_GroupMessages: String { return self._s[2840]! }
public func Call_EmojiDescription(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2817]!, self._r[2817]!, [_0])
+ return formatWithArgumentRanges(self._s[2841]!, self._r[2841]!, [_0])
}
- public var MediaPicker_LivePhotoDescription: String { return self._s[2818]! }
+ public var MediaPicker_LivePhotoDescription: String { return self._s[2842]! }
public func Channel_AdminLog_MessageRestrictedName(_ _1: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2819]!, self._r[2819]!, [_1])
+ return formatWithArgumentRanges(self._s[2843]!, self._r[2843]!, [_1])
}
- public var Notification_PaymentSent: String { return self._s[2820]! }
- public var PhotoEditor_CurvesGreen: String { return self._s[2821]! }
- public var Notification_Exceptions_PreviewAlwaysOff: String { return self._s[2822]! }
- public var AutoNightTheme_System: String { return self._s[2823]! }
- public var SaveIncomingPhotosSettings_Title: String { return self._s[2824]! }
- public var CreatePoll_QuizTitle: String { return self._s[2825]! }
- public var NotificationSettings_ShowNotificationsAllAccounts: String { return self._s[2826]! }
- public var VoiceOver_Chat_PagePreview: String { return self._s[2827]! }
+ public var Notification_PaymentSent: String { return self._s[2844]! }
+ public var PhotoEditor_CurvesGreen: String { return self._s[2845]! }
+ public var Notification_Exceptions_PreviewAlwaysOff: String { return self._s[2846]! }
+ public var AutoNightTheme_System: String { return self._s[2847]! }
+ public var SaveIncomingPhotosSettings_Title: String { return self._s[2848]! }
+ public var CreatePoll_QuizTitle: String { return self._s[2849]! }
+ public var NotificationSettings_ShowNotificationsAllAccounts: String { return self._s[2850]! }
+ public var VoiceOver_Chat_PagePreview: String { return self._s[2851]! }
public func PUSH_MESSAGE_SCREENSHOT(_ _1: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2830]!, self._r[2830]!, [_1])
+ return formatWithArgumentRanges(self._s[2854]!, self._r[2854]!, [_1])
}
public func PUSH_MESSAGE_PHOTO_SECRET(_ _1: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2831]!, self._r[2831]!, [_1])
+ return formatWithArgumentRanges(self._s[2855]!, self._r[2855]!, [_1])
}
public func ApplyLanguage_UnsufficientDataText(_ _1: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2832]!, self._r[2832]!, [_1])
+ return formatWithArgumentRanges(self._s[2856]!, self._r[2856]!, [_1])
}
- public var NetworkUsageSettings_CallDataSection: String { return self._s[2834]! }
- public var PasscodeSettings_HelpTop: String { return self._s[2835]! }
- public var Conversation_WalletRequiredTitle: String { return self._s[2836]! }
- public var PeerInfo_AddToContacts: String { return self._s[2837]! }
- public var Group_OwnershipTransfer_ErrorAdminsTooMuch: String { return self._s[2838]! }
- public var Passport_Address_TypeRentalAgreement: String { return self._s[2839]! }
- public var FeaturedStickers_OtherSection: String { return self._s[2840]! }
- public var EditTheme_ShortLink: String { return self._s[2842]! }
- public var Theme_Colors_ColorWallpaperWarning: String { return self._s[2843]! }
- public var ProxyServer_VoiceOver_Active: String { return self._s[2844]! }
- public var ReportPeer_ReasonOther_Placeholder: String { return self._s[2845]! }
- public var CheckoutInfo_ErrorPhoneInvalid: String { return self._s[2846]! }
- public var Call_Accept: String { return self._s[2848]! }
- public var GroupRemoved_RemoveInfo: String { return self._s[2849]! }
- public var Month_GenMarch: String { return self._s[2851]! }
- public var PhotoEditor_ShadowsTool: String { return self._s[2852]! }
- public var LoginPassword_Title: String { return self._s[2853]! }
- public var Call_End: String { return self._s[2854]! }
- public var Watch_Conversation_GroupInfo: String { return self._s[2855]! }
- public var VoiceOver_Chat_Contact: String { return self._s[2856]! }
- public var EditTheme_Create_Preview_IncomingText: String { return self._s[2857]! }
- public var CallSettings_Always: String { return self._s[2858]! }
- public var CallFeedback_Success: String { return self._s[2859]! }
- public var TwoStepAuth_SetupHint: String { return self._s[2860]! }
+ public var NetworkUsageSettings_CallDataSection: String { return self._s[2858]! }
+ public var PasscodeSettings_HelpTop: String { return self._s[2859]! }
+ public var Conversation_WalletRequiredTitle: String { return self._s[2860]! }
+ public var PeerInfo_AddToContacts: String { return self._s[2861]! }
+ public var Group_OwnershipTransfer_ErrorAdminsTooMuch: String { return self._s[2862]! }
+ public var Passport_Address_TypeRentalAgreement: String { return self._s[2863]! }
+ public var FeaturedStickers_OtherSection: String { return self._s[2864]! }
+ public var EditTheme_ShortLink: String { return self._s[2866]! }
+ public var Theme_Colors_ColorWallpaperWarning: String { return self._s[2867]! }
+ public var ProxyServer_VoiceOver_Active: String { return self._s[2868]! }
+ public var ReportPeer_ReasonOther_Placeholder: String { return self._s[2869]! }
+ public var CheckoutInfo_ErrorPhoneInvalid: String { return self._s[2870]! }
+ public var Call_Accept: String { return self._s[2872]! }
+ public var GroupRemoved_RemoveInfo: String { return self._s[2873]! }
+ public var Month_GenMarch: String { return self._s[2875]! }
+ public var PhotoEditor_ShadowsTool: String { return self._s[2876]! }
+ public var LoginPassword_Title: String { return self._s[2877]! }
+ public var Call_End: String { return self._s[2878]! }
+ public var Watch_Conversation_GroupInfo: String { return self._s[2879]! }
+ public var VoiceOver_Chat_Contact: String { return self._s[2880]! }
+ public var EditTheme_Create_Preview_IncomingText: String { return self._s[2881]! }
+ public var CallSettings_Always: String { return self._s[2882]! }
+ public var CallFeedback_Success: String { return self._s[2883]! }
+ public var TwoStepAuth_SetupHint: String { return self._s[2884]! }
public func AddContact_ContactWillBeSharedAfterMutual(_ _1: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2861]!, self._r[2861]!, [_1])
+ return formatWithArgumentRanges(self._s[2885]!, self._r[2885]!, [_1])
}
- public var ConversationProfile_UsersTooMuchError: String { return self._s[2862]! }
- public var PeerInfo_ButtonAddMember: String { return self._s[2863]! }
- public var Login_PhoneTitle: String { return self._s[2864]! }
- public var Passport_FieldPhoneHelp: String { return self._s[2865]! }
- public var Weekday_ShortSunday: String { return self._s[2866]! }
- public var Passport_InfoFAQ_URL: String { return self._s[2867]! }
- public var ContactInfo_Job: String { return self._s[2869]! }
- public var UserInfo_InviteBotToGroup: String { return self._s[2870]! }
- public var Appearance_ThemeCarouselNightBlue: String { return self._s[2871]! }
- public var CreatePoll_QuizTip: String { return self._s[2872]! }
- public var TwoFactorSetup_Email_Text: String { return self._s[2873]! }
- public var TwoStepAuth_PasswordRemovePassportConfirmation: String { return self._s[2874]! }
- public var Invite_ChannelsTooMuch: String { return self._s[2875]! }
- public var Wallet_Send_ConfirmationConfirm: String { return self._s[2876]! }
- public var Wallet_TransactionInfo_OtherFeeInfo: String { return self._s[2877]! }
- public var SettingsSearch_Synonyms_Notifications_InAppNotificationsPreview: String { return self._s[2878]! }
- public var Wallet_Receive_AmountText: String { return self._s[2879]! }
- public var TwoStepAuth_Disable: String { return self._s[2880]! }
- public var Passport_DeletePersonalDetailsConfirmation: String { return self._s[2881]! }
- public var CallFeedback_ReasonNoise: String { return self._s[2882]! }
- public var Appearance_AppIconDefault: String { return self._s[2884]! }
- public var Passport_Identity_AddInternalPassport: String { return self._s[2885]! }
- public var MediaPicker_AddCaption: String { return self._s[2886]! }
- public var CallSettings_TabIconDescription: String { return self._s[2887]! }
+ public var ConversationProfile_UsersTooMuchError: String { return self._s[2886]! }
+ public var PeerInfo_ButtonAddMember: String { return self._s[2887]! }
+ public var Login_PhoneTitle: String { return self._s[2888]! }
+ public var Passport_FieldPhoneHelp: String { return self._s[2889]! }
+ public var Weekday_ShortSunday: String { return self._s[2890]! }
+ public var Passport_InfoFAQ_URL: String { return self._s[2891]! }
+ public var ContactInfo_Job: String { return self._s[2893]! }
+ public var UserInfo_InviteBotToGroup: String { return self._s[2894]! }
+ public var Appearance_ThemeCarouselNightBlue: String { return self._s[2895]! }
+ public var CreatePoll_QuizTip: String { return self._s[2896]! }
+ public var TwoFactorSetup_Email_Text: String { return self._s[2897]! }
+ public var TwoStepAuth_PasswordRemovePassportConfirmation: String { return self._s[2898]! }
+ public var Invite_ChannelsTooMuch: String { return self._s[2899]! }
+ public var Wallet_Send_ConfirmationConfirm: String { return self._s[2900]! }
+ public var Wallet_TransactionInfo_OtherFeeInfo: String { return self._s[2901]! }
+ public var SettingsSearch_Synonyms_Notifications_InAppNotificationsPreview: String { return self._s[2902]! }
+ public var Wallet_Receive_AmountText: String { return self._s[2903]! }
+ public var TwoStepAuth_Disable: String { return self._s[2904]! }
+ public var Passport_DeletePersonalDetailsConfirmation: String { return self._s[2905]! }
+ public var CallFeedback_ReasonNoise: String { return self._s[2906]! }
+ public var Appearance_AppIconDefault: String { return self._s[2908]! }
+ public var Passport_Identity_AddInternalPassport: String { return self._s[2909]! }
+ public var MediaPicker_AddCaption: String { return self._s[2910]! }
+ public var CallSettings_TabIconDescription: String { return self._s[2911]! }
public func VoiceOver_Chat_Caption(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2888]!, self._r[2888]!, [_0])
+ return formatWithArgumentRanges(self._s[2912]!, self._r[2912]!, [_0])
}
- public var IntentsSettings_SuggestedChatsGroups: String { return self._s[2889]! }
+ public var IntentsSettings_SuggestedChatsGroups: String { return self._s[2913]! }
public func Map_SearchNoResultsDescription(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2890]!, self._r[2890]!, [_0])
+ return formatWithArgumentRanges(self._s[2914]!, self._r[2914]!, [_0])
}
- public var CreatePoll_ExplanationHeader: String { return self._s[2892]! }
- public var ChatList_UndoArchiveHiddenTitle: String { return self._s[2893]! }
- public var Privacy_GroupsAndChannels_AlwaysAllow: String { return self._s[2894]! }
- public var Passport_Identity_TypePersonalDetails: String { return self._s[2895]! }
- public var DialogList_SearchSectionRecent: String { return self._s[2896]! }
- public var PrivacyPolicy_DeclineMessage: String { return self._s[2897]! }
- public var CreatePoll_Anonymous: String { return self._s[2898]! }
- public var LogoutOptions_ClearCacheText: String { return self._s[2901]! }
- public var Stats_GroupTopInviter_Promote: String { return self._s[2902]! }
- public var LastSeen_WithinAWeek: String { return self._s[2903]! }
- public var ChannelMembers_GroupAdminsTitle: String { return self._s[2904]! }
- public var SettingsSearch_Synonyms_ChatSettings_IntentsSettings: String { return self._s[2906]! }
- public var Conversation_CloudStorage_ChatStatus: String { return self._s[2907]! }
- public var VoiceOver_Media_PlaybackRateNormal: String { return self._s[2909]! }
+ public var CreatePoll_ExplanationHeader: String { return self._s[2916]! }
+ public var ChatList_UndoArchiveHiddenTitle: String { return self._s[2917]! }
+ public var Privacy_GroupsAndChannels_AlwaysAllow: String { return self._s[2918]! }
+ public var Passport_Identity_TypePersonalDetails: String { return self._s[2919]! }
+ public var DialogList_SearchSectionRecent: String { return self._s[2920]! }
+ public var PrivacyPolicy_DeclineMessage: String { return self._s[2921]! }
+ public var CreatePoll_Anonymous: String { return self._s[2922]! }
+ public var LogoutOptions_ClearCacheText: String { return self._s[2925]! }
+ public var Stats_GroupTopInviter_Promote: String { return self._s[2926]! }
+ public var LastSeen_WithinAWeek: String { return self._s[2927]! }
+ public var ChannelMembers_GroupAdminsTitle: String { return self._s[2928]! }
+ public var SettingsSearch_Synonyms_ChatSettings_IntentsSettings: String { return self._s[2930]! }
+ public var Conversation_CloudStorage_ChatStatus: String { return self._s[2931]! }
+ public var VoiceOver_Media_PlaybackRateNormal: String { return self._s[2933]! }
public func AddContact_SharedContactExceptionInfo(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2910]!, self._r[2910]!, [_0])
+ return formatWithArgumentRanges(self._s[2934]!, self._r[2934]!, [_0])
}
- public var Passport_Address_TypeResidentialAddress: String { return self._s[2911]! }
- public var Conversation_StatusLeftGroup: String { return self._s[2912]! }
- public var SocksProxySetup_ProxyDetailsTitle: String { return self._s[2913]! }
- public var OwnershipTransfer_Transfer: String { return self._s[2915]! }
- public var SettingsSearch_Synonyms_Calls_Title: String { return self._s[2916]! }
- public var ProfilePhoto_MainPhoto: String { return self._s[2917]! }
- public var GroupPermission_AddSuccess: String { return self._s[2919]! }
- public var PhotoEditor_BlurToolRadial: String { return self._s[2921]! }
- public var Conversation_ContextMenuCopy: String { return self._s[2922]! }
- public var AccessDenied_CallMicrophone: String { return self._s[2923]! }
+ public var Passport_Address_TypeResidentialAddress: String { return self._s[2935]! }
+ public var Conversation_StatusLeftGroup: String { return self._s[2936]! }
+ public var SocksProxySetup_ProxyDetailsTitle: String { return self._s[2937]! }
+ public var OwnershipTransfer_Transfer: String { return self._s[2939]! }
+ public var SettingsSearch_Synonyms_Calls_Title: String { return self._s[2940]! }
+ public var ProfilePhoto_MainPhoto: String { return self._s[2941]! }
+ public var GroupPermission_AddSuccess: String { return self._s[2943]! }
+ public var PhotoEditor_BlurToolRadial: String { return self._s[2945]! }
+ public var Conversation_ContextMenuCopy: String { return self._s[2946]! }
+ public var AccessDenied_CallMicrophone: String { return self._s[2947]! }
public func Time_PreciseDate_m2(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2924]!, self._r[2924]!, [_1, _2, _3])
+ return formatWithArgumentRanges(self._s[2948]!, self._r[2948]!, [_1, _2, _3])
}
- public var Login_InvalidFirstNameError: String { return self._s[2925]! }
- public var Notifications_Badge_CountUnreadMessages_InfoOn: String { return self._s[2926]! }
- public var Checkout_PaymentMethod_New: String { return self._s[2927]! }
- public var ShareMenu_CopyShareLinkGame: String { return self._s[2928]! }
- public var PhotoEditor_QualityTool: String { return self._s[2929]! }
- public var Login_SendCodeViaSms: String { return self._s[2930]! }
- public var SettingsSearch_Synonyms_Privacy_DeleteAccountIfAwayFor: String { return self._s[2931]! }
- public var Chat_SlowmodeAttachmentLimitReached: String { return self._s[2932]! }
- public var Wallet_Receive_CopyAddress: String { return self._s[2933]! }
- public var Login_EmailNotConfiguredError: String { return self._s[2934]! }
- public var Stats_GroupTopAdminsTitle: String { return self._s[2935]! }
- public var SocksProxySetup_Status: String { return self._s[2936]! }
- public var Conversation_ScheduleMessage_SendWhenOnline: String { return self._s[2937]! }
- public var PrivacyPolicy_Accept: String { return self._s[2938]! }
- public var Notifications_ExceptionsMessagePlaceholder: String { return self._s[2939]! }
- public var Appearance_AppIconClassicX: String { return self._s[2940]! }
+ public var Login_InvalidFirstNameError: String { return self._s[2949]! }
+ public var Notifications_Badge_CountUnreadMessages_InfoOn: String { return self._s[2950]! }
+ public var Checkout_PaymentMethod_New: String { return self._s[2951]! }
+ public var ShareMenu_CopyShareLinkGame: String { return self._s[2952]! }
+ public var PhotoEditor_QualityTool: String { return self._s[2953]! }
+ public var Login_SendCodeViaSms: String { return self._s[2954]! }
+ public var SettingsSearch_Synonyms_Privacy_DeleteAccountIfAwayFor: String { return self._s[2955]! }
+ public var Chat_SlowmodeAttachmentLimitReached: String { return self._s[2956]! }
+ public var Wallet_Receive_CopyAddress: String { return self._s[2957]! }
+ public var Login_EmailNotConfiguredError: String { return self._s[2958]! }
+ public var Stats_GroupTopAdminsTitle: String { return self._s[2959]! }
+ public var SocksProxySetup_Status: String { return self._s[2960]! }
+ public var Conversation_ScheduleMessage_SendWhenOnline: String { return self._s[2961]! }
+ public var PrivacyPolicy_Accept: String { return self._s[2962]! }
+ public var Notifications_ExceptionsMessagePlaceholder: String { return self._s[2963]! }
+ public var Appearance_AppIconClassicX: String { return self._s[2964]! }
public func PUSH_CHAT_MESSAGE_TEXT(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2941]!, self._r[2941]!, [_1, _2, _3])
+ return formatWithArgumentRanges(self._s[2965]!, self._r[2965]!, [_1, _2, _3])
}
- public var OwnershipTransfer_SecurityRequirements: String { return self._s[2942]! }
- public var InfoPlist_NSLocationAlwaysUsageDescription: String { return self._s[2944]! }
- public var AutoNightTheme_Automatic: String { return self._s[2945]! }
- public var Channel_Username_InvalidStartsWithNumber: String { return self._s[2946]! }
- public var Privacy_ContactsSyncHelp: String { return self._s[2947]! }
- public var Cache_Help: String { return self._s[2948]! }
- public var Group_ErrorAccessDenied: String { return self._s[2949]! }
- public var Passport_Language_fa: String { return self._s[2950]! }
- public var Wallet_Intro_Text: String { return self._s[2951]! }
- public var ProfilePhoto_SetMainVideo: String { return self._s[2952]! }
- public var Login_ResetAccountProtected_TimerTitle: String { return self._s[2953]! }
- public var VoiceOver_Chat_YourVideoMessage: String { return self._s[2954]! }
- public var PrivacySettings_LastSeen: String { return self._s[2955]! }
+ public var OwnershipTransfer_SecurityRequirements: String { return self._s[2966]! }
+ public var InfoPlist_NSLocationAlwaysUsageDescription: String { return self._s[2968]! }
+ public var AutoNightTheme_Automatic: String { return self._s[2969]! }
+ public var Channel_Username_InvalidStartsWithNumber: String { return self._s[2970]! }
+ public var Privacy_ContactsSyncHelp: String { return self._s[2971]! }
+ public var Cache_Help: String { return self._s[2972]! }
+ public var Group_ErrorAccessDenied: String { return self._s[2973]! }
+ public var Passport_Language_fa: String { return self._s[2974]! }
+ public var Wallet_Intro_Text: String { return self._s[2975]! }
+ public var ProfilePhoto_SetMainVideo: String { return self._s[2976]! }
+ public var Login_ResetAccountProtected_TimerTitle: String { return self._s[2977]! }
+ public var VoiceOver_Chat_YourVideoMessage: String { return self._s[2978]! }
+ public var PrivacySettings_LastSeen: String { return self._s[2979]! }
public func DialogList_MultipleTyping(_ _0: String, _ _1: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2956]!, self._r[2956]!, [_0, _1])
+ return formatWithArgumentRanges(self._s[2980]!, self._r[2980]!, [_0, _1])
}
- public var Wallet_Configuration_Apply: String { return self._s[2960]! }
- public var Preview_SaveGif: String { return self._s[2961]! }
- public var SettingsSearch_Synonyms_Privacy_TwoStepAuth: String { return self._s[2962]! }
- public var Profile_About: String { return self._s[2963]! }
- public var Channel_About_Placeholder: String { return self._s[2965]! }
- public var Login_InfoTitle: String { return self._s[2966]! }
+ public var Wallet_Configuration_Apply: String { return self._s[2984]! }
+ public var Preview_SaveGif: String { return self._s[2985]! }
+ public var SettingsSearch_Synonyms_Privacy_TwoStepAuth: String { return self._s[2986]! }
+ public var Profile_About: String { return self._s[2987]! }
+ public var Channel_About_Placeholder: String { return self._s[2989]! }
+ public var Login_InfoTitle: String { return self._s[2990]! }
public func TwoStepAuth_SetupPendingEmail(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2967]!, self._r[2967]!, [_0])
- }
- public var EditTheme_Expand_Preview_IncomingReplyText: String { return self._s[2968]! }
- public var Watch_Suggestion_CantTalk: String { return self._s[2971]! }
- public var ContactInfo_Title: String { return self._s[2972]! }
- public var Media_ShareThisVideo: String { return self._s[2973]! }
- public var Chat_GenericPsaTooltip: String { return self._s[2974]! }
- public var Weekday_ShortFriday: String { return self._s[2975]! }
- public var AccessDenied_Contacts: String { return self._s[2977]! }
- public var Notification_CallIncomingShort: String { return self._s[2978]! }
- public var Group_Setup_TypePublic: String { return self._s[2979]! }
- public var Notifications_MessageNotificationsExceptions: String { return self._s[2980]! }
- public var Notifications_Badge_IncludeChannels: String { return self._s[2981]! }
- public var Settings_EditAccount: String { return self._s[2984]! }
- public var Notifications_MessageNotificationsPreview: String { return self._s[2985]! }
- public var ConversationProfile_ErrorCreatingConversation: String { return self._s[2986]! }
- public var Group_ErrorAddTooMuchBots: String { return self._s[2987]! }
- public var Privacy_GroupsAndChannels_CustomShareHelp: String { return self._s[2988]! }
- public var Permissions_CellularDataAllowInSettings_v0: String { return self._s[2989]! }
- public func Call_RemoteVideoPaused(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[2990]!, self._r[2990]!, [_0])
- }
- public func Wallet_SecureStorageChanged_BiometryText(_ _0: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[2991]!, self._r[2991]!, [_0])
}
- public var DialogList_Typing: String { return self._s[2992]! }
- public var CallFeedback_IncludeLogs: String { return self._s[2994]! }
- public var Checkout_Phone: String { return self._s[2996]! }
- public var Login_InfoFirstNamePlaceholder: String { return self._s[2999]! }
- public var Privacy_Calls_Integration: String { return self._s[3000]! }
- public var Notifications_PermissionsAllow: String { return self._s[3001]! }
- public var TwoStepAuth_AddHintDescription: String { return self._s[3007]! }
- public var Settings_ChatSettings: String { return self._s[3008]! }
- public var Conversation_SendingOptionsTooltip: String { return self._s[3009]! }
- public func UserInfo_StartSecretChatConfirmation(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3011]!, self._r[3011]!, [_0])
+ public var EditTheme_Expand_Preview_IncomingReplyText: String { return self._s[2992]! }
+ public var Watch_Suggestion_CantTalk: String { return self._s[2995]! }
+ public var ContactInfo_Title: String { return self._s[2996]! }
+ public var Media_ShareThisVideo: String { return self._s[2997]! }
+ public var Chat_GenericPsaTooltip: String { return self._s[2998]! }
+ public var Weekday_ShortFriday: String { return self._s[2999]! }
+ public var AccessDenied_Contacts: String { return self._s[3001]! }
+ public var Notification_CallIncomingShort: String { return self._s[3002]! }
+ public var Group_Setup_TypePublic: String { return self._s[3003]! }
+ public var Notifications_MessageNotificationsExceptions: String { return self._s[3004]! }
+ public var Notifications_Badge_IncludeChannels: String { return self._s[3005]! }
+ public var Settings_EditAccount: String { return self._s[3008]! }
+ public var Notifications_MessageNotificationsPreview: String { return self._s[3009]! }
+ public var ConversationProfile_ErrorCreatingConversation: String { return self._s[3010]! }
+ public var Group_ErrorAddTooMuchBots: String { return self._s[3011]! }
+ public var Privacy_GroupsAndChannels_CustomShareHelp: String { return self._s[3012]! }
+ public var Permissions_CellularDataAllowInSettings_v0: String { return self._s[3013]! }
+ public func Call_RemoteVideoPaused(_ _0: String) -> (String, [(Int, NSRange)]) {
+ return formatWithArgumentRanges(self._s[3014]!, self._r[3014]!, [_0])
}
- public func Channel_AdminLog_MessageInvitedNameUsername(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3012]!, self._r[3012]!, [_1, _2])
- }
- public var GroupRemoved_DeleteUser: String { return self._s[3014]! }
- public func Channel_AdminLog_PollStopped(_ _0: String) -> (String, [(Int, NSRange)]) {
+ public func Wallet_SecureStorageChanged_BiometryText(_ _0: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[3015]!, self._r[3015]!, [_0])
}
- public var ChatListFolder_CategoryMuted: String { return self._s[3016]! }
+ public var DialogList_Typing: String { return self._s[3016]! }
+ public var CallFeedback_IncludeLogs: String { return self._s[3018]! }
+ public var Checkout_Phone: String { return self._s[3020]! }
+ public var Login_InfoFirstNamePlaceholder: String { return self._s[3023]! }
+ public var Privacy_Calls_Integration: String { return self._s[3024]! }
+ public var Notifications_PermissionsAllow: String { return self._s[3025]! }
+ public var TwoStepAuth_AddHintDescription: String { return self._s[3031]! }
+ public var Settings_ChatSettings: String { return self._s[3032]! }
+ public var Conversation_SendingOptionsTooltip: String { return self._s[3033]! }
+ public func UserInfo_StartSecretChatConfirmation(_ _0: String) -> (String, [(Int, NSRange)]) {
+ return formatWithArgumentRanges(self._s[3035]!, self._r[3035]!, [_0])
+ }
+ public func Channel_AdminLog_MessageInvitedNameUsername(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
+ return formatWithArgumentRanges(self._s[3036]!, self._r[3036]!, [_1, _2])
+ }
+ public var GroupRemoved_DeleteUser: String { return self._s[3038]! }
+ public func Channel_AdminLog_PollStopped(_ _0: String) -> (String, [(Int, NSRange)]) {
+ return formatWithArgumentRanges(self._s[3039]!, self._r[3039]!, [_0])
+ }
+ public var ChatListFolder_CategoryMuted: String { return self._s[3040]! }
public func PUSH_MESSAGE_PHOTO(_ _1: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3017]!, self._r[3017]!, [_1])
+ return formatWithArgumentRanges(self._s[3041]!, self._r[3041]!, [_1])
}
- public var Login_ContinueWithLocalization: String { return self._s[3018]! }
- public var Watch_Message_ForwardedFrom: String { return self._s[3019]! }
- public var TwoStepAuth_EnterEmailCode: String { return self._s[3021]! }
- public var Notification_VideoCallIncoming: String { return self._s[3022]! }
- public var Conversation_Unblock: String { return self._s[3023]! }
- public var PrivacySettings_DataSettings: String { return self._s[3024]! }
- public var WallpaperPreview_PatternPaternApply: String { return self._s[3025]! }
- public var Group_PublicLink_Info: String { return self._s[3026]! }
+ public var Login_ContinueWithLocalization: String { return self._s[3042]! }
+ public var Watch_Message_ForwardedFrom: String { return self._s[3043]! }
+ public var TwoStepAuth_EnterEmailCode: String { return self._s[3045]! }
+ public var Notification_VideoCallIncoming: String { return self._s[3046]! }
+ public var Conversation_Unblock: String { return self._s[3047]! }
+ public var PrivacySettings_DataSettings: String { return self._s[3048]! }
+ public var WallpaperPreview_PatternPaternApply: String { return self._s[3049]! }
+ public var Group_PublicLink_Info: String { return self._s[3050]! }
public func Wallet_Time_PreciseDate_m1(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3027]!, self._r[3027]!, [_1, _2, _3])
+ return formatWithArgumentRanges(self._s[3051]!, self._r[3051]!, [_1, _2, _3])
}
- public var Notifications_InAppNotificationsVibrate: String { return self._s[3028]! }
+ public var Notifications_InAppNotificationsVibrate: String { return self._s[3052]! }
public func Privacy_GroupsAndChannels_InviteToChannelError(_ _0: String, _ _1: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3029]!, self._r[3029]!, [_0, _1])
+ return formatWithArgumentRanges(self._s[3053]!, self._r[3053]!, [_0, _1])
}
- public var ChatList_FolderAllChats: String { return self._s[3030]! }
- public var OldChannels_ChannelsHeader: String { return self._s[3032]! }
- public var Wallet_RestoreFailed_CreateWallet: String { return self._s[3033]! }
- public var PrivacySettings_Passcode: String { return self._s[3035]! }
- public var Call_Mute: String { return self._s[3036]! }
- public var Wallet_Weekday_Yesterday: String { return self._s[3037]! }
- public var Passport_Language_dz: String { return self._s[3038]! }
- public var Wallet_Receive_AmountHeader: String { return self._s[3039]! }
- public var Wallet_TransactionInfo_OtherFeeInfoUrl: String { return self._s[3040]! }
- public var Passport_Language_tk: String { return self._s[3041]! }
+ public var ChatList_FolderAllChats: String { return self._s[3054]! }
+ public var OldChannels_ChannelsHeader: String { return self._s[3056]! }
+ public var Wallet_RestoreFailed_CreateWallet: String { return self._s[3057]! }
+ public var PrivacySettings_Passcode: String { return self._s[3059]! }
+ public var Call_Mute: String { return self._s[3060]! }
+ public var Call_CameraTooltip: String { return self._s[3061]! }
+ public var Wallet_Weekday_Yesterday: String { return self._s[3062]! }
+ public var Passport_Language_dz: String { return self._s[3063]! }
+ public var Wallet_Receive_AmountHeader: String { return self._s[3064]! }
+ public var Wallet_TransactionInfo_OtherFeeInfoUrl: String { return self._s[3065]! }
+ public var Passport_Language_tk: String { return self._s[3066]! }
public func Login_EmailCodeSubject(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3042]!, self._r[3042]!, [_0])
+ return formatWithArgumentRanges(self._s[3067]!, self._r[3067]!, [_0])
}
- public var Settings_Search: String { return self._s[3043]! }
- public var Wallet_Month_ShortFebruary: String { return self._s[3044]! }
- public var InfoPlist_NSPhotoLibraryUsageDescription: String { return self._s[3045]! }
- public var Wallet_Configuration_SourceJSON: String { return self._s[3046]! }
- public var Conversation_ContextMenuReply: String { return self._s[3047]! }
- public var WallpaperSearch_ColorBrown: String { return self._s[3048]! }
- public var Chat_AttachmentMultipleForwardDisabled: String { return self._s[3049]! }
- public var Tour_Title1: String { return self._s[3050]! }
- public var Wallet_Alert_Cancel: String { return self._s[3051]! }
- public var Stats_Total: String { return self._s[3053]! }
- public var Conversation_ClearGroupHistory: String { return self._s[3054]! }
- public var Wallet_TransactionInfo_RecipientHeader: String { return self._s[3055]! }
- public var WallpaperPreview_Motion: String { return self._s[3056]! }
+ public var Settings_Search: String { return self._s[3068]! }
+ public var Wallet_Month_ShortFebruary: String { return self._s[3069]! }
+ public var InfoPlist_NSPhotoLibraryUsageDescription: String { return self._s[3070]! }
+ public var Wallet_Configuration_SourceJSON: String { return self._s[3071]! }
+ public var Conversation_ContextMenuReply: String { return self._s[3072]! }
+ public var WallpaperSearch_ColorBrown: String { return self._s[3073]! }
+ public var Chat_AttachmentMultipleForwardDisabled: String { return self._s[3074]! }
+ public var Tour_Title1: String { return self._s[3075]! }
+ public var Wallet_Alert_Cancel: String { return self._s[3076]! }
+ public var Stats_Total: String { return self._s[3078]! }
+ public var Conversation_ClearGroupHistory: String { return self._s[3079]! }
+ public var Wallet_TransactionInfo_RecipientHeader: String { return self._s[3080]! }
+ public var WallpaperPreview_Motion: String { return self._s[3081]! }
public func Checkout_PasswordEntry_Text(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3057]!, self._r[3057]!, [_0])
+ return formatWithArgumentRanges(self._s[3082]!, self._r[3082]!, [_0])
}
- public var Wallet_Configuration_ApplyErrorTextJSONInvalidData: String { return self._s[3058]! }
- public var Call_RateCall: String { return self._s[3059]! }
- public var Channel_AdminLog_BanSendStickersAndGifs: String { return self._s[3060]! }
- public var Passport_PasswordCompleteSetup: String { return self._s[3061]! }
- public var Conversation_InputTextSilentBroadcastPlaceholder: String { return self._s[3062]! }
- public var UserInfo_LastNamePlaceholder: String { return self._s[3064]! }
+ public var Wallet_Configuration_ApplyErrorTextJSONInvalidData: String { return self._s[3083]! }
+ public var Call_RateCall: String { return self._s[3084]! }
+ public var Channel_AdminLog_BanSendStickersAndGifs: String { return self._s[3085]! }
+ public var Passport_PasswordCompleteSetup: String { return self._s[3086]! }
+ public var Conversation_InputTextSilentBroadcastPlaceholder: String { return self._s[3087]! }
+ public var UserInfo_LastNamePlaceholder: String { return self._s[3089]! }
public func Login_WillCallYou(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3066]!, self._r[3066]!, [_0])
+ return formatWithArgumentRanges(self._s[3091]!, self._r[3091]!, [_0])
}
- public var Compose_Create: String { return self._s[3067]! }
- public var Contacts_InviteToTelegram: String { return self._s[3068]! }
- public var GroupInfo_Notifications: String { return self._s[3069]! }
- public var ChatList_DeleteSavedMessagesConfirmationAction: String { return self._s[3071]! }
- public var Message_PinnedLiveLocationMessage: String { return self._s[3072]! }
- public var Month_GenApril: String { return self._s[3073]! }
- public var Appearance_AutoNightTheme: String { return self._s[3074]! }
- public var ChatSettings_AutomaticAudioDownload: String { return self._s[3076]! }
- public var Login_CodeSentSms: String { return self._s[3078]! }
+ public var Compose_Create: String { return self._s[3092]! }
+ public var Contacts_InviteToTelegram: String { return self._s[3093]! }
+ public var GroupInfo_Notifications: String { return self._s[3094]! }
+ public var ChatList_DeleteSavedMessagesConfirmationAction: String { return self._s[3096]! }
+ public var Message_PinnedLiveLocationMessage: String { return self._s[3097]! }
+ public var Month_GenApril: String { return self._s[3098]! }
+ public var Appearance_AutoNightTheme: String { return self._s[3099]! }
+ public var ChatSettings_AutomaticAudioDownload: String { return self._s[3101]! }
+ public var Login_CodeSentSms: String { return self._s[3103]! }
public func UserInfo_UnblockConfirmation(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3079]!, self._r[3079]!, [_0])
+ return formatWithArgumentRanges(self._s[3104]!, self._r[3104]!, [_0])
}
- public var EmptyGroupInfo_Line3: String { return self._s[3080]! }
- public var LogoutOptions_ContactSupportText: String { return self._s[3081]! }
- public var Passport_Language_hr: String { return self._s[3082]! }
- public var Common_ActionNotAllowedError: String { return self._s[3083]! }
+ public var EmptyGroupInfo_Line3: String { return self._s[3105]! }
+ public var LogoutOptions_ContactSupportText: String { return self._s[3106]! }
+ public var Passport_Language_hr: String { return self._s[3107]! }
+ public var Common_ActionNotAllowedError: String { return self._s[3108]! }
public func Channel_AdminLog_MessageRestrictedNewSetting(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3084]!, self._r[3084]!, [_0])
+ return formatWithArgumentRanges(self._s[3109]!, self._r[3109]!, [_0])
}
- public var GroupInfo_InviteLink_CopyLink: String { return self._s[3085]! }
- public var Wallet_Info_TransactionFrom: String { return self._s[3086]! }
- public var Wallet_Send_ErrorDecryptionFailed: String { return self._s[3087]! }
- public var Conversation_InputTextBroadcastPlaceholder: String { return self._s[3088]! }
- public var Privacy_SecretChatsTitle: String { return self._s[3089]! }
- public var Notification_SecretChatMessageScreenshotSelf: String { return self._s[3091]! }
- public var GroupInfo_AddUserLeftError: String { return self._s[3092]! }
- public var AutoDownloadSettings_TypePrivateChats: String { return self._s[3093]! }
- public var ChatListFolder_NameSectionHeader: String { return self._s[3094]! }
- public var LogoutOptions_ContactSupportTitle: String { return self._s[3095]! }
- public var Appearance_ThemePreview_Chat_7_Text: String { return self._s[3096]! }
- public var Conversation_Unarchive: String { return self._s[3097]! }
- public var Channel_AddBotErrorHaveRights: String { return self._s[3098]! }
- public var Preview_DeleteGif: String { return self._s[3099]! }
- public var GroupInfo_Permissions_Exceptions: String { return self._s[3100]! }
- public var Group_ErrorNotMutualContact: String { return self._s[3101]! }
- public var Notification_MessageLifetime5s: String { return self._s[3102]! }
- public var Wallet_Send_OwnAddressAlertText: String { return self._s[3103]! }
- public var OldChannels_ChannelFormat: String { return self._s[3104]! }
+ public var GroupInfo_InviteLink_CopyLink: String { return self._s[3110]! }
+ public var Wallet_Info_TransactionFrom: String { return self._s[3111]! }
+ public var Wallet_Send_ErrorDecryptionFailed: String { return self._s[3112]! }
+ public var Conversation_InputTextBroadcastPlaceholder: String { return self._s[3113]! }
+ public var Privacy_SecretChatsTitle: String { return self._s[3114]! }
+ public var Notification_SecretChatMessageScreenshotSelf: String { return self._s[3116]! }
+ public var GroupInfo_AddUserLeftError: String { return self._s[3117]! }
+ public var AutoDownloadSettings_TypePrivateChats: String { return self._s[3118]! }
+ public var ChatListFolder_NameSectionHeader: String { return self._s[3119]! }
+ public var LogoutOptions_ContactSupportTitle: String { return self._s[3120]! }
+ public var Appearance_ThemePreview_Chat_7_Text: String { return self._s[3121]! }
+ public var Conversation_Unarchive: String { return self._s[3122]! }
+ public var Channel_AddBotErrorHaveRights: String { return self._s[3123]! }
+ public var Preview_DeleteGif: String { return self._s[3124]! }
+ public var GroupInfo_Permissions_Exceptions: String { return self._s[3125]! }
+ public var Group_ErrorNotMutualContact: String { return self._s[3126]! }
+ public var Notification_MessageLifetime5s: String { return self._s[3127]! }
+ public var Wallet_Send_OwnAddressAlertText: String { return self._s[3128]! }
+ public var OldChannels_ChannelFormat: String { return self._s[3129]! }
public func Watch_LastSeen_AtDate(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3105]!, self._r[3105]!, [_0])
+ return formatWithArgumentRanges(self._s[3130]!, self._r[3130]!, [_0])
}
- public var VoiceOver_Chat_Video: String { return self._s[3106]! }
- public var Channel_OwnershipTransfer_ErrorPublicChannelsTooMuch: String { return self._s[3108]! }
- public var ReportSpam_DeleteThisChat: String { return self._s[3109]! }
- public var Passport_Address_AddBankStatement: String { return self._s[3110]! }
- public var Notification_CallIncoming: String { return self._s[3111]! }
- public var Wallet_Words_NotDoneTitle: String { return self._s[3112]! }
- public var Compose_NewGroupTitle: String { return self._s[3113]! }
- public var TwoStepAuth_RecoveryCodeHelp: String { return self._s[3115]! }
- public var Passport_Address_Postcode: String { return self._s[3117]! }
+ public var VoiceOver_Chat_Video: String { return self._s[3131]! }
+ public var Channel_OwnershipTransfer_ErrorPublicChannelsTooMuch: String { return self._s[3133]! }
+ public var ReportSpam_DeleteThisChat: String { return self._s[3134]! }
+ public var Passport_Address_AddBankStatement: String { return self._s[3135]! }
+ public var Notification_CallIncoming: String { return self._s[3136]! }
+ public var Wallet_Words_NotDoneTitle: String { return self._s[3137]! }
+ public var Compose_NewGroupTitle: String { return self._s[3138]! }
+ public var TwoStepAuth_RecoveryCodeHelp: String { return self._s[3140]! }
+ public var Passport_Address_Postcode: String { return self._s[3142]! }
public func LastSeen_YesterdayAt(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3118]!, self._r[3118]!, [_0])
+ return formatWithArgumentRanges(self._s[3143]!, self._r[3143]!, [_0])
}
- public var Checkout_NewCard_SaveInfoHelp: String { return self._s[3119]! }
- public var Wallet_Month_ShortOctober: String { return self._s[3120]! }
- public var VoiceOver_Chat_YourMusic: String { return self._s[3121]! }
- public var WallpaperColors_Title: String { return self._s[3122]! }
- public var SocksProxySetup_ShareQRCodeInfo: String { return self._s[3123]! }
- public var VoiceOver_MessageContextForward: String { return self._s[3124]! }
- public var GroupPermission_Duration: String { return self._s[3125]! }
+ public var Checkout_NewCard_SaveInfoHelp: String { return self._s[3144]! }
+ public var Wallet_Month_ShortOctober: String { return self._s[3145]! }
+ public var VoiceOver_Chat_YourMusic: String { return self._s[3146]! }
+ public var WallpaperColors_Title: String { return self._s[3147]! }
+ public var SocksProxySetup_ShareQRCodeInfo: String { return self._s[3148]! }
+ public var VoiceOver_MessageContextForward: String { return self._s[3149]! }
+ public var GroupPermission_Duration: String { return self._s[3150]! }
public func Cache_Clear(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3126]!, self._r[3126]!, [_0])
+ return formatWithArgumentRanges(self._s[3151]!, self._r[3151]!, [_0])
}
- public var Bot_GroupStatusDoesNotReadHistory: String { return self._s[3127]! }
- public var Username_Placeholder: String { return self._s[3128]! }
- public var CallFeedback_WhatWentWrong: String { return self._s[3129]! }
- public var Passport_FieldAddressUploadHelp: String { return self._s[3130]! }
- public var Permissions_NotificationsAllowInSettings_v0: String { return self._s[3131]! }
+ public var Bot_GroupStatusDoesNotReadHistory: String { return self._s[3152]! }
+ public var Username_Placeholder: String { return self._s[3153]! }
+ public var CallFeedback_WhatWentWrong: String { return self._s[3154]! }
+ public var Passport_FieldAddressUploadHelp: String { return self._s[3155]! }
+ public var Permissions_NotificationsAllowInSettings_v0: String { return self._s[3156]! }
public func Channel_AdminLog_MessageChangedUnlinkedChannel(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3134]!, self._r[3134]!, [_1, _2])
+ return formatWithArgumentRanges(self._s[3159]!, self._r[3159]!, [_1, _2])
}
- public var Passport_PasswordDescription: String { return self._s[3135]! }
- public var Channel_MessagePhotoUpdated: String { return self._s[3136]! }
- public var MediaPicker_TapToUngroupDescription: String { return self._s[3137]! }
- public var SettingsSearch_Synonyms_Notifications_BadgeCountUnreadMessages: String { return self._s[3138]! }
- public var AttachmentMenu_PhotoOrVideo: String { return self._s[3139]! }
- public var Conversation_ContextMenuMore: String { return self._s[3140]! }
- public var Privacy_PaymentsClearInfo: String { return self._s[3141]! }
- public var CallSettings_TabIcon: String { return self._s[3142]! }
- public var KeyCommand_Find: String { return self._s[3143]! }
- public var ClearCache_FreeSpaceDescription: String { return self._s[3144]! }
- public var Appearance_ThemePreview_ChatList_7_Text: String { return self._s[3145]! }
- public var EditTheme_Edit_Preview_IncomingText: String { return self._s[3146]! }
+ public var Passport_PasswordDescription: String { return self._s[3160]! }
+ public var Channel_MessagePhotoUpdated: String { return self._s[3161]! }
+ public var MediaPicker_TapToUngroupDescription: String { return self._s[3162]! }
+ public var SettingsSearch_Synonyms_Notifications_BadgeCountUnreadMessages: String { return self._s[3163]! }
+ public var AttachmentMenu_PhotoOrVideo: String { return self._s[3164]! }
+ public var Conversation_ContextMenuMore: String { return self._s[3165]! }
+ public var Privacy_PaymentsClearInfo: String { return self._s[3166]! }
+ public var CallSettings_TabIcon: String { return self._s[3167]! }
+ public var KeyCommand_Find: String { return self._s[3168]! }
+ public var ClearCache_FreeSpaceDescription: String { return self._s[3169]! }
+ public var Appearance_ThemePreview_ChatList_7_Text: String { return self._s[3170]! }
+ public var EditTheme_Edit_Preview_IncomingText: String { return self._s[3171]! }
public func Conversation_NoticeInvitedByInChannel(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3147]!, self._r[3147]!, [_0])
+ return formatWithArgumentRanges(self._s[3172]!, self._r[3172]!, [_0])
}
- public var Message_PinnedGame: String { return self._s[3148]! }
- public var VoiceOver_Chat_ForwardedFromYou: String { return self._s[3149]! }
- public var Notifications_Badge_CountUnreadMessages_InfoOff: String { return self._s[3151]! }
- public var Login_CallRequestState2: String { return self._s[3153]! }
- public var CheckoutInfo_ReceiverInfoNamePlaceholder: String { return self._s[3155]! }
+ public var Message_PinnedGame: String { return self._s[3173]! }
+ public var VoiceOver_Chat_ForwardedFromYou: String { return self._s[3174]! }
+ public var Notifications_Badge_CountUnreadMessages_InfoOff: String { return self._s[3176]! }
+ public var Login_CallRequestState2: String { return self._s[3178]! }
+ public var CheckoutInfo_ReceiverInfoNamePlaceholder: String { return self._s[3180]! }
public func VoiceOver_Chat_PhotoFrom(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3156]!, self._r[3156]!, [_0])
+ return formatWithArgumentRanges(self._s[3181]!, self._r[3181]!, [_0])
}
public func Checkout_PayPrice(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3158]!, self._r[3158]!, [_0])
+ return formatWithArgumentRanges(self._s[3183]!, self._r[3183]!, [_0])
}
- public var AuthSessions_AddDevice: String { return self._s[3159]! }
- public var WallpaperPreview_Blurred: String { return self._s[3160]! }
- public var Conversation_InstantPagePreview: String { return self._s[3161]! }
- public var PeerInfo_ButtonUnmute: String { return self._s[3162]! }
+ public var AuthSessions_AddDevice: String { return self._s[3184]! }
+ public var WallpaperPreview_Blurred: String { return self._s[3185]! }
+ public var Conversation_InstantPagePreview: String { return self._s[3186]! }
+ public var PeerInfo_ButtonUnmute: String { return self._s[3187]! }
public func DialogList_SingleUploadingVideoSuffix(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3163]!, self._r[3163]!, [_0])
+ return formatWithArgumentRanges(self._s[3188]!, self._r[3188]!, [_0])
}
- public var ChatList_PeerTypeChannel: String { return self._s[3164]! }
- public var SecretTimer_VideoDescription: String { return self._s[3167]! }
- public var WallpaperSearch_ColorRed: String { return self._s[3168]! }
- public var GroupPermission_NoPinMessages: String { return self._s[3169]! }
- public var Passport_Language_es: String { return self._s[3170]! }
- public var Permissions_ContactsAllow_v0: String { return self._s[3172]! }
- public var Conversation_EditingMessageMediaEditCurrentVideo: String { return self._s[3173]! }
+ public var ChatList_PeerTypeChannel: String { return self._s[3189]! }
+ public var SecretTimer_VideoDescription: String { return self._s[3192]! }
+ public var WallpaperSearch_ColorRed: String { return self._s[3193]! }
+ public var GroupPermission_NoPinMessages: String { return self._s[3194]! }
+ public var Passport_Language_es: String { return self._s[3195]! }
+ public var Permissions_ContactsAllow_v0: String { return self._s[3197]! }
+ public var Conversation_EditingMessageMediaEditCurrentVideo: String { return self._s[3198]! }
public func PUSH_CHAT_MESSAGE_CONTACT(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3174]!, self._r[3174]!, [_1, _2])
+ return formatWithArgumentRanges(self._s[3199]!, self._r[3199]!, [_1, _2])
}
- public var Privacy_Forwards_CustomHelp: String { return self._s[3175]! }
- public var WebPreview_GettingLinkInfo: String { return self._s[3177]! }
- public var Watch_UserInfo_Unmute: String { return self._s[3178]! }
- public var GroupInfo_ChannelListNamePlaceholder: String { return self._s[3179]! }
- public var AccessDenied_CameraRestricted: String { return self._s[3181]! }
+ public var Privacy_Forwards_CustomHelp: String { return self._s[3200]! }
+ public var WebPreview_GettingLinkInfo: String { return self._s[3202]! }
+ public var Watch_UserInfo_Unmute: String { return self._s[3203]! }
+ public var GroupInfo_ChannelListNamePlaceholder: String { return self._s[3204]! }
+ public var AccessDenied_CameraRestricted: String { return self._s[3206]! }
public func Conversation_Kilobytes(_ _0: Int) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3182]!, self._r[3182]!, ["\(_0)"])
+ return formatWithArgumentRanges(self._s[3207]!, self._r[3207]!, ["\(_0)"])
}
- public var ChatList_ReadAll: String { return self._s[3184]! }
- public var Settings_CopyUsername: String { return self._s[3185]! }
- public var Contacts_SearchLabel: String { return self._s[3186]! }
- public var Map_OpenInYandexNavigator: String { return self._s[3188]! }
- public var PasscodeSettings_EncryptData: String { return self._s[3189]! }
- public var Settings_Wallet: String { return self._s[3190]! }
- public var Group_ErrorSupergroupConversionNotPossible: String { return self._s[3191]! }
- public var ChatList_PeerTypeBot: String { return self._s[3192]! }
- public var WallpaperSearch_ColorPrefix: String { return self._s[3193]! }
- public var Notifications_GroupNotificationsPreview: String { return self._s[3194]! }
- public var DialogList_AdNoticeAlert: String { return self._s[3195]! }
- public var Wallet_Month_GenMay: String { return self._s[3197]! }
- public var CheckoutInfo_ShippingInfoAddress1: String { return self._s[3198]! }
- public var CheckoutInfo_ShippingInfoAddress2: String { return self._s[3199]! }
- public var Localization_LanguageCustom: String { return self._s[3200]! }
- public var Passport_Identity_TypeDriversLicenseUploadScan: String { return self._s[3201]! }
- public var CallFeedback_Title: String { return self._s[3202]! }
- public var VoiceOver_Chat_RecordPreviewVoiceMessage: String { return self._s[3205]! }
- public var Passport_Address_OneOfTypePassportRegistration: String { return self._s[3206]! }
- public var Wallet_Intro_CreateErrorTitle: String { return self._s[3207]! }
- public var Conversation_InfoGroup: String { return self._s[3208]! }
- public var Compose_NewMessage: String { return self._s[3209]! }
- public var FastTwoStepSetup_HintPlaceholder: String { return self._s[3210]! }
- public var ChatSettings_AutoDownloadVideoMessages: String { return self._s[3211]! }
- public var Wallet_SecureStorageReset_BiometryFaceId: String { return self._s[3212]! }
- public var Channel_DiscussionGroup_UnlinkChannel: String { return self._s[3213]! }
+ public var ChatList_ReadAll: String { return self._s[3209]! }
+ public var Settings_CopyUsername: String { return self._s[3210]! }
+ public var Contacts_SearchLabel: String { return self._s[3211]! }
+ public var Map_OpenInYandexNavigator: String { return self._s[3213]! }
+ public var PasscodeSettings_EncryptData: String { return self._s[3214]! }
+ public var Settings_Wallet: String { return self._s[3215]! }
+ public var Group_ErrorSupergroupConversionNotPossible: String { return self._s[3216]! }
+ public var ChatList_PeerTypeBot: String { return self._s[3217]! }
+ public var WallpaperSearch_ColorPrefix: String { return self._s[3218]! }
+ public var Notifications_GroupNotificationsPreview: String { return self._s[3219]! }
+ public var DialogList_AdNoticeAlert: String { return self._s[3220]! }
+ public var Wallet_Month_GenMay: String { return self._s[3222]! }
+ public var CheckoutInfo_ShippingInfoAddress1: String { return self._s[3223]! }
+ public var CheckoutInfo_ShippingInfoAddress2: String { return self._s[3224]! }
+ public var Localization_LanguageCustom: String { return self._s[3225]! }
+ public var Passport_Identity_TypeDriversLicenseUploadScan: String { return self._s[3226]! }
+ public var CallFeedback_Title: String { return self._s[3227]! }
+ public var VoiceOver_Chat_RecordPreviewVoiceMessage: String { return self._s[3230]! }
+ public var Passport_Address_OneOfTypePassportRegistration: String { return self._s[3231]! }
+ public var Wallet_Intro_CreateErrorTitle: String { return self._s[3232]! }
+ public var Conversation_InfoGroup: String { return self._s[3233]! }
+ public var Compose_NewMessage: String { return self._s[3234]! }
+ public var FastTwoStepSetup_HintPlaceholder: String { return self._s[3235]! }
+ public var ChatSettings_AutoDownloadVideoMessages: String { return self._s[3236]! }
+ public var Wallet_SecureStorageReset_BiometryFaceId: String { return self._s[3237]! }
+ public var Channel_DiscussionGroup_UnlinkChannel: String { return self._s[3238]! }
public func Passport_Scans_ScanIndex(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3214]!, self._r[3214]!, [_0])
+ return formatWithArgumentRanges(self._s[3239]!, self._r[3239]!, [_0])
}
- public var Channel_AdminLog_CanDeleteMessages: String { return self._s[3215]! }
- public var Login_CancelSignUpConfirmation: String { return self._s[3216]! }
- public var ChangePhoneNumberCode_Help: String { return self._s[3217]! }
- public var PrivacySettings_DeleteAccountHelp: String { return self._s[3218]! }
- public var ChatList_Context_RemoveFromFolder: String { return self._s[3219]! }
- public var Channel_BlackList_Title: String { return self._s[3220]! }
- public var UserInfo_PhoneCall: String { return self._s[3221]! }
- public var Passport_Address_OneOfTypeBankStatement: String { return self._s[3223]! }
- public var Wallet_Month_ShortJanuary: String { return self._s[3224]! }
- public var State_connecting: String { return self._s[3225]! }
- public var Appearance_ThemePreview_ChatList_6_Text: String { return self._s[3226]! }
- public var Wallet_Month_GenMarch: String { return self._s[3227]! }
- public var EditTheme_Expand_BottomInfo: String { return self._s[3228]! }
- public var AuthSessions_AddedDeviceTerminate: String { return self._s[3229]! }
+ public var Channel_AdminLog_CanDeleteMessages: String { return self._s[3240]! }
+ public var Login_CancelSignUpConfirmation: String { return self._s[3241]! }
+ public var ChangePhoneNumberCode_Help: String { return self._s[3242]! }
+ public var PrivacySettings_DeleteAccountHelp: String { return self._s[3243]! }
+ public var ChatList_Context_RemoveFromFolder: String { return self._s[3244]! }
+ public var Channel_BlackList_Title: String { return self._s[3245]! }
+ public var UserInfo_PhoneCall: String { return self._s[3246]! }
+ public var Passport_Address_OneOfTypeBankStatement: String { return self._s[3248]! }
+ public var Wallet_Month_ShortJanuary: String { return self._s[3249]! }
+ public var State_connecting: String { return self._s[3250]! }
+ public var Appearance_ThemePreview_ChatList_6_Text: String { return self._s[3251]! }
+ public var Wallet_Month_GenMarch: String { return self._s[3252]! }
+ public var EditTheme_Expand_BottomInfo: String { return self._s[3253]! }
+ public var AuthSessions_AddedDeviceTerminate: String { return self._s[3254]! }
public func LastSeen_TodayAt(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3230]!, self._r[3230]!, [_0])
+ return formatWithArgumentRanges(self._s[3255]!, self._r[3255]!, [_0])
}
public func DialogList_SingleRecordingAudioSuffix(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3231]!, self._r[3231]!, [_0])
+ return formatWithArgumentRanges(self._s[3256]!, self._r[3256]!, [_0])
}
- public var Notifications_GroupNotifications: String { return self._s[3232]! }
- public var Conversation_SendMessageErrorTooMuchScheduled: String { return self._s[3233]! }
- public var Passport_Identity_EditPassport: String { return self._s[3234]! }
- public var EnterPasscode_RepeatNewPasscode: String { return self._s[3236]! }
- public var Localization_EnglishLanguageName: String { return self._s[3237]! }
- public var Share_AuthDescription: String { return self._s[3238]! }
- public var SettingsSearch_Synonyms_Notifications_ChannelNotificationsAlert: String { return self._s[3239]! }
- public var Passport_Identity_Surname: String { return self._s[3240]! }
- public var Compose_TokenListPlaceholder: String { return self._s[3241]! }
- public var Wallet_AccessDenied_Camera: String { return self._s[3242]! }
- public var Passport_Identity_OneOfTypePassport: String { return self._s[3243]! }
- public var Settings_AboutEmpty: String { return self._s[3244]! }
- public var Conversation_Unmute: String { return self._s[3245]! }
- public var CreateGroup_ChannelsTooMuch: String { return self._s[3247]! }
- public var Wallet_Sending_Text: String { return self._s[3248]! }
+ public var Notifications_GroupNotifications: String { return self._s[3257]! }
+ public var Conversation_SendMessageErrorTooMuchScheduled: String { return self._s[3258]! }
+ public var Passport_Identity_EditPassport: String { return self._s[3259]! }
+ public var EnterPasscode_RepeatNewPasscode: String { return self._s[3261]! }
+ public var Localization_EnglishLanguageName: String { return self._s[3262]! }
+ public var Share_AuthDescription: String { return self._s[3263]! }
+ public var SettingsSearch_Synonyms_Notifications_ChannelNotificationsAlert: String { return self._s[3264]! }
+ public var Passport_Identity_Surname: String { return self._s[3265]! }
+ public var Compose_TokenListPlaceholder: String { return self._s[3266]! }
+ public var Wallet_AccessDenied_Camera: String { return self._s[3267]! }
+ public var Passport_Identity_OneOfTypePassport: String { return self._s[3268]! }
+ public var Settings_AboutEmpty: String { return self._s[3269]! }
+ public var Conversation_Unmute: String { return self._s[3270]! }
+ public var CreateGroup_ChannelsTooMuch: String { return self._s[3272]! }
+ public var Wallet_Sending_Text: String { return self._s[3273]! }
public func PUSH_CONTACT_JOINED(_ _1: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3249]!, self._r[3249]!, [_1])
+ return formatWithArgumentRanges(self._s[3274]!, self._r[3274]!, [_1])
}
- public var Login_CodeSentCall: String { return self._s[3250]! }
- public var ContactInfo_PhoneLabelHomeFax: String { return self._s[3252]! }
- public var ChatSettings_Appearance: String { return self._s[3253]! }
- public var ClearCache_StorageUsage: String { return self._s[3254]! }
- public var ChatListFolder_NameContacts: String { return self._s[3255]! }
- public var Appearance_PickAccentColor: String { return self._s[3257]! }
+ public var Login_CodeSentCall: String { return self._s[3275]! }
+ public var ContactInfo_PhoneLabelHomeFax: String { return self._s[3277]! }
+ public var ChatSettings_Appearance: String { return self._s[3278]! }
+ public var ClearCache_StorageUsage: String { return self._s[3279]! }
+ public var ChatListFolder_NameContacts: String { return self._s[3280]! }
+ public var Appearance_PickAccentColor: String { return self._s[3282]! }
public func PUSH_CHAT_MESSAGE_NOTEXT(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3258]!, self._r[3258]!, [_1, _2])
+ return formatWithArgumentRanges(self._s[3283]!, self._r[3283]!, [_1, _2])
}
public func PUSH_MESSAGE_GEO(_ _1: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3259]!, self._r[3259]!, [_1])
+ return formatWithArgumentRanges(self._s[3284]!, self._r[3284]!, [_1])
}
- public var Notification_CallMissed: String { return self._s[3260]! }
- public var SettingsSearch_Synonyms_Appearance_ChatBackground_Custom: String { return self._s[3261]! }
- public var Channel_AdminLogFilter_EventsInfo: String { return self._s[3262]! }
- public var Wallet_Month_GenOctober: String { return self._s[3264]! }
- public var ChatAdmins_AdminLabel: String { return self._s[3265]! }
- public var KeyCommand_JumpToNextChat: String { return self._s[3266]! }
- public var Conversation_StopPollConfirmationTitle: String { return self._s[3268]! }
- public var ChangePhoneNumberCode_CodePlaceholder: String { return self._s[3269]! }
- public var Month_GenJune: String { return self._s[3270]! }
- public var IntentsSettings_MainAccountInfo: String { return self._s[3271]! }
- public var Watch_Location_Current: String { return self._s[3272]! }
- public var Wallet_Receive_CopyInvoiceUrl: String { return self._s[3273]! }
- public var Conversation_TitleMute: String { return self._s[3274]! }
- public var Map_PlacesInThisArea: String { return self._s[3275]! }
+ public var Notification_CallMissed: String { return self._s[3285]! }
+ public var SettingsSearch_Synonyms_Appearance_ChatBackground_Custom: String { return self._s[3286]! }
+ public var Channel_AdminLogFilter_EventsInfo: String { return self._s[3287]! }
+ public var Wallet_Month_GenOctober: String { return self._s[3289]! }
+ public var ChatAdmins_AdminLabel: String { return self._s[3290]! }
+ public var KeyCommand_JumpToNextChat: String { return self._s[3291]! }
+ public var Conversation_StopPollConfirmationTitle: String { return self._s[3293]! }
+ public var ChangePhoneNumberCode_CodePlaceholder: String { return self._s[3294]! }
+ public var Month_GenJune: String { return self._s[3295]! }
+ public var IntentsSettings_MainAccountInfo: String { return self._s[3296]! }
+ public var Watch_Location_Current: String { return self._s[3297]! }
+ public var Wallet_Receive_CopyInvoiceUrl: String { return self._s[3298]! }
+ public var Conversation_TitleMute: String { return self._s[3299]! }
+ public var Map_PlacesInThisArea: String { return self._s[3300]! }
public func PUSH_CHANNEL_MESSAGE_ROUND(_ _1: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3276]!, self._r[3276]!, [_1])
+ return formatWithArgumentRanges(self._s[3301]!, self._r[3301]!, [_1])
}
- public var GroupInfo_DeleteAndExit: String { return self._s[3277]! }
+ public var GroupInfo_DeleteAndExit: String { return self._s[3302]! }
public func Conversation_Moderate_DeleteAllMessages(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3278]!, self._r[3278]!, [_0])
+ return formatWithArgumentRanges(self._s[3303]!, self._r[3303]!, [_0])
}
- public var Call_ReportPlaceholder: String { return self._s[3279]! }
- public var Chat_SlowmodeSendError: String { return self._s[3280]! }
- public var MaskStickerSettings_Info: String { return self._s[3281]! }
- public var EditTheme_Expand_TopInfo: String { return self._s[3282]! }
+ public var Call_ReportPlaceholder: String { return self._s[3304]! }
+ public var Chat_SlowmodeSendError: String { return self._s[3305]! }
+ public var MaskStickerSettings_Info: String { return self._s[3306]! }
+ public var EditTheme_Expand_TopInfo: String { return self._s[3307]! }
public func GroupInfo_AddParticipantConfirmation(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3283]!, self._r[3283]!, [_0])
+ return formatWithArgumentRanges(self._s[3308]!, self._r[3308]!, [_0])
}
- public var Checkout_NewCard_PostcodeTitle: String { return self._s[3284]! }
- public var Passport_Address_RegionPlaceholder: String { return self._s[3286]! }
- public var Contacts_ShareTelegram: String { return self._s[3287]! }
- public var EnterPasscode_EnterNewPasscodeNew: String { return self._s[3288]! }
- public var Map_AddressOnMap: String { return self._s[3289]! }
- public var Channel_ErrorAccessDenied: String { return self._s[3290]! }
- public var UserInfo_ScamBotWarning: String { return self._s[3292]! }
- public var Stickers_GroupChooseStickerPack: String { return self._s[3293]! }
- public var Call_ConnectionErrorTitle: String { return self._s[3294]! }
- public var UserInfo_NotificationsEnable: String { return self._s[3295]! }
- public var ArchivedChats_IntroText1: String { return self._s[3296]! }
- public var Tour_Text4: String { return self._s[3299]! }
- public var WallpaperSearch_Recent: String { return self._s[3300]! }
- public var GroupInfo_ScamGroupWarning: String { return self._s[3301]! }
- public var PeopleNearby_MakeVisibleTitle: String { return self._s[3302]! }
- public var Profile_MessageLifetime2s: String { return self._s[3304]! }
- public var Appearance_ThemePreview_ChatList_5_Text: String { return self._s[3305]! }
- public var Notification_MessageLifetime2s: String { return self._s[3306]! }
+ public var Checkout_NewCard_PostcodeTitle: String { return self._s[3309]! }
+ public var Passport_Address_RegionPlaceholder: String { return self._s[3311]! }
+ public var Contacts_ShareTelegram: String { return self._s[3312]! }
+ public var EnterPasscode_EnterNewPasscodeNew: String { return self._s[3313]! }
+ public var Map_AddressOnMap: String { return self._s[3314]! }
+ public var Channel_ErrorAccessDenied: String { return self._s[3315]! }
+ public var UserInfo_ScamBotWarning: String { return self._s[3317]! }
+ public var Stickers_GroupChooseStickerPack: String { return self._s[3318]! }
+ public var Call_ConnectionErrorTitle: String { return self._s[3319]! }
+ public var UserInfo_NotificationsEnable: String { return self._s[3320]! }
+ public var ArchivedChats_IntroText1: String { return self._s[3321]! }
+ public var Tour_Text4: String { return self._s[3324]! }
+ public var WallpaperSearch_Recent: String { return self._s[3325]! }
+ public var GroupInfo_ScamGroupWarning: String { return self._s[3326]! }
+ public var PeopleNearby_MakeVisibleTitle: String { return self._s[3327]! }
+ public var Profile_MessageLifetime2s: String { return self._s[3329]! }
+ public var Appearance_ThemePreview_ChatList_5_Text: String { return self._s[3330]! }
+ public var Notification_MessageLifetime2s: String { return self._s[3331]! }
public func Time_PreciseDate_m10(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3307]!, self._r[3307]!, [_1, _2, _3])
+ return formatWithArgumentRanges(self._s[3332]!, self._r[3332]!, [_1, _2, _3])
}
- public var Cache_ClearCache: String { return self._s[3308]! }
- public var AutoNightTheme_UpdateLocation: String { return self._s[3309]! }
- public var Permissions_NotificationsUnreachableText_v0: String { return self._s[3310]! }
+ public var Cache_ClearCache: String { return self._s[3333]! }
+ public var AutoNightTheme_UpdateLocation: String { return self._s[3334]! }
+ public var Permissions_NotificationsUnreachableText_v0: String { return self._s[3335]! }
public func Channel_AdminLog_MessageChangedGroupUsername(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3314]!, self._r[3314]!, [_0])
+ return formatWithArgumentRanges(self._s[3339]!, self._r[3339]!, [_0])
}
public func Conversation_ShareMyPhoneNumber_StatusSuccess(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3316]!, self._r[3316]!, [_0])
+ return formatWithArgumentRanges(self._s[3341]!, self._r[3341]!, [_0])
}
- public var LocalGroup_Text: String { return self._s[3317]! }
- public var PeerInfo_PaneMembers: String { return self._s[3318]! }
- public var Channel_AdminLog_EmptyFilterTitle: String { return self._s[3319]! }
- public var SocksProxySetup_TypeSocks: String { return self._s[3320]! }
- public var ChatList_UnarchiveAction: String { return self._s[3321]! }
- public var AutoNightTheme_Title: String { return self._s[3322]! }
- public var InstantPage_FeedbackButton: String { return self._s[3323]! }
- public var Passport_FieldAddress: String { return self._s[3324]! }
+ public var LocalGroup_Text: String { return self._s[3342]! }
+ public var PeerInfo_PaneMembers: String { return self._s[3343]! }
+ public var Channel_AdminLog_EmptyFilterTitle: String { return self._s[3344]! }
+ public var SocksProxySetup_TypeSocks: String { return self._s[3345]! }
+ public var ChatList_UnarchiveAction: String { return self._s[3346]! }
+ public var AutoNightTheme_Title: String { return self._s[3347]! }
+ public var InstantPage_FeedbackButton: String { return self._s[3348]! }
+ public var Passport_FieldAddress: String { return self._s[3349]! }
public func Channel_AdminLog_SetSlowmode(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3325]!, self._r[3325]!, [_1, _2])
- }
- public var Month_ShortMarch: String { return self._s[3326]! }
- public func PUSH_MESSAGE_INVOICE(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3327]!, self._r[3327]!, [_1, _2])
- }
- public var SocksProxySetup_UsernamePlaceholder: String { return self._s[3328]! }
- public var Conversation_ShareInlineBotLocationConfirmation: String { return self._s[3329]! }
- public var Passport_FloodError: String { return self._s[3330]! }
- public var SecretGif_Title: String { return self._s[3331]! }
- public var NotificationSettings_ShowNotificationsAllAccountsInfoOn: String { return self._s[3332]! }
- public var ChatList_Context_UnhideArchive: String { return self._s[3333]! }
- public var Passport_Language_th: String { return self._s[3335]! }
- public var Passport_Address_Address: String { return self._s[3336]! }
- public var Login_InvalidLastNameError: String { return self._s[3337]! }
- public var Notifications_InAppNotificationsPreview: String { return self._s[3338]! }
- public var Notifications_PermissionsUnreachableTitle: String { return self._s[3339]! }
- public var ChatList_Context_Archive: String { return self._s[3340]! }
- public var SettingsSearch_FAQ: String { return self._s[3341]! }
- public var ShareMenu_Send: String { return self._s[3342]! }
- public var ChatState_Connecting: String { return self._s[3343]! }
- public var WallpaperSearch_ColorYellow: String { return self._s[3345]! }
- public var Month_GenNovember: String { return self._s[3347]! }
- public var SettingsSearch_Synonyms_Appearance_LargeEmoji: String { return self._s[3349]! }
- public func Conversation_ShareMyPhoneNumberConfirmation(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[3350]!, self._r[3350]!, [_1, _2])
}
- public var ChatListFolder_CategoryChannels: String { return self._s[3351]! }
- public var Conversation_SwipeToReplyHintText: String { return self._s[3352]! }
- public var Checkout_Email: String { return self._s[3353]! }
- public var NotificationsSound_Tritone: String { return self._s[3354]! }
- public var Paint_Marker: String { return self._s[3356]! }
- public var StickerPacksSettings_ManagingHelp: String { return self._s[3358]! }
- public var Wallet_ContextMenuCopy: String { return self._s[3360]! }
- public func Wallet_Time_PreciseDate_m6(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3362]!, self._r[3362]!, [_1, _2, _3])
+ public var Month_ShortMarch: String { return self._s[3351]! }
+ public func PUSH_MESSAGE_INVOICE(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
+ return formatWithArgumentRanges(self._s[3352]!, self._r[3352]!, [_1, _2])
}
- public var Appearance_TextSize_Automatic: String { return self._s[3363]! }
- public var Stickers_Installed: String { return self._s[3365]! }
+ public var SocksProxySetup_UsernamePlaceholder: String { return self._s[3353]! }
+ public var Conversation_ShareInlineBotLocationConfirmation: String { return self._s[3354]! }
+ public var Passport_FloodError: String { return self._s[3355]! }
+ public var SecretGif_Title: String { return self._s[3356]! }
+ public var NotificationSettings_ShowNotificationsAllAccountsInfoOn: String { return self._s[3357]! }
+ public var ChatList_Context_UnhideArchive: String { return self._s[3358]! }
+ public var Passport_Language_th: String { return self._s[3360]! }
+ public var Passport_Address_Address: String { return self._s[3361]! }
+ public var Login_InvalidLastNameError: String { return self._s[3362]! }
+ public var Notifications_InAppNotificationsPreview: String { return self._s[3363]! }
+ public var Notifications_PermissionsUnreachableTitle: String { return self._s[3364]! }
+ public var ChatList_Context_Archive: String { return self._s[3365]! }
+ public var SettingsSearch_FAQ: String { return self._s[3366]! }
+ public var ShareMenu_Send: String { return self._s[3367]! }
+ public var ChatState_Connecting: String { return self._s[3368]! }
+ public var WallpaperSearch_ColorYellow: String { return self._s[3370]! }
+ public var Month_GenNovember: String { return self._s[3372]! }
+ public var SettingsSearch_Synonyms_Appearance_LargeEmoji: String { return self._s[3374]! }
+ public func Conversation_ShareMyPhoneNumberConfirmation(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
+ return formatWithArgumentRanges(self._s[3375]!, self._r[3375]!, [_1, _2])
+ }
+ public var ChatListFolder_CategoryChannels: String { return self._s[3376]! }
+ public var Conversation_SwipeToReplyHintText: String { return self._s[3377]! }
+ public var Checkout_Email: String { return self._s[3378]! }
+ public var NotificationsSound_Tritone: String { return self._s[3379]! }
+ public var Paint_Marker: String { return self._s[3381]! }
+ public var StickerPacksSettings_ManagingHelp: String { return self._s[3383]! }
+ public var Wallet_ContextMenuCopy: String { return self._s[3385]! }
+ public func Wallet_Time_PreciseDate_m6(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
+ return formatWithArgumentRanges(self._s[3387]!, self._r[3387]!, [_1, _2, _3])
+ }
+ public var Appearance_TextSize_Automatic: String { return self._s[3388]! }
+ public var Stickers_Installed: String { return self._s[3390]! }
public func PUSH_PINNED_ROUND(_ _1: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3366]!, self._r[3366]!, [_1])
+ return formatWithArgumentRanges(self._s[3391]!, self._r[3391]!, [_1])
}
public func StickerPackActionInfo_AddedText(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3367]!, self._r[3367]!, [_0])
+ return formatWithArgumentRanges(self._s[3392]!, self._r[3392]!, [_0])
}
- public var ChangePhoneNumberNumber_Help: String { return self._s[3368]! }
+ public var ChangePhoneNumberNumber_Help: String { return self._s[3393]! }
public func Checkout_LiabilityAlert(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3369]!, self._r[3369]!, [_1, _1, _1, _2])
+ return formatWithArgumentRanges(self._s[3394]!, self._r[3394]!, [_1, _1, _1, _2])
}
- public var ChatList_UndoArchiveTitle: String { return self._s[3370]! }
- public var Notification_Exceptions_Add: String { return self._s[3371]! }
- public var DialogList_You: String { return self._s[3372]! }
- public var ChatList_PsaLabel_covid: String { return self._s[3374]! }
- public var MediaPicker_Send: String { return self._s[3376]! }
- public var SettingsSearch_Synonyms_Stickers_Title: String { return self._s[3377]! }
- public var Appearance_ThemePreview_ChatList_4_Text: String { return self._s[3378]! }
- public var Call_AudioRouteSpeaker: String { return self._s[3379]! }
- public var Watch_UserInfo_Title: String { return self._s[3380]! }
- public var VoiceOver_Chat_PollFinalResults: String { return self._s[3381]! }
- public var Appearance_AccentColor: String { return self._s[3383]! }
+ public var ChatList_UndoArchiveTitle: String { return self._s[3395]! }
+ public var Notification_Exceptions_Add: String { return self._s[3396]! }
+ public var DialogList_You: String { return self._s[3397]! }
+ public var ChatList_PsaLabel_covid: String { return self._s[3399]! }
+ public var MediaPicker_Send: String { return self._s[3401]! }
+ public var SettingsSearch_Synonyms_Stickers_Title: String { return self._s[3402]! }
+ public var Appearance_ThemePreview_ChatList_4_Text: String { return self._s[3403]! }
+ public var Call_AudioRouteSpeaker: String { return self._s[3404]! }
+ public var Watch_UserInfo_Title: String { return self._s[3405]! }
+ public var VoiceOver_Chat_PollFinalResults: String { return self._s[3406]! }
+ public var Appearance_AccentColor: String { return self._s[3408]! }
public func Login_EmailPhoneSubject(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3384]!, self._r[3384]!, [_0])
+ return formatWithArgumentRanges(self._s[3409]!, self._r[3409]!, [_0])
}
- public var Permissions_ContactsAllowInSettings_v0: String { return self._s[3385]! }
+ public var Permissions_ContactsAllowInSettings_v0: String { return self._s[3410]! }
public func PUSH_CHANNEL_MESSAGE_GAME(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3386]!, self._r[3386]!, [_1, _2])
+ return formatWithArgumentRanges(self._s[3411]!, self._r[3411]!, [_1, _2])
}
- public var Conversation_ClousStorageInfo_Description2: String { return self._s[3387]! }
- public var WebSearch_RecentClearConfirmation: String { return self._s[3388]! }
- public var Notification_CallOutgoing: String { return self._s[3389]! }
- public var PrivacySettings_PasscodeAndFaceId: String { return self._s[3390]! }
- public var Channel_DiscussionGroup_MakeHistoryPublic: String { return self._s[3391]! }
- public var Call_RecordingDisabledMessage: String { return self._s[3392]! }
- public var Message_Game: String { return self._s[3393]! }
- public var Conversation_PressVolumeButtonForSound: String { return self._s[3394]! }
- public var PrivacyLastSeenSettings_CustomHelp: String { return self._s[3395]! }
- public var Channel_DiscussionGroup_PrivateGroup: String { return self._s[3396]! }
- public var Channel_EditAdmin_PermissionAddAdmins: String { return self._s[3397]! }
- public var Date_DialogDateFormat: String { return self._s[3399]! }
- public var WallpaperColors_SetCustomColor: String { return self._s[3400]! }
- public var Notifications_InAppNotifications: String { return self._s[3401]! }
+ public var Conversation_ClousStorageInfo_Description2: String { return self._s[3412]! }
+ public var WebSearch_RecentClearConfirmation: String { return self._s[3413]! }
+ public var Notification_CallOutgoing: String { return self._s[3414]! }
+ public var PrivacySettings_PasscodeAndFaceId: String { return self._s[3415]! }
+ public var Channel_DiscussionGroup_MakeHistoryPublic: String { return self._s[3416]! }
+ public var Call_RecordingDisabledMessage: String { return self._s[3417]! }
+ public var Message_Game: String { return self._s[3418]! }
+ public var Conversation_PressVolumeButtonForSound: String { return self._s[3419]! }
+ public var PrivacyLastSeenSettings_CustomHelp: String { return self._s[3420]! }
+ public var Channel_DiscussionGroup_PrivateGroup: String { return self._s[3421]! }
+ public var Channel_EditAdmin_PermissionAddAdmins: String { return self._s[3422]! }
+ public var Date_DialogDateFormat: String { return self._s[3424]! }
+ public var WallpaperColors_SetCustomColor: String { return self._s[3425]! }
+ public var Notifications_InAppNotifications: String { return self._s[3426]! }
public func Channel_Management_RemovedBy(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3402]!, self._r[3402]!, [_0])
+ return formatWithArgumentRanges(self._s[3427]!, self._r[3427]!, [_0])
}
public func Settings_ApplyProxyAlert(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3403]!, self._r[3403]!, [_1, _2])
- }
- public var NewContact_Title: String { return self._s[3404]! }
- public func AutoDownloadSettings_UpToForAll(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3405]!, self._r[3405]!, [_0])
- }
- public var Stats_GroupTopPoster_Promote: String { return self._s[3406]! }
- public var Conversation_ViewContactDetails: String { return self._s[3407]! }
- public func PUSH_CHANNEL_MESSAGE_CONTACT(_ _1: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3409]!, self._r[3409]!, [_1])
- }
- public var Checkout_NewCard_CardholderNameTitle: String { return self._s[3410]! }
- public var Passport_Identity_ExpiryDateNone: String { return self._s[3411]! }
- public var PrivacySettings_Title: String { return self._s[3412]! }
- public var Conversation_SilentBroadcastTooltipOff: String { return self._s[3415]! }
- public var GroupRemoved_UsersSectionTitle: String { return self._s[3416]! }
- public var VoiceOver_Chat_ContactEmail: String { return self._s[3417]! }
- public var Contacts_PhoneNumber: String { return self._s[3418]! }
- public var PeerInfo_ButtonMute: String { return self._s[3419]! }
- public var TwoFactorSetup_Password_PlaceholderConfirmPassword: String { return self._s[3421]! }
- public var Map_ShowPlaces: String { return self._s[3422]! }
- public var ChatAdmins_Title: String { return self._s[3423]! }
- public var InstantPage_Reference: String { return self._s[3425]! }
- public var Wallet_Info_Updating: String { return self._s[3426]! }
- public var ReportGroupLocation_Text: String { return self._s[3427]! }
- public func PUSH_CHAT_MESSAGE_FWD(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[3428]!, self._r[3428]!, [_1, _2])
}
- public var Camera_FlashOff: String { return self._s[3429]! }
- public var Watch_UserInfo_Block: String { return self._s[3430]! }
- public var ChatSettings_Stickers: String { return self._s[3431]! }
- public var ChatSettings_DownloadInBackground: String { return self._s[3432]! }
- public var Appearance_ThemeCarouselTintedNight: String { return self._s[3433]! }
+ public var NewContact_Title: String { return self._s[3429]! }
+ public func AutoDownloadSettings_UpToForAll(_ _0: String) -> (String, [(Int, NSRange)]) {
+ return formatWithArgumentRanges(self._s[3430]!, self._r[3430]!, [_0])
+ }
+ public var Stats_GroupTopPoster_Promote: String { return self._s[3431]! }
+ public var Conversation_ViewContactDetails: String { return self._s[3432]! }
+ public func PUSH_CHANNEL_MESSAGE_CONTACT(_ _1: String) -> (String, [(Int, NSRange)]) {
+ return formatWithArgumentRanges(self._s[3434]!, self._r[3434]!, [_1])
+ }
+ public var Checkout_NewCard_CardholderNameTitle: String { return self._s[3435]! }
+ public var Passport_Identity_ExpiryDateNone: String { return self._s[3436]! }
+ public var PrivacySettings_Title: String { return self._s[3437]! }
+ public var Conversation_SilentBroadcastTooltipOff: String { return self._s[3440]! }
+ public var GroupRemoved_UsersSectionTitle: String { return self._s[3441]! }
+ public var VoiceOver_Chat_ContactEmail: String { return self._s[3442]! }
+ public var Contacts_PhoneNumber: String { return self._s[3443]! }
+ public var PeerInfo_ButtonMute: String { return self._s[3444]! }
+ public var TwoFactorSetup_Password_PlaceholderConfirmPassword: String { return self._s[3446]! }
+ public var Map_ShowPlaces: String { return self._s[3447]! }
+ public var ChatAdmins_Title: String { return self._s[3448]! }
+ public var InstantPage_Reference: String { return self._s[3450]! }
+ public var Wallet_Info_Updating: String { return self._s[3451]! }
+ public var ReportGroupLocation_Text: String { return self._s[3452]! }
+ public func PUSH_CHAT_MESSAGE_FWD(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
+ return formatWithArgumentRanges(self._s[3453]!, self._r[3453]!, [_1, _2])
+ }
+ public var Camera_FlashOff: String { return self._s[3454]! }
+ public var Watch_UserInfo_Block: String { return self._s[3455]! }
+ public var ChatSettings_Stickers: String { return self._s[3456]! }
+ public var ChatSettings_DownloadInBackground: String { return self._s[3457]! }
+ public var Appearance_ThemeCarouselTintedNight: String { return self._s[3458]! }
public func UserInfo_BlockConfirmation(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3434]!, self._r[3434]!, [_0])
+ return formatWithArgumentRanges(self._s[3459]!, self._r[3459]!, [_0])
}
- public var Settings_ViewPhoto: String { return self._s[3435]! }
- public var Login_CheckOtherSessionMessages: String { return self._s[3436]! }
- public var AutoDownloadSettings_Cellular: String { return self._s[3437]! }
- public var Wallet_Created_ExportErrorTitle: String { return self._s[3438]! }
- public var SettingsSearch_Synonyms_Notifications_GroupNotificationsExceptions: String { return self._s[3439]! }
- public var VoiceOver_MessageContextShare: String { return self._s[3440]! }
+ public var Settings_ViewPhoto: String { return self._s[3460]! }
+ public var Login_CheckOtherSessionMessages: String { return self._s[3461]! }
+ public var AutoDownloadSettings_Cellular: String { return self._s[3462]! }
+ public var Wallet_Created_ExportErrorTitle: String { return self._s[3463]! }
+ public var SettingsSearch_Synonyms_Notifications_GroupNotificationsExceptions: String { return self._s[3464]! }
+ public var VoiceOver_MessageContextShare: String { return self._s[3465]! }
public func Target_InviteToGroupConfirmation(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3442]!, self._r[3442]!, [_0])
+ return formatWithArgumentRanges(self._s[3467]!, self._r[3467]!, [_0])
}
- public var Privacy_DeleteDrafts: String { return self._s[3443]! }
- public var Wallpaper_SetCustomBackgroundInfo: String { return self._s[3444]! }
+ public var Privacy_DeleteDrafts: String { return self._s[3468]! }
+ public var Wallpaper_SetCustomBackgroundInfo: String { return self._s[3469]! }
public func LastSeen_AtDate(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3445]!, self._r[3445]!, [_0])
+ return formatWithArgumentRanges(self._s[3470]!, self._r[3470]!, [_0])
}
- public var DialogList_SavedMessagesHelp: String { return self._s[3446]! }
- public var Wallet_SecureStorageNotAvailable_Title: String { return self._s[3447]! }
- public var DialogList_SavedMessages: String { return self._s[3448]! }
- public var GroupInfo_UpgradeButton: String { return self._s[3449]! }
- public var Appearance_ThemePreview_ChatList_3_Text: String { return self._s[3451]! }
- public var DialogList_Pin: String { return self._s[3452]! }
+ public var DialogList_SavedMessagesHelp: String { return self._s[3471]! }
+ public var Wallet_SecureStorageNotAvailable_Title: String { return self._s[3472]! }
+ public var DialogList_SavedMessages: String { return self._s[3473]! }
+ public var GroupInfo_UpgradeButton: String { return self._s[3474]! }
+ public var Appearance_ThemePreview_ChatList_3_Text: String { return self._s[3476]! }
+ public var DialogList_Pin: String { return self._s[3477]! }
public func ForwardedAuthors2(_ _0: String, _ _1: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3453]!, self._r[3453]!, [_0, _1])
+ return formatWithArgumentRanges(self._s[3478]!, self._r[3478]!, [_0, _1])
}
public func Login_PhoneGenericEmailSubject(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3454]!, self._r[3454]!, [_0])
+ return formatWithArgumentRanges(self._s[3479]!, self._r[3479]!, [_0])
}
- public var Notification_Exceptions_AlwaysOn: String { return self._s[3455]! }
- public var UserInfo_NotificationsDisable: String { return self._s[3456]! }
- public var Conversation_UnarchiveDone: String { return self._s[3457]! }
- public var Conversation_ContextMenuCancelEditing: String { return self._s[3458]! }
- public var Paint_Outlined: String { return self._s[3459]! }
- public var Activity_PlayingGame: String { return self._s[3460]! }
- public var SearchImages_NoImagesFound: String { return self._s[3461]! }
- public var SocksProxySetup_ProxyType: String { return self._s[3462]! }
- public var AppleWatch_ReplyPresetsHelp: String { return self._s[3464]! }
- public var Conversation_ContextMenuCancelSending: String { return self._s[3465]! }
- public var Settings_AppLanguage: String { return self._s[3466]! }
- public var TwoStepAuth_ResetAccountHelp: String { return self._s[3467]! }
- public var Common_ChoosePhoto: String { return self._s[3468]! }
- public var AuthSessions_AddDevice_InvalidQRCode: String { return self._s[3469]! }
- public var CallFeedback_ReasonEcho: String { return self._s[3470]! }
+ public var Notification_Exceptions_AlwaysOn: String { return self._s[3480]! }
+ public var UserInfo_NotificationsDisable: String { return self._s[3481]! }
+ public var Conversation_UnarchiveDone: String { return self._s[3482]! }
+ public var Conversation_ContextMenuCancelEditing: String { return self._s[3483]! }
+ public var Paint_Outlined: String { return self._s[3484]! }
+ public var Activity_PlayingGame: String { return self._s[3485]! }
+ public var SearchImages_NoImagesFound: String { return self._s[3486]! }
+ public var SocksProxySetup_ProxyType: String { return self._s[3487]! }
+ public var AppleWatch_ReplyPresetsHelp: String { return self._s[3489]! }
+ public var Conversation_ContextMenuCancelSending: String { return self._s[3490]! }
+ public var Settings_AppLanguage: String { return self._s[3491]! }
+ public var TwoStepAuth_ResetAccountHelp: String { return self._s[3492]! }
+ public var Common_ChoosePhoto: String { return self._s[3493]! }
+ public var AuthSessions_AddDevice_InvalidQRCode: String { return self._s[3494]! }
+ public var CallFeedback_ReasonEcho: String { return self._s[3495]! }
public func PUSH_PINNED_AUDIO(_ _1: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3471]!, self._r[3471]!, [_1])
+ return formatWithArgumentRanges(self._s[3496]!, self._r[3496]!, [_1])
}
- public var Privacy_Calls_AlwaysAllow: String { return self._s[3472]! }
- public var PollResults_Collapse: String { return self._s[3473]! }
- public var Activity_UploadingVideo: String { return self._s[3474]! }
- public var Conversation_WalletRequiredNotNow: String { return self._s[3475]! }
- public var ChannelInfo_DeleteChannelConfirmation: String { return self._s[3476]! }
- public var NetworkUsageSettings_Wifi: String { return self._s[3477]! }
- public var VoiceOver_Editing_ClearText: String { return self._s[3478]! }
- public var PUSH_SENDER_YOU: String { return self._s[3479]! }
- public var Channel_BanUser_PermissionReadMessages: String { return self._s[3480]! }
- public var Checkout_PayWithTouchId: String { return self._s[3481]! }
- public var Wallpaper_ResetWallpapersConfirmation: String { return self._s[3482]! }
+ public var Privacy_Calls_AlwaysAllow: String { return self._s[3497]! }
+ public var PollResults_Collapse: String { return self._s[3498]! }
+ public var Activity_UploadingVideo: String { return self._s[3499]! }
+ public var Conversation_WalletRequiredNotNow: String { return self._s[3500]! }
+ public var ChannelInfo_DeleteChannelConfirmation: String { return self._s[3501]! }
+ public var NetworkUsageSettings_Wifi: String { return self._s[3502]! }
+ public var VoiceOver_Editing_ClearText: String { return self._s[3503]! }
+ public var PUSH_SENDER_YOU: String { return self._s[3504]! }
+ public var Channel_BanUser_PermissionReadMessages: String { return self._s[3505]! }
+ public var Checkout_PayWithTouchId: String { return self._s[3506]! }
+ public var Wallpaper_ResetWallpapersConfirmation: String { return self._s[3507]! }
public func PUSH_LOCKED_MESSAGE(_ _1: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3484]!, self._r[3484]!, [_1])
+ return formatWithArgumentRanges(self._s[3509]!, self._r[3509]!, [_1])
}
- public var Notifications_ExceptionsNone: String { return self._s[3485]! }
+ public var Notifications_ExceptionsNone: String { return self._s[3510]! }
public func Message_ForwardedMessageShort(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3486]!, self._r[3486]!, [_0])
+ return formatWithArgumentRanges(self._s[3511]!, self._r[3511]!, [_0])
}
public func PUSH_PINNED_GEO(_ _1: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3487]!, self._r[3487]!, [_1])
+ return formatWithArgumentRanges(self._s[3512]!, self._r[3512]!, [_1])
}
- public var AuthSessions_IncompleteAttempts: String { return self._s[3489]! }
- public var Passport_Address_Region: String { return self._s[3492]! }
- public var ChatList_DeleteChat: String { return self._s[3493]! }
- public var LogoutOptions_ClearCacheTitle: String { return self._s[3494]! }
- public var PhotoEditor_TiltShift: String { return self._s[3495]! }
- public var Settings_FAQ_URL: String { return self._s[3496]! }
- public var TwoFactorSetup_EmailVerification_ChangeAction: String { return self._s[3497]! }
- public var Passport_Language_sl: String { return self._s[3499]! }
- public var Settings_PrivacySettings: String { return self._s[3501]! }
- public var SharedMedia_TitleLink: String { return self._s[3502]! }
- public var Passport_Identity_TypePassportUploadScan: String { return self._s[3503]! }
- public var Settings_SetProfilePhoto: String { return self._s[3504]! }
- public var Channel_About_Help: String { return self._s[3505]! }
- public var Contacts_PermissionsEnable: String { return self._s[3506]! }
- public var Wallet_Sending_Title: String { return self._s[3507]! }
- public var PeerInfo_PaneMedia: String { return self._s[3508]! }
- public var SettingsSearch_Synonyms_Notifications_GroupNotificationsAlert: String { return self._s[3509]! }
- public var AttachmentMenu_SendAsFiles: String { return self._s[3510]! }
- public var CallFeedback_ReasonInterruption: String { return self._s[3512]! }
- public var Passport_Address_AddTemporaryRegistration: String { return self._s[3513]! }
- public var AutoDownloadSettings_AutodownloadVideos: String { return self._s[3514]! }
- public var ChatSettings_AutoDownloadSettings_Delimeter: String { return self._s[3515]! }
- public var OldChannels_Title: String { return self._s[3516]! }
- public var PrivacySettings_DeleteAccountTitle: String { return self._s[3517]! }
- public var AccessDenied_VideoMessageCamera: String { return self._s[3519]! }
- public var Map_OpenInYandexMaps: String { return self._s[3521]! }
- public var CreateGroup_ErrorLocatedGroupsTooMuch: String { return self._s[3522]! }
- public var VoiceOver_MessageContextReply: String { return self._s[3523]! }
- public var ChatListFolder_DiscardConfirmation: String { return self._s[3525]! }
- public var PhotoEditor_SaturationTool: String { return self._s[3526]! }
+ public var AuthSessions_IncompleteAttempts: String { return self._s[3514]! }
+ public var Passport_Address_Region: String { return self._s[3517]! }
+ public var ChatList_DeleteChat: String { return self._s[3518]! }
+ public var LogoutOptions_ClearCacheTitle: String { return self._s[3519]! }
+ public var PhotoEditor_TiltShift: String { return self._s[3520]! }
+ public var Settings_FAQ_URL: String { return self._s[3521]! }
+ public var TwoFactorSetup_EmailVerification_ChangeAction: String { return self._s[3522]! }
+ public var SharedMedia_TitleLink: String { return self._s[3525]! }
+ public var Settings_PrivacySettings: String { return self._s[3526]! }
+ public var Passport_Identity_TypePassportUploadScan: String { return self._s[3527]! }
+ public var Passport_Language_sl: String { return self._s[3528]! }
+ public var Settings_SetProfilePhoto: String { return self._s[3529]! }
+ public var Channel_About_Help: String { return self._s[3530]! }
+ public var Contacts_PermissionsEnable: String { return self._s[3531]! }
+ public var Wallet_Sending_Title: String { return self._s[3532]! }
+ public var PeerInfo_PaneMedia: String { return self._s[3533]! }
+ public var SettingsSearch_Synonyms_Notifications_GroupNotificationsAlert: String { return self._s[3534]! }
+ public var AttachmentMenu_SendAsFiles: String { return self._s[3535]! }
+ public var CallFeedback_ReasonInterruption: String { return self._s[3537]! }
+ public var Passport_Address_AddTemporaryRegistration: String { return self._s[3538]! }
+ public var AutoDownloadSettings_AutodownloadVideos: String { return self._s[3539]! }
+ public var ChatSettings_AutoDownloadSettings_Delimeter: String { return self._s[3540]! }
+ public var OldChannels_Title: String { return self._s[3541]! }
+ public var PrivacySettings_DeleteAccountTitle: String { return self._s[3542]! }
+ public var AccessDenied_VideoMessageCamera: String { return self._s[3544]! }
+ public var Map_OpenInYandexMaps: String { return self._s[3546]! }
+ public var CreateGroup_ErrorLocatedGroupsTooMuch: String { return self._s[3547]! }
+ public var VoiceOver_MessageContextReply: String { return self._s[3548]! }
+ public var ChatListFolder_DiscardConfirmation: String { return self._s[3550]! }
+ public var PhotoEditor_SaturationTool: String { return self._s[3551]! }
public func PUSH_MESSAGE_STICKER(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3527]!, self._r[3527]!, [_1, _2])
+ return formatWithArgumentRanges(self._s[3552]!, self._r[3552]!, [_1, _2])
}
- public var PrivacyPhoneNumberSettings_CustomHelp: String { return self._s[3528]! }
- public var Notification_Exceptions_NewException_NotificationHeader: String { return self._s[3529]! }
- public var Group_OwnershipTransfer_ErrorLocatedGroupsTooMuch: String { return self._s[3530]! }
+ public var PrivacyPhoneNumberSettings_CustomHelp: String { return self._s[3553]! }
+ public var Notification_Exceptions_NewException_NotificationHeader: String { return self._s[3554]! }
+ public var Group_OwnershipTransfer_ErrorLocatedGroupsTooMuch: String { return self._s[3555]! }
public func LOCAL_MESSAGE_FWDS(_ _1: String, _ _2: Int) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3531]!, self._r[3531]!, [_1, "\(_2)"])
+ return formatWithArgumentRanges(self._s[3556]!, self._r[3556]!, [_1, "\(_2)"])
}
- public var Appearance_ThemePreview_ChatList_2_Text: String { return self._s[3532]! }
- public var Channel_Username_InvalidTooShort: String { return self._s[3534]! }
- public var SettingsSearch_Synonyms_Wallet: String { return self._s[3535]! }
+ public var Appearance_ThemePreview_ChatList_2_Text: String { return self._s[3557]! }
+ public var Channel_Username_InvalidTooShort: String { return self._s[3559]! }
+ public var SettingsSearch_Synonyms_Wallet: String { return self._s[3560]! }
public func Group_OwnershipTransfer_DescriptionInfo(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3536]!, self._r[3536]!, [_1, _2])
+ return formatWithArgumentRanges(self._s[3561]!, self._r[3561]!, [_1, _2])
}
- public var Forward_ErrorPublicPollDisabledInChannels: String { return self._s[3537]! }
+ public var Forward_ErrorPublicPollDisabledInChannels: String { return self._s[3562]! }
public func PUSH_CHAT_MESSAGE_GAME(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3538]!, self._r[3538]!, [_1, _2, _3])
+ return formatWithArgumentRanges(self._s[3563]!, self._r[3563]!, [_1, _2, _3])
}
- public var WallpaperPreview_PatternTitle: String { return self._s[3539]! }
- public var GroupInfo_PublicLinkAdd: String { return self._s[3540]! }
- public var Passport_PassportInformation: String { return self._s[3543]! }
- public var Theme_Unsupported: String { return self._s[3544]! }
- public var WatchRemote_AlertTitle: String { return self._s[3545]! }
- public var Privacy_GroupsAndChannels_NeverAllow: String { return self._s[3546]! }
- public var ConvertToSupergroup_HelpText: String { return self._s[3548]! }
+ public var WallpaperPreview_PatternTitle: String { return self._s[3564]! }
+ public var GroupInfo_PublicLinkAdd: String { return self._s[3565]! }
+ public var Passport_PassportInformation: String { return self._s[3568]! }
+ public var Theme_Unsupported: String { return self._s[3569]! }
+ public var WatchRemote_AlertTitle: String { return self._s[3570]! }
+ public var Privacy_GroupsAndChannels_NeverAllow: String { return self._s[3571]! }
+ public var ConvertToSupergroup_HelpText: String { return self._s[3573]! }
public func Time_MonthOfYear_m7(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3549]!, self._r[3549]!, [_0])
+ return formatWithArgumentRanges(self._s[3574]!, self._r[3574]!, [_0])
}
public func PUSH_PHONE_CALL_REQUEST(_ _1: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3550]!, self._r[3550]!, [_1])
+ return formatWithArgumentRanges(self._s[3575]!, self._r[3575]!, [_1])
}
- public var Privacy_GroupsAndChannels_CustomHelp: String { return self._s[3551]! }
- public var Wallet_Navigation_Done: String { return self._s[3553]! }
- public var TwoStepAuth_RecoveryCodeInvalid: String { return self._s[3554]! }
- public var AccessDenied_CameraDisabled: String { return self._s[3555]! }
+ public var Privacy_GroupsAndChannels_CustomHelp: String { return self._s[3576]! }
+ public var Wallet_Navigation_Done: String { return self._s[3578]! }
+ public var TwoStepAuth_RecoveryCodeInvalid: String { return self._s[3579]! }
+ public var AccessDenied_CameraDisabled: String { return self._s[3580]! }
public func Channel_Username_UsernameIsAvailable(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3556]!, self._r[3556]!, [_0])
+ return formatWithArgumentRanges(self._s[3581]!, self._r[3581]!, [_0])
}
- public var ClearCache_Forever: String { return self._s[3557]! }
- public var AuthSessions_AddDeviceIntro_Title: String { return self._s[3558]! }
- public var CreatePoll_Quiz: String { return self._s[3559]! }
- public var PhotoEditor_ContrastTool: String { return self._s[3562]! }
+ public var ClearCache_Forever: String { return self._s[3582]! }
+ public var AuthSessions_AddDeviceIntro_Title: String { return self._s[3583]! }
+ public var CreatePoll_Quiz: String { return self._s[3584]! }
+ public var PhotoEditor_ContrastTool: String { return self._s[3587]! }
public func PUSH_PINNED_DOC(_ _1: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3563]!, self._r[3563]!, [_1])
+ return formatWithArgumentRanges(self._s[3588]!, self._r[3588]!, [_1])
}
- public var DialogList_Draft: String { return self._s[3564]! }
- public var Wallet_Configuration_BlockchainIdInfo: String { return self._s[3565]! }
+ public var DialogList_Draft: String { return self._s[3589]! }
+ public var Wallet_Configuration_BlockchainIdInfo: String { return self._s[3590]! }
public func PeopleNearby_VisibleUntil(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3566]!, self._r[3566]!, [_0])
+ return formatWithArgumentRanges(self._s[3591]!, self._r[3591]!, [_0])
}
- public var ChatList_PsaAlert_covid: String { return self._s[3567]! }
- public var Privacy_TopPeersDelete: String { return self._s[3569]! }
- public var LoginPassword_PasswordPlaceholder: String { return self._s[3570]! }
- public var Passport_Identity_TypeIdentityCardUploadScan: String { return self._s[3571]! }
- public var WebSearch_RecentSectionClear: String { return self._s[3572]! }
- public var EditTheme_ErrorInvalidCharacters: String { return self._s[3573]! }
- public var Watch_ChatList_NoConversationsTitle: String { return self._s[3575]! }
- public var PeerInfo_ButtonMore: String { return self._s[3577]! }
- public var Common_Done: String { return self._s[3578]! }
- public var Shortcut_SwitchAccount: String { return self._s[3579]! }
- public var AuthSessions_EmptyText: String { return self._s[3580]! }
- public var Wallet_Configuration_BlockchainNameChangedTitle: String { return self._s[3581]! }
- public var Conversation_ShareBotContactConfirmation: String { return self._s[3582]! }
- public var Tour_Title5: String { return self._s[3584]! }
- public var Wallet_Settings_Title: String { return self._s[3585]! }
+ public var ChatList_PsaAlert_covid: String { return self._s[3592]! }
+ public var Privacy_TopPeersDelete: String { return self._s[3594]! }
+ public var LoginPassword_PasswordPlaceholder: String { return self._s[3595]! }
+ public var Passport_Identity_TypeIdentityCardUploadScan: String { return self._s[3596]! }
+ public var WebSearch_RecentSectionClear: String { return self._s[3597]! }
+ public var EditTheme_ErrorInvalidCharacters: String { return self._s[3598]! }
+ public var Watch_ChatList_NoConversationsTitle: String { return self._s[3600]! }
+ public var PeerInfo_ButtonMore: String { return self._s[3602]! }
+ public var Common_Done: String { return self._s[3603]! }
+ public var Shortcut_SwitchAccount: String { return self._s[3604]! }
+ public var AuthSessions_EmptyText: String { return self._s[3605]! }
+ public var Wallet_Configuration_BlockchainNameChangedTitle: String { return self._s[3606]! }
+ public var Conversation_ShareBotContactConfirmation: String { return self._s[3607]! }
+ public var Tour_Title5: String { return self._s[3609]! }
+ public var Wallet_Settings_Title: String { return self._s[3610]! }
public func Map_DirectionsDriveEta(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3586]!, self._r[3586]!, [_0])
+ return formatWithArgumentRanges(self._s[3611]!, self._r[3611]!, [_0])
}
- public var ApplyLanguage_UnsufficientDataTitle: String { return self._s[3587]! }
- public var Conversation_LinkDialogSave: String { return self._s[3588]! }
- public var GroupInfo_ActionRestrict: String { return self._s[3589]! }
- public var Checkout_Title: String { return self._s[3591]! }
- public var Channel_DiscussionGroup_HeaderLabel: String { return self._s[3593]! }
- public var Channel_AdminLog_CanChangeInfo: String { return self._s[3595]! }
- public var Notification_RenamedGroup: String { return self._s[3596]! }
- public var PeopleNearby_Groups: String { return self._s[3597]! }
- public var Checkout_PayWithFaceId: String { return self._s[3598]! }
- public var Channel_BanList_BlockedTitle: String { return self._s[3599]! }
- public var SettingsSearch_Synonyms_Notifications_InAppNotificationsSound: String { return self._s[3601]! }
- public var Checkout_WebConfirmation_Title: String { return self._s[3602]! }
- public var Notifications_MessageNotificationsAlert: String { return self._s[3603]! }
+ public var ApplyLanguage_UnsufficientDataTitle: String { return self._s[3612]! }
+ public var Conversation_LinkDialogSave: String { return self._s[3613]! }
+ public var GroupInfo_ActionRestrict: String { return self._s[3614]! }
+ public var Checkout_Title: String { return self._s[3616]! }
+ public var Channel_DiscussionGroup_HeaderLabel: String { return self._s[3618]! }
+ public var Channel_AdminLog_CanChangeInfo: String { return self._s[3620]! }
+ public var Notification_RenamedGroup: String { return self._s[3621]! }
+ public var PeopleNearby_Groups: String { return self._s[3622]! }
+ public var Checkout_PayWithFaceId: String { return self._s[3623]! }
+ public var Channel_BanList_BlockedTitle: String { return self._s[3624]! }
+ public var SettingsSearch_Synonyms_Notifications_InAppNotificationsSound: String { return self._s[3626]! }
+ public var Checkout_WebConfirmation_Title: String { return self._s[3627]! }
+ public var Notifications_MessageNotificationsAlert: String { return self._s[3628]! }
public func Activity_RemindAboutGroup(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3604]!, self._r[3604]!, [_0])
+ return formatWithArgumentRanges(self._s[3629]!, self._r[3629]!, [_0])
}
- public var Stats_GroupGrowthTitle: String { return self._s[3605]! }
- public var Profile_AddToExisting: String { return self._s[3607]! }
+ public var Stats_GroupGrowthTitle: String { return self._s[3630]! }
+ public var Profile_AddToExisting: String { return self._s[3632]! }
public func Profile_CreateEncryptedChatOutdatedError(_ _0: String, _ _1: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3608]!, self._r[3608]!, [_0, _1])
+ return formatWithArgumentRanges(self._s[3633]!, self._r[3633]!, [_0, _1])
}
- public var Cache_Files: String { return self._s[3610]! }
- public var Permissions_PrivacyPolicy: String { return self._s[3612]! }
- public var SocksProxySetup_ConnectAndSave: String { return self._s[3613]! }
- public var UserInfo_NotificationsDefaultDisabled: String { return self._s[3614]! }
- public var AutoDownloadSettings_TypeContacts: String { return self._s[3616]! }
- public var Appearance_ThemePreview_ChatList_1_Text: String { return self._s[3618]! }
- public var Calls_NoCallsPlaceholder: String { return self._s[3619]! }
+ public var Cache_Files: String { return self._s[3635]! }
+ public var Permissions_PrivacyPolicy: String { return self._s[3637]! }
+ public var SocksProxySetup_ConnectAndSave: String { return self._s[3638]! }
+ public var UserInfo_NotificationsDefaultDisabled: String { return self._s[3639]! }
+ public var AutoDownloadSettings_TypeContacts: String { return self._s[3641]! }
+ public var Appearance_ThemePreview_ChatList_1_Text: String { return self._s[3643]! }
+ public var Calls_NoCallsPlaceholder: String { return self._s[3644]! }
public func Wallet_Receive_ShareInvoiceUrlInfo(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3620]!, self._r[3620]!, [_0])
+ return formatWithArgumentRanges(self._s[3645]!, self._r[3645]!, [_0])
}
- public var Channel_Username_RevokeExistingUsernamesInfo: String { return self._s[3621]! }
- public var VoiceOver_AttachMedia: String { return self._s[3624]! }
- public var Notifications_ExceptionsGroupPlaceholder: String { return self._s[3625]! }
+ public var Channel_Username_RevokeExistingUsernamesInfo: String { return self._s[3646]! }
+ public var VoiceOver_AttachMedia: String { return self._s[3649]! }
+ public var Notifications_ExceptionsGroupPlaceholder: String { return self._s[3650]! }
public func PUSH_CHAT_MESSAGE_INVOICE(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3626]!, self._r[3626]!, [_1, _2, _3])
+ return formatWithArgumentRanges(self._s[3651]!, self._r[3651]!, [_1, _2, _3])
}
- public var SettingsSearch_Synonyms_Notifications_GroupNotificationsSound: String { return self._s[3627]! }
- public var Conversation_SetReminder_Title: String { return self._s[3628]! }
- public var Passport_FieldAddressHelp: String { return self._s[3629]! }
- public var Privacy_GroupsAndChannels_InviteToChannelMultipleError: String { return self._s[3630]! }
- public var PUSH_REMINDER_TITLE: String { return self._s[3631]! }
+ public var SettingsSearch_Synonyms_Notifications_GroupNotificationsSound: String { return self._s[3652]! }
+ public var Conversation_SetReminder_Title: String { return self._s[3653]! }
+ public var Passport_FieldAddressHelp: String { return self._s[3654]! }
+ public var Privacy_GroupsAndChannels_InviteToChannelMultipleError: String { return self._s[3655]! }
+ public var PUSH_REMINDER_TITLE: String { return self._s[3656]! }
public func Login_TermsOfService_ProceedBot(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3632]!, self._r[3632]!, [_0])
+ return formatWithArgumentRanges(self._s[3657]!, self._r[3657]!, [_0])
}
- public var Channel_AdminLog_EmptyTitle: String { return self._s[3633]! }
- public var Privacy_Calls_NeverAllow_Title: String { return self._s[3634]! }
- public var Login_UnknownError: String { return self._s[3635]! }
- public var Group_UpgradeNoticeText2: String { return self._s[3638]! }
- public var Watch_Compose_AddContact: String { return self._s[3639]! }
- public var ClearCache_StorageServiceFiles: String { return self._s[3640]! }
- public var Web_Error: String { return self._s[3641]! }
- public var Paint_Neon: String { return self._s[3642]! }
- public var Gif_Search: String { return self._s[3643]! }
- public var Profile_MessageLifetime1h: String { return self._s[3644]! }
- public var CheckoutInfo_ReceiverInfoEmailPlaceholder: String { return self._s[3645]! }
- public var Channel_Username_CheckingUsername: String { return self._s[3646]! }
- public var CallFeedback_ReasonSilentRemote: String { return self._s[3647]! }
- public var AutoDownloadSettings_TypeChannels: String { return self._s[3648]! }
- public var Channel_AboutItem: String { return self._s[3649]! }
- public var Privacy_GroupsAndChannels_AlwaysAllow_Placeholder: String { return self._s[3652]! }
- public var VoiceOver_Chat_VoiceMessage: String { return self._s[3653]! }
- public var GroupInfo_SharedMedia: String { return self._s[3654]! }
+ public var Channel_AdminLog_EmptyTitle: String { return self._s[3658]! }
+ public var Privacy_Calls_NeverAllow_Title: String { return self._s[3659]! }
+ public var Login_UnknownError: String { return self._s[3660]! }
+ public var Group_UpgradeNoticeText2: String { return self._s[3663]! }
+ public var Watch_Compose_AddContact: String { return self._s[3664]! }
+ public var ClearCache_StorageServiceFiles: String { return self._s[3665]! }
+ public var Web_Error: String { return self._s[3666]! }
+ public var Paint_Neon: String { return self._s[3667]! }
+ public var Gif_Search: String { return self._s[3668]! }
+ public var Profile_MessageLifetime1h: String { return self._s[3669]! }
+ public var CheckoutInfo_ReceiverInfoEmailPlaceholder: String { return self._s[3670]! }
+ public var Channel_Username_CheckingUsername: String { return self._s[3671]! }
+ public var CallFeedback_ReasonSilentRemote: String { return self._s[3672]! }
+ public var AutoDownloadSettings_TypeChannels: String { return self._s[3673]! }
+ public var Channel_AboutItem: String { return self._s[3674]! }
+ public var Privacy_GroupsAndChannels_AlwaysAllow_Placeholder: String { return self._s[3677]! }
+ public var VoiceOver_Chat_VoiceMessage: String { return self._s[3678]! }
+ public var GroupInfo_SharedMedia: String { return self._s[3679]! }
public func Channel_AdminLog_MessagePromotedName(_ _1: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3655]!, self._r[3655]!, [_1])
+ return formatWithArgumentRanges(self._s[3680]!, self._r[3680]!, [_1])
}
- public var Call_PhoneCallInProgressMessage: String { return self._s[3656]! }
+ public var Call_PhoneCallInProgressMessage: String { return self._s[3681]! }
public func PUSH_CHANNEL_ALBUM(_ _1: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3657]!, self._r[3657]!, [_1])
+ return formatWithArgumentRanges(self._s[3682]!, self._r[3682]!, [_1])
}
- public var ChatList_UndoArchiveRevealedText: String { return self._s[3658]! }
- public var GroupInfo_InviteLink_RevokeAlert_Text: String { return self._s[3659]! }
- public var Conversation_SearchByName_Placeholder: String { return self._s[3660]! }
- public var CreatePoll_AddOption: String { return self._s[3661]! }
- public var GroupInfo_Permissions_SearchPlaceholder: String { return self._s[3662]! }
- public var Group_UpgradeNoticeHeader: String { return self._s[3663]! }
- public var Channel_Management_AddModerator: String { return self._s[3664]! }
- public var AutoDownloadSettings_MaxFileSize: String { return self._s[3665]! }
- public var StickerPacksSettings_ShowStickersButton: String { return self._s[3666]! }
- public var Wallet_Info_RefreshErrorNetworkText: String { return self._s[3667]! }
- public var Theme_Colors_Background: String { return self._s[3668]! }
- public var NotificationsSound_Hello: String { return self._s[3671]! }
- public var SocksProxySetup_SavedProxies: String { return self._s[3673]! }
- public var Channel_Stickers_Placeholder: String { return self._s[3675]! }
+ public var ChatList_UndoArchiveRevealedText: String { return self._s[3683]! }
+ public var GroupInfo_InviteLink_RevokeAlert_Text: String { return self._s[3684]! }
+ public var Conversation_SearchByName_Placeholder: String { return self._s[3685]! }
+ public var CreatePoll_AddOption: String { return self._s[3686]! }
+ public var GroupInfo_Permissions_SearchPlaceholder: String { return self._s[3687]! }
+ public var Group_UpgradeNoticeHeader: String { return self._s[3688]! }
+ public var Channel_Management_AddModerator: String { return self._s[3689]! }
+ public var AutoDownloadSettings_MaxFileSize: String { return self._s[3690]! }
+ public var StickerPacksSettings_ShowStickersButton: String { return self._s[3691]! }
+ public var Wallet_Info_RefreshErrorNetworkText: String { return self._s[3692]! }
+ public var Theme_Colors_Background: String { return self._s[3693]! }
+ public var NotificationsSound_Hello: String { return self._s[3696]! }
+ public var SocksProxySetup_SavedProxies: String { return self._s[3698]! }
+ public var Channel_Stickers_Placeholder: String { return self._s[3700]! }
public func Login_EmailCodeBody(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3676]!, self._r[3676]!, [_0])
+ return formatWithArgumentRanges(self._s[3701]!, self._r[3701]!, [_0])
}
- public var PrivacyPolicy_DeclineDeclineAndDelete: String { return self._s[3677]! }
- public var Channel_Management_AddModeratorHelp: String { return self._s[3678]! }
- public var ContactInfo_BirthdayLabel: String { return self._s[3679]! }
- public var ChangePhoneNumberCode_RequestingACall: String { return self._s[3680]! }
- public var AutoDownloadSettings_Channels: String { return self._s[3681]! }
- public var Passport_Language_mn: String { return self._s[3682]! }
- public var Settings_ChatFolders: String { return self._s[3683]! }
+ public var PrivacyPolicy_DeclineDeclineAndDelete: String { return self._s[3702]! }
+ public var Channel_Management_AddModeratorHelp: String { return self._s[3703]! }
+ public var ContactInfo_BirthdayLabel: String { return self._s[3704]! }
+ public var ChangePhoneNumberCode_RequestingACall: String { return self._s[3705]! }
+ public var AutoDownloadSettings_Channels: String { return self._s[3706]! }
+ public var Passport_Language_mn: String { return self._s[3707]! }
+ public var Settings_ChatFolders: String { return self._s[3708]! }
public func ChatList_AddedToFolderTooltip(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3684]!, self._r[3684]!, [_1, _2])
+ return formatWithArgumentRanges(self._s[3709]!, self._r[3709]!, [_1, _2])
}
- public var Notifications_ResetAllNotificationsHelp: String { return self._s[3687]! }
- public var GroupInfo_Permissions_SlowmodeValue_Off: String { return self._s[3688]! }
- public var Passport_Language_ja: String { return self._s[3690]! }
- public var Settings_About_Title: String { return self._s[3691]! }
- public var Settings_NotificationsAndSounds: String { return self._s[3692]! }
- public var ChannelInfo_DeleteGroup: String { return self._s[3693]! }
- public var Settings_BlockedUsers: String { return self._s[3694]! }
+ public var Notifications_ResetAllNotificationsHelp: String { return self._s[3712]! }
+ public var GroupInfo_Permissions_SlowmodeValue_Off: String { return self._s[3713]! }
+ public var Passport_Language_ja: String { return self._s[3715]! }
+ public var Settings_About_Title: String { return self._s[3716]! }
+ public var Settings_NotificationsAndSounds: String { return self._s[3717]! }
+ public var ChannelInfo_DeleteGroup: String { return self._s[3718]! }
+ public var Settings_BlockedUsers: String { return self._s[3719]! }
public func Time_MonthOfYear_m4(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3695]!, self._r[3695]!, [_0])
+ return formatWithArgumentRanges(self._s[3720]!, self._r[3720]!, [_0])
}
- public var EditTheme_Create_Preview_OutgoingText: String { return self._s[3696]! }
- public var Wallet_Weekday_Today: String { return self._s[3697]! }
- public var ChatListFolderSettings_AddRecommended: String { return self._s[3698]! }
- public var AutoDownloadSettings_PreloadVideo: String { return self._s[3699]! }
- public var Widget_ApplicationLocked: String { return self._s[3700]! }
- public var Passport_Address_AddResidentialAddress: String { return self._s[3701]! }
- public var Channel_Username_Title: String { return self._s[3702]! }
+ public var EditTheme_Create_Preview_OutgoingText: String { return self._s[3721]! }
+ public var Wallet_Weekday_Today: String { return self._s[3722]! }
+ public var ChatListFolderSettings_AddRecommended: String { return self._s[3723]! }
+ public var AutoDownloadSettings_PreloadVideo: String { return self._s[3724]! }
+ public var Widget_ApplicationLocked: String { return self._s[3725]! }
+ public var Passport_Address_AddResidentialAddress: String { return self._s[3726]! }
+ public var Channel_Username_Title: String { return self._s[3727]! }
public func Notification_RemovedGroupPhoto(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3703]!, self._r[3703]!, [_0])
+ return formatWithArgumentRanges(self._s[3728]!, self._r[3728]!, [_0])
}
- public var AttachmentMenu_File: String { return self._s[3705]! }
- public var AppleWatch_Title: String { return self._s[3706]! }
- public var Activity_RecordingVideoMessage: String { return self._s[3707]! }
+ public var AttachmentMenu_File: String { return self._s[3730]! }
+ public var AppleWatch_Title: String { return self._s[3731]! }
+ public var Activity_RecordingVideoMessage: String { return self._s[3732]! }
public func Channel_DiscussionGroup_PublicChannelLink(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3708]!, self._r[3708]!, [_1, _2])
+ return formatWithArgumentRanges(self._s[3733]!, self._r[3733]!, [_1, _2])
}
- public var Theme_Colors_Messages: String { return self._s[3709]! }
- public var Weekday_Saturday: String { return self._s[3710]! }
- public var WallpaperPreview_SwipeColorsTopText: String { return self._s[3711]! }
- public var Conversation_Timer_Send: String { return self._s[3712]! }
- public var Settings_CancelUpload: String { return self._s[3713]! }
- public var Profile_CreateEncryptedChatError: String { return self._s[3714]! }
- public var Common_Next: String { return self._s[3716]! }
- public var Channel_Stickers_YourStickers: String { return self._s[3718]! }
- public var Message_Theme: String { return self._s[3719]! }
- public var Call_AudioRouteHeadphones: String { return self._s[3720]! }
- public var TwoStepAuth_EnterPasswordForgot: String { return self._s[3722]! }
- public var Watch_Contacts_NoResults: String { return self._s[3724]! }
- public var PhotoEditor_TintTool: String { return self._s[3727]! }
- public var LoginPassword_ResetAccount: String { return self._s[3729]! }
- public var Settings_SavedMessages: String { return self._s[3730]! }
- public var SettingsSearch_Synonyms_Appearance_Animations: String { return self._s[3731]! }
- public var Bot_GenericSupportStatus: String { return self._s[3732]! }
- public var StickerPack_Add: String { return self._s[3733]! }
- public var Checkout_TotalAmount: String { return self._s[3734]! }
- public var Your_cards_number_is_invalid: String { return self._s[3735]! }
- public var SettingsSearch_Synonyms_Appearance_AutoNightTheme: String { return self._s[3736]! }
- public var VoiceOver_Chat_VideoMessage: String { return self._s[3737]! }
+ public var Theme_Colors_Messages: String { return self._s[3734]! }
+ public var Weekday_Saturday: String { return self._s[3735]! }
+ public var WallpaperPreview_SwipeColorsTopText: String { return self._s[3736]! }
+ public var Conversation_Timer_Send: String { return self._s[3737]! }
+ public var Settings_CancelUpload: String { return self._s[3738]! }
+ public var Profile_CreateEncryptedChatError: String { return self._s[3739]! }
+ public var Common_Next: String { return self._s[3741]! }
+ public var Channel_Stickers_YourStickers: String { return self._s[3743]! }
+ public var Message_Theme: String { return self._s[3744]! }
+ public var Call_AudioRouteHeadphones: String { return self._s[3745]! }
+ public var TwoStepAuth_EnterPasswordForgot: String { return self._s[3747]! }
+ public var Watch_Contacts_NoResults: String { return self._s[3749]! }
+ public var PhotoEditor_TintTool: String { return self._s[3752]! }
+ public var LoginPassword_ResetAccount: String { return self._s[3754]! }
+ public var Settings_SavedMessages: String { return self._s[3755]! }
+ public var SettingsSearch_Synonyms_Appearance_Animations: String { return self._s[3756]! }
+ public var Bot_GenericSupportStatus: String { return self._s[3757]! }
+ public var StickerPack_Add: String { return self._s[3758]! }
+ public var Checkout_TotalAmount: String { return self._s[3759]! }
+ public var Your_cards_number_is_invalid: String { return self._s[3760]! }
+ public var SettingsSearch_Synonyms_Appearance_AutoNightTheme: String { return self._s[3761]! }
+ public var VoiceOver_Chat_VideoMessage: String { return self._s[3762]! }
public func ChangePhoneNumberCode_CallTimer(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3738]!, self._r[3738]!, [_0])
+ return formatWithArgumentRanges(self._s[3763]!, self._r[3763]!, [_0])
}
public func GroupPermission_AddedInfo(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3739]!, self._r[3739]!, [_1, _2])
+ return formatWithArgumentRanges(self._s[3764]!, self._r[3764]!, [_1, _2])
}
- public var ChatSettings_ConnectionType_UseSocks5: String { return self._s[3740]! }
+ public var ChatSettings_ConnectionType_UseSocks5: String { return self._s[3765]! }
public func PUSH_CHAT_PHOTO_EDITED(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3742]!, self._r[3742]!, [_1, _2])
+ return formatWithArgumentRanges(self._s[3767]!, self._r[3767]!, [_1, _2])
}
public func Conversation_RestrictedTextTimed(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3743]!, self._r[3743]!, [_0])
+ return formatWithArgumentRanges(self._s[3768]!, self._r[3768]!, [_0])
}
- public var GroupInfo_InviteLink_ShareLink: String { return self._s[3744]! }
- public var StickerPack_Share: String { return self._s[3745]! }
- public var Passport_DeleteAddress: String { return self._s[3746]! }
- public var Settings_Passport: String { return self._s[3747]! }
- public var SharedMedia_EmptyFilesText: String { return self._s[3748]! }
- public var Conversation_DeleteMessagesForMe: String { return self._s[3749]! }
- public var PasscodeSettings_AutoLock_IfAwayFor_1hour: String { return self._s[3750]! }
- public var Contacts_PermissionsText: String { return self._s[3751]! }
- public var Group_Setup_HistoryVisible: String { return self._s[3752]! }
- public var Wallet_Month_ShortDecember: String { return self._s[3754]! }
- public var PrivacySettings_AutoArchiveTitle: String { return self._s[3756]! }
- public var Channel_EditAdmin_PermissionEnabledByDefault: String { return self._s[3757]! }
- public var Passport_Address_AddRentalAgreement: String { return self._s[3758]! }
- public var SocksProxySetup_Title: String { return self._s[3759]! }
- public var Notification_Mute1h: String { return self._s[3760]! }
+ public var GroupInfo_InviteLink_ShareLink: String { return self._s[3769]! }
+ public var StickerPack_Share: String { return self._s[3770]! }
+ public var Passport_DeleteAddress: String { return self._s[3771]! }
+ public var Settings_Passport: String { return self._s[3772]! }
+ public var SharedMedia_EmptyFilesText: String { return self._s[3773]! }
+ public var Conversation_DeleteMessagesForMe: String { return self._s[3774]! }
+ public var PasscodeSettings_AutoLock_IfAwayFor_1hour: String { return self._s[3775]! }
+ public var Contacts_PermissionsText: String { return self._s[3776]! }
+ public var Group_Setup_HistoryVisible: String { return self._s[3777]! }
+ public var Wallet_Month_ShortDecember: String { return self._s[3779]! }
+ public var PrivacySettings_AutoArchiveTitle: String { return self._s[3781]! }
+ public var Channel_EditAdmin_PermissionEnabledByDefault: String { return self._s[3782]! }
+ public var Passport_Address_AddRentalAgreement: String { return self._s[3783]! }
+ public var SocksProxySetup_Title: String { return self._s[3784]! }
+ public var Notification_Mute1h: String { return self._s[3785]! }
public func Passport_Email_CodeHelp(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3761]!, self._r[3761]!, [_0])
+ return formatWithArgumentRanges(self._s[3786]!, self._r[3786]!, [_0])
}
- public var NotificationSettings_ShowNotificationsAllAccountsInfoOff: String { return self._s[3762]! }
+ public var NotificationSettings_ShowNotificationsAllAccountsInfoOff: String { return self._s[3787]! }
public func PUSH_PINNED_GEOLIVE(_ _1: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3763]!, self._r[3763]!, [_1])
+ return formatWithArgumentRanges(self._s[3788]!, self._r[3788]!, [_1])
}
- public var FastTwoStepSetup_PasswordSection: String { return self._s[3764]! }
- public var NetworkUsageSettings_ResetStatsConfirmation: String { return self._s[3767]! }
- public var InfoPlist_NSFaceIDUsageDescription: String { return self._s[3769]! }
- public var DialogList_NoMessagesText: String { return self._s[3770]! }
- public var Privacy_ContactsResetConfirmation: String { return self._s[3771]! }
- public var Privacy_Calls_P2PHelp: String { return self._s[3772]! }
- public var Channel_DiscussionGroup_SearchPlaceholder: String { return self._s[3774]! }
- public var Your_cards_expiration_year_is_invalid: String { return self._s[3775]! }
- public var Common_TakePhotoOrVideo: String { return self._s[3776]! }
- public var Wallet_Words_Text: String { return self._s[3777]! }
- public var Call_StatusBusy: String { return self._s[3778]! }
- public var Conversation_PinnedMessage: String { return self._s[3779]! }
- public var AutoDownloadSettings_VoiceMessagesTitle: String { return self._s[3780]! }
- public var ChatList_EmptyChatListNewMessage: String { return self._s[3781]! }
- public var Wallet_Configuration_BlockchainNameChangedProceed: String { return self._s[3782]! }
- public var TwoStepAuth_SetupPasswordConfirmFailed: String { return self._s[3783]! }
- public var Undo_ChatCleared: String { return self._s[3784]! }
- public var CreatePoll_Explanation: String { return self._s[3785]! }
- public var AppleWatch_ReplyPresets: String { return self._s[3786]! }
- public var Passport_DiscardMessageDescription: String { return self._s[3788]! }
- public var Login_NetworkError: String { return self._s[3789]! }
+ public var FastTwoStepSetup_PasswordSection: String { return self._s[3789]! }
+ public var NetworkUsageSettings_ResetStatsConfirmation: String { return self._s[3792]! }
+ public var InfoPlist_NSFaceIDUsageDescription: String { return self._s[3794]! }
+ public var DialogList_NoMessagesText: String { return self._s[3795]! }
+ public var Privacy_ContactsResetConfirmation: String { return self._s[3796]! }
+ public var Privacy_Calls_P2PHelp: String { return self._s[3797]! }
+ public var Channel_DiscussionGroup_SearchPlaceholder: String { return self._s[3799]! }
+ public var Your_cards_expiration_year_is_invalid: String { return self._s[3800]! }
+ public var Common_TakePhotoOrVideo: String { return self._s[3801]! }
+ public var Wallet_Words_Text: String { return self._s[3802]! }
+ public var Call_StatusBusy: String { return self._s[3803]! }
+ public var Conversation_PinnedMessage: String { return self._s[3804]! }
+ public var AutoDownloadSettings_VoiceMessagesTitle: String { return self._s[3805]! }
+ public var ChatList_EmptyChatListNewMessage: String { return self._s[3806]! }
+ public var Wallet_Configuration_BlockchainNameChangedProceed: String { return self._s[3807]! }
+ public var TwoStepAuth_SetupPasswordConfirmFailed: String { return self._s[3808]! }
+ public var Undo_ChatCleared: String { return self._s[3809]! }
+ public var CreatePoll_Explanation: String { return self._s[3810]! }
+ public var AppleWatch_ReplyPresets: String { return self._s[3811]! }
+ public var Passport_DiscardMessageDescription: String { return self._s[3813]! }
+ public var Login_NetworkError: String { return self._s[3814]! }
public func Notification_PinnedRoundMessage(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3790]!, self._r[3790]!, [_0])
+ return formatWithArgumentRanges(self._s[3815]!, self._r[3815]!, [_0])
}
public func Channel_AdminLog_MessageRemovedChannelUsername(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3791]!, self._r[3791]!, [_0])
+ return formatWithArgumentRanges(self._s[3816]!, self._r[3816]!, [_0])
}
- public var SocksProxySetup_PasswordPlaceholder: String { return self._s[3792]! }
- public var Wallet_WordCheck_ViewWords: String { return self._s[3794]! }
- public var Login_ResetAccountProtected_LimitExceeded: String { return self._s[3795]! }
+ public var SocksProxySetup_PasswordPlaceholder: String { return self._s[3817]! }
+ public var Wallet_WordCheck_ViewWords: String { return self._s[3819]! }
+ public var Login_ResetAccountProtected_LimitExceeded: String { return self._s[3820]! }
public func Watch_LastSeen_YesterdayAt(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3797]!, self._r[3797]!, [_0])
+ return formatWithArgumentRanges(self._s[3822]!, self._r[3822]!, [_0])
}
- public var Call_ConnectionErrorMessage: String { return self._s[3798]! }
- public var VoiceOver_Chat_Music: String { return self._s[3799]! }
- public var ChatListFolder_CategoryContacts: String { return self._s[3800]! }
- public var SettingsSearch_Synonyms_Notifications_MessageNotificationsSound: String { return self._s[3801]! }
- public var Compose_GroupTokenListPlaceholder: String { return self._s[3803]! }
- public var ConversationMedia_Title: String { return self._s[3804]! }
- public var EncryptionKey_Title: String { return self._s[3806]! }
- public var TwoStepAuth_EnterPasswordTitle: String { return self._s[3807]! }
- public var Notification_Exceptions_AddException: String { return self._s[3808]! }
- public var PrivacySettings_BlockedPeersEmpty: String { return self._s[3809]! }
- public var Profile_MessageLifetime1m: String { return self._s[3810]! }
+ public var Call_ConnectionErrorMessage: String { return self._s[3823]! }
+ public var VoiceOver_Chat_Music: String { return self._s[3824]! }
+ public var ChatListFolder_CategoryContacts: String { return self._s[3825]! }
+ public var SettingsSearch_Synonyms_Notifications_MessageNotificationsSound: String { return self._s[3826]! }
+ public var Compose_GroupTokenListPlaceholder: String { return self._s[3828]! }
+ public var ConversationMedia_Title: String { return self._s[3829]! }
+ public var EncryptionKey_Title: String { return self._s[3831]! }
+ public var TwoStepAuth_EnterPasswordTitle: String { return self._s[3832]! }
+ public var Notification_Exceptions_AddException: String { return self._s[3833]! }
+ public var PrivacySettings_BlockedPeersEmpty: String { return self._s[3834]! }
+ public var Profile_MessageLifetime1m: String { return self._s[3835]! }
public func Channel_AdminLog_MessageUnkickedName(_ _1: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3811]!, self._r[3811]!, [_1])
+ return formatWithArgumentRanges(self._s[3836]!, self._r[3836]!, [_1])
}
- public var Month_GenMay: String { return self._s[3812]! }
+ public var Month_GenMay: String { return self._s[3837]! }
public func LiveLocationUpdated_TodayAt(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3813]!, self._r[3813]!, [_0])
+ return formatWithArgumentRanges(self._s[3838]!, self._r[3838]!, [_0])
}
- public var PeopleNearby_Users: String { return self._s[3814]! }
- public var Wallet_Send_AddressInfo: String { return self._s[3815]! }
- public var ChannelMembers_WhoCanAddMembersAllHelp: String { return self._s[3816]! }
- public var AutoDownloadSettings_ResetSettings: String { return self._s[3817]! }
+ public var PeopleNearby_Users: String { return self._s[3839]! }
+ public var Wallet_Send_AddressInfo: String { return self._s[3840]! }
+ public var ChannelMembers_WhoCanAddMembersAllHelp: String { return self._s[3841]! }
+ public var AutoDownloadSettings_ResetSettings: String { return self._s[3842]! }
public func Wallet_Updated_AtDate(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3819]!, self._r[3819]!, [_0])
+ return formatWithArgumentRanges(self._s[3844]!, self._r[3844]!, [_0])
}
- public var Stats_LoadingTitle: String { return self._s[3820]! }
- public var Conversation_EmptyPlaceholder: String { return self._s[3821]! }
- public var Passport_Address_AddPassportRegistration: String { return self._s[3822]! }
- public var Notifications_ChannelNotificationsAlert: String { return self._s[3823]! }
- public var ChatSettings_AutoDownloadUsingCellular: String { return self._s[3824]! }
- public var Camera_TapAndHoldForVideo: String { return self._s[3825]! }
- public var Channel_JoinChannel: String { return self._s[3828]! }
- public var Appearance_Animations: String { return self._s[3831]! }
+ public var Stats_LoadingTitle: String { return self._s[3845]! }
+ public var Conversation_EmptyPlaceholder: String { return self._s[3846]! }
+ public var Passport_Address_AddPassportRegistration: String { return self._s[3847]! }
+ public var Notifications_ChannelNotificationsAlert: String { return self._s[3848]! }
+ public var ChatSettings_AutoDownloadUsingCellular: String { return self._s[3849]! }
+ public var Camera_TapAndHoldForVideo: String { return self._s[3850]! }
+ public var Channel_JoinChannel: String { return self._s[3853]! }
+ public var Appearance_Animations: String { return self._s[3856]! }
public func Notification_MessageLifetimeChanged(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3832]!, self._r[3832]!, [_1, _2])
+ return formatWithArgumentRanges(self._s[3857]!, self._r[3857]!, [_1, _2])
}
- public var Stickers_GroupStickers: String { return self._s[3834]! }
- public var Appearance_ShareTheme: String { return self._s[3835]! }
- public var TwoFactorSetup_Hint_Placeholder: String { return self._s[3836]! }
- public var ConvertToSupergroup_HelpTitle: String { return self._s[3840]! }
- public var StickerPackActionInfo_RemovedTitle: String { return self._s[3841]! }
- public var Passport_Address_Street: String { return self._s[3842]! }
- public var Conversation_AddContact: String { return self._s[3843]! }
- public var Login_PhonePlaceholder: String { return self._s[3844]! }
- public var Channel_Members_InviteLink: String { return self._s[3846]! }
- public var Bot_Stop: String { return self._s[3847]! }
- public var SettingsSearch_Synonyms_Proxy_UseForCalls: String { return self._s[3849]! }
- public var Notification_PassportValueAddress: String { return self._s[3850]! }
- public var Month_ShortJuly: String { return self._s[3851]! }
- public var Passport_Address_TypeTemporaryRegistrationUploadScan: String { return self._s[3852]! }
- public var Channel_AdminLog_BanSendMedia: String { return self._s[3853]! }
- public var Passport_Identity_ReverseSide: String { return self._s[3854]! }
- public var Watch_Stickers_Recents: String { return self._s[3858]! }
- public var PrivacyLastSeenSettings_EmpryUsersPlaceholder: String { return self._s[3860]! }
- public var Map_SendThisLocation: String { return self._s[3861]! }
+ public var Stickers_GroupStickers: String { return self._s[3859]! }
+ public var Appearance_ShareTheme: String { return self._s[3860]! }
+ public var TwoFactorSetup_Hint_Placeholder: String { return self._s[3861]! }
+ public var ConvertToSupergroup_HelpTitle: String { return self._s[3865]! }
+ public var StickerPackActionInfo_RemovedTitle: String { return self._s[3866]! }
+ public var Passport_Address_Street: String { return self._s[3867]! }
+ public var Conversation_AddContact: String { return self._s[3868]! }
+ public var Login_PhonePlaceholder: String { return self._s[3869]! }
+ public var Channel_Members_InviteLink: String { return self._s[3871]! }
+ public var Bot_Stop: String { return self._s[3872]! }
+ public var SettingsSearch_Synonyms_Proxy_UseForCalls: String { return self._s[3874]! }
+ public var Notification_PassportValueAddress: String { return self._s[3875]! }
+ public var Month_ShortJuly: String { return self._s[3876]! }
+ public var Passport_Address_TypeTemporaryRegistrationUploadScan: String { return self._s[3877]! }
+ public var Channel_AdminLog_BanSendMedia: String { return self._s[3878]! }
+ public var Passport_Identity_ReverseSide: String { return self._s[3879]! }
+ public var Watch_Stickers_Recents: String { return self._s[3883]! }
+ public var PrivacyLastSeenSettings_EmpryUsersPlaceholder: String { return self._s[3885]! }
+ public var Map_SendThisLocation: String { return self._s[3886]! }
public func Time_MonthOfYear_m1(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3862]!, self._r[3862]!, [_0])
- }
- public func InviteText_SingleContact(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3863]!, self._r[3863]!, [_0])
- }
- public var ConvertToSupergroup_Note: String { return self._s[3864]! }
- public var Wallet_Intro_NotNow: String { return self._s[3865]! }
- public var Stats_GroupMembers: String { return self._s[3866]! }
- public func FileSize_MB(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3867]!, self._r[3867]!, [_0])
- }
- public var NetworkUsageSettings_GeneralDataSection: String { return self._s[3868]! }
- public func Compatibility_SecretMediaVersionTooLow(_ _0: String, _ _1: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3869]!, self._r[3869]!, [_0, _1])
- }
- public var Login_CallRequestState3: String { return self._s[3871]! }
- public var Wallpaper_SearchShort: String { return self._s[3872]! }
- public var SettingsSearch_Synonyms_Appearance_ColorTheme: String { return self._s[3874]! }
- public var PasscodeSettings_UnlockWithFaceId: String { return self._s[3875]! }
- public var Channel_BotDoesntSupportGroups: String { return self._s[3876]! }
- public func PUSH_CHAT_MESSAGE_GEOLIVE(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3877]!, self._r[3877]!, [_1, _2])
- }
- public var Channel_AdminLogFilter_Title: String { return self._s[3878]! }
- public var Appearance_ThemePreview_Chat_4_Text: String { return self._s[3880]! }
- public var Notifications_GroupNotificationsExceptions: String { return self._s[3883]! }
- public func FileSize_B(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3884]!, self._r[3884]!, [_0])
- }
- public var Passport_CorrectErrors: String { return self._s[3885]! }
- public var VoiceOver_Chat_YourAnonymousPoll: String { return self._s[3886]! }
- public func Channel_MessageTitleUpdated(_ _0: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[3887]!, self._r[3887]!, [_0])
}
- public var Map_SendMyCurrentLocation: String { return self._s[3888]! }
- public var Channel_DiscussionGroup: String { return self._s[3890]! }
- public var TwoFactorSetup_Email_SkipConfirmationSkip: String { return self._s[3891]! }
+ public func InviteText_SingleContact(_ _0: String) -> (String, [(Int, NSRange)]) {
+ return formatWithArgumentRanges(self._s[3888]!, self._r[3888]!, [_0])
+ }
+ public var ConvertToSupergroup_Note: String { return self._s[3889]! }
+ public var Wallet_Intro_NotNow: String { return self._s[3890]! }
+ public var Stats_GroupMembers: String { return self._s[3891]! }
+ public func FileSize_MB(_ _0: String) -> (String, [(Int, NSRange)]) {
+ return formatWithArgumentRanges(self._s[3892]!, self._r[3892]!, [_0])
+ }
+ public var NetworkUsageSettings_GeneralDataSection: String { return self._s[3893]! }
+ public func Compatibility_SecretMediaVersionTooLow(_ _0: String, _ _1: String) -> (String, [(Int, NSRange)]) {
+ return formatWithArgumentRanges(self._s[3894]!, self._r[3894]!, [_0, _1])
+ }
+ public var Login_CallRequestState3: String { return self._s[3896]! }
+ public var Wallpaper_SearchShort: String { return self._s[3897]! }
+ public var SettingsSearch_Synonyms_Appearance_ColorTheme: String { return self._s[3899]! }
+ public var PasscodeSettings_UnlockWithFaceId: String { return self._s[3900]! }
+ public var Channel_BotDoesntSupportGroups: String { return self._s[3901]! }
+ public func PUSH_CHAT_MESSAGE_GEOLIVE(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
+ return formatWithArgumentRanges(self._s[3902]!, self._r[3902]!, [_1, _2])
+ }
+ public var Channel_AdminLogFilter_Title: String { return self._s[3903]! }
+ public var Appearance_ThemePreview_Chat_4_Text: String { return self._s[3905]! }
+ public var Notifications_GroupNotificationsExceptions: String { return self._s[3908]! }
+ public func FileSize_B(_ _0: String) -> (String, [(Int, NSRange)]) {
+ return formatWithArgumentRanges(self._s[3909]!, self._r[3909]!, [_0])
+ }
+ public var Passport_CorrectErrors: String { return self._s[3910]! }
+ public var VoiceOver_Chat_YourAnonymousPoll: String { return self._s[3911]! }
+ public func Channel_MessageTitleUpdated(_ _0: String) -> (String, [(Int, NSRange)]) {
+ return formatWithArgumentRanges(self._s[3912]!, self._r[3912]!, [_0])
+ }
+ public var Map_SendMyCurrentLocation: String { return self._s[3913]! }
+ public var Channel_DiscussionGroup: String { return self._s[3915]! }
+ public var TwoFactorSetup_Email_SkipConfirmationSkip: String { return self._s[3916]! }
public func PUSH_PINNED_CONTACT(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3892]!, self._r[3892]!, [_1, _2])
+ return formatWithArgumentRanges(self._s[3917]!, self._r[3917]!, [_1, _2])
}
- public var SharedMedia_SearchNoResults: String { return self._s[3893]! }
- public var Permissions_NotificationsText_v0: String { return self._s[3894]! }
- public var Channel_EditAdmin_PermissionDeleteMessagesOfOthers: String { return self._s[3895]! }
- public var Appearance_AppIcon: String { return self._s[3896]! }
- public var Appearance_ThemePreview_ChatList_3_AuthorName: String { return self._s[3897]! }
- public var LoginPassword_FloodError: String { return self._s[3898]! }
- public var Wallet_Send_OwnAddressAlertProceed: String { return self._s[3900]! }
- public var Group_Setup_HistoryHiddenHelp: String { return self._s[3901]! }
+ public var SharedMedia_SearchNoResults: String { return self._s[3918]! }
+ public var Permissions_NotificationsText_v0: String { return self._s[3919]! }
+ public var Channel_EditAdmin_PermissionDeleteMessagesOfOthers: String { return self._s[3920]! }
+ public var Appearance_AppIcon: String { return self._s[3921]! }
+ public var Appearance_ThemePreview_ChatList_3_AuthorName: String { return self._s[3922]! }
+ public var LoginPassword_FloodError: String { return self._s[3923]! }
+ public var Wallet_Send_OwnAddressAlertProceed: String { return self._s[3925]! }
+ public var Group_Setup_HistoryHiddenHelp: String { return self._s[3926]! }
public func TwoStepAuth_PendingEmailHelp(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3902]!, self._r[3902]!, [_0])
+ return formatWithArgumentRanges(self._s[3927]!, self._r[3927]!, [_0])
}
- public var Passport_Language_bn: String { return self._s[3903]! }
+ public var Passport_Language_bn: String { return self._s[3928]! }
public func DialogList_SingleUploadingPhotoSuffix(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3904]!, self._r[3904]!, [_0])
+ return formatWithArgumentRanges(self._s[3929]!, self._r[3929]!, [_0])
}
- public var ChatList_Context_Pin: String { return self._s[3905]! }
+ public var ChatList_Context_Pin: String { return self._s[3930]! }
public func Notification_PinnedAudioMessage(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3906]!, self._r[3906]!, [_0])
+ return formatWithArgumentRanges(self._s[3931]!, self._r[3931]!, [_0])
}
public func Channel_AdminLog_MessageChangedGroupStickerPack(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3907]!, self._r[3907]!, [_0])
+ return formatWithArgumentRanges(self._s[3932]!, self._r[3932]!, [_0])
}
- public var Wallet_Navigation_Close: String { return self._s[3908]! }
- public var GroupInfo_InvitationLinkGroupFull: String { return self._s[3912]! }
- public var Group_EditAdmin_PermissionChangeInfo: String { return self._s[3914]! }
- public var Wallet_Month_GenDecember: String { return self._s[3915]! }
- public var Contacts_PermissionsAllow: String { return self._s[3916]! }
- public var ReportPeer_ReasonCopyright: String { return self._s[3917]! }
- public var Channel_EditAdmin_PermissinAddAdminOn: String { return self._s[3918]! }
- public var WallpaperPreview_Pattern: String { return self._s[3919]! }
- public var Paint_Duplicate: String { return self._s[3920]! }
- public var Passport_Address_Country: String { return self._s[3921]! }
- public var Notification_RenamedChannel: String { return self._s[3923]! }
- public var DialogList_UnknownPinLimitError: String { return self._s[3924]! }
- public var CheckoutInfo_ErrorPostcodeInvalid: String { return self._s[3925]! }
- public var ChatList_Context_Unmute: String { return self._s[3926]! }
- public var KeyCommand_SearchInChat: String { return self._s[3927]! }
- public var Group_MessagePhotoUpdated: String { return self._s[3928]! }
- public var Channel_BanUser_PermissionSendMedia: String { return self._s[3929]! }
- public var Conversation_ContextMenuBan: String { return self._s[3930]! }
- public var TwoStepAuth_EmailSent: String { return self._s[3931]! }
- public var Settings_SetProfilePhotoOrVideo: String { return self._s[3932]! }
- public var MessagePoll_NoVotes: String { return self._s[3933]! }
- public var Wallet_Send_ErrorNotEnoughFundsTitle: String { return self._s[3934]! }
- public var Passport_Language_is: String { return self._s[3936]! }
- public var PeopleNearby_UsersEmpty: String { return self._s[3938]! }
- public var Tour_Text5: String { return self._s[3939]! }
+ public var Wallet_Navigation_Close: String { return self._s[3933]! }
+ public var GroupInfo_InvitationLinkGroupFull: String { return self._s[3937]! }
+ public var Group_EditAdmin_PermissionChangeInfo: String { return self._s[3939]! }
+ public var Wallet_Month_GenDecember: String { return self._s[3940]! }
+ public var Contacts_PermissionsAllow: String { return self._s[3941]! }
+ public var ReportPeer_ReasonCopyright: String { return self._s[3942]! }
+ public var Channel_EditAdmin_PermissinAddAdminOn: String { return self._s[3943]! }
+ public var WallpaperPreview_Pattern: String { return self._s[3944]! }
+ public var Paint_Duplicate: String { return self._s[3945]! }
+ public var Passport_Address_Country: String { return self._s[3946]! }
+ public var Notification_RenamedChannel: String { return self._s[3948]! }
+ public var DialogList_UnknownPinLimitError: String { return self._s[3949]! }
+ public var CheckoutInfo_ErrorPostcodeInvalid: String { return self._s[3950]! }
+ public var ChatList_Context_Unmute: String { return self._s[3951]! }
+ public var KeyCommand_SearchInChat: String { return self._s[3952]! }
+ public var Group_MessagePhotoUpdated: String { return self._s[3953]! }
+ public var Channel_BanUser_PermissionSendMedia: String { return self._s[3954]! }
+ public var Conversation_ContextMenuBan: String { return self._s[3955]! }
+ public var TwoStepAuth_EmailSent: String { return self._s[3956]! }
+ public var Settings_SetProfilePhotoOrVideo: String { return self._s[3957]! }
+ public var MessagePoll_NoVotes: String { return self._s[3958]! }
+ public var Wallet_Send_ErrorNotEnoughFundsTitle: String { return self._s[3959]! }
+ public var Passport_Language_is: String { return self._s[3961]! }
+ public var PeopleNearby_UsersEmpty: String { return self._s[3963]! }
+ public var Tour_Text5: String { return self._s[3964]! }
public func Call_GroupFormat(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3942]!, self._r[3942]!, [_1, _2])
+ return formatWithArgumentRanges(self._s[3967]!, self._r[3967]!, [_1, _2])
}
- public var Undo_SecretChatDeleted: String { return self._s[3943]! }
- public var SocksProxySetup_ShareQRCode: String { return self._s[3944]! }
+ public var Undo_SecretChatDeleted: String { return self._s[3968]! }
+ public var SocksProxySetup_ShareQRCode: String { return self._s[3969]! }
public func VoiceOver_Chat_Size(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3945]!, self._r[3945]!, [_0])
+ return formatWithArgumentRanges(self._s[3970]!, self._r[3970]!, [_0])
}
- public var Forward_ErrorDisabledForChat: String { return self._s[3946]! }
- public var LogoutOptions_ChangePhoneNumberText: String { return self._s[3948]! }
- public var Paint_Edit: String { return self._s[3950]! }
- public var ScheduledMessages_ReminderNotification: String { return self._s[3952]! }
- public var Undo_DeletedGroup: String { return self._s[3954]! }
- public var LoginPassword_ForgotPassword: String { return self._s[3955]! }
- public var Wallet_WordImport_IncorrectTitle: String { return self._s[3956]! }
- public var GroupInfo_GroupNamePlaceholder: String { return self._s[3957]! }
+ public var Forward_ErrorDisabledForChat: String { return self._s[3971]! }
+ public var LogoutOptions_ChangePhoneNumberText: String { return self._s[3973]! }
+ public var Paint_Edit: String { return self._s[3975]! }
+ public var ScheduledMessages_ReminderNotification: String { return self._s[3977]! }
+ public var Undo_DeletedGroup: String { return self._s[3979]! }
+ public var LoginPassword_ForgotPassword: String { return self._s[3980]! }
+ public var Wallet_WordImport_IncorrectTitle: String { return self._s[3981]! }
+ public var GroupInfo_GroupNamePlaceholder: String { return self._s[3982]! }
public func Notification_Kicked(_ _0: String, _ _1: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3958]!, self._r[3958]!, [_0, _1])
+ return formatWithArgumentRanges(self._s[3983]!, self._r[3983]!, [_0, _1])
}
- public var AppWallet_TransactionInfo_FeeInfoURL: String { return self._s[3959]! }
- public var Conversation_InputTextCaptionPlaceholder: String { return self._s[3960]! }
- public var AutoDownloadSettings_VideoMessagesTitle: String { return self._s[3961]! }
- public var Conversation_PinMessageAlertGroup: String { return self._s[3962]! }
- public var Passport_Language_uz: String { return self._s[3963]! }
- public var SettingsSearch_Synonyms_Privacy_GroupsAndChannels: String { return self._s[3964]! }
- public var Channel_MessageVideoUpdated: String { return self._s[3966]! }
- public var Map_StopLiveLocation: String { return self._s[3967]! }
- public var VoiceOver_MessageContextSend: String { return self._s[3969]! }
- public var PasscodeSettings_Help: String { return self._s[3970]! }
- public var NotificationsSound_Input: String { return self._s[3971]! }
- public var ProfilePhoto_MainVideo: String { return self._s[3973]! }
- public var Share_Title: String { return self._s[3975]! }
- public var LogoutOptions_Title: String { return self._s[3976]! }
- public var Wallet_Send_AddressText: String { return self._s[3977]! }
- public var Login_TermsOfServiceAgree: String { return self._s[3978]! }
- public var Compose_NewEncryptedChatTitle: String { return self._s[3979]! }
- public var Channel_AdminLog_TitleSelectedEvents: String { return self._s[3980]! }
- public var Channel_EditAdmin_PermissionEditMessages: String { return self._s[3981]! }
- public var EnterPasscode_EnterTitle: String { return self._s[3982]! }
+ public var AppWallet_TransactionInfo_FeeInfoURL: String { return self._s[3984]! }
+ public var Conversation_InputTextCaptionPlaceholder: String { return self._s[3985]! }
+ public var Conversation_ContextMenuMention: String { return self._s[3986]! }
+ public var AutoDownloadSettings_VideoMessagesTitle: String { return self._s[3987]! }
+ public var Conversation_PinMessageAlertGroup: String { return self._s[3988]! }
+ public var Passport_Language_uz: String { return self._s[3989]! }
+ public var SettingsSearch_Synonyms_Privacy_GroupsAndChannels: String { return self._s[3990]! }
+ public var Channel_MessageVideoUpdated: String { return self._s[3992]! }
+ public var Map_StopLiveLocation: String { return self._s[3993]! }
+ public var VoiceOver_MessageContextSend: String { return self._s[3995]! }
+ public var PasscodeSettings_Help: String { return self._s[3996]! }
+ public var NotificationsSound_Input: String { return self._s[3997]! }
+ public var ProfilePhoto_MainVideo: String { return self._s[3999]! }
+ public var Share_Title: String { return self._s[4001]! }
+ public var LogoutOptions_Title: String { return self._s[4002]! }
+ public var Wallet_Send_AddressText: String { return self._s[4003]! }
+ public var Login_TermsOfServiceAgree: String { return self._s[4004]! }
+ public var Compose_NewEncryptedChatTitle: String { return self._s[4005]! }
+ public var Channel_AdminLog_TitleSelectedEvents: String { return self._s[4006]! }
+ public var Channel_EditAdmin_PermissionEditMessages: String { return self._s[4007]! }
+ public var EnterPasscode_EnterTitle: String { return self._s[4008]! }
public func Call_PrivacyErrorMessage(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3983]!, self._r[3983]!, [_0])
+ return formatWithArgumentRanges(self._s[4009]!, self._r[4009]!, [_0])
}
- public var Settings_CopyPhoneNumber: String { return self._s[3984]! }
- public var Conversation_AddToContacts: String { return self._s[3985]! }
+ public var Settings_CopyPhoneNumber: String { return self._s[4010]! }
+ public var Conversation_AddToContacts: String { return self._s[4011]! }
public func VoiceOver_Chat_ReplyFrom(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3986]!, self._r[3986]!, [_0])
+ return formatWithArgumentRanges(self._s[4012]!, self._r[4012]!, [_0])
}
- public var NotificationsSound_Keys: String { return self._s[3987]! }
+ public var NotificationsSound_Keys: String { return self._s[4013]! }
public func Call_ParticipantVersionOutdatedError(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3988]!, self._r[3988]!, [_0])
+ return formatWithArgumentRanges(self._s[4014]!, self._r[4014]!, [_0])
}
- public var Notification_MessageLifetime1w: String { return self._s[3989]! }
- public var Message_Video: String { return self._s[3990]! }
- public var AutoDownloadSettings_CellularTitle: String { return self._s[3991]! }
+ public var Notification_MessageLifetime1w: String { return self._s[4015]! }
+ public var Message_Video: String { return self._s[4016]! }
+ public var AutoDownloadSettings_CellularTitle: String { return self._s[4017]! }
public func PUSH_CHANNEL_MESSAGE_PHOTO(_ _1: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3992]!, self._r[3992]!, [_1])
+ return formatWithArgumentRanges(self._s[4018]!, self._r[4018]!, [_1])
}
- public var Wallet_Receive_AmountInfo: String { return self._s[3995]! }
- public var Stats_Overview: String { return self._s[3996]! }
+ public var Wallet_Receive_AmountInfo: String { return self._s[4021]! }
+ public var Stats_Overview: String { return self._s[4022]! }
public func Notification_JoinedChat(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3997]!, self._r[3997]!, [_0])
+ return formatWithArgumentRanges(self._s[4023]!, self._r[4023]!, [_0])
}
public func PrivacySettings_LastSeenContactsPlus(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[3998]!, self._r[3998]!, [_0])
+ return formatWithArgumentRanges(self._s[4024]!, self._r[4024]!, [_0])
}
- public var ChatListFolder_ExcludeChatsTitle: String { return self._s[3999]! }
- public var Passport_Language_mk: String { return self._s[4000]! }
- public var ChatListFolder_CategoryNonContacts: String { return self._s[4001]! }
+ public var ChatListFolder_ExcludeChatsTitle: String { return self._s[4025]! }
+ public var Passport_Language_mk: String { return self._s[4026]! }
+ public var ChatListFolder_CategoryNonContacts: String { return self._s[4027]! }
public func Wallet_Time_PreciseDate_m2(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[4002]!, self._r[4002]!, [_1, _2, _3])
+ return formatWithArgumentRanges(self._s[4028]!, self._r[4028]!, [_1, _2, _3])
}
- public var CreatePoll_CancelConfirmation: String { return self._s[4003]! }
- public var MessagePoll_LabelAnonymousQuiz: String { return self._s[4004]! }
- public var Conversation_SilentBroadcastTooltipOn: String { return self._s[4006]! }
- public var PrivacyPolicy_Decline: String { return self._s[4007]! }
- public var Passport_Identity_DoesNotExpire: String { return self._s[4008]! }
- public var Channel_AdminLogFilter_EventsRestrictions: String { return self._s[4009]! }
- public var AuthSessions_AddDeviceIntro_Action: String { return self._s[4010]! }
- public var Permissions_SiriAllow_v0: String { return self._s[4012]! }
- public var Wallet_Month_ShortAugust: String { return self._s[4013]! }
- public var Appearance_ThemeCarouselNight: String { return self._s[4014]! }
+ public var CreatePoll_CancelConfirmation: String { return self._s[4029]! }
+ public var MessagePoll_LabelAnonymousQuiz: String { return self._s[4030]! }
+ public var Conversation_SilentBroadcastTooltipOn: String { return self._s[4032]! }
+ public var PrivacyPolicy_Decline: String { return self._s[4033]! }
+ public var Passport_Identity_DoesNotExpire: String { return self._s[4034]! }
+ public var Channel_AdminLogFilter_EventsRestrictions: String { return self._s[4035]! }
+ public var AuthSessions_AddDeviceIntro_Action: String { return self._s[4036]! }
+ public var Permissions_SiriAllow_v0: String { return self._s[4038]! }
+ public var Wallet_Month_ShortAugust: String { return self._s[4039]! }
+ public var Appearance_ThemeCarouselNight: String { return self._s[4040]! }
public func LOCAL_CHAT_MESSAGE_FWDS(_ _1: String, _ _2: Int) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[4015]!, self._r[4015]!, [_1, "\(_2)"])
+ return formatWithArgumentRanges(self._s[4041]!, self._r[4041]!, [_1, "\(_2)"])
}
public func Notification_RenamedChat(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[4016]!, self._r[4016]!, [_0])
+ return formatWithArgumentRanges(self._s[4042]!, self._r[4042]!, [_0])
}
- public var Paint_Regular: String { return self._s[4017]! }
- public var ChatSettings_AutoDownloadReset: String { return self._s[4018]! }
- public var SocksProxySetup_ShareLink: String { return self._s[4019]! }
- public var Wallet_Qr_Title: String { return self._s[4020]! }
- public var BlockedUsers_SelectUserTitle: String { return self._s[4021]! }
- public var VoiceOver_Chat_RecordModeVoiceMessage: String { return self._s[4023]! }
- public var Wallet_Settings_Configuration: String { return self._s[4024]! }
- public var GroupInfo_InviteByLink: String { return self._s[4025]! }
- public var MessageTimer_Custom: String { return self._s[4026]! }
- public var UserInfo_NotificationsDefaultEnabled: String { return self._s[4027]! }
- public var Conversation_StopQuizConfirmationTitle: String { return self._s[4028]! }
- public var Passport_Address_TypeTemporaryRegistration: String { return self._s[4030]! }
- public var Conversation_SendMessage_SetReminder: String { return self._s[4031]! }
- public var VoiceOver_Chat_Selected: String { return self._s[4032]! }
- public var Paint_Pen: String { return self._s[4033]! }
- public var ChatSettings_AutoDownloadUsingWiFi: String { return self._s[4034]! }
- public var Channel_Username_InvalidTaken: String { return self._s[4035]! }
- public var Conversation_ClousStorageInfo_Description3: String { return self._s[4036]! }
- public var Wallet_WordCheck_TryAgain: String { return self._s[4037]! }
- public var Wallet_Info_TransactionPendingHeader: String { return self._s[4038]! }
- public var Settings_ChatBackground: String { return self._s[4039]! }
- public var Channel_Subscribers_Title: String { return self._s[4040]! }
- public var Wallet_Receive_InvoiceUrlHeader: String { return self._s[4041]! }
- public var ApplyLanguage_ChangeLanguageTitle: String { return self._s[4042]! }
- public var Watch_ConnectionDescription: String { return self._s[4043]! }
- public var OldChannels_NoticeText: String { return self._s[4046]! }
- public var Wallet_Configuration_ApplyErrorTitle: String { return self._s[4047]! }
- public var IntentsSettings_SuggestBy: String { return self._s[4049]! }
- public var Theme_ThemeChangedText: String { return self._s[4050]! }
- public var ChatList_ArchivedChatsTitle: String { return self._s[4051]! }
- public var Wallpaper_ResetWallpapers: String { return self._s[4052]! }
- public var Wallet_Send_TransactionInProgress: String { return self._s[4053]! }
- public var Conversation_SendDice: String { return self._s[4054]! }
- public var EditProfile_Title: String { return self._s[4055]! }
- public var NotificationsSound_Bamboo: String { return self._s[4057]! }
- public var Channel_AdminLog_MessagePreviousMessage: String { return self._s[4059]! }
- public var Login_SmsRequestState2: String { return self._s[4060]! }
- public var Passport_Language_ar: String { return self._s[4061]! }
+ public var Paint_Regular: String { return self._s[4043]! }
+ public var ChatSettings_AutoDownloadReset: String { return self._s[4044]! }
+ public var SocksProxySetup_ShareLink: String { return self._s[4045]! }
+ public var Wallet_Qr_Title: String { return self._s[4046]! }
+ public var BlockedUsers_SelectUserTitle: String { return self._s[4047]! }
+ public var VoiceOver_Chat_RecordModeVoiceMessage: String { return self._s[4049]! }
+ public var Wallet_Settings_Configuration: String { return self._s[4050]! }
+ public var GroupInfo_InviteByLink: String { return self._s[4051]! }
+ public var MessageTimer_Custom: String { return self._s[4052]! }
+ public var UserInfo_NotificationsDefaultEnabled: String { return self._s[4053]! }
+ public var Conversation_StopQuizConfirmationTitle: String { return self._s[4054]! }
+ public var Passport_Address_TypeTemporaryRegistration: String { return self._s[4056]! }
+ public var Conversation_SendMessage_SetReminder: String { return self._s[4057]! }
+ public var VoiceOver_Chat_Selected: String { return self._s[4058]! }
+ public var Paint_Pen: String { return self._s[4059]! }
+ public var ChatSettings_AutoDownloadUsingWiFi: String { return self._s[4060]! }
+ public var Channel_Username_InvalidTaken: String { return self._s[4061]! }
+ public var Conversation_ClousStorageInfo_Description3: String { return self._s[4062]! }
+ public var Wallet_WordCheck_TryAgain: String { return self._s[4063]! }
+ public var Wallet_Info_TransactionPendingHeader: String { return self._s[4064]! }
+ public var Settings_ChatBackground: String { return self._s[4065]! }
+ public var Channel_Subscribers_Title: String { return self._s[4066]! }
+ public var Wallet_Receive_InvoiceUrlHeader: String { return self._s[4067]! }
+ public var ApplyLanguage_ChangeLanguageTitle: String { return self._s[4068]! }
+ public var Watch_ConnectionDescription: String { return self._s[4069]! }
+ public var OldChannels_NoticeText: String { return self._s[4072]! }
+ public var Wallet_Configuration_ApplyErrorTitle: String { return self._s[4073]! }
+ public var IntentsSettings_SuggestBy: String { return self._s[4075]! }
+ public var Theme_ThemeChangedText: String { return self._s[4076]! }
+ public var ChatList_ArchivedChatsTitle: String { return self._s[4077]! }
+ public var Wallpaper_ResetWallpapers: String { return self._s[4078]! }
+ public var Wallet_Send_TransactionInProgress: String { return self._s[4079]! }
+ public var Conversation_SendDice: String { return self._s[4080]! }
+ public var EditProfile_Title: String { return self._s[4081]! }
+ public var NotificationsSound_Bamboo: String { return self._s[4083]! }
+ public var Channel_AdminLog_MessagePreviousMessage: String { return self._s[4085]! }
+ public var Login_SmsRequestState2: String { return self._s[4086]! }
+ public var Passport_Language_ar: String { return self._s[4087]! }
public func Message_AuthorPinnedGame(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[4062]!, self._r[4062]!, [_0])
+ return formatWithArgumentRanges(self._s[4088]!, self._r[4088]!, [_0])
}
- public var SettingsSearch_Synonyms_EditProfile_Title: String { return self._s[4063]! }
- public var Wallet_Created_Text: String { return self._s[4064]! }
- public var Conversation_MessageDialogEdit: String { return self._s[4066]! }
- public var Wallet_Created_Proceed: String { return self._s[4067]! }
- public var Wallet_Words_Done: String { return self._s[4068]! }
- public var VoiceOver_Media_PlaybackPause: String { return self._s[4069]! }
- public var ChatListFolder_NameChannels: String { return self._s[4070]! }
+ public var SettingsSearch_Synonyms_EditProfile_Title: String { return self._s[4089]! }
+ public var Wallet_Created_Text: String { return self._s[4090]! }
+ public var Conversation_MessageDialogEdit: String { return self._s[4092]! }
+ public var Wallet_Created_Proceed: String { return self._s[4093]! }
+ public var Wallet_Words_Done: String { return self._s[4094]! }
+ public var VoiceOver_Media_PlaybackPause: String { return self._s[4095]! }
+ public var ChatListFolder_NameChannels: String { return self._s[4096]! }
public func PUSH_AUTH_UNKNOWN(_ _1: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[4071]!, self._r[4071]!, [_1])
+ return formatWithArgumentRanges(self._s[4097]!, self._r[4097]!, [_1])
}
- public var Common_Close: String { return self._s[4073]! }
- public var GroupInfo_PublicLink: String { return self._s[4074]! }
- public var Channel_OwnershipTransfer_ErrorPrivacyRestricted: String { return self._s[4075]! }
- public var SettingsSearch_Synonyms_Notifications_GroupNotificationsPreview: String { return self._s[4076]! }
+ public var Common_Close: String { return self._s[4099]! }
+ public var GroupInfo_PublicLink: String { return self._s[4100]! }
+ public var Channel_OwnershipTransfer_ErrorPrivacyRestricted: String { return self._s[4101]! }
+ public var SettingsSearch_Synonyms_Notifications_GroupNotificationsPreview: String { return self._s[4102]! }
+ public var Conversation_ContextMenuOpenChannel: String { return self._s[4106]! }
public func Channel_AdminLog_MessageToggleInvitesOff(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[4080]!, self._r[4080]!, [_0])
- }
- public var UserInfo_About_Placeholder: String { return self._s[4081]! }
- public func Conversation_FileHowToText(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[4082]!, self._r[4082]!, [_0])
- }
- public var GroupInfo_Permissions_SectionTitle: String { return self._s[4083]! }
- public var Channel_Info_Banned: String { return self._s[4085]! }
- public func Time_MonthOfYear_m11(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[4086]!, self._r[4086]!, [_0])
- }
- public var Appearance_Other: String { return self._s[4087]! }
- public var Passport_Language_my: String { return self._s[4088]! }
- public var Group_Setup_BasicHistoryHiddenHelp: String { return self._s[4089]! }
- public func Time_PreciseDate_m9(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[4090]!, self._r[4090]!, [_1, _2, _3])
- }
- public var SettingsSearch_Synonyms_Privacy_PasscodeAndFaceId: String { return self._s[4091]! }
- public var IntentsSettings_SuggestedAndSpotlightChatsInfo: String { return self._s[4092]! }
- public var Preview_CopyAddress: String { return self._s[4093]! }
- public func DialogList_SinglePlayingGameSuffix(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[4094]!, self._r[4094]!, [_0])
- }
- public var KeyCommand_JumpToPreviousChat: String { return self._s[4095]! }
- public var UserInfo_BotSettings: String { return self._s[4096]! }
- public var LiveLocation_MenuStopAll: String { return self._s[4098]! }
- public var Passport_PasswordCreate: String { return self._s[4099]! }
- public var StickerSettings_MaskContextInfo: String { return self._s[4100]! }
- public var Message_PinnedLocationMessage: String { return self._s[4101]! }
- public var Map_Satellite: String { return self._s[4102]! }
- public var Watch_Message_Unsupported: String { return self._s[4103]! }
- public var Username_TooManyPublicUsernamesError: String { return self._s[4104]! }
- public var TwoStepAuth_EnterPasswordInvalid: String { return self._s[4105]! }
- public func Notification_PinnedTextMessage(_ _0: String, _ _1: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[4106]!, self._r[4106]!, [_0, _1])
- }
- public func Conversation_OpenBotLinkText(_ _0: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[4107]!, self._r[4107]!, [_0])
}
- public var Wallet_WordImport_Continue: String { return self._s[4108]! }
- public func TwoFactorSetup_EmailVerification_Text(_ _0: String) -> (String, [(Int, NSRange)]) {
+ public var UserInfo_About_Placeholder: String { return self._s[4108]! }
+ public func Conversation_FileHowToText(_ _0: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[4109]!, self._r[4109]!, [_0])
}
- public var Notifications_ChannelNotificationsHelp: String { return self._s[4110]! }
- public var Privacy_Calls_P2PContacts: String { return self._s[4111]! }
- public var NotificationsSound_None: String { return self._s[4112]! }
- public var Wallet_TransactionInfo_StorageFeeHeader: String { return self._s[4113]! }
- public var Channel_DiscussionGroup_UnlinkGroup: String { return self._s[4115]! }
- public var AccessDenied_VoiceMicrophone: String { return self._s[4116]! }
- public func ApplyLanguage_ChangeLanguageAlreadyActive(_ _1: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[4117]!, self._r[4117]!, [_1])
+ public var GroupInfo_Permissions_SectionTitle: String { return self._s[4110]! }
+ public var Channel_Info_Banned: String { return self._s[4112]! }
+ public func Time_MonthOfYear_m11(_ _0: String) -> (String, [(Int, NSRange)]) {
+ return formatWithArgumentRanges(self._s[4113]!, self._r[4113]!, [_0])
}
- public var Cache_Indexing: String { return self._s[4118]! }
- public var DialogList_RecentTitlePeople: String { return self._s[4120]! }
- public var DialogList_EncryptionRejected: String { return self._s[4121]! }
- public var GroupInfo_Administrators: String { return self._s[4122]! }
- public var Passport_ScanPassportHelp: String { return self._s[4123]! }
- public var Application_Name: String { return self._s[4124]! }
- public var Channel_AdminLogFilter_ChannelEventsInfo: String { return self._s[4125]! }
- public var Conversation_Timer_Title: String { return self._s[4126]! }
- public var ChatList_PeerTypeGroup: String { return self._s[4127]! }
- public var PeopleNearby_MakeVisible: String { return self._s[4129]! }
- public var Appearance_ThemeCarouselDay: String { return self._s[4130]! }
- public var Stats_GrowthTitle: String { return self._s[4131]! }
- public var Passport_Identity_TranslationHelp: String { return self._s[4132]! }
- public func VoiceOver_Chat_VideoMessageFrom(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[4133]!, self._r[4133]!, [_0])
+ public var Appearance_Other: String { return self._s[4114]! }
+ public var Passport_Language_my: String { return self._s[4115]! }
+ public var Group_Setup_BasicHistoryHiddenHelp: String { return self._s[4116]! }
+ public func Time_PreciseDate_m9(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
+ return formatWithArgumentRanges(self._s[4117]!, self._r[4117]!, [_1, _2, _3])
}
- public func Notification_JoinedGroupByLink(_ _0: String) -> (String, [(Int, NSRange)]) {
+ public var SettingsSearch_Synonyms_Privacy_PasscodeAndFaceId: String { return self._s[4118]! }
+ public var IntentsSettings_SuggestedAndSpotlightChatsInfo: String { return self._s[4119]! }
+ public var Preview_CopyAddress: String { return self._s[4120]! }
+ public func DialogList_SinglePlayingGameSuffix(_ _0: String) -> (String, [(Int, NSRange)]) {
+ return formatWithArgumentRanges(self._s[4121]!, self._r[4121]!, [_0])
+ }
+ public var KeyCommand_JumpToPreviousChat: String { return self._s[4122]! }
+ public var UserInfo_BotSettings: String { return self._s[4123]! }
+ public var LiveLocation_MenuStopAll: String { return self._s[4125]! }
+ public var Passport_PasswordCreate: String { return self._s[4126]! }
+ public var StickerSettings_MaskContextInfo: String { return self._s[4127]! }
+ public var Message_PinnedLocationMessage: String { return self._s[4128]! }
+ public var Map_Satellite: String { return self._s[4129]! }
+ public var Watch_Message_Unsupported: String { return self._s[4130]! }
+ public var Username_TooManyPublicUsernamesError: String { return self._s[4131]! }
+ public var TwoStepAuth_EnterPasswordInvalid: String { return self._s[4132]! }
+ public func Notification_PinnedTextMessage(_ _0: String, _ _1: String) -> (String, [(Int, NSRange)]) {
+ return formatWithArgumentRanges(self._s[4133]!, self._r[4133]!, [_0, _1])
+ }
+ public func Conversation_OpenBotLinkText(_ _0: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[4134]!, self._r[4134]!, [_0])
}
+ public var Wallet_WordImport_Continue: String { return self._s[4135]! }
+ public func TwoFactorSetup_EmailVerification_Text(_ _0: String) -> (String, [(Int, NSRange)]) {
+ return formatWithArgumentRanges(self._s[4136]!, self._r[4136]!, [_0])
+ }
+ public var Notifications_ChannelNotificationsHelp: String { return self._s[4137]! }
+ public var Privacy_Calls_P2PContacts: String { return self._s[4138]! }
+ public var NotificationsSound_None: String { return self._s[4139]! }
+ public var Wallet_TransactionInfo_StorageFeeHeader: String { return self._s[4140]! }
+ public var Channel_DiscussionGroup_UnlinkGroup: String { return self._s[4142]! }
+ public var AccessDenied_VoiceMicrophone: String { return self._s[4143]! }
+ public func ApplyLanguage_ChangeLanguageAlreadyActive(_ _1: String) -> (String, [(Int, NSRange)]) {
+ return formatWithArgumentRanges(self._s[4144]!, self._r[4144]!, [_1])
+ }
+ public var Cache_Indexing: String { return self._s[4145]! }
+ public var DialogList_RecentTitlePeople: String { return self._s[4147]! }
+ public var DialogList_EncryptionRejected: String { return self._s[4148]! }
+ public var GroupInfo_Administrators: String { return self._s[4149]! }
+ public var Passport_ScanPassportHelp: String { return self._s[4150]! }
+ public var Application_Name: String { return self._s[4151]! }
+ public var Channel_AdminLogFilter_ChannelEventsInfo: String { return self._s[4152]! }
+ public var Conversation_Timer_Title: String { return self._s[4153]! }
+ public var ChatList_PeerTypeGroup: String { return self._s[4154]! }
+ public var PeopleNearby_MakeVisible: String { return self._s[4156]! }
+ public var Appearance_ThemeCarouselDay: String { return self._s[4157]! }
+ public var Stats_GrowthTitle: String { return self._s[4158]! }
+ public var Passport_Identity_TranslationHelp: String { return self._s[4159]! }
+ public func VoiceOver_Chat_VideoMessageFrom(_ _0: String) -> (String, [(Int, NSRange)]) {
+ return formatWithArgumentRanges(self._s[4160]!, self._r[4160]!, [_0])
+ }
+ public func Notification_JoinedGroupByLink(_ _0: String) -> (String, [(Int, NSRange)]) {
+ return formatWithArgumentRanges(self._s[4161]!, self._r[4161]!, [_0])
+ }
public func DialogList_EncryptedChatStartedOutgoing(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[4135]!, self._r[4135]!, [_0])
+ return formatWithArgumentRanges(self._s[4162]!, self._r[4162]!, [_0])
}
- public var Channel_EditAdmin_PermissionDeleteMessages: String { return self._s[4136]! }
- public var Privacy_ChatsTitle: String { return self._s[4137]! }
- public var DialogList_ClearHistoryConfirmation: String { return self._s[4138]! }
- public var SettingsSearch_Synonyms_Data_Storage_ClearCache: String { return self._s[4139]! }
- public var Watch_Suggestion_HoldOn: String { return self._s[4140]! }
- public var Group_EditAdmin_TransferOwnership: String { return self._s[4141]! }
- public var WebBrowser_Title: String { return self._s[4142]! }
- public var Group_LinkedChannel: String { return self._s[4143]! }
- public var VoiceOver_Chat_SeenByRecipient: String { return self._s[4144]! }
- public var SocksProxySetup_RequiredCredentials: String { return self._s[4145]! }
- public var Passport_Address_TypeRentalAgreementUploadScan: String { return self._s[4146]! }
- public var Appearance_TextSize_UseSystem: String { return self._s[4147]! }
- public var TwoStepAuth_EmailSkipAlert: String { return self._s[4148]! }
- public var ScheduledMessages_RemindersTitle: String { return self._s[4150]! }
- public var Channel_Setup_TypePublic: String { return self._s[4152]! }
+ public var Channel_EditAdmin_PermissionDeleteMessages: String { return self._s[4163]! }
+ public var Privacy_ChatsTitle: String { return self._s[4164]! }
+ public var DialogList_ClearHistoryConfirmation: String { return self._s[4165]! }
+ public var SettingsSearch_Synonyms_Data_Storage_ClearCache: String { return self._s[4166]! }
+ public var Watch_Suggestion_HoldOn: String { return self._s[4167]! }
+ public var Group_EditAdmin_TransferOwnership: String { return self._s[4168]! }
+ public var WebBrowser_Title: String { return self._s[4169]! }
+ public var Group_LinkedChannel: String { return self._s[4170]! }
+ public var VoiceOver_Chat_SeenByRecipient: String { return self._s[4171]! }
+ public var SocksProxySetup_RequiredCredentials: String { return self._s[4172]! }
+ public var Passport_Address_TypeRentalAgreementUploadScan: String { return self._s[4173]! }
+ public var Appearance_TextSize_UseSystem: String { return self._s[4174]! }
+ public var TwoStepAuth_EmailSkipAlert: String { return self._s[4175]! }
+ public var ScheduledMessages_RemindersTitle: String { return self._s[4177]! }
+ public var Channel_Setup_TypePublic: String { return self._s[4179]! }
public func Channel_AdminLog_MessageToggleInvitesOn(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[4153]!, self._r[4153]!, [_0])
+ return formatWithArgumentRanges(self._s[4180]!, self._r[4180]!, [_0])
}
- public var Channel_TypeSetup_Title: String { return self._s[4155]! }
- public var MessagePoll_ViewResults: String { return self._s[4156]! }
- public var Map_OpenInMaps: String { return self._s[4158]! }
+ public var Channel_TypeSetup_Title: String { return self._s[4182]! }
+ public var MessagePoll_ViewResults: String { return self._s[4183]! }
+ public var Map_OpenInMaps: String { return self._s[4185]! }
public func PUSH_PINNED_NOTEXT(_ _1: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[4159]!, self._r[4159]!, [_1])
+ return formatWithArgumentRanges(self._s[4186]!, self._r[4186]!, [_1])
}
- public var NotificationsSound_Tremolo: String { return self._s[4161]! }
+ public var NotificationsSound_Tremolo: String { return self._s[4188]! }
public func Date_ChatDateHeaderYear(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[4162]!, self._r[4162]!, [_1, _2, _3])
+ return formatWithArgumentRanges(self._s[4189]!, self._r[4189]!, [_1, _2, _3])
}
- public var ConversationProfile_UnknownAddMemberError: String { return self._s[4163]! }
- public var Channel_OwnershipTransfer_PasswordPlaceholder: String { return self._s[4164]! }
- public var Passport_PasswordHelp: String { return self._s[4166]! }
- public var Login_CodeExpiredError: String { return self._s[4167]! }
- public var Channel_EditAdmin_PermissionChangeInfo: String { return self._s[4168]! }
- public var Conversation_TitleUnmute: String { return self._s[4169]! }
- public var Passport_Identity_ScansHelp: String { return self._s[4170]! }
- public var Passport_Language_lo: String { return self._s[4171]! }
- public var Camera_FlashAuto: String { return self._s[4172]! }
- public var Conversation_OpenBotLinkOpen: String { return self._s[4173]! }
- public var Common_Cancel: String { return self._s[4174]! }
- public var DialogList_SavedMessagesTooltip: String { return self._s[4175]! }
- public var TwoStepAuth_SetupPasswordTitle: String { return self._s[4176]! }
- public var Appearance_TintAllColors: String { return self._s[4177]! }
+ public var ConversationProfile_UnknownAddMemberError: String { return self._s[4190]! }
+ public var Channel_OwnershipTransfer_PasswordPlaceholder: String { return self._s[4191]! }
+ public var Passport_PasswordHelp: String { return self._s[4193]! }
+ public var Login_CodeExpiredError: String { return self._s[4194]! }
+ public var Channel_EditAdmin_PermissionChangeInfo: String { return self._s[4195]! }
+ public var Conversation_TitleUnmute: String { return self._s[4196]! }
+ public var Passport_Identity_ScansHelp: String { return self._s[4197]! }
+ public var Passport_Language_lo: String { return self._s[4198]! }
+ public var Camera_FlashAuto: String { return self._s[4199]! }
+ public var Conversation_OpenBotLinkOpen: String { return self._s[4200]! }
+ public var Common_Cancel: String { return self._s[4201]! }
+ public var DialogList_SavedMessagesTooltip: String { return self._s[4202]! }
+ public var TwoStepAuth_SetupPasswordTitle: String { return self._s[4203]! }
+ public var Appearance_TintAllColors: String { return self._s[4204]! }
public func PUSH_MESSAGE_FWD(_ _1: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[4178]!, self._r[4178]!, [_1])
+ return formatWithArgumentRanges(self._s[4205]!, self._r[4205]!, [_1])
}
- public var Conversation_ReportSpamConfirmation: String { return self._s[4179]! }
- public var ChatSettings_Title: String { return self._s[4181]! }
- public var Passport_PasswordReset: String { return self._s[4182]! }
- public var SocksProxySetup_TypeNone: String { return self._s[4183]! }
- public var EditTheme_Title: String { return self._s[4186]! }
- public var PhoneNumberHelp_Help: String { return self._s[4187]! }
- public var Checkout_EnterPassword: String { return self._s[4188]! }
- public var Activity_UploadingDocument: String { return self._s[4190]! }
- public var Share_AuthTitle: String { return self._s[4191]! }
- public var State_Connecting: String { return self._s[4192]! }
- public var Profile_MessageLifetime1w: String { return self._s[4193]! }
- public var Conversation_ContextMenuReport: String { return self._s[4194]! }
- public var CheckoutInfo_ReceiverInfoPhone: String { return self._s[4195]! }
- public var AutoNightTheme_ScheduledTo: String { return self._s[4196]! }
+ public var Conversation_ReportSpamConfirmation: String { return self._s[4206]! }
+ public var ChatSettings_Title: String { return self._s[4208]! }
+ public var Passport_PasswordReset: String { return self._s[4209]! }
+ public var SocksProxySetup_TypeNone: String { return self._s[4210]! }
+ public var EditTheme_Title: String { return self._s[4213]! }
+ public var PhoneNumberHelp_Help: String { return self._s[4214]! }
+ public var Checkout_EnterPassword: String { return self._s[4215]! }
+ public var Activity_UploadingDocument: String { return self._s[4217]! }
+ public var Share_AuthTitle: String { return self._s[4218]! }
+ public var State_Connecting: String { return self._s[4219]! }
+ public var Profile_MessageLifetime1w: String { return self._s[4220]! }
+ public var Conversation_ContextMenuReport: String { return self._s[4221]! }
+ public var CheckoutInfo_ReceiverInfoPhone: String { return self._s[4222]! }
+ public var AutoNightTheme_ScheduledTo: String { return self._s[4223]! }
public func VoiceOver_Chat_AnonymousPollFrom(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[4197]!, self._r[4197]!, [_0])
+ return formatWithArgumentRanges(self._s[4224]!, self._r[4224]!, [_0])
}
- public var AuthSessions_Terminate: String { return self._s[4198]! }
- public var Wallet_WordImport_CanNotRemember: String { return self._s[4199]! }
- public var PeerInfo_PaneAudio: String { return self._s[4200]! }
+ public var AuthSessions_Terminate: String { return self._s[4225]! }
+ public var Wallet_WordImport_CanNotRemember: String { return self._s[4226]! }
+ public var PeerInfo_PaneAudio: String { return self._s[4227]! }
public func Message_ForwardedPsa_covid(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[4201]!, self._r[4201]!, [_0])
+ return formatWithArgumentRanges(self._s[4228]!, self._r[4228]!, [_0])
}
- public var Checkout_NewCard_CardholderNamePlaceholder: String { return self._s[4203]! }
- public var KeyCommand_JumpToPreviousUnreadChat: String { return self._s[4204]! }
- public var PhotoEditor_Set: String { return self._s[4205]! }
- public var EmptyGroupInfo_Title: String { return self._s[4206]! }
- public var Login_PadPhoneHelp: String { return self._s[4208]! }
- public var AutoDownloadSettings_TypeGroupChats: String { return self._s[4210]! }
- public var PrivacyPolicy_DeclineLastWarning: String { return self._s[4212]! }
- public var NotificationsSound_Complete: String { return self._s[4213]! }
- public var SettingsSearch_Synonyms_Privacy_Data_Title: String { return self._s[4214]! }
- public var Group_Info_AdminLog: String { return self._s[4215]! }
- public var GroupPermission_NotAvailableInPublicGroups: String { return self._s[4216]! }
+ public var Checkout_NewCard_CardholderNamePlaceholder: String { return self._s[4230]! }
+ public var KeyCommand_JumpToPreviousUnreadChat: String { return self._s[4231]! }
+ public var PhotoEditor_Set: String { return self._s[4232]! }
+ public var EmptyGroupInfo_Title: String { return self._s[4233]! }
+ public var Login_PadPhoneHelp: String { return self._s[4235]! }
+ public var AutoDownloadSettings_TypeGroupChats: String { return self._s[4237]! }
+ public var PrivacyPolicy_DeclineLastWarning: String { return self._s[4239]! }
+ public var NotificationsSound_Complete: String { return self._s[4240]! }
+ public var SettingsSearch_Synonyms_Privacy_Data_Title: String { return self._s[4241]! }
+ public var Group_Info_AdminLog: String { return self._s[4242]! }
+ public var GroupPermission_NotAvailableInPublicGroups: String { return self._s[4243]! }
public func Wallet_Time_PreciseDate_m11(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[4217]!, self._r[4217]!, [_1, _2, _3])
+ return formatWithArgumentRanges(self._s[4244]!, self._r[4244]!, [_1, _2, _3])
}
- public var Channel_AdminLog_InfoPanelAlertText: String { return self._s[4218]! }
- public var Group_Location_CreateInThisPlace: String { return self._s[4220]! }
- public var Conversation_Admin: String { return self._s[4221]! }
- public var Conversation_GifTooltip: String { return self._s[4222]! }
- public var Passport_NotLoggedInMessage: String { return self._s[4223]! }
+ public var Channel_AdminLog_InfoPanelAlertText: String { return self._s[4245]! }
+ public var Group_Location_CreateInThisPlace: String { return self._s[4247]! }
+ public var Conversation_Admin: String { return self._s[4248]! }
+ public var Conversation_GifTooltip: String { return self._s[4249]! }
+ public var Passport_NotLoggedInMessage: String { return self._s[4250]! }
public func AutoDownloadSettings_OnFor(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[4225]!, self._r[4225]!, [_0])
+ return formatWithArgumentRanges(self._s[4252]!, self._r[4252]!, [_0])
}
- public var Profile_MessageLifetimeForever: String { return self._s[4226]! }
- public var SharedMedia_EmptyTitle: String { return self._s[4228]! }
- public var Channel_Edit_PrivatePublicLinkAlert: String { return self._s[4230]! }
- public var Username_Help: String { return self._s[4231]! }
- public var DialogList_LanguageTooltip: String { return self._s[4233]! }
- public var Map_LoadError: String { return self._s[4234]! }
- public var Login_PhoneNumberAlreadyAuthorized: String { return self._s[4235]! }
- public var Channel_AdminLog_AddMembers: String { return self._s[4236]! }
- public var ArchivedChats_IntroTitle2: String { return self._s[4237]! }
- public var Notification_Exceptions_NewException: String { return self._s[4238]! }
- public var TwoStepAuth_EmailTitle: String { return self._s[4239]! }
- public var WatchRemote_AlertText: String { return self._s[4240]! }
+ public var Profile_MessageLifetimeForever: String { return self._s[4253]! }
+ public var SharedMedia_EmptyTitle: String { return self._s[4255]! }
+ public var Channel_Edit_PrivatePublicLinkAlert: String { return self._s[4257]! }
+ public var Username_Help: String { return self._s[4258]! }
+ public var DialogList_LanguageTooltip: String { return self._s[4260]! }
+ public var Map_LoadError: String { return self._s[4261]! }
+ public var Login_PhoneNumberAlreadyAuthorized: String { return self._s[4262]! }
+ public var Channel_AdminLog_AddMembers: String { return self._s[4263]! }
+ public var ArchivedChats_IntroTitle2: String { return self._s[4264]! }
+ public var Notification_Exceptions_NewException: String { return self._s[4265]! }
+ public var TwoStepAuth_EmailTitle: String { return self._s[4266]! }
+ public var WatchRemote_AlertText: String { return self._s[4267]! }
public func Wallet_Send_ConfirmationText(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[4241]!, self._r[4241]!, [_1, _2, _3])
+ return formatWithArgumentRanges(self._s[4268]!, self._r[4268]!, [_1, _2, _3])
}
- public var ChatSettings_ConnectionType_Title: String { return self._s[4245]! }
+ public var ChatSettings_ConnectionType_Title: String { return self._s[4272]! }
public func PUSH_PINNED_QUIZ(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[4246]!, self._r[4246]!, [_1, _2])
+ return formatWithArgumentRanges(self._s[4273]!, self._r[4273]!, [_1, _2])
}
public func Settings_CheckPhoneNumberTitle(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[4247]!, self._r[4247]!, [_0])
+ return formatWithArgumentRanges(self._s[4274]!, self._r[4274]!, [_0])
}
- public var SettingsSearch_Synonyms_Calls_CallTab: String { return self._s[4248]! }
- public var WebBrowser_DefaultBrowser: String { return self._s[4249]! }
- public var Passport_Address_CountryPlaceholder: String { return self._s[4250]! }
+ public var SettingsSearch_Synonyms_Calls_CallTab: String { return self._s[4275]! }
+ public var WebBrowser_DefaultBrowser: String { return self._s[4276]! }
+ public var Passport_Address_CountryPlaceholder: String { return self._s[4277]! }
public func DialogList_AwaitingEncryption(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[4251]!, self._r[4251]!, [_0])
+ return formatWithArgumentRanges(self._s[4278]!, self._r[4278]!, [_0])
}
public func Time_PreciseDate_m6(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[4252]!, self._r[4252]!, [_1, _2, _3])
+ return formatWithArgumentRanges(self._s[4279]!, self._r[4279]!, [_1, _2, _3])
}
- public var Group_AdminLog_EmptyText: String { return self._s[4253]! }
- public var SettingsSearch_Synonyms_Appearance_Title: String { return self._s[4254]! }
- public var Conversation_PrivateChannelTooltip: String { return self._s[4256]! }
- public var Wallet_Created_ExportErrorText: String { return self._s[4257]! }
- public var ChatList_UndoArchiveText1: String { return self._s[4258]! }
- public var ChatListFolder_IncludedSectionHeader: String { return self._s[4259]! }
- public var AccessDenied_VideoMicrophone: String { return self._s[4260]! }
- public var Conversation_ContextMenuStickerPackAdd: String { return self._s[4261]! }
- public var Stats_GroupTopInviter_History: String { return self._s[4262]! }
- public var Cache_ClearNone: String { return self._s[4263]! }
- public var SocksProxySetup_FailedToConnect: String { return self._s[4264]! }
- public var Permissions_NotificationsTitle_v0: String { return self._s[4265]! }
+ public var Group_AdminLog_EmptyText: String { return self._s[4280]! }
+ public var SettingsSearch_Synonyms_Appearance_Title: String { return self._s[4281]! }
+ public var Conversation_PrivateChannelTooltip: String { return self._s[4283]! }
+ public var Wallet_Created_ExportErrorText: String { return self._s[4284]! }
+ public var ChatList_UndoArchiveText1: String { return self._s[4285]! }
+ public var ChatListFolder_IncludedSectionHeader: String { return self._s[4286]! }
+ public var AccessDenied_VideoMicrophone: String { return self._s[4287]! }
+ public var Conversation_ContextMenuStickerPackAdd: String { return self._s[4288]! }
+ public var Stats_GroupTopInviter_History: String { return self._s[4289]! }
+ public var Cache_ClearNone: String { return self._s[4290]! }
+ public var SocksProxySetup_FailedToConnect: String { return self._s[4291]! }
+ public var Permissions_NotificationsTitle_v0: String { return self._s[4292]! }
public func Channel_AdminLog_MessageEdited(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[4266]!, self._r[4266]!, [_0])
+ return formatWithArgumentRanges(self._s[4293]!, self._r[4293]!, [_0])
}
- public var Passport_Identity_Country: String { return self._s[4267]! }
+ public var Passport_Identity_Country: String { return self._s[4294]! }
public func ChatSettings_AutoDownloadSettings_TypeFile(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[4268]!, self._r[4268]!, [_0])
+ return formatWithArgumentRanges(self._s[4295]!, self._r[4295]!, [_0])
}
public func Notification_CreatedChat(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[4269]!, self._r[4269]!, [_0])
+ return formatWithArgumentRanges(self._s[4296]!, self._r[4296]!, [_0])
}
- public var Exceptions_AddToExceptions: String { return self._s[4270]! }
- public var AccessDenied_Settings: String { return self._s[4271]! }
- public var Passport_Address_TypeUtilityBillUploadScan: String { return self._s[4272]! }
- public var Month_ShortMay: String { return self._s[4274]! }
- public var Compose_NewGroup: String { return self._s[4276]! }
- public var Group_Setup_TypePrivate: String { return self._s[4278]! }
- public var Login_PadPhoneHelpTitle: String { return self._s[4280]! }
- public var Appearance_ThemeDayClassic: String { return self._s[4281]! }
- public var Channel_AdminLog_MessagePreviousCaption: String { return self._s[4282]! }
- public var AutoDownloadSettings_OffForAll: String { return self._s[4283]! }
- public var Privacy_GroupsAndChannels_WhoCanAddMe: String { return self._s[4284]! }
- public var Conversation_typing: String { return self._s[4286]! }
- public var Undo_ScheduledMessagesCleared: String { return self._s[4287]! }
- public var Paint_Masks: String { return self._s[4288]! }
- public var Contacts_DeselectAll: String { return self._s[4289]! }
+ public var Exceptions_AddToExceptions: String { return self._s[4297]! }
+ public var AccessDenied_Settings: String { return self._s[4298]! }
+ public var Passport_Address_TypeUtilityBillUploadScan: String { return self._s[4299]! }
+ public var Month_ShortMay: String { return self._s[4301]! }
+ public var Compose_NewGroup: String { return self._s[4303]! }
+ public var Group_Setup_TypePrivate: String { return self._s[4305]! }
+ public var Login_PadPhoneHelpTitle: String { return self._s[4307]! }
+ public var Appearance_ThemeDayClassic: String { return self._s[4308]! }
+ public var Channel_AdminLog_MessagePreviousCaption: String { return self._s[4309]! }
+ public var AutoDownloadSettings_OffForAll: String { return self._s[4310]! }
+ public var Privacy_GroupsAndChannels_WhoCanAddMe: String { return self._s[4311]! }
+ public var Conversation_typing: String { return self._s[4313]! }
+ public var Undo_ScheduledMessagesCleared: String { return self._s[4314]! }
+ public var Paint_Masks: String { return self._s[4315]! }
+ public var Contacts_DeselectAll: String { return self._s[4316]! }
public func Wallet_Updated_YesterdayAt(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[4290]!, self._r[4290]!, [_0])
+ return formatWithArgumentRanges(self._s[4317]!, self._r[4317]!, [_0])
}
- public var CreatePoll_MultipleChoiceQuizAlert: String { return self._s[4291]! }
- public var Stats_GroupMembersTitle: String { return self._s[4292]! }
- public var Username_InvalidTaken: String { return self._s[4293]! }
- public var Call_StatusNoAnswer: String { return self._s[4294]! }
- public var TwoStepAuth_EmailAddSuccess: String { return self._s[4295]! }
- public var SettingsSearch_Synonyms_Privacy_BlockedUsers: String { return self._s[4296]! }
- public var Passport_Identity_Selfie: String { return self._s[4297]! }
- public var Login_InfoLastNamePlaceholder: String { return self._s[4298]! }
- public var Privacy_SecretChatsLinkPreviewsHelp: String { return self._s[4299]! }
- public var Conversation_ClearSecretHistory: String { return self._s[4300]! }
- public var PeopleNearby_Description: String { return self._s[4302]! }
- public var NetworkUsageSettings_Title: String { return self._s[4303]! }
- public var Your_cards_security_code_is_invalid: String { return self._s[4305]! }
- public var Stats_EnabledNotifications: String { return self._s[4306]! }
+ public var CreatePoll_MultipleChoiceQuizAlert: String { return self._s[4318]! }
+ public var Stats_GroupMembersTitle: String { return self._s[4319]! }
+ public var Username_InvalidTaken: String { return self._s[4320]! }
+ public var Call_StatusNoAnswer: String { return self._s[4321]! }
+ public var TwoStepAuth_EmailAddSuccess: String { return self._s[4322]! }
+ public var SettingsSearch_Synonyms_Privacy_BlockedUsers: String { return self._s[4323]! }
+ public var Passport_Identity_Selfie: String { return self._s[4324]! }
+ public var Login_InfoLastNamePlaceholder: String { return self._s[4325]! }
+ public var Privacy_SecretChatsLinkPreviewsHelp: String { return self._s[4326]! }
+ public var Conversation_ClearSecretHistory: String { return self._s[4327]! }
+ public var PeopleNearby_Description: String { return self._s[4329]! }
+ public var NetworkUsageSettings_Title: String { return self._s[4330]! }
+ public var Your_cards_security_code_is_invalid: String { return self._s[4332]! }
+ public var Stats_EnabledNotifications: String { return self._s[4333]! }
public func Notification_LeftChannel(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[4309]!, self._r[4309]!, [_0])
+ return formatWithArgumentRanges(self._s[4336]!, self._r[4336]!, [_0])
}
public func Call_CallInProgressMessage(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[4310]!, self._r[4310]!, [_1, _2])
+ return formatWithArgumentRanges(self._s[4337]!, self._r[4337]!, [_1, _2])
}
- public var SaveIncomingPhotosSettings_From: String { return self._s[4312]! }
- public var VoiceOver_Navigation_Search: String { return self._s[4313]! }
- public var Map_LiveLocationTitle: String { return self._s[4314]! }
- public var Login_InfoAvatarAdd: String { return self._s[4315]! }
- public var Passport_Identity_FilesView: String { return self._s[4316]! }
- public var ChatListFolderSettings_Title: String { return self._s[4317]! }
- public var UserInfo_GenericPhoneLabel: String { return self._s[4318]! }
- public var Privacy_Calls_NeverAllow: String { return self._s[4319]! }
- public var VoiceOver_Chat_File: String { return self._s[4320]! }
- public var Wallet_Settings_DeleteWalletInfo: String { return self._s[4321]! }
+ public var SaveIncomingPhotosSettings_From: String { return self._s[4339]! }
+ public var VoiceOver_Navigation_Search: String { return self._s[4340]! }
+ public var Map_LiveLocationTitle: String { return self._s[4341]! }
+ public var Login_InfoAvatarAdd: String { return self._s[4342]! }
+ public var Passport_Identity_FilesView: String { return self._s[4343]! }
+ public var ChatListFolderSettings_Title: String { return self._s[4344]! }
+ public var UserInfo_GenericPhoneLabel: String { return self._s[4345]! }
+ public var Privacy_Calls_NeverAllow: String { return self._s[4346]! }
+ public var VoiceOver_Chat_File: String { return self._s[4347]! }
+ public var Wallet_Settings_DeleteWalletInfo: String { return self._s[4348]! }
public func Contacts_AddPhoneNumber(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[4322]!, self._r[4322]!, [_0])
+ return formatWithArgumentRanges(self._s[4349]!, self._r[4349]!, [_0])
}
- public var ChatList_EmptyChatList: String { return self._s[4324]! }
- public var ContactInfo_PhoneNumberHidden: String { return self._s[4325]! }
- public var TwoStepAuth_ConfirmationText: String { return self._s[4326]! }
- public var ChatSettings_AutomaticVideoMessageDownload: String { return self._s[4327]! }
+ public var ChatList_EmptyChatList: String { return self._s[4351]! }
+ public var ContactInfo_PhoneNumberHidden: String { return self._s[4352]! }
+ public var TwoStepAuth_ConfirmationText: String { return self._s[4353]! }
+ public var ChatSettings_AutomaticVideoMessageDownload: String { return self._s[4354]! }
public func PUSH_CHAT_MESSAGE_VIDEOS(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[4328]!, self._r[4328]!, [_1, _2, _3])
+ return formatWithArgumentRanges(self._s[4355]!, self._r[4355]!, [_1, _2, _3])
}
- public var Channel_AdminLogFilter_AdminsAll: String { return self._s[4329]! }
- public var Wallet_Intro_CreateErrorText: String { return self._s[4330]! }
- public var Tour_Title2: String { return self._s[4331]! }
- public var Wallet_Sent_ViewWallet: String { return self._s[4332]! }
- public var Stats_GroupMessagesTitle: String { return self._s[4333]! }
- public var Conversation_FileOpenIn: String { return self._s[4334]! }
- public var Checkout_ErrorPrecheckoutFailed: String { return self._s[4335]! }
- public var Wallet_Send_ErrorInvalidAddress: String { return self._s[4336]! }
- public var Wallpaper_Set: String { return self._s[4337]! }
- public var Passport_Identity_Translations: String { return self._s[4340]! }
+ public var Channel_AdminLogFilter_AdminsAll: String { return self._s[4356]! }
+ public var Wallet_Intro_CreateErrorText: String { return self._s[4357]! }
+ public var Tour_Title2: String { return self._s[4358]! }
+ public var Wallet_Sent_ViewWallet: String { return self._s[4359]! }
+ public var Stats_GroupMessagesTitle: String { return self._s[4360]! }
+ public var Conversation_FileOpenIn: String { return self._s[4361]! }
+ public var Checkout_ErrorPrecheckoutFailed: String { return self._s[4362]! }
+ public var Wallet_Send_ErrorInvalidAddress: String { return self._s[4363]! }
+ public var Wallpaper_Set: String { return self._s[4364]! }
+ public var Passport_Identity_Translations: String { return self._s[4367]! }
public func Channel_AdminLog_MessageChangedChannelAbout(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[4341]!, self._r[4341]!, [_0])
+ return formatWithArgumentRanges(self._s[4368]!, self._r[4368]!, [_0])
}
- public var Channel_LeaveChannel: String { return self._s[4343]! }
+ public var Channel_LeaveChannel: String { return self._s[4370]! }
public func PINNED_INVOICE(_ _1: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[4344]!, self._r[4344]!, [_1])
+ return formatWithArgumentRanges(self._s[4371]!, self._r[4371]!, [_1])
}
- public var SettingsSearch_Synonyms_Proxy_AddProxy: String { return self._s[4346]! }
- public var PhotoEditor_HighlightsTint: String { return self._s[4347]! }
- public var MessagePoll_LabelPoll: String { return self._s[4348]! }
- public var Passport_Email_Delete: String { return self._s[4349]! }
- public var Conversation_Mute: String { return self._s[4351]! }
- public var Channel_AddBotAsAdmin: String { return self._s[4352]! }
- public var Channel_AdminLog_CanSendMessages: String { return self._s[4354]! }
- public var Wallet_Configuration_BlockchainNameChangedText: String { return self._s[4355]! }
- public var ChatSettings_IntentsSettings: String { return self._s[4357]! }
- public var Channel_Management_LabelOwner: String { return self._s[4358]! }
+ public var SettingsSearch_Synonyms_Proxy_AddProxy: String { return self._s[4373]! }
+ public var PhotoEditor_HighlightsTint: String { return self._s[4374]! }
+ public var MessagePoll_LabelPoll: String { return self._s[4375]! }
+ public var Passport_Email_Delete: String { return self._s[4376]! }
+ public var Conversation_Mute: String { return self._s[4378]! }
+ public var Channel_AddBotAsAdmin: String { return self._s[4379]! }
+ public var Channel_AdminLog_CanSendMessages: String { return self._s[4381]! }
+ public var Wallet_Configuration_BlockchainNameChangedText: String { return self._s[4382]! }
+ public var ChatSettings_IntentsSettings: String { return self._s[4384]! }
+ public var Channel_Management_LabelOwner: String { return self._s[4385]! }
public func Notification_PassportValuesSentMessage(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[4359]!, self._r[4359]!, [_1, _2])
+ return formatWithArgumentRanges(self._s[4386]!, self._r[4386]!, [_1, _2])
}
- public var Calls_CallTabDescription: String { return self._s[4360]! }
- public var Passport_Identity_NativeNameHelp: String { return self._s[4361]! }
- public var Common_No: String { return self._s[4362]! }
- public var Weekday_Sunday: String { return self._s[4363]! }
- public var Notification_Reply: String { return self._s[4364]! }
- public var Conversation_ViewMessage: String { return self._s[4365]! }
+ public var Calls_CallTabDescription: String { return self._s[4387]! }
+ public var Passport_Identity_NativeNameHelp: String { return self._s[4388]! }
+ public var Common_No: String { return self._s[4389]! }
+ public var Weekday_Sunday: String { return self._s[4390]! }
+ public var Notification_Reply: String { return self._s[4391]! }
+ public var Conversation_ViewMessage: String { return self._s[4392]! }
public func Checkout_SavePasswordTimeoutAndFaceId(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[4366]!, self._r[4366]!, [_0])
+ return formatWithArgumentRanges(self._s[4393]!, self._r[4393]!, [_0])
}
public func Map_LiveLocationPrivateDescription(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[4367]!, self._r[4367]!, [_0])
+ return formatWithArgumentRanges(self._s[4394]!, self._r[4394]!, [_0])
}
public func Wallet_Time_PreciseDate_m7(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[4368]!, self._r[4368]!, [_1, _2, _3])
+ return formatWithArgumentRanges(self._s[4395]!, self._r[4395]!, [_1, _2, _3])
}
- public var SettingsSearch_Synonyms_EditProfile_AddAccount: String { return self._s[4369]! }
- public var Wallet_Send_Title: String { return self._s[4370]! }
- public var Message_PinnedDocumentMessage: String { return self._s[4371]! }
- public var Wallet_Info_RefreshErrorText: String { return self._s[4372]! }
- public var DialogList_TabTitle: String { return self._s[4374]! }
- public var ChatSettings_AutoPlayTitle: String { return self._s[4375]! }
- public var Passport_FieldEmail: String { return self._s[4376]! }
- public var Conversation_UnpinMessageAlert: String { return self._s[4377]! }
- public var Passport_Address_TypeBankStatement: String { return self._s[4378]! }
- public var Wallet_SecureStorageReset_Title: String { return self._s[4379]! }
- public var Passport_Identity_ExpiryDate: String { return self._s[4380]! }
- public var Privacy_Calls_P2P: String { return self._s[4381]! }
+ public var SettingsSearch_Synonyms_EditProfile_AddAccount: String { return self._s[4396]! }
+ public var Wallet_Send_Title: String { return self._s[4397]! }
+ public var Message_PinnedDocumentMessage: String { return self._s[4398]! }
+ public var Wallet_Info_RefreshErrorText: String { return self._s[4399]! }
+ public var DialogList_TabTitle: String { return self._s[4401]! }
+ public var ChatSettings_AutoPlayTitle: String { return self._s[4402]! }
+ public var Passport_FieldEmail: String { return self._s[4403]! }
+ public var Conversation_UnpinMessageAlert: String { return self._s[4404]! }
+ public var Passport_Address_TypeBankStatement: String { return self._s[4405]! }
+ public var Wallet_SecureStorageReset_Title: String { return self._s[4406]! }
+ public var Passport_Identity_ExpiryDate: String { return self._s[4407]! }
+ public var Privacy_Calls_P2P: String { return self._s[4408]! }
public func CancelResetAccount_Success(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[4383]!, self._r[4383]!, [_0])
+ return formatWithArgumentRanges(self._s[4410]!, self._r[4410]!, [_0])
}
- public var SocksProxySetup_UseForCallsHelp: String { return self._s[4384]! }
+ public var SocksProxySetup_UseForCallsHelp: String { return self._s[4411]! }
public func PUSH_CHAT_ALBUM(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[4385]!, self._r[4385]!, [_1, _2])
+ return formatWithArgumentRanges(self._s[4412]!, self._r[4412]!, [_1, _2])
}
- public var Stickers_ClearRecent: String { return self._s[4386]! }
- public var EnterPasscode_ChangeTitle: String { return self._s[4387]! }
- public var TwoFactorSetup_Email_Title: String { return self._s[4388]! }
- public var Passport_InfoText: String { return self._s[4389]! }
- public var Checkout_NewCard_SaveInfoEnableHelp: String { return self._s[4390]! }
+ public var Stickers_ClearRecent: String { return self._s[4413]! }
+ public var EnterPasscode_ChangeTitle: String { return self._s[4414]! }
+ public var TwoFactorSetup_Email_Title: String { return self._s[4415]! }
+ public var Passport_InfoText: String { return self._s[4416]! }
+ public var Checkout_NewCard_SaveInfoEnableHelp: String { return self._s[4417]! }
public func Login_InvalidPhoneEmailSubject(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[4391]!, self._r[4391]!, [_0])
+ return formatWithArgumentRanges(self._s[4418]!, self._r[4418]!, [_0])
}
public func Time_PreciseDate_m3(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[4392]!, self._r[4392]!, [_1, _2, _3])
+ return formatWithArgumentRanges(self._s[4419]!, self._r[4419]!, [_1, _2, _3])
}
- public var SettingsSearch_Synonyms_Notifications_BadgeIncludeMutedChannels: String { return self._s[4393]! }
- public var ScheduledMessages_PollUnavailable: String { return self._s[4394]! }
- public var VoiceOver_Navigation_Compose: String { return self._s[4395]! }
- public var Passport_Identity_EditDriversLicense: String { return self._s[4396]! }
- public var Conversation_TapAndHoldToRecord: String { return self._s[4398]! }
- public var SettingsSearch_Synonyms_Notifications_BadgeIncludeMutedChats: String { return self._s[4399]! }
+ public var SettingsSearch_Synonyms_Notifications_BadgeIncludeMutedChannels: String { return self._s[4420]! }
+ public var ScheduledMessages_PollUnavailable: String { return self._s[4421]! }
+ public var VoiceOver_Navigation_Compose: String { return self._s[4422]! }
+ public var Passport_Identity_EditDriversLicense: String { return self._s[4423]! }
+ public var Conversation_TapAndHoldToRecord: String { return self._s[4425]! }
+ public var SettingsSearch_Synonyms_Notifications_BadgeIncludeMutedChats: String { return self._s[4426]! }
public func Notification_CallTimeFormat(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[4400]!, self._r[4400]!, [_1, _2])
+ return formatWithArgumentRanges(self._s[4427]!, self._r[4427]!, [_1, _2])
}
- public var Channel_EditAdmin_PermissionInviteViaLink: String { return self._s[4403]! }
- public var ChatSettings_OpenLinksIn: String { return self._s[4404]! }
- public var Map_HomeAndWorkTitle: String { return self._s[4405]! }
+ public var Channel_EditAdmin_PermissionInviteViaLink: String { return self._s[4430]! }
+ public var ChatSettings_OpenLinksIn: String { return self._s[4431]! }
+ public var Map_HomeAndWorkTitle: String { return self._s[4432]! }
public func Generic_OpenHiddenLinkAlert(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[4407]!, self._r[4407]!, [_0])
+ return formatWithArgumentRanges(self._s[4434]!, self._r[4434]!, [_0])
}
- public var DialogList_Unread: String { return self._s[4408]! }
+ public var DialogList_Unread: String { return self._s[4435]! }
public func PUSH_CHAT_MESSAGE_GIF(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[4409]!, self._r[4409]!, [_1, _2])
+ return formatWithArgumentRanges(self._s[4436]!, self._r[4436]!, [_1, _2])
}
- public var User_DeletedAccount: String { return self._s[4410]! }
- public var ChatList_TabIconFoldersTooltipEmptyFolders: String { return self._s[4411]! }
- public var OwnershipTransfer_SetupTwoStepAuth: String { return self._s[4412]! }
+ public var User_DeletedAccount: String { return self._s[4437]! }
+ public var ChatList_TabIconFoldersTooltipEmptyFolders: String { return self._s[4438]! }
+ public var OwnershipTransfer_SetupTwoStepAuth: String { return self._s[4439]! }
public func Watch_Time_ShortYesterdayAt(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[4413]!, self._r[4413]!, [_0])
+ return formatWithArgumentRanges(self._s[4440]!, self._r[4440]!, [_0])
}
- public var UserInfo_NotificationsDefault: String { return self._s[4414]! }
- public var SharedMedia_CategoryMedia: String { return self._s[4415]! }
- public var SocksProxySetup_ProxyStatusUnavailable: String { return self._s[4416]! }
- public var Channel_AdminLog_MessageRestrictedForever: String { return self._s[4417]! }
- public var Watch_ChatList_Compose: String { return self._s[4418]! }
- public var Notifications_MessageNotificationsExceptionsHelp: String { return self._s[4419]! }
- public var AutoDownloadSettings_Delimeter: String { return self._s[4420]! }
- public var Watch_Microphone_Access: String { return self._s[4421]! }
- public var Group_Setup_HistoryHeader: String { return self._s[4422]! }
- public var Map_SetThisLocation: String { return self._s[4423]! }
- public var Appearance_ThemePreview_Chat_2_ReplyName: String { return self._s[4424]! }
- public var Activity_UploadingPhoto: String { return self._s[4425]! }
- public var Conversation_Edit: String { return self._s[4427]! }
- public var Group_ErrorSendRestrictedMedia: String { return self._s[4428]! }
- public var Login_TermsOfServiceDecline: String { return self._s[4429]! }
- public var Message_PinnedContactMessage: String { return self._s[4430]! }
+ public var UserInfo_NotificationsDefault: String { return self._s[4441]! }
+ public var SharedMedia_CategoryMedia: String { return self._s[4442]! }
+ public var SocksProxySetup_ProxyStatusUnavailable: String { return self._s[4443]! }
+ public var Channel_AdminLog_MessageRestrictedForever: String { return self._s[4444]! }
+ public var Watch_ChatList_Compose: String { return self._s[4445]! }
+ public var Notifications_MessageNotificationsExceptionsHelp: String { return self._s[4446]! }
+ public var AutoDownloadSettings_Delimeter: String { return self._s[4447]! }
+ public var Watch_Microphone_Access: String { return self._s[4448]! }
+ public var Cache_MaximumCacheSize: String { return self._s[4449]! }
+ public var Group_Setup_HistoryHeader: String { return self._s[4450]! }
+ public var Map_SetThisLocation: String { return self._s[4451]! }
+ public var Appearance_ThemePreview_Chat_2_ReplyName: String { return self._s[4452]! }
+ public var Activity_UploadingPhoto: String { return self._s[4453]! }
+ public var Conversation_Edit: String { return self._s[4455]! }
+ public var Group_ErrorSendRestrictedMedia: String { return self._s[4456]! }
+ public var Login_TermsOfServiceDecline: String { return self._s[4457]! }
+ public var Message_PinnedContactMessage: String { return self._s[4458]! }
public func Channel_AdminLog_MessageRestrictedNameUsername(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[4431]!, self._r[4431]!, [_1, _2])
+ return formatWithArgumentRanges(self._s[4459]!, self._r[4459]!, [_1, _2])
}
public func Login_PhoneBannedEmailBody(_ _1: String, _ _2: String, _ _3: String, _ _4: String, _ _5: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[4432]!, self._r[4432]!, [_1, _2, _3, _4, _5])
+ return formatWithArgumentRanges(self._s[4460]!, self._r[4460]!, [_1, _2, _3, _4, _5])
}
- public var Appearance_LargeEmoji: String { return self._s[4433]! }
- public var TwoStepAuth_AdditionalPassword: String { return self._s[4435]! }
- public var EditTheme_Edit_Preview_IncomingReplyText: String { return self._s[4436]! }
+ public var Appearance_LargeEmoji: String { return self._s[4461]! }
+ public var TwoStepAuth_AdditionalPassword: String { return self._s[4463]! }
+ public var EditTheme_Edit_Preview_IncomingReplyText: String { return self._s[4464]! }
public func PUSH_CHAT_DELETE_YOU(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[4437]!, self._r[4437]!, [_1, _2])
+ return formatWithArgumentRanges(self._s[4465]!, self._r[4465]!, [_1, _2])
}
- public var Passport_Phone_EnterOtherNumber: String { return self._s[4438]! }
- public var Message_PinnedPhotoMessage: String { return self._s[4439]! }
- public var Passport_FieldPhone: String { return self._s[4440]! }
- public var TwoStepAuth_RecoveryEmailAddDescription: String { return self._s[4441]! }
- public var Stats_NotificationsTitle: String { return self._s[4442]! }
- public var ChatSettings_AutoPlayGifs: String { return self._s[4443]! }
- public var InfoPlist_NSCameraUsageDescription: String { return self._s[4445]! }
- public var Conversation_Call: String { return self._s[4446]! }
- public var Common_TakePhoto: String { return self._s[4448]! }
- public var Group_EditAdmin_RankTitle: String { return self._s[4449]! }
- public var Wallet_Receive_CommentHeader: String { return self._s[4450]! }
- public var Channel_NotificationLoading: String { return self._s[4451]! }
+ public var Passport_Phone_EnterOtherNumber: String { return self._s[4466]! }
+ public var Message_PinnedPhotoMessage: String { return self._s[4467]! }
+ public var Passport_FieldPhone: String { return self._s[4468]! }
+ public var TwoStepAuth_RecoveryEmailAddDescription: String { return self._s[4469]! }
+ public var Stats_NotificationsTitle: String { return self._s[4470]! }
+ public var ChatSettings_AutoPlayGifs: String { return self._s[4471]! }
+ public var InfoPlist_NSCameraUsageDescription: String { return self._s[4473]! }
+ public var Conversation_Call: String { return self._s[4474]! }
+ public var Common_TakePhoto: String { return self._s[4476]! }
+ public var Group_EditAdmin_RankTitle: String { return self._s[4477]! }
+ public var Wallet_Receive_CommentHeader: String { return self._s[4478]! }
+ public var Channel_NotificationLoading: String { return self._s[4479]! }
public func Notification_Exceptions_Sound(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[4452]!, self._r[4452]!, [_0])
+ return formatWithArgumentRanges(self._s[4480]!, self._r[4480]!, [_0])
}
public func ScheduledMessages_ScheduledDate(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[4453]!, self._r[4453]!, [_0])
+ return formatWithArgumentRanges(self._s[4481]!, self._r[4481]!, [_0])
}
public func PUSH_CHANNEL_MESSAGE_VIDEO(_ _1: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[4454]!, self._r[4454]!, [_1])
+ return formatWithArgumentRanges(self._s[4482]!, self._r[4482]!, [_1])
}
- public var Permissions_SiriTitle_v0: String { return self._s[4455]! }
+ public var Permissions_SiriTitle_v0: String { return self._s[4483]! }
public func VoiceOver_Chat_VoiceMessageFrom(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[4456]!, self._r[4456]!, [_0])
+ return formatWithArgumentRanges(self._s[4484]!, self._r[4484]!, [_0])
}
public func Login_ResetAccountProtected_Text(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[4457]!, self._r[4457]!, [_0])
+ return formatWithArgumentRanges(self._s[4485]!, self._r[4485]!, [_0])
}
- public var Channel_MessagePhotoRemoved: String { return self._s[4458]! }
- public var Wallet_Info_ReceiveGrams: String { return self._s[4459]! }
- public var ClearCache_FreeSpace: String { return self._s[4460]! }
- public var Appearance_BubbleCorners_Apply: String { return self._s[4461]! }
- public var Common_edit: String { return self._s[4462]! }
- public var PrivacySettings_AuthSessions: String { return self._s[4463]! }
- public var Month_ShortJune: String { return self._s[4464]! }
- public var PrivacyLastSeenSettings_AlwaysShareWith_Placeholder: String { return self._s[4465]! }
- public var Call_ReportSend: String { return self._s[4466]! }
- public var Watch_LastSeen_JustNow: String { return self._s[4467]! }
- public var Notifications_MessageNotifications: String { return self._s[4468]! }
- public var WallpaperSearch_ColorGreen: String { return self._s[4469]! }
- public var BroadcastListInfo_AddRecipient: String { return self._s[4471]! }
- public var Group_Status: String { return self._s[4472]! }
+ public var Channel_MessagePhotoRemoved: String { return self._s[4486]! }
+ public var Wallet_Info_ReceiveGrams: String { return self._s[4487]! }
+ public var ClearCache_FreeSpace: String { return self._s[4488]! }
+ public var Appearance_BubbleCorners_Apply: String { return self._s[4489]! }
+ public var Common_edit: String { return self._s[4490]! }
+ public var PrivacySettings_AuthSessions: String { return self._s[4491]! }
+ public func PUSH_VIDEO_CALL_REQUEST(_ _1: String) -> (String, [(Int, NSRange)]) {
+ return formatWithArgumentRanges(self._s[4492]!, self._r[4492]!, [_1])
+ }
+ public var Month_ShortJune: String { return self._s[4493]! }
+ public var PrivacyLastSeenSettings_AlwaysShareWith_Placeholder: String { return self._s[4494]! }
+ public var Call_ReportSend: String { return self._s[4495]! }
+ public var Watch_LastSeen_JustNow: String { return self._s[4496]! }
+ public var Notifications_MessageNotifications: String { return self._s[4497]! }
+ public var WallpaperSearch_ColorGreen: String { return self._s[4498]! }
+ public var BroadcastListInfo_AddRecipient: String { return self._s[4500]! }
+ public var Group_Status: String { return self._s[4501]! }
public func AutoNightTheme_LocationHelp(_ _0: String, _ _1: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[4473]!, self._r[4473]!, [_0, _1])
+ return formatWithArgumentRanges(self._s[4502]!, self._r[4502]!, [_0, _1])
}
- public var TextFormat_AddLinkTitle: String { return self._s[4474]! }
- public var ShareMenu_ShareTo: String { return self._s[4475]! }
- public var Conversation_Moderate_Ban: String { return self._s[4476]! }
+ public var TextFormat_AddLinkTitle: String { return self._s[4503]! }
+ public var ShareMenu_ShareTo: String { return self._s[4504]! }
+ public var Conversation_Moderate_Ban: String { return self._s[4505]! }
public func Conversation_DeleteMessagesFor(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[4477]!, self._r[4477]!, [_0])
+ return formatWithArgumentRanges(self._s[4506]!, self._r[4506]!, [_0])
}
- public var SharedMedia_ViewInChat: String { return self._s[4478]! }
- public var Map_LiveLocationFor8Hours: String { return self._s[4479]! }
+ public var SharedMedia_ViewInChat: String { return self._s[4507]! }
+ public var Map_LiveLocationFor8Hours: String { return self._s[4508]! }
public func PUSH_PINNED_PHOTO(_ _1: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[4480]!, self._r[4480]!, [_1])
+ return formatWithArgumentRanges(self._s[4509]!, self._r[4509]!, [_1])
}
public func PUSH_PINNED_POLL(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[4481]!, self._r[4481]!, [_1, _2])
+ return formatWithArgumentRanges(self._s[4510]!, self._r[4510]!, [_1, _2])
}
public func Map_AccurateTo(_ _0: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[4483]!, self._r[4483]!, [_0])
+ return formatWithArgumentRanges(self._s[4512]!, self._r[4512]!, [_0])
}
- public var Map_OpenInHereMaps: String { return self._s[4484]! }
- public var Appearance_ReduceMotion: String { return self._s[4485]! }
+ public var Map_OpenInHereMaps: String { return self._s[4513]! }
+ public var Appearance_ReduceMotion: String { return self._s[4514]! }
public func PUSH_MESSAGE_TEXT(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
- return formatWithArgumentRanges(self._s[4486]!, self._r[4486]!, [_1, _2])
+ return formatWithArgumentRanges(self._s[4515]!, self._r[4515]!, [_1, _2])
}
- public var Channel_Setup_TypePublicHelp: String { return self._s[4487]! }
- public var Passport_Identity_EditInternalPassport: String { return self._s[4488]! }
- public var PhotoEditor_Skip: String { return self._s[4489]! }
- public func Notification_GameScoreExtended(_ value: Int32) -> String {
+ public var Channel_Setup_TypePublicHelp: String { return self._s[4516]! }
+ public var Passport_Identity_EditInternalPassport: String { return self._s[4517]! }
+ public var PhotoEditor_Skip: String { return self._s[4518]! }
+ public func Notifications_ExceptionMuteExpires_Hours(_ value: Int32) -> String {
let form = getPluralizationForm(self.lc, value)
let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
return String(format: self._ps[0 * 6 + Int(form.rawValue)]!, stringValue)
}
- public func GroupInfo_ParticipantCount(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[1 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func MessagePoll_VotedCount(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[2 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func MessageTimer_Minutes(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[3 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func OldChannels_Leave(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[4 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func Map_ETAHours(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[5 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func ForwardedPhotos(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[6 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func PUSH_MESSAGE_VIDEOS(_ selector: Int32, _ _1: String, _ _2: Int32) -> String {
+ public func PUSH_CHANNEL_MESSAGES(_ selector: Int32, _ _1: String, _ _2: Int32) -> String {
let form = getPluralizationForm(self.lc, selector)
- return String(format: self._ps[7 * 6 + Int(form.rawValue)]!, _1, _2)
- }
- public func MessageTimer_ShortMinutes(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[8 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func OldChannels_InactiveYear(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[9 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func GroupInfo_ShowMoreMembers(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[10 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func ServiceMessage_GameScoreSelfSimple(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[11 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func ChatList_MessageVideos(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[12 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func StickerPack_AddMaskCount(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[13 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func ServiceMessage_GameScoreSimple(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[14 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func ForwardedVideoMessages(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[15 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func SharedMedia_Generic(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[16 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func CreatePoll_AddMoreOptions(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[17 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func StickerPack_AddStickerCount(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[18 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func Watch_LastSeen_HoursAgo(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[19 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func Stats_MessageForwards(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[20 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func ForwardedFiles(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[21 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func ForwardedAuthorsOthers(_ selector: Int32, _ _0: String, _ _1: String) -> String {
- let form = getPluralizationForm(self.lc, selector)
- return String(format: self._ps[22 * 6 + Int(form.rawValue)]!, _0, _1)
- }
- public func Chat_DeleteMessagesConfirmation(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[23 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func PollResults_ShowMore(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[24 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func Stats_MessageViews(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[25 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func ForwardedMessages(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[26 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func PUSH_MESSAGE_PHOTOS(_ selector: Int32, _ _1: String, _ _2: Int32) -> String {
- let form = getPluralizationForm(self.lc, selector)
- return String(format: self._ps[27 * 6 + Int(form.rawValue)]!, _1, _2)
+ return String(format: self._ps[1 * 6 + Int(form.rawValue)]!, _1, _2)
}
public func MuteExpires_Minutes(_ value: Int32) -> String {
let form = getPluralizationForm(self.lc, value)
let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[28 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func VoiceOver_Chat_PollVotes(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[29 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func Stats_GroupTopPosterChars(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[30 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func PUSH_CHAT_MESSAGE_VIDEOS(_ selector: Int32, _ _2: String, _ _1: String, _ _3: Int32) -> String {
- let form = getPluralizationForm(self.lc, selector)
- return String(format: self._ps[31 * 6 + Int(form.rawValue)]!, _2, _1, _3)
- }
- public func SharedMedia_Photo(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[32 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func MuteFor_Days(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[33 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func PrivacyLastSeenSettings_AddUsers(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[34 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func MuteExpires_Days(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[35 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func StickerPack_StickerCount(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[36 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func PUSH_MESSAGE_ROUNDS(_ selector: Int32, _ _1: String, _ _2: Int32) -> String {
- let form = getPluralizationForm(self.lc, selector)
- return String(format: self._ps[37 * 6 + Int(form.rawValue)]!, _1, _2)
- }
- public func OldChannels_InactiveWeek(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[38 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func AttachmentMenu_SendGif(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[39 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func Stats_GroupTopInviterInvites(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[40 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func Stats_GroupTopAdminBans(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[41 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func OldChannels_GroupFormat(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[42 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func Wallpaper_DeleteConfirmation(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[43 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func Theme_UsersCount(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[44 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func Passport_Scans(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[45 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func Call_Minutes(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[46 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func VoiceOver_Chat_ContactEmailCount(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[47 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func ChatListFilter_ShowMoreChats(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[48 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func StickerPack_RemoveMaskCount(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[49 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func UserCount(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[50 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func ChatList_MessagePhotos(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[51 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func Conversation_SelectedMessages(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[52 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func ForwardedStickers(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[53 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func MessageTimer_ShortDays(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[54 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func Conversation_StatusOnline(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[55 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func ServiceMessage_GameScoreSelfExtended(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[56 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func Invitation_Members(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[57 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func MessageTimer_Years(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[58 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func InviteText_ContactsCountText(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[59 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func PasscodeSettings_FailedAttempts(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[60 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func MessageTimer_Seconds(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[61 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func Notifications_Exceptions(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[62 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func LiveLocation_MenuChatsCount(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[63 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func MessagePoll_QuizCount(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[64 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func MuteFor_Hours(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[65 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func ForwardedVideos(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[66 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func VoiceOver_Chat_PollOptionCount(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[67 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func PUSH_CHANNEL_MESSAGE_PHOTOS(_ selector: Int32, _ _1: String, _ _2: Int32) -> String {
- let form = getPluralizationForm(self.lc, selector)
- return String(format: self._ps[68 * 6 + Int(form.rawValue)]!, _1, _2)
- }
- public func Conversation_StatusMembers(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[69 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func LastSeen_HoursAgo(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[70 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func Stats_GroupTopAdminKicks(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[71 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func Media_ShareVideo(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[72 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func Call_ShortSeconds(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[73 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func Stats_GroupShowMoreTopPosters(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[74 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func ChatList_DeletedChats(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[75 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func Contacts_ImportersCount(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[76 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func Contacts_InviteContacts(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[77 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func PUSH_CHAT_MESSAGE_FWDS(_ selector: Int32, _ _2: String, _ _1: String, _ _3: Int32) -> String {
- let form = getPluralizationForm(self.lc, selector)
- return String(format: self._ps[78 * 6 + Int(form.rawValue)]!, _2, _1, _3)
- }
- public func MessageTimer_ShortHours(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[79 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func ForwardedGifs(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[80 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func MessageTimer_Months(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[81 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func ForwardedAudios(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[82 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func LiveLocationUpdated_MinutesAgo(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[83 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func Stats_GroupShowMoreTopAdmins(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[84 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func OldChannels_InactiveMonth(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[85 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func QuickSend_Photos(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[86 * 6 + Int(form.rawValue)]!, stringValue)
+ return String(format: self._ps[2 * 6 + Int(form.rawValue)]!, stringValue)
}
public func AttachmentMenu_SendItem(_ value: Int32) -> String {
let form = getPluralizationForm(self.lc, value)
let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[87 * 6 + Int(form.rawValue)]!, stringValue)
+ return String(format: self._ps[3 * 6 + Int(form.rawValue)]!, stringValue)
}
- public func PUSH_CHANNEL_MESSAGES(_ selector: Int32, _ _1: String, _ _2: Int32) -> String {
- let form = getPluralizationForm(self.lc, selector)
- return String(format: self._ps[88 * 6 + Int(form.rawValue)]!, _1, _2)
- }
- public func Wallet_Updated_MinutesAgo(_ value: Int32) -> String {
+ public func Conversation_SelectedMessages(_ value: Int32) -> String {
let form = getPluralizationForm(self.lc, value)
let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[89 * 6 + Int(form.rawValue)]!, stringValue)
+ return String(format: self._ps[4 * 6 + Int(form.rawValue)]!, stringValue)
}
- public func Watch_LastSeen_MinutesAgo(_ value: Int32) -> String {
+ public func MuteFor_Hours(_ value: Int32) -> String {
let form = getPluralizationForm(self.lc, value)
let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[90 * 6 + Int(form.rawValue)]!, stringValue)
+ return String(format: self._ps[5 * 6 + Int(form.rawValue)]!, stringValue)
}
- public func PUSH_CHAT_MESSAGES(_ selector: Int32, _ _2: String, _ _1: String, _ _3: Int32) -> String {
- let form = getPluralizationForm(self.lc, selector)
- return String(format: self._ps[91 * 6 + Int(form.rawValue)]!, _2, _1, _3)
- }
- public func ChatList_SelectedChats(_ value: Int32) -> String {
+ public func Watch_LastSeen_HoursAgo(_ value: Int32) -> String {
let form = getPluralizationForm(self.lc, value)
let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[92 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func PUSH_CHAT_MESSAGE_ROUNDS(_ selector: Int32, _ _2: String, _ _1: String, _ _3: Int32) -> String {
- let form = getPluralizationForm(self.lc, selector)
- return String(format: self._ps[93 * 6 + Int(form.rawValue)]!, _2, _1, _3)
- }
- public func Watch_UserInfo_Mute(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[94 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func Notifications_ExceptionMuteExpires_Hours(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[95 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func PeopleNearby_ShowMorePeople(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[96 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func Media_ShareItem(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[97 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func MessageTimer_Days(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[98 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func ForwardedLocations(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[99 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func Call_Seconds(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[100 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func Wallet_Updated_HoursAgo(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[101 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func Forward_ConfirmMultipleFiles(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[102 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func Notification_GameScoreSimple(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[103 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func LastSeen_MinutesAgo(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[104 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func AttachmentMenu_SendVideo(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[105 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func MessageTimer_ShortWeeks(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[106 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func Stats_GroupTopAdminDeletions(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[107 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func PUSH_MESSAGES(_ selector: Int32, _ _1: String, _ _2: Int32) -> String {
- let form = getPluralizationForm(self.lc, selector)
- return String(format: self._ps[108 * 6 + Int(form.rawValue)]!, _1, _2)
+ return String(format: self._ps[6 * 6 + Int(form.rawValue)]!, stringValue)
}
public func Conversation_StatusSubscribers(_ value: Int32) -> String {
let form = getPluralizationForm(self.lc, value)
let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[109 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func InstantPage_Views(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[110 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func ForwardedContacts(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[111 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func Conversation_LiveLocationMembersCount(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[112 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func SharedMedia_File(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[113 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func PUSH_CHANNEL_MESSAGE_ROUNDS(_ selector: Int32, _ _1: String, _ _2: Int32) -> String {
- let form = getPluralizationForm(self.lc, selector)
- return String(format: self._ps[114 * 6 + Int(form.rawValue)]!, _1, _2)
- }
- public func Map_ETAMinutes(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[115 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func Call_ShortMinutes(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[116 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func ChatList_DeleteConfirmation(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[117 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func Notifications_ExceptionMuteExpires_Days(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[118 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func MuteExpires_Hours(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[119 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func PUSH_MESSAGE_FWDS(_ selector: Int32, _ _1: String, _ _2: Int32) -> String {
- let form = getPluralizationForm(self.lc, selector)
- return String(format: self._ps[120 * 6 + Int(form.rawValue)]!, _1, _2)
- }
- public func VoiceOver_Chat_ContactPhoneNumberCount(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[121 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func PUSH_CHANNEL_MESSAGE_VIDEOS(_ selector: Int32, _ _1: String, _ _2: Int32) -> String {
- let form = getPluralizationForm(self.lc, selector)
- return String(format: self._ps[122 * 6 + Int(form.rawValue)]!, _1, _2)
- }
- public func Notification_GameScoreSelfExtended(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[123 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func SharedMedia_Link(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[124 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func Media_SharePhoto(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[125 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func Stats_GroupShowMoreTopInviters(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[126 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func MessageTimer_Weeks(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[127 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func MessageTimer_ShortSeconds(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[128 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func PUSH_CHANNEL_MESSAGE_FWDS(_ selector: Int32, _ _1: String, _ _2: Int32) -> String {
- let form = getPluralizationForm(self.lc, selector)
- return String(format: self._ps[129 * 6 + Int(form.rawValue)]!, _1, _2)
- }
- public func Stats_GroupTopPosterMessages(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[130 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func SharedMedia_Video(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[131 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func AttachmentMenu_SendPhoto(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[132 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func ServiceMessage_GameScoreExtended(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[133 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func Notification_GameScoreSelfSimple(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[134 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func PUSH_CHAT_MESSAGE_PHOTOS(_ selector: Int32, _ _2: String, _ _1: String, _ _3: Int32) -> String {
- let form = getPluralizationForm(self.lc, selector)
- return String(format: self._ps[135 * 6 + Int(form.rawValue)]!, _2, _1, _3)
+ return String(format: self._ps[7 * 6 + Int(form.rawValue)]!, stringValue)
}
public func Notifications_ExceptionMuteExpires_Minutes(_ value: Int32) -> String {
let form = getPluralizationForm(self.lc, value)
let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[136 * 6 + Int(form.rawValue)]!, stringValue)
+ return String(format: self._ps[8 * 6 + Int(form.rawValue)]!, stringValue)
}
- public func ForwardedPolls(_ value: Int32) -> String {
+ public func Conversation_StatusMembers(_ value: Int32) -> String {
let form = getPluralizationForm(self.lc, value)
let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[137 * 6 + Int(form.rawValue)]!, stringValue)
+ return String(format: self._ps[9 * 6 + Int(form.rawValue)]!, stringValue)
}
- public func StickerPack_RemoveStickerCount(_ value: Int32) -> String {
+ public func OldChannels_InactiveMonth(_ value: Int32) -> String {
let form = getPluralizationForm(self.lc, value)
let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[138 * 6 + Int(form.rawValue)]!, stringValue)
+ return String(format: self._ps[10 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func Stats_GroupTopInviterInvites(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[11 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func Passport_Scans(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[12 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func LastSeen_HoursAgo(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[13 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func Call_Minutes(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[14 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func Notification_GameScoreSimple(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[15 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func Watch_LastSeen_MinutesAgo(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[16 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func PUSH_MESSAGE_ROUNDS(_ selector: Int32, _ _1: String, _ _2: Int32) -> String {
+ let form = getPluralizationForm(self.lc, selector)
+ return String(format: self._ps[17 * 6 + Int(form.rawValue)]!, _1, _2)
+ }
+ public func Forward_ConfirmMultipleFiles(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[18 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func CreatePoll_AddMoreOptions(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[19 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func Stats_MessageViews(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[20 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func PasscodeSettings_FailedAttempts(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[21 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func ForwardedVideoMessages(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[22 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func Conversation_StatusOnline(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[23 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func ForwardedFiles(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[24 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func ServiceMessage_GameScoreSelfExtended(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[25 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func Stats_GroupTopAdminDeletions(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[26 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func InviteText_ContactsCountText(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[27 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func Media_ShareVideo(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[28 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func SharedMedia_Photo(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[29 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func MessageTimer_ShortHours(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[30 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func MessageTimer_ShortSeconds(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[31 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func ChatListFilter_ShowMoreChats(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[32 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func MessageTimer_Weeks(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[33 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func Call_Seconds(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[34 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func Stats_MessageForwards(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[35 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func PUSH_CHAT_MESSAGE_VIDEOS(_ selector: Int32, _ _2: String, _ _1: String, _ _3: Int32) -> String {
+ let form = getPluralizationForm(self.lc, selector)
+ return String(format: self._ps[36 * 6 + Int(form.rawValue)]!, _2, _1, _3)
+ }
+ public func Wallet_Updated_MinutesAgo(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[37 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func Notification_GameScoreExtended(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[38 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func Wallpaper_DeleteConfirmation(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[39 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func MessageTimer_Years(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[40 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func StickerPack_RemoveMaskCount(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[41 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func ForwardedLocations(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[42 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func PUSH_CHANNEL_MESSAGE_PHOTOS(_ selector: Int32, _ _1: String, _ _2: Int32) -> String {
+ let form = getPluralizationForm(self.lc, selector)
+ return String(format: self._ps[43 * 6 + Int(form.rawValue)]!, _1, _2)
+ }
+ public func PUSH_CHAT_MESSAGE_ROUNDS(_ selector: Int32, _ _2: String, _ _1: String, _ _3: Int32) -> String {
+ let form = getPluralizationForm(self.lc, selector)
+ return String(format: self._ps[44 * 6 + Int(form.rawValue)]!, _2, _1, _3)
+ }
+ public func MessageTimer_ShortWeeks(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[45 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func MessageTimer_Days(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[46 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func ForwardedPhotos(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[47 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func Notification_GameScoreSelfSimple(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[48 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func InstantPage_Views(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[49 * 6 + Int(form.rawValue)]!, stringValue)
}
public func DialogList_LiveLocationChatsCount(_ value: Int32) -> String {
let form = getPluralizationForm(self.lc, value)
let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[139 * 6 + Int(form.rawValue)]!, stringValue)
+ return String(format: self._ps[50 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func AttachmentMenu_SendPhoto(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[51 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func StickerPack_RemoveStickerCount(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[52 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func StickerPack_AddMaskCount(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[53 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func Contacts_InviteContacts(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[54 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func Call_ShortMinutes(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[55 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func PUSH_CHAT_MESSAGE_PHOTOS(_ selector: Int32, _ _2: String, _ _1: String, _ _3: Int32) -> String {
+ let form = getPluralizationForm(self.lc, selector)
+ return String(format: self._ps[56 * 6 + Int(form.rawValue)]!, _2, _1, _3)
+ }
+ public func ForwardedPolls(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[57 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func ChatList_MessageVideos(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[58 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func PUSH_CHAT_MESSAGES(_ selector: Int32, _ _2: String, _ _1: String, _ _3: Int32) -> String {
+ let form = getPluralizationForm(self.lc, selector)
+ return String(format: self._ps[59 * 6 + Int(form.rawValue)]!, _2, _1, _3)
+ }
+ public func PUSH_MESSAGE_VIDEOS(_ selector: Int32, _ _1: String, _ _2: Int32) -> String {
+ let form = getPluralizationForm(self.lc, selector)
+ return String(format: self._ps[60 * 6 + Int(form.rawValue)]!, _1, _2)
+ }
+ public func MuteExpires_Days(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[61 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func ServiceMessage_GameScoreSelfSimple(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[62 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func MuteExpires_Hours(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[63 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func ForwardedStickers(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[64 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func Notifications_Exceptions(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[65 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func GroupInfo_ShowMoreMembers(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[66 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func MessageTimer_Minutes(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[67 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func MessagePoll_QuizCount(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[68 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func PUSH_MESSAGE_PHOTOS(_ selector: Int32, _ _1: String, _ _2: Int32) -> String {
+ let form = getPluralizationForm(self.lc, selector)
+ return String(format: self._ps[69 * 6 + Int(form.rawValue)]!, _1, _2)
+ }
+ public func PUSH_CHANNEL_MESSAGE_VIDEOS(_ selector: Int32, _ _1: String, _ _2: Int32) -> String {
+ let form = getPluralizationForm(self.lc, selector)
+ return String(format: self._ps[70 * 6 + Int(form.rawValue)]!, _1, _2)
+ }
+ public func Stats_GroupShowMoreTopAdmins(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[71 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func SharedMedia_Link(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[72 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func AttachmentMenu_SendGif(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[73 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func PUSH_MESSAGES(_ selector: Int32, _ _1: String, _ _2: Int32) -> String {
+ let form = getPluralizationForm(self.lc, selector)
+ return String(format: self._ps[74 * 6 + Int(form.rawValue)]!, _1, _2)
+ }
+ public func VoiceOver_Chat_PollOptionCount(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[75 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func ForwardedGifs(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[76 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func StickerPack_AddStickerCount(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[77 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func Wallet_Updated_HoursAgo(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[78 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func Watch_UserInfo_Mute(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[79 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func Conversation_LiveLocationMembersCount(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[80 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func Media_SharePhoto(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[81 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func ForwardedContacts(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[82 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func OldChannels_InactiveWeek(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[83 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func Contacts_ImportersCount(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[84 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func Map_ETAHours(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[85 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func ForwardedAuthorsOthers(_ selector: Int32, _ _0: String, _ _1: String) -> String {
+ let form = getPluralizationForm(self.lc, selector)
+ return String(format: self._ps[86 * 6 + Int(form.rawValue)]!, _0, _1)
+ }
+ public func Stats_GroupTopAdminBans(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[87 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func Stats_GroupTopAdminKicks(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[88 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func VoiceOver_Chat_ContactPhoneNumberCount(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[89 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func SharedMedia_File(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[90 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func AttachmentMenu_SendVideo(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[91 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func MessageTimer_Months(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[92 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func UserCount(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[93 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func PUSH_MESSAGE_FWDS(_ selector: Int32, _ _1: String, _ _2: Int32) -> String {
+ let form = getPluralizationForm(self.lc, selector)
+ return String(format: self._ps[94 * 6 + Int(form.rawValue)]!, _1, _2)
+ }
+ public func ServiceMessage_GameScoreExtended(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[95 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func Theme_UsersCount(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[96 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func VoiceOver_Chat_PollVotes(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[97 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func ChatList_MessagePhotos(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[98 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func MessageTimer_ShortMinutes(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[99 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func PeopleNearby_ShowMorePeople(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[100 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func Call_ShortSeconds(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[101 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func GroupInfo_ParticipantCount(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[102 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func ForwardedVideos(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[103 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func PUSH_CHANNEL_MESSAGE_ROUNDS(_ selector: Int32, _ _1: String, _ _2: Int32) -> String {
+ let form = getPluralizationForm(self.lc, selector)
+ return String(format: self._ps[104 * 6 + Int(form.rawValue)]!, _1, _2)
+ }
+ public func Media_ShareItem(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[105 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func Map_ETAMinutes(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[106 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func PUSH_CHAT_MESSAGE_FWDS(_ selector: Int32, _ _2: String, _ _1: String, _ _3: Int32) -> String {
+ let form = getPluralizationForm(self.lc, selector)
+ return String(format: self._ps[107 * 6 + Int(form.rawValue)]!, _2, _1, _3)
+ }
+ public func MuteFor_Days(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[108 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func LastSeen_MinutesAgo(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[109 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func SharedMedia_Generic(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[110 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func MessagePoll_VotedCount(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[111 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func OldChannels_Leave(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[112 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func PUSH_CHANNEL_MESSAGE_FWDS(_ selector: Int32, _ _1: String, _ _2: Int32) -> String {
+ let form = getPluralizationForm(self.lc, selector)
+ return String(format: self._ps[113 * 6 + Int(form.rawValue)]!, _1, _2)
+ }
+ public func Notification_GameScoreSelfExtended(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[114 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func ServiceMessage_GameScoreSimple(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[115 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func MessageTimer_Seconds(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[116 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func Stats_GroupShowMoreTopInviters(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[117 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func LiveLocation_MenuChatsCount(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[118 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func SharedMedia_Video(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[119 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func ForwardedMessages(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[120 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func StickerPack_StickerCount(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[121 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func Notifications_ExceptionMuteExpires_Days(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[122 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func VoiceOver_Chat_ContactEmailCount(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[123 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func Stats_GroupShowMoreTopPosters(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[124 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func Stats_GroupTopPosterMessages(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[125 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func Chat_DeleteMessagesConfirmation(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[126 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func ChatList_SelectedChats(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[127 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func ForwardedAudios(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[128 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func Invitation_Members(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[129 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func OldChannels_GroupFormat(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[130 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func MessageTimer_ShortDays(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[131 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func Stats_GroupTopPosterChars(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[132 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func ChatList_DeleteConfirmation(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[133 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func LiveLocationUpdated_MinutesAgo(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[134 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func MessageTimer_Hours(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[135 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func QuickSend_Photos(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[136 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func OldChannels_InactiveYear(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[137 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func ChatList_DeletedChats(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[138 * 6 + Int(form.rawValue)]!, stringValue)
}
public func SharedMedia_DeleteItemsConfirmation(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[139 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func PollResults_ShowMore(_ value: Int32) -> String {
let form = getPluralizationForm(self.lc, value)
let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
return String(format: self._ps[140 * 6 + Int(form.rawValue)]!, stringValue)
}
- public func MessageTimer_Hours(_ value: Int32) -> String {
+ public func PrivacyLastSeenSettings_AddUsers(_ value: Int32) -> String {
let form = getPluralizationForm(self.lc, value)
let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
return String(format: self._ps[141 * 6 + Int(form.rawValue)]!, stringValue)
diff --git a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourceKey.swift b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourceKey.swift
index df0a347265..8ba11022da 100644
--- a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourceKey.swift
+++ b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourceKey.swift
@@ -55,6 +55,9 @@ public enum PresentationResourceKey: Int32 {
case itemListCornersBottom
case itemListCornersBoth
+ case itemListVoiceCallIcon
+ case itemListVideoCallIcon
+
case chatListLockTopUnlockedImage
case chatListLockBottomUnlockedImage
case chatListPending
@@ -207,8 +210,12 @@ public enum PresentationResourceKey: Int32 {
case chatBubbleIncomingCallButtonImage
case chatBubbleOutgoingCallButtonImage
+
+ case chatBubbleIncomingVideoCallButtonImage
+ case chatBubbleOutgoingVideoCallButtonImage
case callListOutgoingIcon
+ case callListOutgoingVideoIcon
case callListInfoButton
case genericSearchBarLoupeImage
diff --git a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesCallList.swift b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesCallList.swift
index 519697a987..07c3aced71 100644
--- a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesCallList.swift
+++ b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesCallList.swift
@@ -10,6 +10,12 @@ public struct PresentationResourcesCallList {
})
}
+ public static func outgoingVideoIcon(_ theme: PresentationTheme) -> UIImage? {
+ return theme.image(PresentationResourceKey.callListOutgoingVideoIcon.rawValue, { theme in
+ return generateTintedImage(image: UIImage(bundleImageName: "Call List/OutgoingVideoIcon"), color: theme.list.disclosureArrowColor)
+ })
+ }
+
public static func infoButton(_ theme: PresentationTheme) -> UIImage? {
return theme.image(PresentationResourceKey.callListInfoButton.rawValue, { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Call List/InfoButton"), color: theme.list.itemAccentColor)
diff --git a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesChat.swift b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesChat.swift
index 2d90995d69..42eaae3348 100644
--- a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesChat.swift
+++ b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesChat.swift
@@ -723,6 +723,18 @@ public struct PresentationResourcesChat {
})
}
+ public static func chatBubbleIncomingVideoCallButtonImage(_ theme: PresentationTheme) -> UIImage? {
+ return theme.image(PresentationResourceKey.chatBubbleIncomingVideoCallButtonImage.rawValue, { theme in
+ return generateTintedImage(image: UIImage(bundleImageName: "Chat/Info/VideoCallButton"), color: theme.chat.message.incoming.accentControlColor)
+ })
+ }
+
+ public static func chatBubbleOutgoingVideoCallButtonImage(_ theme: PresentationTheme) -> UIImage? {
+ return theme.image(PresentationResourceKey.chatBubbleOutgoingVideoCallButtonImage.rawValue, { theme in
+ return generateTintedImage(image: UIImage(bundleImageName: "Chat/Info/VideoCallButton"), color: theme.chat.message.outgoing.accentControlColor)
+ })
+ }
+
public static func chatInputSearchPanelUpImage(_ theme: PresentationTheme) -> UIImage? {
return theme.image(PresentationResourceKey.chatInputSearchPanelUpImage.rawValue, { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Search/UpButton"), color: theme.chat.inputPanel.panelControlAccentColor)
diff --git a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesItemList.swift b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesItemList.swift
index 8a3e6fc808..aaf11406da 100644
--- a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesItemList.swift
+++ b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesItemList.swift
@@ -108,6 +108,18 @@ public struct PresentationResourcesItemList {
})
}
+ public static func voiceCallIcon(_ theme: PresentationTheme) -> UIImage? {
+ return theme.image(PresentationResourceKey.itemListVoiceCallIcon.rawValue, { theme in
+ return generateTintedImage(image: UIImage(bundleImageName: "Chat/Info/CallButton"), color: theme.list.itemAccentColor)
+ })
+ }
+
+ public static func videoCallIcon(_ theme: PresentationTheme) -> UIImage? {
+ return theme.image(PresentationResourceKey.itemListVideoCallIcon.rawValue, { theme in
+ return generateTintedImage(image: UIImage(bundleImageName: "Chat/Info/VideoCallButton"), color: theme.list.itemAccentColor)
+ })
+ }
+
public static func addPhoneIcon(_ theme: PresentationTheme) -> UIImage? {
return theme.image(PresentationResourceKey.itemListAddPhoneIcon.rawValue, { theme in
guard let image = generateTintedImage(image: UIImage(bundleImageName: "Item List/AddItemIcon"), color: theme.list.itemAccentColor) else {
diff --git a/submodules/TelegramUI/Images.xcassets/Call List/OutgoingIcon.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Call List/OutgoingIcon.imageset/Contents.json
index 3eb9c046c6..4a566054b2 100644
--- a/submodules/TelegramUI/Images.xcassets/Call List/OutgoingIcon.imageset/Contents.json
+++ b/submodules/TelegramUI/Images.xcassets/Call List/OutgoingIcon.imageset/Contents.json
@@ -1,12 +1,12 @@
{
"images" : [
{
- "idiom" : "universal",
- "filename" : "ic_outgoingcall.pdf"
+ "filename" : "ic_outvoice.pdf",
+ "idiom" : "universal"
}
],
"info" : {
- "version" : 1,
- "author" : "xcode"
+ "author" : "xcode",
+ "version" : 1
}
-}
\ No newline at end of file
+}
diff --git a/submodules/TelegramUI/Images.xcassets/Call List/OutgoingIcon.imageset/ic_outvoice.pdf b/submodules/TelegramUI/Images.xcassets/Call List/OutgoingIcon.imageset/ic_outvoice.pdf
new file mode 100644
index 0000000000..6e1cba8f64
Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Call List/OutgoingIcon.imageset/ic_outvoice.pdf differ
diff --git a/submodules/TelegramUI/Images.xcassets/Call List/OutgoingVideoIcon.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Call List/OutgoingVideoIcon.imageset/Contents.json
new file mode 100644
index 0000000000..242b4a3982
--- /dev/null
+++ b/submodules/TelegramUI/Images.xcassets/Call List/OutgoingVideoIcon.imageset/Contents.json
@@ -0,0 +1,12 @@
+{
+ "images" : [
+ {
+ "filename" : "ic_outvideo (2).pdf",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/submodules/TelegramUI/Images.xcassets/Call List/OutgoingVideoIcon.imageset/ic_outvideo (2).pdf b/submodules/TelegramUI/Images.xcassets/Call List/OutgoingVideoIcon.imageset/ic_outvideo (2).pdf
new file mode 100644
index 0000000000..27f1c55637
Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Call List/OutgoingVideoIcon.imageset/ic_outvideo (2).pdf differ
diff --git a/submodules/TelegramUI/Images.xcassets/Call/CallAirpodsButton.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Call/CallAirpodsButton.imageset/Contents.json
new file mode 100644
index 0000000000..0b0b845832
--- /dev/null
+++ b/submodules/TelegramUI/Images.xcassets/Call/CallAirpodsButton.imageset/Contents.json
@@ -0,0 +1,12 @@
+{
+ "images" : [
+ {
+ "filename" : "ic_call_audioairpods.pdf",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/submodules/TelegramUI/Images.xcassets/Call/CallAirpodsButton.imageset/ic_call_audioairpods.pdf b/submodules/TelegramUI/Images.xcassets/Call/CallAirpodsButton.imageset/ic_call_audioairpods.pdf
new file mode 100644
index 0000000000..2f31d498cc
Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Call/CallAirpodsButton.imageset/ic_call_audioairpods.pdf differ
diff --git a/submodules/TelegramUI/Images.xcassets/Call/CallAirpodsProButton.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Call/CallAirpodsProButton.imageset/Contents.json
new file mode 100644
index 0000000000..370377d8ee
--- /dev/null
+++ b/submodules/TelegramUI/Images.xcassets/Call/CallAirpodsProButton.imageset/Contents.json
@@ -0,0 +1,12 @@
+{
+ "images" : [
+ {
+ "filename" : "ic_call_audioairpodspro.pdf",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/submodules/TelegramUI/Images.xcassets/Call/CallAirpodsProButton.imageset/ic_call_audioairpodspro.pdf b/submodules/TelegramUI/Images.xcassets/Call/CallAirpodsProButton.imageset/ic_call_audioairpodspro.pdf
new file mode 100644
index 0000000000..305f6ba52b
Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Call/CallAirpodsProButton.imageset/ic_call_audioairpodspro.pdf differ
diff --git a/submodules/TelegramUI/Images.xcassets/Call/CallBluetoothButton.imageset/CallBluetoothIcon@2x.png b/submodules/TelegramUI/Images.xcassets/Call/CallBluetoothButton.imageset/CallBluetoothIcon@2x.png
deleted file mode 100644
index bc5951252f..0000000000
Binary files a/submodules/TelegramUI/Images.xcassets/Call/CallBluetoothButton.imageset/CallBluetoothIcon@2x.png and /dev/null differ
diff --git a/submodules/TelegramUI/Images.xcassets/Call/CallBluetoothButton.imageset/CallBluetoothIcon@3x.png b/submodules/TelegramUI/Images.xcassets/Call/CallBluetoothButton.imageset/CallBluetoothIcon@3x.png
deleted file mode 100644
index 846ad33529..0000000000
Binary files a/submodules/TelegramUI/Images.xcassets/Call/CallBluetoothButton.imageset/CallBluetoothIcon@3x.png and /dev/null differ
diff --git a/submodules/TelegramUI/Images.xcassets/Call/CallBluetoothButton.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Call/CallBluetoothButton.imageset/Contents.json
index f97cc97939..a448d215b7 100644
--- a/submodules/TelegramUI/Images.xcassets/Call/CallBluetoothButton.imageset/Contents.json
+++ b/submodules/TelegramUI/Images.xcassets/Call/CallBluetoothButton.imageset/Contents.json
@@ -1,22 +1,12 @@
{
"images" : [
{
- "idiom" : "universal",
- "scale" : "1x"
- },
- {
- "idiom" : "universal",
- "filename" : "CallBluetoothIcon@2x.png",
- "scale" : "2x"
- },
- {
- "idiom" : "universal",
- "filename" : "CallBluetoothIcon@3x.png",
- "scale" : "3x"
+ "filename" : "ic_call_audiobt.pdf",
+ "idiom" : "universal"
}
],
"info" : {
- "version" : 1,
- "author" : "xcode"
+ "author" : "xcode",
+ "version" : 1
}
-}
\ No newline at end of file
+}
diff --git a/submodules/TelegramUI/Images.xcassets/Call/CallBluetoothButton.imageset/ic_call_audiobt.pdf b/submodules/TelegramUI/Images.xcassets/Call/CallBluetoothButton.imageset/ic_call_audiobt.pdf
new file mode 100644
index 0000000000..a2f8cad6cf
Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Call/CallBluetoothButton.imageset/ic_call_audiobt.pdf differ
diff --git a/submodules/TelegramUI/Images.xcassets/Call/CallCameraButton.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Call/CallCameraButton.imageset/Contents.json
index 1a290513b6..324f180936 100644
--- a/submodules/TelegramUI/Images.xcassets/Call/CallCameraButton.imageset/Contents.json
+++ b/submodules/TelegramUI/Images.xcassets/Call/CallCameraButton.imageset/Contents.json
@@ -1,7 +1,7 @@
{
"images" : [
{
- "filename" : "ic_calls_video.pdf",
+ "filename" : "ic_call_camera.pdf",
"idiom" : "universal"
}
],
diff --git a/submodules/TelegramUI/Images.xcassets/Call/CallSwitchCameraButton.imageset/ic_calls_cameraflip.pdf b/submodules/TelegramUI/Images.xcassets/Call/CallCameraButton.imageset/ic_call_camera.pdf
similarity index 75%
rename from submodules/TelegramUI/Images.xcassets/Call/CallSwitchCameraButton.imageset/ic_calls_cameraflip.pdf
rename to submodules/TelegramUI/Images.xcassets/Call/CallCameraButton.imageset/ic_call_camera.pdf
index 4f0db952ed..92a55e486d 100644
Binary files a/submodules/TelegramUI/Images.xcassets/Call/CallSwitchCameraButton.imageset/ic_calls_cameraflip.pdf and b/submodules/TelegramUI/Images.xcassets/Call/CallCameraButton.imageset/ic_call_camera.pdf differ
diff --git a/submodules/TelegramUI/Images.xcassets/Call/CallCameraHDButton.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Call/CallCameraHDButton.imageset/Contents.json
new file mode 100644
index 0000000000..05d9903d16
--- /dev/null
+++ b/submodules/TelegramUI/Images.xcassets/Call/CallCameraHDButton.imageset/Contents.json
@@ -0,0 +1,12 @@
+{
+ "images" : [
+ {
+ "filename" : "ic_call_camerahd.pdf",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/submodules/TelegramUI/Images.xcassets/Call/CallCameraHDButton.imageset/ic_call_camerahd.pdf b/submodules/TelegramUI/Images.xcassets/Call/CallCameraHDButton.imageset/ic_call_camerahd.pdf
new file mode 100644
index 0000000000..51d0299b88
Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Call/CallCameraHDButton.imageset/ic_call_camerahd.pdf differ
diff --git a/submodules/TelegramUI/Images.xcassets/Call/CallDisableHD.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Call/CallDisableHD.imageset/Contents.json
new file mode 100644
index 0000000000..9deae5fa49
--- /dev/null
+++ b/submodules/TelegramUI/Images.xcassets/Call/CallDisableHD.imageset/Contents.json
@@ -0,0 +1,12 @@
+{
+ "images" : [
+ {
+ "filename" : "ic_menu_hdoff.pdf",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/submodules/TelegramUI/Images.xcassets/Call/CallDisableHD.imageset/ic_menu_hdoff.pdf b/submodules/TelegramUI/Images.xcassets/Call/CallDisableHD.imageset/ic_menu_hdoff.pdf
new file mode 100644
index 0000000000..ccb2a9a6bf
Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Call/CallDisableHD.imageset/ic_menu_hdoff.pdf differ
diff --git a/submodules/TelegramUI/Images.xcassets/Call/CallEnableHD.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Call/CallEnableHD.imageset/Contents.json
new file mode 100644
index 0000000000..8c4a6d824e
--- /dev/null
+++ b/submodules/TelegramUI/Images.xcassets/Call/CallEnableHD.imageset/Contents.json
@@ -0,0 +1,12 @@
+{
+ "images" : [
+ {
+ "filename" : "ic_menu_hdon.pdf",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/submodules/TelegramUI/Images.xcassets/Call/CallCameraButton.imageset/ic_calls_video.pdf b/submodules/TelegramUI/Images.xcassets/Call/CallEnableHD.imageset/ic_menu_hdon.pdf
similarity index 75%
rename from submodules/TelegramUI/Images.xcassets/Call/CallCameraButton.imageset/ic_calls_video.pdf
rename to submodules/TelegramUI/Images.xcassets/Call/CallEnableHD.imageset/ic_menu_hdon.pdf
index 436c916812..374497845e 100644
Binary files a/submodules/TelegramUI/Images.xcassets/Call/CallCameraButton.imageset/ic_calls_video.pdf and b/submodules/TelegramUI/Images.xcassets/Call/CallEnableHD.imageset/ic_menu_hdon.pdf differ
diff --git a/submodules/TelegramUI/Images.xcassets/Call/CallMuteButton.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Call/CallMuteButton.imageset/Contents.json
index 1d8c3321c7..be885df064 100644
--- a/submodules/TelegramUI/Images.xcassets/Call/CallMuteButton.imageset/Contents.json
+++ b/submodules/TelegramUI/Images.xcassets/Call/CallMuteButton.imageset/Contents.json
@@ -1,7 +1,7 @@
{
"images" : [
{
- "filename" : "ic_calls_mute.pdf",
+ "filename" : "ic_call_microphone.pdf",
"idiom" : "universal"
}
],
diff --git a/submodules/TelegramUI/Images.xcassets/Call/CallMuteButton.imageset/ic_calls_mute.pdf b/submodules/TelegramUI/Images.xcassets/Call/CallMuteButton.imageset/ic_call_microphone.pdf
similarity index 73%
rename from submodules/TelegramUI/Images.xcassets/Call/CallMuteButton.imageset/ic_calls_mute.pdf
rename to submodules/TelegramUI/Images.xcassets/Call/CallMuteButton.imageset/ic_call_microphone.pdf
index 22a473dbc8..99ad802193 100644
Binary files a/submodules/TelegramUI/Images.xcassets/Call/CallMuteButton.imageset/ic_calls_mute.pdf and b/submodules/TelegramUI/Images.xcassets/Call/CallMuteButton.imageset/ic_call_microphone.pdf differ
diff --git a/submodules/TelegramUI/Images.xcassets/Call/CallSpeakerButton.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Call/CallSpeakerButton.imageset/Contents.json
index d0d69abee5..8876671fab 100644
--- a/submodules/TelegramUI/Images.xcassets/Call/CallSpeakerButton.imageset/Contents.json
+++ b/submodules/TelegramUI/Images.xcassets/Call/CallSpeakerButton.imageset/Contents.json
@@ -1,7 +1,7 @@
{
"images" : [
{
- "filename" : "ic_calls_speaker.pdf",
+ "filename" : "ic_call_speaker.pdf",
"idiom" : "universal"
}
],
diff --git a/submodules/TelegramUI/Images.xcassets/Call/CallSpeakerButton.imageset/ic_calls_speaker.pdf b/submodules/TelegramUI/Images.xcassets/Call/CallSpeakerButton.imageset/ic_call_speaker.pdf
similarity index 68%
rename from submodules/TelegramUI/Images.xcassets/Call/CallSpeakerButton.imageset/ic_calls_speaker.pdf
rename to submodules/TelegramUI/Images.xcassets/Call/CallSpeakerButton.imageset/ic_call_speaker.pdf
index 22af6e39c6..104b537f82 100644
Binary files a/submodules/TelegramUI/Images.xcassets/Call/CallSpeakerButton.imageset/ic_calls_speaker.pdf and b/submodules/TelegramUI/Images.xcassets/Call/CallSpeakerButton.imageset/ic_call_speaker.pdf differ
diff --git a/submodules/TelegramUI/Images.xcassets/Call/CallSwitchCameraButton.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Call/CallSwitchCameraButton.imageset/Contents.json
index 389dd744c8..958ae1e032 100644
--- a/submodules/TelegramUI/Images.xcassets/Call/CallSwitchCameraButton.imageset/Contents.json
+++ b/submodules/TelegramUI/Images.xcassets/Call/CallSwitchCameraButton.imageset/Contents.json
@@ -1,7 +1,7 @@
{
"images" : [
{
- "filename" : "ic_calls_cameraflip.pdf",
+ "filename" : "ic_call_flip.pdf",
"idiom" : "universal"
}
],
diff --git a/submodules/TelegramUI/Images.xcassets/Call/CallSwitchCameraButton.imageset/ic_call_flip.pdf b/submodules/TelegramUI/Images.xcassets/Call/CallSwitchCameraButton.imageset/ic_call_flip.pdf
new file mode 100644
index 0000000000..3a34ee5aa7
Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Call/CallSwitchCameraButton.imageset/ic_call_flip.pdf differ
diff --git a/submodules/TelegramUI/Images.xcassets/Call/CallToastBattery.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Call/CallToastBattery.imageset/Contents.json
new file mode 100644
index 0000000000..323afbd45b
--- /dev/null
+++ b/submodules/TelegramUI/Images.xcassets/Call/CallToastBattery.imageset/Contents.json
@@ -0,0 +1,12 @@
+{
+ "images" : [
+ {
+ "filename" : "ic_call_batteryislow.pdf",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/submodules/TelegramUI/Images.xcassets/Call/CallToastBattery.imageset/ic_call_batteryislow.pdf b/submodules/TelegramUI/Images.xcassets/Call/CallToastBattery.imageset/ic_call_batteryislow.pdf
new file mode 100644
index 0000000000..437fc4a39f
Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Call/CallToastBattery.imageset/ic_call_batteryislow.pdf differ
diff --git a/submodules/TelegramUI/Images.xcassets/Call/CallToastCamera.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Call/CallToastCamera.imageset/Contents.json
new file mode 100644
index 0000000000..886fff96dd
--- /dev/null
+++ b/submodules/TelegramUI/Images.xcassets/Call/CallToastCamera.imageset/Contents.json
@@ -0,0 +1,12 @@
+{
+ "images" : [
+ {
+ "filename" : "ic_call_cameraoff.pdf",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/submodules/TelegramUI/Images.xcassets/Call List/OutgoingIcon.imageset/ic_outgoingcall.pdf b/submodules/TelegramUI/Images.xcassets/Call/CallToastCamera.imageset/ic_call_cameraoff.pdf
similarity index 73%
rename from submodules/TelegramUI/Images.xcassets/Call List/OutgoingIcon.imageset/ic_outgoingcall.pdf
rename to submodules/TelegramUI/Images.xcassets/Call/CallToastCamera.imageset/ic_call_cameraoff.pdf
index 57fcc73721..1b299535bd 100644
Binary files a/submodules/TelegramUI/Images.xcassets/Call List/OutgoingIcon.imageset/ic_outgoingcall.pdf and b/submodules/TelegramUI/Images.xcassets/Call/CallToastCamera.imageset/ic_call_cameraoff.pdf differ
diff --git a/submodules/TelegramUI/Images.xcassets/Call/CallToastMicrophone.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Call/CallToastMicrophone.imageset/Contents.json
new file mode 100644
index 0000000000..01ffcb65d9
--- /dev/null
+++ b/submodules/TelegramUI/Images.xcassets/Call/CallToastMicrophone.imageset/Contents.json
@@ -0,0 +1,12 @@
+{
+ "images" : [
+ {
+ "filename" : "ic_call_microphoneoff.pdf",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/submodules/TelegramUI/Images.xcassets/Call/CallToastMicrophone.imageset/ic_call_microphoneoff.pdf b/submodules/TelegramUI/Images.xcassets/Call/CallToastMicrophone.imageset/ic_call_microphoneoff.pdf
new file mode 100644
index 0000000000..16779ba496
Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Call/CallToastMicrophone.imageset/ic_call_microphoneoff.pdf differ
diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Context Menu/Mention.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat/Context Menu/Mention.imageset/Contents.json
new file mode 100644
index 0000000000..f38e9ee4dd
--- /dev/null
+++ b/submodules/TelegramUI/Images.xcassets/Chat/Context Menu/Mention.imageset/Contents.json
@@ -0,0 +1,12 @@
+{
+ "images" : [
+ {
+ "filename" : "ic_mention.pdf",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Context Menu/Mention.imageset/ic_mention.pdf b/submodules/TelegramUI/Images.xcassets/Chat/Context Menu/Mention.imageset/ic_mention.pdf
new file mode 100644
index 0000000000..09644ebc18
Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Chat/Context Menu/Mention.imageset/ic_mention.pdf differ
diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Info/VideoCallButton.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat/Info/VideoCallButton.imageset/Contents.json
new file mode 100644
index 0000000000..f962f0de3c
--- /dev/null
+++ b/submodules/TelegramUI/Images.xcassets/Chat/Info/VideoCallButton.imageset/Contents.json
@@ -0,0 +1,12 @@
+{
+ "images" : [
+ {
+ "filename" : "ic_videocallchat.pdf",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Info/VideoCallButton.imageset/ic_videocallchat.pdf b/submodules/TelegramUI/Images.xcassets/Chat/Info/VideoCallButton.imageset/ic_videocallchat.pdf
new file mode 100644
index 0000000000..8561a6bac6
Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Chat/Info/VideoCallButton.imageset/ic_videocallchat.pdf differ
diff --git a/submodules/TelegramUI/Images.xcassets/Location/GoogleAttribution.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Location/GoogleAttribution.imageset/Contents.json
new file mode 100644
index 0000000000..67b12b6b3f
--- /dev/null
+++ b/submodules/TelegramUI/Images.xcassets/Location/GoogleAttribution.imageset/Contents.json
@@ -0,0 +1,22 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "scale" : "1x"
+ },
+ {
+ "filename" : "powered_by_google_on_white@2x.png",
+ "idiom" : "universal",
+ "scale" : "2x"
+ },
+ {
+ "filename" : "powered_by_google_on_white@3x.png",
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/submodules/TelegramUI/Images.xcassets/Location/GoogleAttribution.imageset/powered_by_google_on_white@2x.png b/submodules/TelegramUI/Images.xcassets/Location/GoogleAttribution.imageset/powered_by_google_on_white@2x.png
new file mode 100644
index 0000000000..4676cb59be
Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Location/GoogleAttribution.imageset/powered_by_google_on_white@2x.png differ
diff --git a/submodules/TelegramUI/Images.xcassets/Location/GoogleAttribution.imageset/powered_by_google_on_white@3x.png b/submodules/TelegramUI/Images.xcassets/Location/GoogleAttribution.imageset/powered_by_google_on_white@3x.png
new file mode 100644
index 0000000000..64b2cab5aa
Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Location/GoogleAttribution.imageset/powered_by_google_on_white@3x.png differ
diff --git a/submodules/TelegramUI/Resources/PresentationStrings.mapping b/submodules/TelegramUI/Resources/PresentationStrings.mapping
index b4f9e0f803..1e3047abea 100644
Binary files a/submodules/TelegramUI/Resources/PresentationStrings.mapping and b/submodules/TelegramUI/Resources/PresentationStrings.mapping differ
diff --git a/submodules/TelegramUI/Sources/AccountContext.swift b/submodules/TelegramUI/Sources/AccountContext.swift
index 415122b9be..c74900e0b3 100644
--- a/submodules/TelegramUI/Sources/AccountContext.swift
+++ b/submodules/TelegramUI/Sources/AccountContext.swift
@@ -141,11 +141,18 @@ public final class AccountContextImpl: AccountContext {
return self._contentSettings.get()
}
+ public var currentAppConfiguration: Atomic
+ private let _appConfiguration = Promise()
+ public var appConfiguration: Signal {
+ return self._appConfiguration.get()
+ }
+
public var watchManager: WatchManager?
private var storedPassword: (String, CFAbsoluteTime, SwiftSignalKit.Timer)?
private var limitsConfigurationDisposable: Disposable?
private var contentSettingsDisposable: Disposable?
+ private var appConfigurationDisposable: Disposable?
private let deviceSpecificContactImportContexts: QueueLocalObject
private var managedAppSpecificContactsDisposable: Disposable?
@@ -177,7 +184,7 @@ public final class AccountContextImpl: AccountContext {
}
#endif
- public init(sharedContext: SharedAccountContextImpl, account: Account, /*tonContext: StoredTonContext?, */limitsConfiguration: LimitsConfiguration, contentSettings: ContentSettings, temp: Bool = false)
+ public init(sharedContext: SharedAccountContextImpl, account: Account, /*tonContext: StoredTonContext?, */limitsConfiguration: LimitsConfiguration, contentSettings: ContentSettings, appConfiguration: AppConfiguration, temp: Bool = false)
{
self.sharedContextImpl = sharedContext
self.account = account
@@ -233,6 +240,16 @@ public final class AccountContextImpl: AccountContext {
let _ = currentContentSettings.swap(value)
})
+ let updatedAppConfiguration = getAppConfiguration(postbox: account.postbox)
+ self.currentAppConfiguration = Atomic(value: appConfiguration)
+ self._appConfiguration.set(.single(appConfiguration) |> then(updatedAppConfiguration))
+
+ let currentAppConfiguration = self.currentAppConfiguration
+ self.appConfigurationDisposable = (self._appConfiguration.get()
+ |> deliverOnMainQueue).start(next: { value in
+ let _ = currentAppConfiguration.swap(value)
+ })
+
let queue = Queue()
self.deviceSpecificContactImportContexts = QueueLocalObject(queue: queue, generate: {
return DeviceSpecificContactImportContexts(queue: queue)
@@ -248,11 +265,8 @@ public final class AccountContextImpl: AccountContext {
})
}
- self.experimentalUISettingsDisposable = (sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.experimentalUISettings])
- |> deliverOnMainQueue).start(next: { sharedData in
- if let settings = sharedData.entries[ApplicationSpecificSharedDataKeys.experimentalUISettings] as? ExperimentalUISettings {
- account.callSessionManager.updateVersions(versions: PresentationCallManagerImpl.voipVersions(includeExperimental: settings.videoCalls))
- }
+ account.callSessionManager.updateVersions(versions: PresentationCallManagerImpl.voipVersions(includeExperimental: true, includeReference: false).map { version, supportsVideo -> CallSessionManagerImplementationVersion in
+ CallSessionManagerImplementationVersion(version: version, supportsVideo: supportsVideo)
})
}
@@ -260,6 +274,7 @@ public final class AccountContextImpl: AccountContext {
self.limitsConfigurationDisposable?.dispose()
self.managedAppSpecificContactsDisposable?.dispose()
self.contentSettingsDisposable?.dispose()
+ self.appConfigurationDisposable?.dispose()
self.experimentalUISettingsDisposable?.dispose()
}
@@ -284,3 +299,17 @@ public final class AccountContextImpl: AccountContext {
}
}
}
+
+func getAppConfiguration(transaction: Transaction) -> AppConfiguration {
+ let appConfiguration: AppConfiguration = transaction.getPreferencesEntry(key: PreferencesKeys.appConfiguration) as? AppConfiguration ?? AppConfiguration.defaultValue
+ return appConfiguration
+}
+
+func getAppConfiguration(postbox: Postbox) -> Signal {
+ return postbox.preferencesView(keys: [PreferencesKeys.appConfiguration])
+ |> map { view -> AppConfiguration in
+ let appConfiguration: AppConfiguration = view.values[PreferencesKeys.appConfiguration] as? AppConfiguration ?? AppConfiguration.defaultValue
+ return appConfiguration
+ }
+ |> distinctUntilChanged
+}
diff --git a/submodules/TelegramUI/Sources/AppDelegate.swift b/submodules/TelegramUI/Sources/AppDelegate.swift
index 46a7105a54..988121e17d 100644
--- a/submodules/TelegramUI/Sources/AppDelegate.swift
+++ b/submodules/TelegramUI/Sources/AppDelegate.swift
@@ -401,7 +401,9 @@ final class SharedApplicationContext {
}
}
- let networkArguments = NetworkInitializationArguments(apiId: apiId, apiHash: apiHash, languagesCategory: languagesCategory, appVersion: appVersion, voipMaxLayer: PresentationCallManagerImpl.voipMaxLayer, voipVersions: PresentationCallManagerImpl.voipVersions(includeExperimental: false), appData: self.deviceToken.get()
+ let networkArguments = NetworkInitializationArguments(apiId: apiId, apiHash: apiHash, languagesCategory: languagesCategory, appVersion: appVersion, voipMaxLayer: PresentationCallManagerImpl.voipMaxLayer, voipVersions: PresentationCallManagerImpl.voipVersions(includeExperimental: true, includeReference: false).map { version, supportsVideo -> CallSessionManagerImplementationVersion in
+ CallSessionManagerImplementationVersion(version: version, supportsVideo: supportsVideo)
+ }, appData: self.deviceToken.get()
|> map { token in
let data = buildConfig.bundleData(withAppToken: token, signatureDict: signatureDict)
if let data = data, let jsonString = String(data: data, encoding: .utf8) {
@@ -990,7 +992,7 @@ final class SharedApplicationContext {
}
return true
})
- |> mapToSignal { account -> Signal<(Account, LimitsConfiguration, CallListSettings, ContentSettings)?, NoError> in
+ |> mapToSignal { account -> Signal<(Account, LimitsConfiguration, CallListSettings, ContentSettings, AppConfiguration)?, NoError> in
return sharedApplicationContext.sharedContext.accountManager.transaction { transaction -> CallListSettings? in
return transaction.getSharedData(ApplicationSpecificSharedDataKeys.callListSettings) as? CallListSettings
}
@@ -1003,12 +1005,13 @@ final class SharedApplicationContext {
}
return result
}
- |> mapToSignal { callListSettings -> Signal<(Account, LimitsConfiguration, CallListSettings, ContentSettings)?, NoError> in
+ |> mapToSignal { callListSettings -> Signal<(Account, LimitsConfiguration, CallListSettings, ContentSettings, AppConfiguration)?, NoError> in
if let account = account {
- return account.postbox.transaction { transaction -> (Account, LimitsConfiguration, CallListSettings, ContentSettings)? in
+ return account.postbox.transaction { transaction -> (Account, LimitsConfiguration, CallListSettings, ContentSettings, AppConfiguration)? in
let limitsConfiguration = transaction.getPreferencesEntry(key: PreferencesKeys.limitsConfiguration) as? LimitsConfiguration ?? LimitsConfiguration.defaultValue
let contentSettings = getContentSettings(transaction: transaction)
- return (account, limitsConfiguration, callListSettings ?? CallListSettings.defaultSettings, contentSettings)
+ let appConfiguration = getAppConfiguration(transaction: transaction)
+ return (account, limitsConfiguration, callListSettings ?? CallListSettings.defaultSettings, contentSettings, appConfiguration)
}
} else {
return .single(nil)
@@ -1017,11 +1020,8 @@ final class SharedApplicationContext {
}
|> deliverOnMainQueue
|> map { accountAndSettings -> AuthorizedApplicationContext? in
- return accountAndSettings.flatMap { account, limitsConfiguration, callListSettings, contentSettings in
- #if ENABLE_WALLET
- let tonContext = StoredTonContext(basePath: account.basePath, postbox: account.postbox, network: account.network, keychain: tonKeychain)
- #endif
- let context = AccountContextImpl(sharedContext: sharedApplicationContext.sharedContext, account: account/*, tonContext: tonContext*/, limitsConfiguration: limitsConfiguration, contentSettings: contentSettings)
+ return accountAndSettings.flatMap { account, limitsConfiguration, callListSettings, contentSettings, appConfiguration in
+ let context = AccountContextImpl(sharedContext: sharedApplicationContext.sharedContext, account: account, limitsConfiguration: limitsConfiguration, contentSettings: contentSettings, appConfiguration: appConfiguration)
return AuthorizedApplicationContext(sharedApplicationContext: sharedApplicationContext, mainWindow: self.mainWindow, watchManagerArguments: watchManagerArgumentsPromise.get(), context: context, accountManager: sharedApplicationContext.sharedContext.accountManager, showCallsTab: callListSettings.showTab, reinitializedNotificationSettings: {
let _ = (self.context.get()
|> take(1)
diff --git a/submodules/TelegramUI/Sources/ApplicationContext.swift b/submodules/TelegramUI/Sources/ApplicationContext.swift
index c48ac38f20..eec674cc7e 100644
--- a/submodules/TelegramUI/Sources/ApplicationContext.swift
+++ b/submodules/TelegramUI/Sources/ApplicationContext.swift
@@ -763,7 +763,7 @@ final class AuthorizedApplicationContext {
guard let strongSelf = self else {
return
}
- let _ = strongSelf.context.sharedContext.callManager?.requestCall(account: strongSelf.context.account, peerId: peerId, isVideo: isVideo, endCurrentIfAny: false)
+ let _ = strongSelf.context.sharedContext.callManager?.requestCall(context: strongSelf.context, peerId: peerId, isVideo: isVideo, endCurrentIfAny: false)
}))
}
diff --git a/submodules/TelegramUI/Sources/ChatContextResultPeekContentNode.swift b/submodules/TelegramUI/Sources/ChatContextResultPeekContentNode.swift
index 318e466a46..4c2703271f 100644
--- a/submodules/TelegramUI/Sources/ChatContextResultPeekContentNode.swift
+++ b/submodules/TelegramUI/Sources/ChatContextResultPeekContentNode.swift
@@ -162,7 +162,7 @@ private final class ChatContextResultPeekNode: ASDisplayNode, PeekControllerCont
imageDimensions = externalReference.content?.dimensions?.cgSize
if let content = externalReference.content, externalReference.type == "gif", let thumbnailResource = imageResource
, let dimensions = content.dimensions {
- videoFileReference = .standalone(media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: content.resource, previewRepresentations: [TelegramMediaImageRepresentation(dimensions: dimensions, resource: thumbnailResource)], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: dimensions, flags: [])]))
+ videoFileReference = .standalone(media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: content.resource, previewRepresentations: [TelegramMediaImageRepresentation(dimensions: dimensions, resource: thumbnailResource, progressiveSizes: [])], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: dimensions, flags: [])]))
imageResource = nil
}
case let .internalReference(internalReference):
@@ -224,7 +224,7 @@ private final class ChatContextResultPeekNode: ASDisplayNode, PeekControllerCont
if updatedImageResource {
if let imageResource = imageResource {
- let tmpRepresentation = TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: Int32(fittedImageDimensions.width * 2.0), height: Int32(fittedImageDimensions.height * 2.0)), resource: imageResource)
+ let tmpRepresentation = TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: Int32(fittedImageDimensions.width * 2.0), height: Int32(fittedImageDimensions.height * 2.0)), resource: imageResource, progressiveSizes: [])
let tmpImage = TelegramMediaImage(imageId: MediaId(namespace: 0, id: 0), representations: [tmpRepresentation], immediateThumbnailData: nil, reference: nil, partialReference: nil, flags: [])
updateImageSignal = chatMessagePhoto(postbox: self.account.postbox, photoReference: .standalone(media: tmpImage))
} else {
diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift
index 364109e5e5..323db8b19e 100644
--- a/submodules/TelegramUI/Sources/ChatController.swift
+++ b/submodules/TelegramUI/Sources/ChatController.swift
@@ -570,6 +570,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
if strongSelf.presentationInterfaceState.interfaceState.selectionState != nil {
return
}
+
+ strongSelf.dismissAllTooltips()
+
let recognizer: TapLongTapOrDoubleTapGestureRecognizer? = anyRecognizer as? TapLongTapOrDoubleTapGestureRecognizer
let gesture: ContextGesture? = anyRecognizer as? ContextGesture
if let messages = strongSelf.chatDisplayNode.historyNode.messageGroupInCurrentHistoryView(message.id) {
@@ -1164,7 +1167,14 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
return $0.updatedInputMode(f)
})
}, openMessageShareMenu: { [weak self] id in
- if let strongSelf = self, let messages = strongSelf.chatDisplayNode.historyNode.messageGroupInCurrentHistoryView(id) {
+ if let strongSelf = self, let messages = strongSelf.chatDisplayNode.historyNode.messageGroupInCurrentHistoryView(id), let message = messages.first {
+ var shares: Int = 0
+ for attribute in message.attributes {
+ if let forwardsAttribute = attribute as? ForwardCountMessageAttribute {
+ shares = forwardsAttribute.count
+ break
+ }
+ }
let shareController = ShareController(context: strongSelf.context, subject: .messages(messages))
shareController.dismissed = { shared in
if shared {
@@ -1205,22 +1215,39 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
return
}
- let callResult = context.sharedContext.callManager?.requestCall(account: context.account, peerId: peer.id, isVideo: isVideo, endCurrentIfAny: false)
+ let callResult = context.sharedContext.callManager?.requestCall(context: context, peerId: peer.id, isVideo: isVideo, endCurrentIfAny: false)
if let callResult = callResult, case let .alreadyInProgress(currentPeerId) = callResult {
if currentPeerId == peer.id {
context.sharedContext.navigateToCurrentCall()
} else {
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
let _ = (context.account.postbox.transaction { transaction -> (Peer?, Peer?) in
+ return (transaction.getPeer(peerId), currentPeerId.flatMap(transaction.getPeer))
+ } |> deliverOnMainQueue).start(next: { [weak self] peer, current in
+ if let peer = peer {
+ if let strongSelf = self, let current = current {
+ strongSelf.present(textAlertController(context: strongSelf.context, title: presentationData.strings.Call_CallInProgressTitle, text: presentationData.strings.Call_CallInProgressMessage(current.compactDisplayTitle, peer.compactDisplayTitle).0, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: {
+ if let strongSelf = self {
+ let _ = strongSelf.context.sharedContext.callManager?.requestCall(context: context, peerId: peerId, isVideo: isVideo, endCurrentIfAny: true)
+ }
+ })]), in: .window(.root))
+ } else {
+ strongSelf.present(textAlertController(context: strongSelf.context, title: presentationData.strings.Call_CallInProgressTitle, text: presentationData.strings.Call_ExternalCallInProgressMessage, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: {
+ })]), in: .window(.root))
+ }
+ }
+ })
+
+ /*let _ = (context.account.postbox.transaction { transaction -> (Peer?, Peer?) in
return (transaction.getPeer(peer.id), transaction.getPeer(currentPeerId))
}
|> deliverOnMainQueue).start(next: { peer, current in
if let peer = peer, let current = current {
strongSelf.present(textAlertController(context: strongSelf.context, title: presentationData.strings.Call_CallInProgressTitle, text: presentationData.strings.Call_CallInProgressMessage(current.compactDisplayTitle, peer.compactDisplayTitle).0, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: {
- let _ = context.sharedContext.callManager?.requestCall(account: context.account, peerId: peer.id, isVideo: isVideo, endCurrentIfAny: true)
+ let _ = context.sharedContext.callManager?.requestCall(context: context, peerId: peer.id, isVideo: isVideo, endCurrentIfAny: true)
})]), in: .window(.root))
}
- })
+ })*/
}
}
})
@@ -1974,7 +2001,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
})
}, displaySwipeToReplyHint: { [weak self] in
if let strongSelf = self, let validLayout = strongSelf.validLayout, min(validLayout.size.width, validLayout.size.height) > 320.0 {
- strongSelf.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .swipeToReply(title: strongSelf.presentationData.strings.Conversation_SwipeToReplyHintTitle, text: strongSelf.presentationData.strings.Conversation_SwipeToReplyHintText), elevatedLayout: true, action: { _ in return false }), in: .window(.root))
+ strongSelf.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .swipeToReply(title: strongSelf.presentationData.strings.Conversation_SwipeToReplyHintTitle, text: strongSelf.presentationData.strings.Conversation_SwipeToReplyHintText), elevatedLayout: false, action: { _ in return false }), in: .current)
}
}, dismissReplyMarkupMessage: { [weak self] message in
guard let strongSelf = self, strongSelf.presentationInterfaceState.keyboardButtonsMessage?.id == message.id else {
@@ -2029,6 +2056,67 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
strongSelf.chatDisplayNode.animateQuizCorrectOptionSelected()
}, greetingStickerNode: { [weak self] in
return self?.chatDisplayNode.greetingStickerNode
+ }, openPeerContextMenu: { [weak self] peer, node, rect, gesture in
+ guard let strongSelf = self else {
+ return
+ }
+
+ if strongSelf.presentationInterfaceState.interfaceState.selectionState != nil {
+ return
+ }
+
+ strongSelf.dismissAllTooltips()
+
+ let context = strongSelf.context
+ let _ = (context.account.postbox.transaction { transaction -> Peer? in
+ return transaction.getPeer(peer.id)
+ }
+ |> deliverOnMainQueue).start(next: { [weak self] peer in
+ guard let strongSelf = self, let peer = peer, peer.smallProfileImage != nil else {
+ return
+ }
+
+ let galleryController = AvatarGalleryController(context: context, peer: peer, remoteEntries: nil, replaceRootController: { controller, ready in
+ }, synchronousLoad: true)
+ galleryController.setHintWillBePresentedInPreviewingContext(true)
+
+ let items: Signal<[ContextMenuItem], NoError> = context.account.postbox.transaction { transaction -> [ContextMenuItem] in
+ let isChannel = peer.id.namespace == Namespaces.Peer.CloudChannel
+ var items: [ContextMenuItem] = [
+ .action(ContextMenuActionItem(text: isChannel ? strongSelf.presentationData.strings.Conversation_ContextMenuOpenChannelProfile : strongSelf.presentationData.strings.Conversation_ContextMenuOpenProfile, icon: { theme in
+ return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Info"), color: theme.actionSheet.primaryTextColor)
+ }, action: { _, f in
+ f(.dismissWithoutContent)
+ self?.openPeer(peerId: peer.id, navigation: .info, fromMessage: nil)
+ }))
+ ]
+ items.append(.action(ContextMenuActionItem(text: isChannel ? strongSelf.presentationData.strings.Conversation_ContextMenuOpenChannel : strongSelf.presentationData.strings.Conversation_ContextMenuSendMessage, icon: { theme in
+ return generateTintedImage(image: UIImage(bundleImageName: isChannel ? "Chat/Context Menu/Channels" : "Chat/Context Menu/Message"), color: theme.actionSheet.primaryTextColor)
+ }, action: { _, f in
+ f(.dismissWithoutContent)
+ self?.openPeer(peerId: peer.id, navigation: .chat(textInputState: nil, subject: nil, peekData: nil), fromMessage: nil)
+ })))
+ if !isChannel {
+ items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.Conversation_ContextMenuMention, icon: { theme in
+ return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Mention"), color: theme.actionSheet.primaryTextColor)
+ }, action: { _, f in
+ f(.dismissWithoutContent)
+
+ self?.interfaceInteraction?.updateTextInputStateAndMode { current, inputMode in
+ var inputMode = inputMode
+ if inputMode == .none {
+ inputMode = .text
+ }
+ return (chatTextInputAddMentionAttribute(current, peer: peer), inputMode)
+ }
+ })))
+ }
+ return items
+ }
+
+ let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: galleryController, sourceNode: node)), items: items, reactionItems: [], gesture: gesture)
+ strongSelf.presentInGlobalOverlay(contextController)
+ })
}, requestMessageUpdate: { [weak self] id in
if let strongSelf = self {
strongSelf.chatDisplayNode.historyNode.requestMessageUpdate(id)
@@ -2847,7 +2935,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
} else if let cachedData = combinedInitialData.cachedData as? CachedUserData {
peerIsBlocked = cachedData.isBlocked
- callsAvailable = cachedData.callsAvailable
+ callsAvailable = cachedData.voiceCallsAvailable
callsPrivate = cachedData.callsPrivate
pinnedMessageId = cachedData.pinnedMessageId
} else if let cachedData = combinedInitialData.cachedData as? CachedGroupData {
@@ -2988,7 +3076,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
} else if let cachedData = cachedData as? CachedUserData {
peerIsBlocked = cachedData.isBlocked
- callsAvailable = cachedData.callsAvailable
+ callsAvailable = cachedData.voiceCallsAvailable
callsPrivate = cachedData.callsPrivate
pinnedMessageId = cachedData.pinnedMessageId
} else if let cachedData = cachedData as? CachedGroupData {
@@ -3934,7 +4022,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
return
}
if isVideo {
- DeviceAccess.authorizeAccess(to: .camera, presentationData: strongSelf.presentationData, present: { c, a in
+ DeviceAccess.authorizeAccess(to: .camera(.video), presentationData: strongSelf.presentationData, present: { c, a in
self?.present(c, in: .window(.root), with: a)
}, openSettings: {
self?.context.sharedContext.applicationBindings.openSettings()
@@ -5067,19 +5155,22 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
if case let .peer(peerId) = self.chatLocation {
let context = self.context
self.keepPeerInfoScreenDataHotDisposable.set(keepPeerInfoScreenDataHot(context: context, peerId: peerId).start())
- self.preloadAvatarDisposable.set((peerInfoProfilePhotosWithCache(context: context, peerId: peerId)
- |> mapToSignal { result -> Signal in
- var signals: [Signal] = [.complete()]
- for i in 0 ..< min(5, result.count) {
- if let video = result[i].videoRepresentations.first {
- let duration: Double = (video.representation.startTimestamp ?? 0.0) + (i == 0 ? 4.0 : 2.0)
- signals.append(preloadVideoResource(postbox: context.account.postbox, resourceReference: video.reference, duration: duration))
+
+ if peerId.namespace == Namespaces.Peer.CloudUser {
+ self.preloadAvatarDisposable.set((peerInfoProfilePhotosWithCache(context: context, peerId: peerId)
+ |> mapToSignal { (complete, result) -> Signal in
+ var signals: [Signal] = [.complete()]
+ for i in 0 ..< min(1, result.count) {
+ if let video = result[i].videoRepresentations.first {
+ let duration: Double = (video.representation.startTimestamp ?? 0.0) + (i == 0 ? 4.0 : 2.0)
+ signals.append(preloadVideoResource(postbox: context.account.postbox, resourceReference: video.reference, duration: duration))
+ }
}
- }
- return combineLatest(signals) |> mapToSignal { _ in
- return .never()
- }
- }).start())
+ return combineLatest(signals) |> mapToSignal { _ in
+ return .never()
+ }
+ }).start())
+ }
}
}
@@ -5160,18 +5251,6 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
self.dismissAllTooltips()
- self.window?.forEachController({ controller in
- if let controller = controller as? UndoOverlayController {
- controller.dismissWithCommitAction()
- }
- })
- self.forEachController({ controller in
- if let controller = controller as? TooltipScreen {
- controller.dismiss()
- }
- return true
- })
-
self.sendMessageActionsController?.dismiss()
if let _ = self.peekData {
@@ -6009,7 +6088,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
disposable.set((signal
|> deliverOnMainQueue).start(completed: { [weak self] in
if let strongSelf = self, let _ = strongSelf.validLayout {
- strongSelf.present(UndoOverlayController(presentationData: presentationData, content: .succeed(text: presentationData.strings.ClearCache_Success("\(dataSizeString(selectedSize, decimalSeparator: presentationData.dateTimeFormat.decimalSeparator))", stringForDeviceType()).0), elevatedLayout: true, action: { _ in return false }), in: .current)
+ strongSelf.present(UndoOverlayController(presentationData: presentationData, content: .succeed(text: presentationData.strings.ClearCache_Success("\(dataSizeString(selectedSize, decimalSeparator: presentationData.dateTimeFormat.decimalSeparator))", stringForDeviceType()).0), elevatedLayout: false, action: { _ in return false }), in: .current)
}
}))
@@ -6381,7 +6460,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
let mimeType = guessMimeTypeByFileExtension((item.fileName as NSString).pathExtension)
var previewRepresentations: [TelegramMediaImageRepresentation] = []
if mimeType == "application/pdf" {
- previewRepresentations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 320, height: 320), resource: ICloudFileResource(urlData: item.urlData, thumbnail: true)))
+ previewRepresentations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 320, height: 320), resource: ICloudFileResource(urlData: item.urlData, thumbnail: true), progressiveSizes: []))
}
let file = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: fileId), partialReference: nil, resource: ICloudFileResource(urlData: item.urlData, thumbnail: false), previewRepresentations: previewRepresentations, videoThumbnails: [], immediateThumbnailData: nil, mimeType: mimeType, size: item.fileSize, attributes: [.FileName(fileName: item.fileName)])
let message: EnqueueMessage = .message(text: "", attributes: [], mediaReference: .standalone(media: file), replyToMessageId: replyMessageId, localGroupingKey: nil)
@@ -6616,7 +6695,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
self.effectiveNavigationController?.pushViewController(contactsController)
self.controllerNavigationDisposable.set((contactsController.result
|> deliverOnMainQueue).start(next: { [weak self] peer in
- if let strongSelf = self, let peer = peer {
+ if let strongSelf = self, let (peer, _) = peer {
let dataSignal: Signal<(Peer?, DeviceContactExtendedData?), NoError>
switch peer {
case let .peer(contact, _, _):
@@ -7050,12 +7129,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
}
if let value = value {
- self.present(UndoOverlayController(presentationData: self.presentationData, content: .dice(dice: dice, account: self.context.account, text: value, action: canSendMessagesToChat(self.presentationInterfaceState) ? self.presentationData.strings.Conversation_SendDice : nil), elevatedLayout: true, action: { [weak self] action in
+ self.present(UndoOverlayController(presentationData: self.presentationData, content: .dice(dice: dice, account: self.context.account, text: value, action: canSendMessagesToChat(self.presentationInterfaceState) ? self.presentationData.strings.Conversation_SendDice : nil), elevatedLayout: false, action: { [weak self] action in
if let strongSelf = self, canSendMessagesToChat(strongSelf.presentationInterfaceState), action == .undo {
strongSelf.sendMessages([.message(text: "", attributes: [], mediaReference: AnyMediaReference.standalone(media: TelegramMediaDice(emoji: dice.emoji)), replyToMessageId: nil, localGroupingKey: nil)])
}
return false
- }), in: .window(.root))
+ }), in: .current)
}
}
@@ -9111,6 +9190,21 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
self.silentPostTooltipController?.dismiss()
self.mediaRecordingModeTooltipController?.dismiss()
self.mediaRestrictedTooltipController?.dismiss()
+
+ self.window?.forEachController({ controller in
+ if let controller = controller as? UndoOverlayController {
+ controller.dismissWithCommitAction()
+ }
+ })
+ self.forEachController({ controller in
+ if let controller = controller as? UndoOverlayController {
+ controller.dismissWithCommitAction()
+ }
+ if let controller = controller as? TooltipScreen {
+ controller.dismiss()
+ }
+ return true
+ })
}
private func commitPurposefulAction() {
@@ -9430,11 +9524,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
override public func updatePossibleControllerDropContent(content: NavigationControllerDropContent?) {
- self.chatDisplayNode.updateEmbeddedTitlePeekContent(content: content)
+ //self.chatDisplayNode.updateEmbeddedTitlePeekContent(content: content)
}
override public func acceptPossibleControllerDropContent(content: NavigationControllerDropContent) -> Bool {
- return self.chatDisplayNode.acceptEmbeddedTitlePeekContent(content: content)
+ //return self.chatDisplayNode.acceptEmbeddedTitlePeekContent(content: content)
+ return false
}
}
diff --git a/submodules/TelegramUI/Sources/ChatControllerInteraction.swift b/submodules/TelegramUI/Sources/ChatControllerInteraction.swift
index 3b4728d6e3..1885abd5ba 100644
--- a/submodules/TelegramUI/Sources/ChatControllerInteraction.swift
+++ b/submodules/TelegramUI/Sources/ChatControllerInteraction.swift
@@ -120,6 +120,7 @@ public final class ChatControllerInteraction {
let displayDiceTooltip: (TelegramMediaDice) -> Void
let animateDiceSuccess: () -> Void
let greetingStickerNode: () -> (ASDisplayNode, ASDisplayNode, ASDisplayNode, () -> Void)?
+ let openPeerContextMenu: (Peer, ASDisplayNode, CGRect, ContextGesture?) -> Void
let requestMessageUpdate: (MessageId) -> Void
let cancelInteractiveKeyboardGestures: () -> Void
@@ -137,7 +138,7 @@ public final class ChatControllerInteraction {
var searchTextHighightState: (String, [MessageIndex])?
var seenOneTimeAnimatedMedia = Set()
- init(openMessage: @escaping (Message, ChatControllerInteractionOpenMessageMode) -> Bool, openPeer: @escaping (PeerId?, ChatControllerInteractionNavigateToPeer, Message?) -> Void, openPeerMention: @escaping (String) -> Void, openMessageContextMenu: @escaping (Message, Bool, ASDisplayNode, CGRect, UIGestureRecognizer?) -> Void, openMessageContextActions: @escaping (Message, ASDisplayNode, CGRect, ContextGesture?) -> Void, navigateToMessage: @escaping (MessageId, MessageId) -> Void, tapMessage: ((Message) -> Void)?, clickThroughMessage: @escaping () -> Void, toggleMessagesSelection: @escaping ([MessageId], Bool) -> Void, sendCurrentMessage: @escaping (Bool) -> Void, sendMessage: @escaping (String) -> Void, sendSticker: @escaping (FileMediaReference, Bool, ASDisplayNode, CGRect) -> Bool, sendGif: @escaping (FileMediaReference, ASDisplayNode, CGRect) -> Bool, sendBotContextResultAsGif: @escaping (ChatContextResultCollection, ChatContextResult, ASDisplayNode, CGRect) -> Bool, requestMessageActionCallback: @escaping (MessageId, MemoryBuffer?, Bool) -> Void, requestMessageActionUrlAuth: @escaping (String, MessageId, Int32) -> Void, activateSwitchInline: @escaping (PeerId?, String) -> Void, openUrl: @escaping (String, Bool, Bool?, Message?) -> Void, shareCurrentLocation: @escaping () -> Void, shareAccountContact: @escaping () -> Void, sendBotCommand: @escaping (MessageId?, String) -> Void, openInstantPage: @escaping (Message, ChatMessageItemAssociatedData?) -> Void, openWallpaper: @escaping (Message) -> Void, openTheme: @escaping (Message) -> Void, openHashtag: @escaping (String?, String) -> Void, updateInputState: @escaping ((ChatTextInputState) -> ChatTextInputState) -> Void, updateInputMode: @escaping ((ChatInputMode) -> ChatInputMode) -> Void, openMessageShareMenu: @escaping (MessageId) -> Void, presentController: @escaping (ViewController, Any?) -> Void, navigationController: @escaping () -> NavigationController?, chatControllerNode: @escaping () -> ASDisplayNode?, reactionContainerNode: @escaping () -> ReactionSelectionParentNode?, presentGlobalOverlayController: @escaping (ViewController, Any?) -> Void, callPeer: @escaping (PeerId, Bool) -> Void, longTap: @escaping (ChatControllerInteractionLongTapAction, Message?) -> Void, openCheckoutOrReceipt: @escaping (MessageId) -> Void, openSearch: @escaping () -> Void, setupReply: @escaping (MessageId) -> Void, canSetupReply: @escaping (Message) -> ChatControllerInteractionSwipeAction, navigateToFirstDateMessage: @escaping(Int32) ->Void, requestRedeliveryOfFailedMessages: @escaping (MessageId) -> Void, addContact: @escaping (String) -> Void, rateCall: @escaping (Message, CallId) -> Void, requestSelectMessagePollOptions: @escaping (MessageId, [Data]) -> Void, requestOpenMessagePollResults: @escaping (MessageId, MediaId) -> Void, openAppStorePage: @escaping () -> Void, displayMessageTooltip: @escaping (MessageId, String, ASDisplayNode?, CGRect?) -> Void, seekToTimecode: @escaping (Message, Double, Bool) -> Void, scheduleCurrentMessage: @escaping () -> Void, sendScheduledMessagesNow: @escaping ([MessageId]) -> Void, editScheduledMessagesTime: @escaping ([MessageId]) -> Void, performTextSelectionAction: @escaping (UInt32, NSAttributedString, TextSelectionAction) -> Void, updateMessageLike: @escaping (MessageId, Bool) -> Void, openMessageReactions: @escaping (MessageId) -> Void, displaySwipeToReplyHint: @escaping () -> Void, dismissReplyMarkupMessage: @escaping (Message) -> Void, openMessagePollResults: @escaping (MessageId, Data) -> Void, openPollCreation: @escaping (Bool?) -> Void, displayPollSolution: @escaping (TelegramMediaPollResults.Solution, ASDisplayNode) -> Void, displayPsa: @escaping (String, ASDisplayNode) -> Void, displayDiceTooltip: @escaping (TelegramMediaDice) -> Void, animateDiceSuccess: @escaping () -> Void, greetingStickerNode: @escaping () -> (ASDisplayNode, ASDisplayNode, ASDisplayNode, () -> Void)?, requestMessageUpdate: @escaping (MessageId) -> Void, cancelInteractiveKeyboardGestures: @escaping () -> Void, automaticMediaDownloadSettings: MediaAutoDownloadSettings, pollActionState: ChatInterfacePollActionState, stickerSettings: ChatInterfaceStickerSettings) {
+ init(openMessage: @escaping (Message, ChatControllerInteractionOpenMessageMode) -> Bool, openPeer: @escaping (PeerId?, ChatControllerInteractionNavigateToPeer, Message?) -> Void, openPeerMention: @escaping (String) -> Void, openMessageContextMenu: @escaping (Message, Bool, ASDisplayNode, CGRect, UIGestureRecognizer?) -> Void, openMessageContextActions: @escaping (Message, ASDisplayNode, CGRect, ContextGesture?) -> Void, navigateToMessage: @escaping (MessageId, MessageId) -> Void, tapMessage: ((Message) -> Void)?, clickThroughMessage: @escaping () -> Void, toggleMessagesSelection: @escaping ([MessageId], Bool) -> Void, sendCurrentMessage: @escaping (Bool) -> Void, sendMessage: @escaping (String) -> Void, sendSticker: @escaping (FileMediaReference, Bool, ASDisplayNode, CGRect) -> Bool, sendGif: @escaping (FileMediaReference, ASDisplayNode, CGRect) -> Bool, sendBotContextResultAsGif: @escaping (ChatContextResultCollection, ChatContextResult, ASDisplayNode, CGRect) -> Bool, requestMessageActionCallback: @escaping (MessageId, MemoryBuffer?, Bool) -> Void, requestMessageActionUrlAuth: @escaping (String, MessageId, Int32) -> Void, activateSwitchInline: @escaping (PeerId?, String) -> Void, openUrl: @escaping (String, Bool, Bool?, Message?) -> Void, shareCurrentLocation: @escaping () -> Void, shareAccountContact: @escaping () -> Void, sendBotCommand: @escaping (MessageId?, String) -> Void, openInstantPage: @escaping (Message, ChatMessageItemAssociatedData?) -> Void, openWallpaper: @escaping (Message) -> Void, openTheme: @escaping (Message) -> Void, openHashtag: @escaping (String?, String) -> Void, updateInputState: @escaping ((ChatTextInputState) -> ChatTextInputState) -> Void, updateInputMode: @escaping ((ChatInputMode) -> ChatInputMode) -> Void, openMessageShareMenu: @escaping (MessageId) -> Void, presentController: @escaping (ViewController, Any?) -> Void, navigationController: @escaping () -> NavigationController?, chatControllerNode: @escaping () -> ASDisplayNode?, reactionContainerNode: @escaping () -> ReactionSelectionParentNode?, presentGlobalOverlayController: @escaping (ViewController, Any?) -> Void, callPeer: @escaping (PeerId, Bool) -> Void, longTap: @escaping (ChatControllerInteractionLongTapAction, Message?) -> Void, openCheckoutOrReceipt: @escaping (MessageId) -> Void, openSearch: @escaping () -> Void, setupReply: @escaping (MessageId) -> Void, canSetupReply: @escaping (Message) -> ChatControllerInteractionSwipeAction, navigateToFirstDateMessage: @escaping(Int32) ->Void, requestRedeliveryOfFailedMessages: @escaping (MessageId) -> Void, addContact: @escaping (String) -> Void, rateCall: @escaping (Message, CallId) -> Void, requestSelectMessagePollOptions: @escaping (MessageId, [Data]) -> Void, requestOpenMessagePollResults: @escaping (MessageId, MediaId) -> Void, openAppStorePage: @escaping () -> Void, displayMessageTooltip: @escaping (MessageId, String, ASDisplayNode?, CGRect?) -> Void, seekToTimecode: @escaping (Message, Double, Bool) -> Void, scheduleCurrentMessage: @escaping () -> Void, sendScheduledMessagesNow: @escaping ([MessageId]) -> Void, editScheduledMessagesTime: @escaping ([MessageId]) -> Void, performTextSelectionAction: @escaping (UInt32, NSAttributedString, TextSelectionAction) -> Void, updateMessageLike: @escaping (MessageId, Bool) -> Void, openMessageReactions: @escaping (MessageId) -> Void, displaySwipeToReplyHint: @escaping () -> Void, dismissReplyMarkupMessage: @escaping (Message) -> Void, openMessagePollResults: @escaping (MessageId, Data) -> Void, openPollCreation: @escaping (Bool?) -> Void, displayPollSolution: @escaping (TelegramMediaPollResults.Solution, ASDisplayNode) -> Void, displayPsa: @escaping (String, ASDisplayNode) -> Void, displayDiceTooltip: @escaping (TelegramMediaDice) -> Void, animateDiceSuccess: @escaping () -> Void, greetingStickerNode: @escaping () -> (ASDisplayNode, ASDisplayNode, ASDisplayNode, () -> Void)?, openPeerContextMenu: @escaping (Peer, ASDisplayNode, CGRect, ContextGesture?) -> Void, requestMessageUpdate: @escaping (MessageId) -> Void, cancelInteractiveKeyboardGestures: @escaping () -> Void, automaticMediaDownloadSettings: MediaAutoDownloadSettings, pollActionState: ChatInterfacePollActionState, stickerSettings: ChatInterfaceStickerSettings) {
self.openMessage = openMessage
self.openPeer = openPeer
self.openPeerMention = openPeerMention
@@ -201,6 +202,7 @@ public final class ChatControllerInteraction {
self.displayDiceTooltip = displayDiceTooltip
self.animateDiceSuccess = animateDiceSuccess
self.greetingStickerNode = greetingStickerNode
+ self.openPeerContextMenu = openPeerContextMenu
self.requestMessageUpdate = requestMessageUpdate
self.cancelInteractiveKeyboardGestures = cancelInteractiveKeyboardGestures
@@ -248,6 +250,7 @@ public final class ChatControllerInteraction {
}, animateDiceSuccess: {
}, greetingStickerNode: {
return nil
+ }, openPeerContextMenu: { _, _, _, _ in
}, requestMessageUpdate: { _ in
}, cancelInteractiveKeyboardGestures: {
}, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings,
diff --git a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift
index 8d8807ab29..a838ce744c 100644
--- a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift
+++ b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift
@@ -1435,7 +1435,13 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
loadState = .loading
}
+ var animateIn = false
if strongSelf.loadState != loadState {
+ if case .loading = strongSelf.loadState {
+ if case .messages = loadState {
+ animateIn = true
+ }
+ }
strongSelf.loadState = loadState
strongSelf.loadStateUpdated?(loadState, animated)
}
@@ -1478,9 +1484,25 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
strongSelf._buttonKeyboardMessage.set(.single(transition.keyboardButtonsMessage))
}
- if transition.animateIn {
- strongSelf.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25)
- }
+ /*if transition.animateIn || animateIn {
+ let heightNorm = strongSelf.bounds.height - strongSelf.insets.top
+ strongSelf.forEachVisibleItemNode { itemNode in
+ if let itemNode = itemNode as? ChatMessageItemView {
+ let delayFactor = itemNode.frame.minY / heightNorm
+ let delay = Double(delayFactor * 0.1)
+
+ itemNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15, delay: delay)
+ itemNode.layer.animateScale(from: 0.9, to: 1.0, duration: 0.4, delay: delay, timingFunction: kCAMediaTimingFunctionSpring)
+ }
+ }
+ strongSelf.forEachItemHeaderNode { itemNode in
+ let delayFactor = itemNode.frame.minY / heightNorm
+ let delay = Double(delayFactor * 0.2)
+
+ itemNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15, delay: delay)
+ itemNode.layer.animateScale(from: 0.9, to: 1.0, duration: 0.4, delay: delay, timingFunction: kCAMediaTimingFunctionSpring)
+ }
+ }*/
if let scrolledToIndex = transition.scrolledToIndex {
if let strongSelf = self {
diff --git a/submodules/TelegramUI/Sources/ChatHistoryViewForLocation.swift b/submodules/TelegramUI/Sources/ChatHistoryViewForLocation.swift
index e74f25cafe..75ed081bcb 100644
--- a/submodules/TelegramUI/Sources/ChatHistoryViewForLocation.swift
+++ b/submodules/TelegramUI/Sources/ChatHistoryViewForLocation.swift
@@ -83,7 +83,7 @@ func chatHistoryViewForLocation(_ location: ChatHistoryLocationInput, account: A
}
var scrollPosition: ChatHistoryViewScrollPosition?
- if let maxReadIndex = view.maxReadIndex, tagMask == nil {
+ if let maxReadIndex = view.maxReadIndex, tagMask == nil, view.isAddedToChatList {
let aroundIndex = maxReadIndex
scrollPosition = .unread(index: maxReadIndex)
@@ -120,7 +120,7 @@ func chatHistoryViewForLocation(_ location: ChatHistoryLocationInput, account: A
}
}
}
- } else if let historyScrollState = (initialData?.chatInterfaceState as? ChatInterfaceState)?.historyScrollState, tagMask == nil {
+ } else if view.isAddedToChatList, let historyScrollState = (initialData?.chatInterfaceState as? ChatInterfaceState)?.historyScrollState, tagMask == nil {
scrollPosition = .positionRestoration(index: historyScrollState.messageIndex, relativeOffset: CGFloat(historyScrollState.relativeOffset))
} else {
if view.entries.isEmpty && (view.holeEarlier || view.holeLater) {
diff --git a/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift b/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift
index ceaa5643f0..5d66223437 100644
--- a/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift
+++ b/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift
@@ -974,10 +974,14 @@ func chatAvailableMessageActionsImpl(postbox: Postbox, accountPeerId: PeerId, me
optionsMap[id]!.insert(.deleteLocally)
} else if let peer = transaction.getPeer(id.peerId) {
var isAction = false
+ var isDice = false
for media in message.media {
if media is TelegramMediaAction || media is TelegramMediaExpiredContent {
isAction = true
}
+ if media is TelegramMediaDice {
+ isDice = true
+ }
}
if let channel = peer as? TelegramChannel {
if message.flags.contains(.Incoming) {
@@ -1064,6 +1068,11 @@ func chatAvailableMessageActionsImpl(postbox: Postbox, accountPeerId: PeerId, me
} else if limitsConfiguration.canRemoveIncomingMessagesInPrivateChats {
canDeleteGlobally = true
}
+
+ let timestamp = Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970)
+ if isDice && Int64(message.timestamp) + 60 * 60 * 24 > Int64(timestamp) {
+ canDeleteGlobally = false
+ }
if message.flags.contains(.Incoming) {
hadPersonalIncoming = true
}
diff --git a/submodules/TelegramUI/Sources/ChatMediaInputStickerPackItem.swift b/submodules/TelegramUI/Sources/ChatMediaInputStickerPackItem.swift
index c54b0f880c..e71e34f069 100644
--- a/submodules/TelegramUI/Sources/ChatMediaInputStickerPackItem.swift
+++ b/submodules/TelegramUI/Sources/ChatMediaInputStickerPackItem.swift
@@ -196,7 +196,7 @@ final class ChatMediaInputStickerPackItemNode: ListViewItemNode {
thumbnailItem = .animated(item.file.resource)
resourceReference = MediaResourceReference.media(media: .standalone(media: item.file), resource: item.file.resource)
} else if let dimensions = item.file.dimensions, let resource = chatMessageStickerResource(file: item.file, small: true) as? TelegramMediaResource {
- thumbnailItem = .still(TelegramMediaImageRepresentation(dimensions: dimensions, resource: resource))
+ thumbnailItem = .still(TelegramMediaImageRepresentation(dimensions: dimensions, resource: resource, progressiveSizes: []))
resourceReference = MediaResourceReference.media(media: .standalone(media: item.file), resource: resource)
}
}
diff --git a/submodules/TelegramUI/Sources/ChatMessageActionItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageActionItemNode.swift
index 00fae31474..c1424d1579 100644
--- a/submodules/TelegramUI/Sources/ChatMessageActionItemNode.swift
+++ b/submodules/TelegramUI/Sources/ChatMessageActionItemNode.swift
@@ -147,7 +147,7 @@ class ChatMessageActionBubbleContentNode: ChatMessageBubbleContentNode {
}
}
- let imageSize = layoutConstants.instantVideo.dimensions
+ let imageSize = CGSize(width: 212.0, height: 212.0)
let (labelLayout, apply) = makeLabelLayout(TextNodeLayoutArguments(attributedString: attributedString, backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: constrainedSize.width - 32.0, height: CGFloat.greatestFiniteMagnitude), alignment: .center, cutout: nil, insets: UIEdgeInsets()))
@@ -221,8 +221,8 @@ class ChatMessageActionBubbleContentNode: ChatMessageBubbleContentNode {
strongSelf.mediaBackgroundNode.image = backgroundImage
if let image = image, let video = image.videoRepresentations.last, let id = image.id?.id {
- let videoFileReference = FileMediaReference.standalone(media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: video.resource, previewRepresentations: image.representations, videoThumbnails: [], immediateThumbnailData: image.immediateThumbnailData, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: video.dimensions, flags: [])]))
- let videoContent = NativeVideoContent(id: .profileVideo(id, "action"), fileReference: videoFileReference, streamVideo: isMediaStreamable(resource: video.resource) ? .conservative : .none, loopVideo: true, enableSound: false, fetchAutomatically: true, onlyFullSizeThumbnail: false, autoFetchFullSizeThumbnail: true, continuePlayingWithoutSoundOnLostAudioSession: false, placeholderColor: .clear)
+ let videoFileReference = FileMediaReference.message(message: MessageReference(item.message), media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: video.resource, previewRepresentations: image.representations, videoThumbnails: [], immediateThumbnailData: image.immediateThumbnailData, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: video.dimensions, flags: [])]))
+ let videoContent = NativeVideoContent(id: .profileVideo(id, "action"), fileReference: videoFileReference, streamVideo: isMediaStreamable(resource: video.resource) ? .conservative : .none, loopVideo: true, enableSound: false, fetchAutomatically: true, onlyFullSizeThumbnail: false, useLargeThumbnail: true, autoFetchFullSizeThumbnail: true, continuePlayingWithoutSoundOnLostAudioSession: false, placeholderColor: .clear)
if videoContent.id != strongSelf.videoContent?.id {
let mediaManager = item.context.sharedContext.mediaManager
let videoNode = UniversalVideoNode(postbox: item.context.account.postbox, audioSession: mediaManager.audioSession, manager: mediaManager.universalVideoManager, decoration: GalleryVideoDecoration(), content: videoContent, priority: .secondaryOverlay)
diff --git a/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift
index ea24c4f4fa..a475c1be81 100644
--- a/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift
+++ b/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift
@@ -228,7 +228,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
}
if let telegramDice = self.telegramDice {
- let animationNode = ManagedDiceAnimationNode(context: item.context, emoji: telegramDice.emoji)
+ let animationNode = ManagedDiceAnimationNode(context: item.context, emoji: telegramDice.emoji.strippedEmoji)
if !item.message.effectivelyIncoming(item.context.account.peerId) {
animationNode.success = { [weak self] in
if let strongSelf = self, let item = strongSelf.item {
@@ -377,12 +377,10 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
if let file = file {
let dimensions = file.dimensions ?? PixelDimensions(width: 512, height: 512)
let fittedSize = isEmoji ? dimensions.cgSize.aspectFilled(CGSize(width: 384.0, height: 384.0)) : dimensions.cgSize.aspectFitted(CGSize(width: 384.0, height: 384.0))
- let mode: AnimatedStickerMode
- if file.resource is LocalFileReferenceMediaResource {
- mode = .direct
- } else {
- mode = .cached
- }
+
+ let pathPrefix = item.context.account.postbox.mediaBox.shortLivedResourceCachePathPrefix(file.resource.id)
+ let mode: AnimatedStickerMode = .direct(cachePathPrefix: pathPrefix)
+
animationNode.setup(source: AnimatedStickerResourceSource(account: item.context.account, resource: file.resource, fitzModifier: fitzModifier), width: Int(fittedSize.width), height: Int(fittedSize.height), playbackMode: playbackMode, mode: mode)
}
}
diff --git a/submodules/TelegramUI/Sources/ChatMessageAvatarAccessoryItem.swift b/submodules/TelegramUI/Sources/ChatMessageAvatarAccessoryItem.swift
index 2bc87950b6..844669dea1 100644
--- a/submodules/TelegramUI/Sources/ChatMessageAvatarAccessoryItem.swift
+++ b/submodules/TelegramUI/Sources/ChatMessageAvatarAccessoryItem.swift
@@ -1,6 +1,7 @@
import Foundation
import UIKit
import Postbox
+import AsyncDisplayKit
import Display
import TelegramCore
import SyncCore
@@ -17,16 +18,18 @@ final class ChatMessageAvatarAccessoryItem: ListViewAccessoryItem {
private let messageReference: MessageReference?
private let messageTimestamp: Int32
private let emptyColor: UIColor
+ private let controllerInteraction: ChatControllerInteraction
private let day: Int32
- init(context: AccountContext, peerId: PeerId, peer: Peer?, messageReference: MessageReference?, messageTimestamp: Int32, emptyColor: UIColor) {
+ init(context: AccountContext, peerId: PeerId, peer: Peer?, messageReference: MessageReference?, messageTimestamp: Int32, emptyColor: UIColor, controllerInteraction: ChatControllerInteraction) {
self.context = context
self.peerId = peerId
self.peer = peer
self.messageReference = messageReference
self.messageTimestamp = messageTimestamp
self.emptyColor = emptyColor
+ self.controllerInteraction = controllerInteraction
var t: time_t = time_t(messageTimestamp)
var timeinfo: tm = tm()
@@ -47,29 +50,57 @@ final class ChatMessageAvatarAccessoryItem: ListViewAccessoryItem {
let node = ChatMessageAvatarAccessoryItemNode()
node.frame = CGRect(origin: CGPoint(), size: CGSize(width: 38.0, height: 38.0))
if let peer = self.peer {
- node.setPeer(context: self.context, theme: self.context.sharedContext.currentPresentationData.with({ $0 }).theme, synchronousLoad: synchronous, peer: peer, authorOfMessage: self.messageReference, emptyColor: self.emptyColor)
+ node.setPeer(context: self.context, theme: self.context.sharedContext.currentPresentationData.with({ $0 }).theme, synchronousLoad: synchronous, peer: peer, authorOfMessage: self.messageReference, emptyColor: self.emptyColor, controllerInteraction: self.controllerInteraction)
}
return node
}
}
final class ChatMessageAvatarAccessoryItemNode: ListViewAccessoryItemNode {
+ var controllerInteraction: ChatControllerInteraction?
+ var peer: Peer?
+
+ let containerNode: ContextControllerSourceNode
let avatarNode: AvatarNode
+ var contextActionIsEnabled: Bool = true {
+ didSet {
+ if self.contextActionIsEnabled != oldValue {
+ self.containerNode.isGestureEnabled = self.contextActionIsEnabled
+ }
+ }
+ }
+
override init() {
- let isLayerBacked = !smartInvertColorsEnabled()
+ self.containerNode = ContextControllerSourceNode()
+ self.containerNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: 38.0, height: 38.0))
self.avatarNode = AvatarNode(font: avatarFont)
- self.avatarNode.isLayerBacked = isLayerBacked
- self.avatarNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: 38.0, height: 38.0))
+ self.avatarNode.isLayerBacked = !smartInvertColorsEnabled()
+ self.avatarNode.frame = self.containerNode.bounds
+ self.avatarNode.isUserInteractionEnabled = false
super.init()
- self.isLayerBacked = isLayerBacked
- self.addSubnode(self.avatarNode)
+ self.isLayerBacked = false
+
+ self.addSubnode(self.containerNode)
+ self.containerNode.addSubnode(self.avatarNode)
+
+ self.containerNode.activated = { [weak self] gesture, _ in
+ guard let strongSelf = self, let controllerInteraction = strongSelf.controllerInteraction, let peer = strongSelf.peer else {
+ return
+ }
+ strongSelf.controllerInteraction?.openPeerContextMenu(peer, strongSelf.containerNode, strongSelf.containerNode.bounds, gesture)
+ }
}
- func setPeer(context: AccountContext, theme: PresentationTheme, synchronousLoad:Bool, peer: Peer, authorOfMessage: MessageReference?, emptyColor: UIColor) {
+ func setPeer(context: AccountContext, theme: PresentationTheme, synchronousLoad:Bool, peer: Peer, authorOfMessage: MessageReference?, emptyColor: UIColor, controllerInteraction: ChatControllerInteraction) {
+ self.controllerInteraction = controllerInteraction
+ self.peer = peer
+
+ self.contextActionIsEnabled = peer.smallProfileImage != nil
+
var overrideImage: AvatarNodeImageOverride?
if peer.isDeleted {
overrideImage = .deletedIcon
diff --git a/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift
index fe6f8da8f9..6b153f1771 100644
--- a/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift
+++ b/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift
@@ -2719,7 +2719,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
}
if let avatarNode = self.accessoryItemNode as? ChatMessageAvatarAccessoryItemNode, avatarNode.frame.contains(point) {
- return self.view
+ return avatarNode.containerNode.view
}
if !self.backgroundNode.frame.contains(point) {
diff --git a/submodules/TelegramUI/Sources/ChatMessageCallBubbleContentNode.swift b/submodules/TelegramUI/Sources/ChatMessageCallBubbleContentNode.swift
index ceb34cd31b..c6193fa5a4 100644
--- a/submodules/TelegramUI/Sources/ChatMessageCallBubbleContentNode.swift
+++ b/submodules/TelegramUI/Sources/ChatMessageCallBubbleContentNode.swift
@@ -85,10 +85,26 @@ class ChatMessageCallBubbleContentNode: ChatMessageBubbleContentNode {
switch discardReason {
case .busy, .disconnect:
callSuccessful = false
- titleString = item.presentationData.strings.Notification_CallCanceled
+ if isVideo {
+ titleString = item.presentationData.strings.Notification_VideoCallCanceled
+ } else {
+ titleString = item.presentationData.strings.Notification_CallCanceled
+ }
case .missed:
callSuccessful = false
- titleString = incoming ? item.presentationData.strings.Notification_CallMissed : item.presentationData.strings.Notification_CallCanceled
+ if incoming {
+ if isVideo {
+ titleString = item.presentationData.strings.Notification_VideoCallMissed
+ } else {
+ titleString = item.presentationData.strings.Notification_CallMissed
+ }
+ } else {
+ if isVideo {
+ titleString = item.presentationData.strings.Notification_VideoCallCanceled
+ } else {
+ titleString = item.presentationData.strings.Notification_CallCanceled
+ }
+ }
case .hangup:
break
}
@@ -99,7 +115,7 @@ class ChatMessageCallBubbleContentNode: ChatMessageBubbleContentNode {
if titleString == nil {
let baseString: String
- if message.flags.contains(.Incoming) {
+ if incoming {
if isVideo {
baseString = item.presentationData.strings.Notification_VideoCallIncoming
} else {
@@ -135,9 +151,17 @@ class ChatMessageCallBubbleContentNode: ChatMessageBubbleContentNode {
var buttonImage: UIImage?
if incoming {
- buttonImage = PresentationResourcesChat.chatBubbleIncomingCallButtonImage(item.presentationData.theme.theme)
+ if isVideo {
+ buttonImage = PresentationResourcesChat.chatBubbleIncomingVideoCallButtonImage(item.presentationData.theme.theme)
+ } else {
+ buttonImage = PresentationResourcesChat.chatBubbleIncomingCallButtonImage(item.presentationData.theme.theme)
+ }
} else {
- buttonImage = PresentationResourcesChat.chatBubbleOutgoingCallButtonImage(item.presentationData.theme.theme)
+ if isVideo {
+ buttonImage = PresentationResourcesChat.chatBubbleOutgoingVideoCallButtonImage(item.presentationData.theme.theme)
+ } else {
+ buttonImage = PresentationResourcesChat.chatBubbleOutgoingCallButtonImage(item.presentationData.theme.theme)
+ }
}
let dateText = stringForMessageTimestampStatus(accountPeerId: item.context.account.peerId, message: item.message, dateTimeFormat: item.presentationData.dateTimeFormat, nameDisplayOrder: item.presentationData.nameDisplayOrder, strings: item.presentationData.strings, reactionCount: 0)
diff --git a/submodules/TelegramUI/Sources/ChatMessageInteractiveFileNode.swift b/submodules/TelegramUI/Sources/ChatMessageInteractiveFileNode.swift
index bcb1b5ae6c..15d5823322 100644
--- a/submodules/TelegramUI/Sources/ChatMessageInteractiveFileNode.swift
+++ b/submodules/TelegramUI/Sources/ChatMessageInteractiveFileNode.swift
@@ -823,7 +823,7 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
switch fetchStatus {
case let .Fetching(_, progress):
let adjustedProgress = max(progress, 0.027)
- state = .progress(value: CGFloat(adjustedProgress), cancelEnabled: true)
+ state = .progress(value: CGFloat(adjustedProgress), cancelEnabled: true, appearance: nil)
case .Local:
if isAudio {
state = .play
diff --git a/submodules/TelegramUI/Sources/ChatMessageInteractiveInstantVideoNode.swift b/submodules/TelegramUI/Sources/ChatMessageInteractiveInstantVideoNode.swift
index 7b34b53c9e..87c3688d60 100644
--- a/submodules/TelegramUI/Sources/ChatMessageInteractiveInstantVideoNode.swift
+++ b/submodules/TelegramUI/Sources/ChatMessageInteractiveInstantVideoNode.swift
@@ -519,7 +519,7 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
}
var isBuffering: Bool?
- if let message = self.item?.message, let media = self.media, let size = media.size, (isMediaStreamable(message: message, media: media) || size <= 256 * 1024) && (self.automaticDownload ?? false) {
+ if let message = self.item?.message, let media = self.media, isMediaStreamable(message: message, media: media) && (self.automaticDownload ?? false) {
if let playerStatus = self.playerStatus, case .buffering = playerStatus.status {
isBuffering = true
} else {
diff --git a/submodules/TelegramUI/Sources/ChatMessageInteractiveMediaNode.swift b/submodules/TelegramUI/Sources/ChatMessageInteractiveMediaNode.swift
index e8ffcdb661..2b00d06405 100644
--- a/submodules/TelegramUI/Sources/ChatMessageInteractiveMediaNode.swift
+++ b/submodules/TelegramUI/Sources/ChatMessageInteractiveMediaNode.swift
@@ -485,9 +485,9 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio
updatedFetchControls = FetchControls(fetch: { manual in
if let strongSelf = self {
if !manual {
- strongSelf.fetchDisposable.set(chatMessagePhotoInteractiveFetched(context: context, photoReference: .message(message: MessageReference(message), media: image), storeToDownloadsPeerType: storeToDownloadsPeerType).start())
- } else if let resource = largestRepresentationForPhoto(image)?.resource {
- strongSelf.fetchDisposable.set(messageMediaImageInteractiveFetched(context: context, message: message, image: image, resource: resource, storeToDownloadsPeerType: storeToDownloadsPeerType).start())
+ strongSelf.fetchDisposable.set(chatMessagePhotoInteractiveFetched(context: context, photoReference: .message(message: MessageReference(message), media: image), displayAtSize: 600, storeToDownloadsPeerType: storeToDownloadsPeerType).start())
+ } else if let representation = largestRepresentationForPhoto(image) {
+ strongSelf.fetchDisposable.set(messageMediaImageInteractiveFetched(context: context, message: message, image: image, resource: representation.resource, range: representationFetchRangeForDisplayAtSize(representation: representation, dimension: 600), storeToDownloadsPeerType: storeToDownloadsPeerType).start())
}
}
}, cancel: {
@@ -604,7 +604,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio
} else {
var representations: [ImageRepresentationWithReference] = file.previewRepresentations.map({ ImageRepresentationWithReference(representation: $0, reference: AnyMediaReference.message(message: MessageReference(message), media: file).resourceReference($0.resource)) })
if file.mimeType == "image/svg+xml" || file.mimeType == "application/x-tgwallpattern" {
- representations.append(ImageRepresentationWithReference(representation: .init(dimensions: PixelDimensions(width: 1440, height: 2960), resource: file.resource), reference: AnyMediaReference.message(message: MessageReference(message), media: file).resourceReference(file.resource)))
+ representations.append(ImageRepresentationWithReference(representation: .init(dimensions: PixelDimensions(width: 1440, height: 2960), resource: file.resource, progressiveSizes: []), reference: AnyMediaReference.message(message: MessageReference(message), media: file).resourceReference(file.resource)))
}
if ["image/png", "image/svg+xml", "application/x-tgwallpattern"].contains(file.mimeType) {
return patternWallpaperImage(account: context.account, accountManager: context.sharedContext.accountManager, representations: representations, mode: .thumbnail)
@@ -649,7 +649,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio
}
}
} else {
- updatedStatusSignal = chatMessagePhotoStatus(context: context, messageId: message.id, photoReference: .message(message: MessageReference(message), media: image))
+ updatedStatusSignal = chatMessagePhotoStatus(context: context, messageId: message.id, photoReference: .message(message: MessageReference(message), media: image), displayAtSize: 600)
|> map { resourceStatus -> (MediaResourceStatus, MediaResourceStatus?) in
return (resourceStatus, nil)
}
diff --git a/submodules/TelegramUI/Sources/ChatMessageItem.swift b/submodules/TelegramUI/Sources/ChatMessageItem.swift
index 0f5350509b..ad139684c6 100644
--- a/submodules/TelegramUI/Sources/ChatMessageItem.swift
+++ b/submodules/TelegramUI/Sources/ChatMessageItem.swift
@@ -368,7 +368,7 @@ public final class ChatMessageItem: ListViewItem, CustomStringConvertible {
}
if !hasActionMedia && !isBroadcastChannel {
if let effectiveAuthor = effectiveAuthor {
- accessoryItem = ChatMessageAvatarAccessoryItem(context: context, peerId: effectiveAuthor.id, peer: effectiveAuthor, messageReference: MessageReference(message), messageTimestamp: content.index.timestamp, emptyColor: presentationData.theme.theme.chat.message.incoming.bubble.withoutWallpaper.fill)
+ accessoryItem = ChatMessageAvatarAccessoryItem(context: context, peerId: effectiveAuthor.id, peer: effectiveAuthor, messageReference: MessageReference(message), messageTimestamp: content.index.timestamp, emptyColor: presentationData.theme.theme.chat.message.incoming.bubble.withoutWallpaper.fill, controllerInteraction: controllerInteraction)
}
}
}
diff --git a/submodules/TelegramUI/Sources/ChatRecentActionsControllerNode.swift b/submodules/TelegramUI/Sources/ChatRecentActionsControllerNode.swift
index 959ea86be8..2211b4da21 100644
--- a/submodules/TelegramUI/Sources/ChatRecentActionsControllerNode.swift
+++ b/submodules/TelegramUI/Sources/ChatRecentActionsControllerNode.swift
@@ -450,6 +450,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
}, animateDiceSuccess: {
}, greetingStickerNode: {
return nil
+ }, openPeerContextMenu: { _, _, _, _ in
}, requestMessageUpdate: { _ in
}, cancelInteractiveKeyboardGestures: {
}, automaticMediaDownloadSettings: self.automaticMediaDownloadSettings,
diff --git a/submodules/TelegramUI/Sources/ChatRecordingPreviewInputPanelNode.swift b/submodules/TelegramUI/Sources/ChatRecordingPreviewInputPanelNode.swift
index ca92c1f8d5..e3c59fed58 100644
--- a/submodules/TelegramUI/Sources/ChatRecordingPreviewInputPanelNode.swift
+++ b/submodules/TelegramUI/Sources/ChatRecordingPreviewInputPanelNode.swift
@@ -176,7 +176,7 @@ final class ChatRecordingPreviewInputPanelNode: ChatInputPanelNode {
|> deliverOnMainQueue).start(next: { [weak self] status in
if let strongSelf = self {
switch status.status {
- case .playing, .buffering(_, true):
+ case .playing, .buffering(_, true, _):
strongSelf.playButton.isHidden = true
default:
strongSelf.playButton.isHidden = false
diff --git a/submodules/TelegramUI/Sources/ChatSearchResultsContollerNode.swift b/submodules/TelegramUI/Sources/ChatSearchResultsContollerNode.swift
index 399e715d5a..a69e9c64c8 100644
--- a/submodules/TelegramUI/Sources/ChatSearchResultsContollerNode.swift
+++ b/submodules/TelegramUI/Sources/ChatSearchResultsContollerNode.swift
@@ -183,7 +183,7 @@ class ChatSearchResultsControllerNode: ViewControllerTracingNode, UIScrollViewDe
}, setPeerIdWithRevealedOptions: { _, _ in
}, setItemPinned: { _, _ in
}, setPeerMuted: { _, _ in
- }, deletePeer: { _ in
+ }, deletePeer: { _, _ in
}, updatePeerGrouping: { _, _ in
}, togglePeerMarkedUnread: { _, _ in
}, toggleArchivedFolderHiddenByDefault: {
diff --git a/submodules/TelegramUI/Sources/ChatTextFormat.swift b/submodules/TelegramUI/Sources/ChatTextFormat.swift
index fef4a10caf..f36db78ff4 100644
--- a/submodules/TelegramUI/Sources/ChatTextFormat.swift
+++ b/submodules/TelegramUI/Sources/ChatTextFormat.swift
@@ -1,5 +1,6 @@
import Foundation
import TextFormat
+import Postbox
import AccountContext
func chatTextInputAddFormattingAttribute(_ state: ChatTextInputState, attribute: NSAttributedString.Key) -> ChatTextInputState {
@@ -75,3 +76,33 @@ func chatTextInputAddLinkAttribute(_ state: ChatTextInputState, url: String) ->
return state
}
}
+
+func chatTextInputAddMentionAttribute(_ state: ChatTextInputState, peer: Peer) -> ChatTextInputState {
+ let inputText = NSMutableAttributedString(attributedString: state.inputText)
+
+ let range = NSMakeRange(state.selectionRange.startIndex, state.selectionRange.endIndex - state.selectionRange.startIndex)
+
+ if let addressName = peer.addressName, !addressName.isEmpty {
+ let replacementText = "@\(addressName) "
+
+ inputText.replaceCharacters(in: range, with: replacementText)
+
+ let selectionPosition = range.lowerBound + (replacementText as NSString).length
+
+ return ChatTextInputState(inputText: inputText, selectionRange: selectionPosition ..< selectionPosition)
+ } else if !peer.compactDisplayTitle.isEmpty {
+ let replacementText = NSMutableAttributedString()
+ replacementText.append(NSAttributedString(string: peer.compactDisplayTitle, attributes: [ChatTextInputAttributes.textMention: ChatTextInputTextMentionAttribute(peerId: peer.id)]))
+ replacementText.append(NSAttributedString(string: " "))
+
+ let updatedRange = NSRange(location: range.location , length: range.length)
+
+ inputText.replaceCharacters(in: updatedRange, with: replacementText)
+
+ let selectionPosition = updatedRange.lowerBound + replacementText.length
+
+ return ChatTextInputState(inputText: inputText, selectionRange: selectionPosition ..< selectionPosition)
+ } else {
+ return state
+ }
+}
diff --git a/submodules/TelegramUI/Sources/ComposeController.swift b/submodules/TelegramUI/Sources/ComposeController.swift
index 4c760f3562..c76dbaae28 100644
--- a/submodules/TelegramUI/Sources/ComposeController.swift
+++ b/submodules/TelegramUI/Sources/ComposeController.swift
@@ -116,7 +116,7 @@ public class ComposeController: ViewController {
self?.activateSearch()
}
- self.contactsNode.contactListNode.openPeer = { [weak self] peer in
+ self.contactsNode.contactListNode.openPeer = { [weak self] peer, _ in
if case let .peer(peer, _, _) = peer {
self?.openPeer(peerId: peer.id)
}
@@ -157,7 +157,7 @@ public class ComposeController: ViewController {
strongSelf.createActionDisposable.set((controller.result
|> take(1)
|> deliverOnMainQueue).start(next: { [weak controller] peer in
- if let strongSelf = self, let contactPeer = peer, case let .peer(peer, _, _) = contactPeer {
+ if let strongSelf = self, let (contactPeer, _) = peer, case let .peer(peer, _, _) = contactPeer {
controller?.dismissSearch()
controller?.displayNavigationActivity = true
strongSelf.createActionDisposable.set((createSecretChat(account: strongSelf.context.account, peerId: peer.id) |> deliverOnMainQueue).start(next: { peerId in
diff --git a/submodules/TelegramUI/Sources/ContactMultiselectionControllerNode.swift b/submodules/TelegramUI/Sources/ContactMultiselectionControllerNode.swift
index 41e936fadb..c9e003959a 100644
--- a/submodules/TelegramUI/Sources/ContactMultiselectionControllerNode.swift
+++ b/submodules/TelegramUI/Sources/ContactMultiselectionControllerNode.swift
@@ -119,7 +119,7 @@ final class ContactMultiselectionControllerNode: ASDisplayNode {
switch self.contentNode {
case let .contacts(contactsNode):
- contactsNode.openPeer = { [weak self] peer in
+ contactsNode.openPeer = { [weak self] peer, _ in
self?.openPeer?(peer)
}
case let .chats(chatsNode):
@@ -186,7 +186,7 @@ final class ContactMultiselectionControllerNode: ASDisplayNode {
globalSearch = false
}
let searchResultsNode = ContactListNode(context: context, presentation: .single(.search(signal: searchText.get(), searchChatList: searchChatList, searchDeviceContacts: false, searchGroups: searchGroups, searchChannels: searchChannels, globalSearch: globalSearch)), filters: filters, selectionState: selectionState, isSearch: true)
- searchResultsNode.openPeer = { peer in
+ searchResultsNode.openPeer = { peer, _ in
self?.tokenListNode.setText("")
self?.openPeer?(peer)
}
diff --git a/submodules/TelegramUI/Sources/ContactSelectionController.swift b/submodules/TelegramUI/Sources/ContactSelectionController.swift
index d8ddcc4c9c..f677f76506 100644
--- a/submodules/TelegramUI/Sources/ContactSelectionController.swift
+++ b/submodules/TelegramUI/Sources/ContactSelectionController.swift
@@ -34,14 +34,15 @@ class ContactSelectionControllerImpl: ViewController, ContactSelectionController
private let titleProducer: (PresentationStrings) -> String
private let options: [ContactListAdditionalOption]
private let displayDeviceContacts: Bool
+ private let displayCallIcons: Bool
private var _ready = Promise()
override var ready: Promise {
return self._ready
}
- private let _result = Promise()
- var result: Signal {
+ private let _result = Promise<(ContactListPeer, ContactListAction)?>()
+ var result: Signal<(ContactListPeer, ContactListAction)?, NoError> {
return self._result.get()
}
@@ -74,6 +75,7 @@ class ContactSelectionControllerImpl: ViewController, ContactSelectionController
self.titleProducer = params.title
self.options = params.options
self.displayDeviceContacts = params.displayDeviceContacts
+ self.displayCallIcons = params.displayCallIcons
self.confirmation = params.confirmation
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
@@ -143,7 +145,7 @@ class ContactSelectionControllerImpl: ViewController, ContactSelectionController
}
override func loadDisplayNode() {
- self.displayNode = ContactSelectionControllerNode(context: self.context, options: self.options, displayDeviceContacts: self.displayDeviceContacts)
+ self.displayNode = ContactSelectionControllerNode(context: self.context, options: self.options, displayDeviceContacts: self.displayDeviceContacts, displayCallIcons: self.displayCallIcons)
self._ready.set(self.contactsNode.contactListNode.ready)
self.contactsNode.navigationBar = self.navigationBar
@@ -153,15 +155,15 @@ class ContactSelectionControllerImpl: ViewController, ContactSelectionController
}
self.contactsNode.requestOpenPeerFromSearch = { [weak self] peer in
- self?.openPeer(peer: peer)
+ self?.openPeer(peer: peer, action: .generic)
}
self.contactsNode.contactListNode.activateSearch = { [weak self] in
self?.activateSearch()
}
- self.contactsNode.contactListNode.openPeer = { [weak self] peer in
- self?.openPeer(peer: peer)
+ self.contactsNode.contactListNode.openPeer = { [weak self] peer, action in
+ self?.openPeer(peer: peer, action: action)
}
self.contactsNode.contactListNode.suppressPermissionWarning = { [weak self] in
@@ -256,12 +258,12 @@ class ContactSelectionControllerImpl: ViewController, ContactSelectionController
}
}
- private func openPeer(peer: ContactListPeer) {
+ private func openPeer(peer: ContactListPeer, action: ContactListAction) {
self.contactsNode.contactListNode.listNode.clearHighlightAnimated(true)
self.confirmationDisposable.set((self.confirmation(peer) |> deliverOnMainQueue).start(next: { [weak self] value in
if let strongSelf = self {
if value {
- strongSelf._result.set(.single(peer))
+ strongSelf._result.set(.single((peer, action)))
if strongSelf.autoDismiss {
strongSelf.dismiss()
}
diff --git a/submodules/TelegramUI/Sources/ContactSelectionControllerNode.swift b/submodules/TelegramUI/Sources/ContactSelectionControllerNode.swift
index b0920be509..baa6cf8b3d 100644
--- a/submodules/TelegramUI/Sources/ContactSelectionControllerNode.swift
+++ b/submodules/TelegramUI/Sources/ContactSelectionControllerNode.swift
@@ -21,7 +21,8 @@ final class ContactSelectionControllerNode: ASDisplayNode {
}
}
- let displayDeviceContacts: Bool
+ private let displayDeviceContacts: Bool
+ private let displayCallIcons: Bool
let contactListNode: ContactListNode
private let dimNode: ASDisplayNode
@@ -40,12 +41,13 @@ final class ContactSelectionControllerNode: ASDisplayNode {
var presentationData: PresentationData
var presentationDataDisposable: Disposable?
- init(context: AccountContext, options: [ContactListAdditionalOption], displayDeviceContacts: Bool) {
+ init(context: AccountContext, options: [ContactListAdditionalOption], displayDeviceContacts: Bool, displayCallIcons: Bool) {
self.context = context
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
self.displayDeviceContacts = displayDeviceContacts
+ self.displayCallIcons = displayCallIcons
- self.contactListNode = ContactListNode(context: context, presentation: .single(.natural(options: options, includeChatList: false)))
+ self.contactListNode = ContactListNode(context: context, presentation: .single(.natural(options: options, includeChatList: false)), displayCallIcons: displayCallIcons)
self.dimNode = ASDisplayNode()
diff --git a/submodules/TelegramUI/Sources/CreateChannelController.swift b/submodules/TelegramUI/Sources/CreateChannelController.swift
index 1828a3d8b5..c1ac7433e0 100644
--- a/submodules/TelegramUI/Sources/CreateChannelController.swift
+++ b/submodules/TelegramUI/Sources/CreateChannelController.swift
@@ -317,7 +317,7 @@ public func createChannelController(context: AccountContext) -> ViewController {
if let data = image.jpegData(compressionQuality: 0.6) {
let resource = LocalFileMediaResource(fileId: arc4random64())
context.account.postbox.mediaBox.storeResourceData(resource.id, data: data)
- let representation = TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 640, height: 640), resource: resource)
+ let representation = TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 640, height: 640), resource: resource, progressiveSizes: [])
uploadedAvatar.set(uploadedPeerPhoto(postbox: context.account.postbox, network: context.account.network, resource: resource))
uploadedVideoAvatar = nil
updateState { current in
@@ -332,7 +332,7 @@ public func createChannelController(context: AccountContext) -> ViewController {
if let data = image.jpegData(compressionQuality: 0.6) {
let photoResource = LocalFileMediaResource(fileId: arc4random64())
context.account.postbox.mediaBox.storeResourceData(photoResource.id, data: data)
- let representation = TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 640, height: 640), resource: photoResource)
+ let representation = TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 640, height: 640), resource: photoResource, progressiveSizes: [])
updateState { state in
var state = state
state.avatar = .image(representation, true)
diff --git a/submodules/TelegramUI/Sources/CreateGroupController.swift b/submodules/TelegramUI/Sources/CreateGroupController.swift
index 6ec14a0c8b..52caf3fc28 100644
--- a/submodules/TelegramUI/Sources/CreateGroupController.swift
+++ b/submodules/TelegramUI/Sources/CreateGroupController.swift
@@ -575,7 +575,7 @@ public func createGroupControllerImpl(context: AccountContext, peerIds: [PeerId]
if let data = image.jpegData(compressionQuality: 0.6) {
let resource = LocalFileMediaResource(fileId: arc4random64())
context.account.postbox.mediaBox.storeResourceData(resource.id, data: data)
- let representation = TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 640, height: 640), resource: resource)
+ let representation = TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 640, height: 640), resource: resource, progressiveSizes: [])
uploadedAvatar.set(uploadedPeerPhoto(postbox: context.account.postbox, network: context.account.network, resource: resource))
uploadedVideoAvatar = nil
updateState { current in
@@ -590,7 +590,7 @@ public func createGroupControllerImpl(context: AccountContext, peerIds: [PeerId]
if let data = image.jpegData(compressionQuality: 0.6) {
let photoResource = LocalFileMediaResource(fileId: arc4random64())
context.account.postbox.mediaBox.storeResourceData(photoResource.id, data: data)
- let representation = TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 640, height: 640), resource: photoResource)
+ let representation = TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 640, height: 640), resource: photoResource, progressiveSizes: [])
updateState { state in
var state = state
state.avatar = .image(representation, true)
diff --git a/submodules/TelegramUI/Sources/DrawingStickersScreen.swift b/submodules/TelegramUI/Sources/DrawingStickersScreen.swift
index d3ee0bcda8..2763398844 100644
--- a/submodules/TelegramUI/Sources/DrawingStickersScreen.swift
+++ b/submodules/TelegramUI/Sources/DrawingStickersScreen.swift
@@ -143,6 +143,7 @@ private final class DrawingStickersScreenNode: ViewControllerTracingNode {
}, animateDiceSuccess: {
}, greetingStickerNode: {
return nil
+ }, openPeerContextMenu: { _, _, _, _ in
}, requestMessageUpdate: { _ in
}, cancelInteractiveKeyboardGestures: {
}, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings,
diff --git a/submodules/TelegramUI/Sources/FileMediaResourceStatus.swift b/submodules/TelegramUI/Sources/FileMediaResourceStatus.swift
index 37d4817207..66f8e5198d 100644
--- a/submodules/TelegramUI/Sources/FileMediaResourceStatus.swift
+++ b/submodules/TelegramUI/Sources/FileMediaResourceStatus.swift
@@ -60,7 +60,7 @@ func messageFileMediaResourceStatus(context: AccountContext, file: TelegramMedia
mediaStatus = .playbackStatus(.playing)
case .paused:
mediaStatus = .playbackStatus(.paused)
- case let .buffering(_, whilePlaying):
+ case let .buffering(_, whilePlaying, _):
if whilePlaying {
mediaStatus = .playbackStatus(.playing)
} else {
@@ -84,7 +84,7 @@ func messageFileMediaResourceStatus(context: AccountContext, file: TelegramMedia
mediaStatus = .playbackStatus(.playing)
case .paused:
mediaStatus = .playbackStatus(.paused)
- case let .buffering(_, whilePlaying):
+ case let .buffering(_, whilePlaying, _):
if whilePlaying {
mediaStatus = .playbackStatus(.playing)
} else {
diff --git a/submodules/TelegramUI/Sources/GifPaneSearchContentNode.swift b/submodules/TelegramUI/Sources/GifPaneSearchContentNode.swift
index c24ab68115..69b9cbf440 100644
--- a/submodules/TelegramUI/Sources/GifPaneSearchContentNode.swift
+++ b/submodules/TelegramUI/Sources/GifPaneSearchContentNode.swift
@@ -100,7 +100,8 @@ func paneGifSearchForQuery(account: Account, query: String, offset: String?, inc
} else {
previews.append(TelegramMediaImageRepresentation(
dimensions: dimensions,
- resource: thumbnailResource
+ resource: thumbnailResource,
+ progressiveSizes: []
))
}
}
diff --git a/submodules/TelegramUI/Sources/HorizontalListContextResultsChatInputPanelItem.swift b/submodules/TelegramUI/Sources/HorizontalListContextResultsChatInputPanelItem.swift
index c6814d2734..9b9573da3a 100644
--- a/submodules/TelegramUI/Sources/HorizontalListContextResultsChatInputPanelItem.swift
+++ b/submodules/TelegramUI/Sources/HorizontalListContextResultsChatInputPanelItem.swift
@@ -313,7 +313,7 @@ final class HorizontalListContextResultsChatInputPanelItemNode: ListViewItemNode
if let stickerFile = stickerFile {
updateImageSignal = chatMessageSticker(account: item.account, file: stickerFile, small: false, fetched: true)
} else {
- let tmpRepresentation = TelegramMediaImageRepresentation(dimensions: PixelDimensions(CGSize(width: fittedImageDimensions.width * 2.0, height: fittedImageDimensions.height * 2.0)), resource: imageResource)
+ let tmpRepresentation = TelegramMediaImageRepresentation(dimensions: PixelDimensions(CGSize(width: fittedImageDimensions.width * 2.0, height: fittedImageDimensions.height * 2.0)), resource: imageResource, progressiveSizes: [])
let tmpImage = TelegramMediaImage(imageId: MediaId(namespace: 0, id: 0), representations: [tmpRepresentation], immediateThumbnailData: nil, reference: nil, partialReference: nil, flags: [])
updateImageSignal = chatMessagePhoto(postbox: item.account.postbox, photoReference: .standalone(media: tmpImage), synchronousLoad: true)
}
diff --git a/submodules/TelegramUI/Sources/LegacyInstantVideoController.swift b/submodules/TelegramUI/Sources/LegacyInstantVideoController.swift
index a84aa1b4d2..09a52d21c1 100644
--- a/submodules/TelegramUI/Sources/LegacyInstantVideoController.swift
+++ b/submodules/TelegramUI/Sources/LegacyInstantVideoController.swift
@@ -158,7 +158,7 @@ func legacyInstantVideoController(theme: PresentationTheme, panelFrame: CGRect,
let thumbnailImage = TGScaleImageToPixelSize(previewImage, thumbnailSize)!
if let thumbnailData = thumbnailImage.jpegData(compressionQuality: 0.4) {
context.account.postbox.mediaBox.storeResourceData(resource.id, data: thumbnailData)
- previewRepresentations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(thumbnailSize), resource: resource))
+ previewRepresentations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(thumbnailSize), resource: resource, progressiveSizes: []))
}
}
diff --git a/submodules/TelegramUI/Sources/ManagedDiceAnimationNode.swift b/submodules/TelegramUI/Sources/ManagedDiceAnimationNode.swift
index 9738f2b163..1c03f653ec 100644
--- a/submodules/TelegramUI/Sources/ManagedDiceAnimationNode.swift
+++ b/submodules/TelegramUI/Sources/ManagedDiceAnimationNode.swift
@@ -72,7 +72,7 @@ private func rollingAnimationItem(account: Account, emojis: Signal<[TelegramMedi
return .single(ManagedAnimationItem(source: .local("Darts_Aiming"), loop: true))
case "🏀":
return .single(ManagedAnimationItem(source: .local("Basketball_Bouncing"), loop: true))
- case "⚽️":
+ case "⚽":
return .single(ManagedAnimationItem(source: .local("Football_Bouncing"), loop: true))
default:
return animationItem(account: account, emojis: emojis, emoji: emoji, value: nil, loop: true)
diff --git a/submodules/TelegramUI/Sources/MediaManager.swift b/submodules/TelegramUI/Sources/MediaManager.swift
index 2132faf34f..24ab8e3734 100644
--- a/submodules/TelegramUI/Sources/MediaManager.swift
+++ b/submodules/TelegramUI/Sources/MediaManager.swift
@@ -149,7 +149,7 @@ public final class MediaManagerImpl: NSObject, MediaManager {
switch value.status.status {
case .playing:
isPlaying = true
- case .buffering(_, true):
+ case .buffering(_, true, _):
isPlaying = true
default:
break
@@ -229,7 +229,7 @@ public final class MediaManagerImpl: NSObject, MediaManager {
updatedGlobalControlOptions.insert(.next)
updatedGlobalControlOptions.insert(.seek)
switch state.status.status {
- case .playing, .buffering(_, true):
+ case .playing, .buffering(_, true, _):
updatedGlobalControlOptions.insert(.pause)
default:
updatedGlobalControlOptions.insert(.play)
@@ -383,7 +383,7 @@ public final class MediaManagerImpl: NSObject, MediaManager {
switch state.status.status {
case .playing:
isPlaying = true
- case let .buffering(_, whilePlaying):
+ case let .buffering(_, whilePlaying, _):
isPlaying = whilePlaying
default:
break
diff --git a/submodules/TelegramUI/Sources/NotificationContentContext.swift b/submodules/TelegramUI/Sources/NotificationContentContext.swift
index 50b8133924..9c45f18203 100644
--- a/submodules/TelegramUI/Sources/NotificationContentContext.swift
+++ b/submodules/TelegramUI/Sources/NotificationContentContext.swift
@@ -327,7 +327,7 @@ public final class NotificationViewControllerImpl {
let dimensions = fileReference.media.dimensions ?? PixelDimensions(width: 512, height: 512)
let fittedDimensions = dimensions.cgSize.aspectFitted(CGSize(width: 512.0, height: 512.0))
strongSelf.imageNode.setSignal(chatMessageAnimatedSticker(postbox: accountAndImage.0.postbox, file: fileReference.media, small: false, size: fittedDimensions))
- animatedStickerNode.setup(source: AnimatedStickerResourceSource(account: accountAndImage.0, resource: fileReference.media.resource), width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .direct)
+ animatedStickerNode.setup(source: AnimatedStickerResourceSource(account: accountAndImage.0, resource: fileReference.media.resource), width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .direct(cachePathPrefix: nil))
animatedStickerNode.visibility = true
accountAndImage.0.network.shouldExplicitelyKeepWorkerConnections.set(.single(true))
diff --git a/submodules/TelegramUI/Sources/OverlayPlayerControllerNode.swift b/submodules/TelegramUI/Sources/OverlayPlayerControllerNode.swift
index 0647db3181..b635f7ac1b 100644
--- a/submodules/TelegramUI/Sources/OverlayPlayerControllerNode.swift
+++ b/submodules/TelegramUI/Sources/OverlayPlayerControllerNode.swift
@@ -130,6 +130,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, UIGestu
}, animateDiceSuccess: {
}, greetingStickerNode: {
return nil
+ }, openPeerContextMenu: { _, _, _, _ in
}, requestMessageUpdate: { _ in
}, cancelInteractiveKeyboardGestures: {
}, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings, pollActionState: ChatInterfacePollActionState(), stickerSettings: ChatInterfaceStickerSettings(loopAnimatedStickers: false))
diff --git a/submodules/TelegramUI/Sources/OverlayPlayerControlsNode.swift b/submodules/TelegramUI/Sources/OverlayPlayerControlsNode.swift
index 617d70e583..f513a740b7 100644
--- a/submodules/TelegramUI/Sources/OverlayPlayerControlsNode.swift
+++ b/submodules/TelegramUI/Sources/OverlayPlayerControlsNode.swift
@@ -304,7 +304,7 @@ final class OverlayPlayerControlsNode: ASDisplayNode {
isPaused = false
case .paused:
isPaused = true
- case let .buffering(_, whilePlaying):
+ case let .buffering(_, whilePlaying, _):
isPaused = !whilePlaying
}
if strongSelf.currentIsPaused != isPaused {
diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoData.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoData.swift
index f4024e2621..83ec96e376 100644
--- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoData.swift
+++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoData.swift
@@ -328,14 +328,14 @@ private func peerInfoProfilePhotos(context: AccountContext, peerId: PeerId) -> S
}
}
|> distinctUntilChanged
- |> mapToSignal { firstEntry -> Signal<[AvatarGalleryEntry], NoError> in
+ |> mapToSignal { firstEntry -> Signal<(Bool, [AvatarGalleryEntry]), NoError> in
if let firstEntry = firstEntry {
return context.account.postbox.loadedPeerWithId(peerId)
- |> mapToSignal { peer -> Signal<[AvatarGalleryEntry], NoError>in
+ |> mapToSignal { peer -> Signal<(Bool, [AvatarGalleryEntry]), NoError>in
return fetchedAvatarGalleryEntries(account: context.account, peer: peer, firstEntry: firstEntry)
}
} else {
- return .single([])
+ return .single((true, []))
}
}
|> map { items -> Any in
@@ -343,10 +343,10 @@ private func peerInfoProfilePhotos(context: AccountContext, peerId: PeerId) -> S
}
}
-func peerInfoProfilePhotosWithCache(context: AccountContext, peerId: PeerId) -> Signal<[AvatarGalleryEntry], NoError> {
+func peerInfoProfilePhotosWithCache(context: AccountContext, peerId: PeerId) -> Signal<(Bool, [AvatarGalleryEntry]), NoError> {
return context.peerChannelMemberCategoriesContextsManager.profilePhotos(postbox: context.account.postbox, network: context.account.network, peerId: peerId, fetch: peerInfoProfilePhotos(context: context, peerId: peerId))
- |> map { items -> [AvatarGalleryEntry] in
- return items as? [AvatarGalleryEntry] ?? []
+ |> map { items -> (Bool, [AvatarGalleryEntry]) in
+ return items as? (Bool, [AvatarGalleryEntry]) ?? (true, [])
}
}
@@ -911,15 +911,17 @@ func peerInfoHeaderButtons(peer: Peer?, cachedData: CachedPeerData?, isOpenedFro
result.append(.message)
}
var callsAvailable = false
+ var videoCallsAvailable = false
if !user.isDeleted, user.botInfo == nil, !user.flags.contains(.isSupport) {
if let cachedUserData = cachedData as? CachedUserData {
- callsAvailable = cachedUserData.callsAvailable
+ callsAvailable = cachedUserData.voiceCallsAvailable
+ videoCallsAvailable = cachedUserData.videoCallsAvailable
}
callsAvailable = true
}
if callsAvailable {
result.append(.call)
- if videoCallsEnabled {
+ if videoCallsEnabled && videoCallsAvailable {
result.append(.videoCall)
}
}
diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift
index a7cc1fabaa..149defb74d 100644
--- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift
+++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift
@@ -198,8 +198,16 @@ final class PeerInfoAvatarListItemNode: ASDisplayNode {
private var videoNode: UniversalVideoNode?
private var videoContent: NativeVideoContent?
private var videoStartTimestamp: Double?
- private let playbackStatusDisposable = MetaDisposable()
+ private let playbackStartDisposable = MetaDisposable()
+ private let statusDisposable = MetaDisposable()
private let preloadDisposable = MetaDisposable()
+ private let statusNode: RadialStatusNode
+
+ private var playerStatus: MediaPlayerStatus?
+ private var isLoading = ValuePromise(false)
+ private var loadingProgress = ValuePromise(nil)
+ private var loadingProgressDisposable = MetaDisposable()
+ private var hasProgress = false
let isReady = Promise()
private var didSetReady: Bool = false
@@ -209,12 +217,7 @@ final class PeerInfoAvatarListItemNode: ASDisplayNode {
private var statusPromise = Promise<(MediaPlayerStatus?, Double?)?>()
var mediaStatus: Signal<(MediaPlayerStatus?, Double?)?, NoError> {
get {
- if let videoNode = self.videoNode {
- let videoStartTimestamp = self.videoStartTimestamp
- return videoNode.status |> map { ($0, videoStartTimestamp) }
- } else {
- return self.statusPromise.get()
- }
+ return self.statusPromise.get()
}
}
@@ -229,7 +232,7 @@ final class PeerInfoAvatarListItemNode: ASDisplayNode {
self.preloadDisposable.set(nil)
} else {
if let videoNode = self.videoNode {
- self.playbackStatusDisposable.set(nil)
+ self.playbackStartDisposable.set(nil)
self.statusPromise.set(.single(nil))
self.videoNode = nil
if self.delayCentralityLose {
@@ -253,19 +256,74 @@ final class PeerInfoAvatarListItemNode: ASDisplayNode {
self.peer = peer
self.imageNode = TransformImageNode()
+ self.statusNode = RadialStatusNode(backgroundNodeColor: UIColor(rgb: 0x000000, alpha: 0.3))
+ self.statusNode.isUserInteractionEnabled = false
+
super.init()
self.clipsToBounds = true
-
+
self.imageNode.contentAnimations = [.firstUpdate, .subsequentUpdates]
self.addSubnode(self.imageNode)
+ self.addSubnode(self.statusNode)
+
+ self.loadingProgressDisposable.set((combineLatest(self.isLoading.get()
+ |> mapToSignal { value -> Signal in
+ if value {
+ return .single(value) |> delay(0.5, queue: Queue.mainQueue())
+ } else {
+ return .single(value)
+ }
+ } |> distinctUntilChanged, self.loadingProgress.get() |> distinctUntilChanged)).start(next: { [weak self] isLoading, progress in
+ guard let strongSelf = self else {
+ return
+ }
+ if isLoading, let progress = progress {
+ strongSelf.hasProgress = true
+ strongSelf.statusNode.transitionToState(.progress(color: .white, lineWidth: nil, value: CGFloat(max(0.027, progress)), cancelEnabled: false), completion: {})
+ } else if strongSelf.hasProgress {
+ strongSelf.hasProgress = false
+ strongSelf.statusNode.transitionToState(.progress(color: .white, lineWidth: nil, value: 1.0, cancelEnabled: false), completion: { [weak self] in
+ guard let strongSelf = self else {
+ return
+ }
+ if !strongSelf.hasProgress {
+ Queue.mainQueue().after(0.3) {
+ strongSelf.statusNode.transitionToState(.none, completion: {})
+ }
+ }
+ })
+ }
+ }))
}
deinit {
- self.playbackStatusDisposable.dispose()
+ self.statusDisposable.dispose()
+ self.playbackStartDisposable.dispose()
self.preloadDisposable.dispose()
}
+ private func updateStatus() {
+ guard let videoContent = self.videoContent else {
+ return
+ }
+
+ var bufferingProgress: Float?
+ if isMediaStreamable(resource: videoContent.fileReference.media.resource) {
+ if let playerStatus = self.playerStatus {
+ if case let .buffering(_, _, progress) = playerStatus.status {
+ bufferingProgress = progress
+ } else if case .playing = playerStatus.status {
+ bufferingProgress = nil
+ }
+ } else {
+ bufferingProgress = nil
+ }
+ }
+ self.loadingProgress.set(bufferingProgress)
+ self.isLoading.set(bufferingProgress != nil)
+ }
+
func updateTransitionFraction(_ fraction: CGFloat, transition: ContainedViewLayoutTransition) {
if let videoNode = self.videoNode {
if case .immediate = transition, fraction == 1.0 {
@@ -287,7 +345,7 @@ final class PeerInfoAvatarListItemNode: ASDisplayNode {
videoNode.isHidden = true
if let _ = self.videoStartTimestamp {
- self.playbackStatusDisposable.set((videoNode.status
+ self.playbackStartDisposable.set((videoNode.status
|> map { status -> Bool in
if let status = status, case .playing = status.status {
return true
@@ -307,7 +365,7 @@ final class PeerInfoAvatarListItemNode: ASDisplayNode {
}
}))
} else {
- self.playbackStatusDisposable.set(nil)
+ self.playbackStartDisposable.set(nil)
videoNode.isHidden = false
}
videoNode.play()
@@ -316,7 +374,17 @@ final class PeerInfoAvatarListItemNode: ASDisplayNode {
let videoStartTimestamp = self.videoStartTimestamp
self.statusPromise.set(videoNode.status |> map { ($0, videoStartTimestamp) })
- self.addSubnode(videoNode)
+ self.statusDisposable.set((self.mediaStatus
+ |> deliverOnMainQueue).start(next: { [weak self] mediaStatus in
+ if let strongSelf = self {
+ if let mediaStatusAndStartTimestamp = mediaStatus {
+ strongSelf.playerStatus = mediaStatusAndStartTimestamp.0
+ }
+ strongSelf.updateStatus()
+ }
+ }))
+
+ self.insertSubnode(videoNode, belowSubnode: self.statusNode)
self.isReady.set(videoNode.ready |> map { return true })
}
@@ -351,7 +419,7 @@ final class PeerInfoAvatarListItemNode: ASDisplayNode {
if let video = videoRepresentations.last, let peerReference = PeerReference(self.peer) {
let videoFileReference = FileMediaReference.avatarList(peer: peerReference, media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: video.representation.resource, previewRepresentations: representations.map { $0.representation }, videoThumbnails: [], immediateThumbnailData: immediateThumbnailData, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: video.representation.dimensions, flags: [])]))
- let videoContent = NativeVideoContent(id: .profileVideo(id, nil), fileReference: videoFileReference, streamVideo: isMediaStreamable(resource: video.representation.resource) ? .conservative : .none, loopVideo: true, enableSound: false, fetchAutomatically: true, onlyFullSizeThumbnail: false, autoFetchFullSizeThumbnail: true, startTimestamp: video.representation.startTimestamp, continuePlayingWithoutSoundOnLostAudioSession: false, placeholderColor: .clear)
+ let videoContent = NativeVideoContent(id: .profileVideo(id, nil), fileReference: videoFileReference, streamVideo: isMediaStreamable(resource: video.representation.resource) ? .conservative : .none, loopVideo: true, enableSound: false, fetchAutomatically: true, onlyFullSizeThumbnail: false, useLargeThumbnail: true, autoFetchFullSizeThumbnail: true, startTimestamp: video.representation.startTimestamp, continuePlayingWithoutSoundOnLostAudioSession: false, placeholderColor: .clear)
if videoContent.id != self.videoContent?.id {
self.videoContent = videoContent
@@ -369,6 +437,8 @@ final class PeerInfoAvatarListItemNode: ASDisplayNode {
self.statusPromise.set(.single(nil))
+ self.statusDisposable.set(nil)
+
self.imageNode.imageUpdated = { [weak self] _ in
guard let strongSelf = self else {
return
@@ -389,6 +459,8 @@ final class PeerInfoAvatarListItemNode: ASDisplayNode {
let imageFrame = CGRect(origin: CGPoint(x: floor((size.width - imageSize.width) / 2.0), y: floor((size.height - imageSize.height) / 2.0)), size: imageSize)
transition.updateFrame(node: self.imageNode, frame: imageFrame)
+ transition.updateFrame(node: self.statusNode, frame: CGRect(origin: CGPoint(x: floor((size.width - 50.0) / 2.0), y: floor((size.height - 50.0) / 2.0)), size: CGSize(width: 50.0, height: 50.0)))
+
if let videoNode = self.videoNode {
videoNode.updateLayout(size: imageSize, transition: .immediate)
videoNode.frame = imageFrame
@@ -572,7 +644,7 @@ final class PeerInfoAvatarListContainerNode: ASDisplayNode {
if let size = self.validLayout {
self.playbackProgress = position
self.loading = loading
- self.updateItems(size: size, transition: .immediate, stripTransition: .animated(duration: 0.3, curve: .spring))
+ self.updateStrips(size: size, itemsAdded: false, stripTransition: .animated(duration: 0.3, curve: .spring))
}
}
@@ -849,17 +921,19 @@ final class PeerInfoAvatarListContainerNode: ASDisplayNode {
case .cancelled, .ended:
let translation = recognizer.translation(in: self.view)
let velocity = recognizer.velocity(in: self.view)
- var directionIsToRight = false
+ var directionIsToRight: Bool?
if abs(velocity.x) > 10.0 {
directionIsToRight = velocity.x < 0.0
- } else {
- directionIsToRight = translation.x > self.bounds.width / 2.0
+ } else if abs(transitionFraction) > 0.5 {
+ directionIsToRight = transitionFraction < 0.0
}
var updatedIndex = self.currentIndex
- if directionIsToRight {
- updatedIndex = min(updatedIndex + 1, self.items.count - 1)
- } else {
- updatedIndex = max(updatedIndex - 1, 0)
+ if let directionIsToRight = directionIsToRight {
+ if directionIsToRight {
+ updatedIndex = min(updatedIndex + 1, self.items.count - 1)
+ } else {
+ updatedIndex = max(updatedIndex - 1, 0)
+ }
}
let previousIndex = self.currentIndex
self.currentIndex = updatedIndex
@@ -979,11 +1053,15 @@ final class PeerInfoAvatarListContainerNode: ASDisplayNode {
if let peer = peer, !self.initializedList {
self.initializedList = true
self.disposable.set((peerInfoProfilePhotosWithCache(context: self.context, peerId: peer.id)
- |> deliverOnMainQueue).start(next: { [weak self] entries in
+ |> deliverOnMainQueue).start(next: { [weak self] (complete, entries) in
guard let strongSelf = self else {
return
}
+ if strongSelf.galleryEntries.count > 1, entries.count == 1 && !complete {
+ return
+ }
+
var entries = entries
var synchronous = false
if !strongSelf.galleryEntries.isEmpty, let updated = entries.first, case let .image(image) = updated, !image.3.isEmpty, let previous = strongSelf.galleryEntries.first, case let .topImage(topImage) = previous {
@@ -1023,6 +1101,80 @@ final class PeerInfoAvatarListContainerNode: ASDisplayNode {
self.updateItems(size: size, transition: transition, stripTransition: transition)
}
+ private func updateStrips(size: CGSize, itemsAdded: Bool, stripTransition: ContainedViewLayoutTransition) {
+ let hadOneStripNode = self.stripNodes.count == 1
+ if self.stripNodes.count != self.items.count {
+ if self.stripNodes.count < self.items.count {
+ for _ in 0 ..< self.items.count - self.stripNodes.count {
+ let stripNode = ASImageNode()
+ stripNode.displaysAsynchronously = false
+ stripNode.displayWithoutProcessing = true
+ stripNode.image = self.activeStripImage
+ stripNode.alpha = 0.2
+ self.stripNodes.append(stripNode)
+ self.stripContainerNode.addSubnode(stripNode)
+ }
+ } else {
+ for i in (self.items.count ..< self.stripNodes.count).reversed() {
+ self.stripNodes[i].removeFromSupernode()
+ self.stripNodes.remove(at: i)
+ }
+ }
+ self.stripContainerNode.addSubnode(self.activeStripNode)
+ self.stripContainerNode.addSubnode(self.loadingStripNode)
+ }
+ if self.appliedStripNodeCurrentIndex != self.currentIndex || itemsAdded {
+ if !self.itemNodes.isEmpty {
+ self.appliedStripNodeCurrentIndex = self.currentIndex
+ }
+
+ if let currentItemNode = self.currentItemNode {
+ self.positionDisposable.set((currentItemNode.mediaStatus
+ |> deliverOnMainQueue).start(next: { [weak self] statusAndVideoStartTimestamp in
+ if let strongSelf = self {
+ strongSelf.playerStatus = statusAndVideoStartTimestamp
+ }
+ }))
+ } else {
+ self.positionDisposable.set(nil)
+ }
+ }
+ if hadOneStripNode && self.stripNodes.count > 1 {
+ self.stripContainerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25)
+ }
+ let stripInset: CGFloat = 8.0
+ let stripSpacing: CGFloat = 4.0
+ let stripWidth: CGFloat = max(5.0, floor((size.width - stripInset * 2.0 - stripSpacing * CGFloat(self.stripNodes.count - 1)) / CGFloat(self.stripNodes.count)))
+ let currentStripMinX = stripInset + CGFloat(self.currentIndex) * (stripWidth + stripSpacing)
+ let currentStripMidX = floor(currentStripMinX + stripWidth / 2.0)
+ let lastStripMaxX = stripInset + CGFloat(self.stripNodes.count - 1) * (stripWidth + stripSpacing) + stripWidth
+ let stripOffset: CGFloat = min(0.0, max(size.width - stripInset - lastStripMaxX, size.width / 2.0 - currentStripMidX))
+ for i in 0 ..< self.stripNodes.count {
+ let stripX: CGFloat = stripInset + CGFloat(i) * (stripWidth + stripSpacing)
+ if i == 0 && self.stripNodes.count == 1 {
+ self.stripNodes[i].isHidden = true
+ } else {
+ self.stripNodes[i].isHidden = false
+ }
+ let stripFrame = CGRect(origin: CGPoint(x: stripOffset + stripX, y: 0.0), size: CGSize(width: stripWidth + 1.0, height: 2.0))
+ stripTransition.updateFrame(node: self.stripNodes[i], frame: stripFrame)
+ }
+
+ if self.currentIndex >= 0 && self.currentIndex < self.stripNodes.count {
+ var frame = self.stripNodes[self.currentIndex].frame
+ stripTransition.updateFrame(node: self.loadingStripNode, frame: frame)
+ if let playbackProgress = self.playbackProgress {
+ frame.size.width = max(frame.size.height, frame.size.width * playbackProgress)
+ }
+ stripTransition.updateFrameAdditive(node: self.activeStripNode, frame: frame)
+ stripTransition.updateAlpha(node: self.activeStripNode, alpha: self.loading ? 0.0 : 1.0)
+ stripTransition.updateAlpha(node: self.loadingStripNode, alpha: self.loading ? 1.0 : 0.0)
+
+ self.activeStripNode.isHidden = self.stripNodes.count < 2
+ self.loadingStripNode.isHidden = self.stripNodes.count < 2 || !self.loading
+ }
+ }
+
private func updateItems(size: CGSize, update: Bool = false, transition: ContainedViewLayoutTransition, stripTransition: ContainedViewLayoutTransition, synchronous: Bool = false) {
var validIds: [WrappedMediaResourceId] = []
var addedItemNodesForAdditiveTransition: [PeerInfoAvatarListItemNode] = []
@@ -1043,7 +1195,7 @@ final class PeerInfoAvatarListContainerNode: ASDisplayNode {
wasAdded = true
let addedItemNode = PeerInfoAvatarListItemNode(context: self.context, peer: peer)
itemNode = addedItemNode
- addedItemNode.setup(item: self.items[i], synchronous: synchronous && i == self.currentIndex)
+ addedItemNode.setup(item: self.items[i], synchronous: (i == 0 && i == self.currentIndex) || (synchronous && i == self.currentIndex))
self.itemNodes[self.items[i].id] = addedItemNode
self.contentNode.addSubnode(addedItemNode)
}
@@ -1083,77 +1235,7 @@ final class PeerInfoAvatarListContainerNode: ASDisplayNode {
}
}
- let hadOneStripNode = self.stripNodes.count == 1
- if self.stripNodes.count != self.items.count {
- if self.stripNodes.count < self.items.count {
- for _ in 0 ..< self.items.count - self.stripNodes.count {
- let stripNode = ASImageNode()
- stripNode.displaysAsynchronously = false
- stripNode.displayWithoutProcessing = true
- stripNode.image = self.activeStripImage
- stripNode.alpha = 0.2
- self.stripNodes.append(stripNode)
- self.stripContainerNode.addSubnode(stripNode)
- }
- } else {
- for i in (self.items.count ..< self.stripNodes.count).reversed() {
- self.stripNodes[i].removeFromSupernode()
- self.stripNodes.remove(at: i)
- }
- }
- self.stripContainerNode.addSubnode(self.activeStripNode)
- self.stripContainerNode.addSubnode(self.loadingStripNode)
- }
- if self.appliedStripNodeCurrentIndex != self.currentIndex || itemsAdded {
- if !self.itemNodes.isEmpty {
- self.appliedStripNodeCurrentIndex = self.currentIndex
- }
-
- if let currentItemNode = self.currentItemNode {
- self.positionDisposable.set((currentItemNode.mediaStatus
- |> deliverOnMainQueue).start(next: { [weak self] statusAndVideoStartTimestamp in
- if let strongSelf = self {
- strongSelf.playerStatus = statusAndVideoStartTimestamp
- }
- }))
- } else {
- self.positionDisposable.set(nil)
- }
- }
- if hadOneStripNode && self.stripNodes.count > 1 {
- self.stripContainerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25)
- }
- let stripInset: CGFloat = 8.0
- let stripSpacing: CGFloat = 4.0
- let stripWidth: CGFloat = max(5.0, floor((size.width - stripInset * 2.0 - stripSpacing * CGFloat(self.stripNodes.count - 1)) / CGFloat(self.stripNodes.count)))
- let currentStripMinX = stripInset + CGFloat(self.currentIndex) * (stripWidth + stripSpacing)
- let currentStripMidX = floor(currentStripMinX + stripWidth / 2.0)
- let lastStripMaxX = stripInset + CGFloat(self.stripNodes.count - 1) * (stripWidth + stripSpacing) + stripWidth
- let stripOffset: CGFloat = min(0.0, max(size.width - stripInset - lastStripMaxX, size.width / 2.0 - currentStripMidX))
- for i in 0 ..< self.stripNodes.count {
- let stripX: CGFloat = stripInset + CGFloat(i) * (stripWidth + stripSpacing)
- if i == 0 && self.stripNodes.count == 1 {
- self.stripNodes[i].isHidden = true
- } else {
- self.stripNodes[i].isHidden = false
- }
- let stripFrame = CGRect(origin: CGPoint(x: stripOffset + stripX, y: 0.0), size: CGSize(width: stripWidth + 1.0, height: 2.0))
- stripTransition.updateFrame(node: self.stripNodes[i], frame: stripFrame)
- }
-
- if self.currentIndex >= 0 && self.currentIndex < self.stripNodes.count {
- var frame = self.stripNodes[self.currentIndex].frame
- stripTransition.updateFrame(node: self.loadingStripNode, frame: frame)
- if let playbackProgress = self.playbackProgress {
- frame.size.width = max(frame.size.height, frame.size.width * playbackProgress)
- }
- stripTransition.updateFrameAdditive(node: self.activeStripNode, frame: frame)
- stripTransition.updateAlpha(node: self.activeStripNode, alpha: self.loading ? 0.0 : 1.0)
- stripTransition.updateAlpha(node: self.loadingStripNode, alpha: self.loading ? 1.0 : 0.0)
-
- self.activeStripNode.isHidden = self.stripNodes.count < 2
- self.loadingStripNode.isHidden = self.stripNodes.count < 2 || !self.loading
- }
+ self.updateStrips(size: size, itemsAdded: itemsAdded, stripTransition: stripTransition)
if let item = self.items.first, let itemNode = self.itemNodes[item.id] {
if !self.didSetReady {
@@ -1186,7 +1268,7 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode {
private var isFirstAvatarLoading = true
var item: PeerInfoAvatarListItem?
- private let playbackStatusDisposable = MetaDisposable()
+ private let playbackStartDisposable = MetaDisposable()
init(context: AccountContext) {
self.context = context
@@ -1202,7 +1284,7 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode {
}
deinit {
- self.playbackStatusDisposable.dispose()
+ self.playbackStartDisposable.dispose()
}
@objc private func tapGesture(_ recognizer: UITapGestureRecognizer) {
@@ -1278,7 +1360,7 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode {
if let video = videoRepresentations.last, let peerReference = PeerReference(peer) {
let videoFileReference = FileMediaReference.avatarList(peer: peerReference, media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: video.representation.resource, previewRepresentations: representations.map { $0.representation }, videoThumbnails: [], immediateThumbnailData: immediateThumbnailData, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: video.representation.dimensions, flags: [])]))
- let videoContent = NativeVideoContent(id: .profileVideo(id, nil), fileReference: videoFileReference, streamVideo: isMediaStreamable(resource: video.representation.resource) ? .conservative : .none, loopVideo: true, enableSound: false, fetchAutomatically: true, onlyFullSizeThumbnail: false, autoFetchFullSizeThumbnail: true, startTimestamp: video.representation.startTimestamp, continuePlayingWithoutSoundOnLostAudioSession: false, placeholderColor: .clear)
+ let videoContent = NativeVideoContent(id: .profileVideo(id, nil), fileReference: videoFileReference, streamVideo: isMediaStreamable(resource: video.representation.resource) ? .conservative : .none, loopVideo: true, enableSound: false, fetchAutomatically: true, onlyFullSizeThumbnail: false, useLargeThumbnail: true, autoFetchFullSizeThumbnail: true, startTimestamp: video.representation.startTimestamp, continuePlayingWithoutSoundOnLostAudioSession: false, placeholderColor: .clear)
if videoContent.id != self.videoContent?.id {
self.videoNode?.removeFromSupernode()
@@ -1289,7 +1371,7 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode {
if let startTimestamp = video.representation.startTimestamp {
self.videoStartTimestamp = startTimestamp
- self.playbackStatusDisposable.set((videoNode.status
+ self.playbackStartDisposable.set((videoNode.status
|> map { status -> Bool in
if let status = status, case .playing = status.status {
return true
@@ -1310,7 +1392,7 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode {
}))
} else {
self.videoStartTimestamp = nil
- self.playbackStatusDisposable.set(nil)
+ self.playbackStartDisposable.set(nil)
videoNode.isHidden = false
}
@@ -1564,7 +1646,7 @@ final class PeerInfoEditingAvatarNode: ASDisplayNode {
if let video = videoRepresentations.last, let peerReference = PeerReference(peer) {
let videoFileReference = FileMediaReference.avatarList(peer: peerReference, media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: video.representation.resource, previewRepresentations: representations.map { $0.representation }, videoThumbnails: [], immediateThumbnailData: immediateThumbnailData, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: video.representation.dimensions, flags: [])]))
- let videoContent = NativeVideoContent(id: .profileVideo(id, nil), fileReference: videoFileReference, streamVideo: isMediaStreamable(resource: video.representation.resource) ? .conservative : .none, loopVideo: true, enableSound: false, fetchAutomatically: true, onlyFullSizeThumbnail: false, autoFetchFullSizeThumbnail: true, startTimestamp: video.representation.startTimestamp, continuePlayingWithoutSoundOnLostAudioSession: false, placeholderColor: .clear)
+ let videoContent = NativeVideoContent(id: .profileVideo(id, nil), fileReference: videoFileReference, streamVideo: isMediaStreamable(resource: video.representation.resource) ? .conservative : .none, loopVideo: true, enableSound: false, fetchAutomatically: true, onlyFullSizeThumbnail: false, useLargeThumbnail: true, autoFetchFullSizeThumbnail: true, startTimestamp: video.representation.startTimestamp, continuePlayingWithoutSoundOnLostAudioSession: false, placeholderColor: .clear)
if videoContent.id != self.videoContent?.id {
self.videoNode?.removeFromSupernode()
@@ -1706,13 +1788,7 @@ final class PeerInfoAvatarListNode: ASDisplayNode {
func animateAvatarCollapse(transition: ContainedViewLayoutTransition) {
if let currentItemNode = self.listContainerNode.currentItemNode, case .animated = transition {
if let _ = self.avatarContainerNode.videoNode {
-// if self.listContainerNode.currentIndex > 0 {
-// transition.updateAlpha(node: currentItemNode, alpha: 0.0, completion: { _ in
-// Queue.mainQueue().after(0.1, {
-// currentItemNode.alpha = 1.0
-// })
-// })
-// }
+
} else if let unroundedImage = self.avatarContainerNode.avatarNode.unroundedImage {
let avatarCopyView = UIImageView()
avatarCopyView.image = unroundedImage
@@ -2489,7 +2565,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
self.isAvatarExpanded = avatarInitiallyExpanded
self.isOpenedFromChat = isOpenedFromChat
self.isSettings = isSettings
- self.videoCallsEnabled = context.sharedContext.immediateExperimentalUISettings.videoCalls
+ self.videoCallsEnabled = VideoCallsConfiguration(appConfiguration: context.currentAppConfiguration.with { $0 }).areVideoCallsEnabled
self.avatarListNode = PeerInfoAvatarListNode(context: context, readyWhenGalleryLoads: avatarInitiallyExpanded)
@@ -2858,7 +2934,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
titleFrame = CGRect(origin: CGPoint(x: floor((width - titleSize.width) / 2.0), y: avatarFrame.maxY + 10.0 + (subtitleSize.height.isZero ? 11.0 : 0.0)), size: titleSize)
let totalSubtitleWidth = subtitleSize.width + usernameSpacing + usernameSize.width
- twoLineInfo = true // totalSubtitleWidth > width - textSideInset * 2.0
+ twoLineInfo = true
if usernameSize.width == 0.0 || twoLineInfo {
subtitleFrame = CGRect(origin: CGPoint(x: floor((width - subtitleSize.width) / 2.0), y: titleFrame.maxY + 1.0), size: subtitleSize)
usernameFrame = CGRect(origin: CGPoint(x: floor((width - usernameSize.width) / 2.0), y: subtitleFrame.maxY + 1.0), size: usernameSize)
diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift
index fff0b678d0..34a2d2a76e 100644
--- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift
+++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift
@@ -647,7 +647,7 @@ private func settingsItems(data: PeerInfoScreenData?, context: AccountContext, p
interaction.openSettings(.avatar)
}))
}
- if let peer = data.peer, peer.addressName == nil {
+ if let peer = data.peer, (peer.addressName ?? "").isEmpty {
items[.edit]!.append(PeerInfoScreenActionItem(id: 1, text: presentationData.strings.Settings_SetUsername, icon: UIImage(bundleImageName: "Settings/SetUsername"), action: {
interaction.openSettings(.username)
}))
@@ -1422,7 +1422,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
self.context = context
self.peerId = peerId
self.isOpenedFromChat = isOpenedFromChat
- self.videoCallsEnabled = context.sharedContext.immediateExperimentalUISettings.videoCalls
+ self.videoCallsEnabled = VideoCallsConfiguration(appConfiguration: context.currentAppConfiguration.with { $0 }).areVideoCallsEnabled
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
self.nearbyPeerDistance = nearbyPeerDistance
self.callMessages = callMessages
@@ -1952,6 +1952,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
}, animateDiceSuccess: {
}, greetingStickerNode: {
return nil
+ }, openPeerContextMenu: { _, _, _, _ in
}, requestMessageUpdate: { _ in
}, cancelInteractiveKeyboardGestures: {
}, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings,
@@ -2117,7 +2118,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
let galleryController = AvatarGalleryController(context: strongSelf.context, peer: peer, sourceCorners: .round(!strongSelf.headerNode.isAvatarExpanded), remoteEntries: entriesPromise, skipInitial: true, centralEntryIndex: centralEntry.flatMap { entries.firstIndex(of: $0) }, replaceRootController: { controller, ready in
})
galleryController.openAvatarSetup = { [weak self] completion in
- self?.openAvatarForEditing(hasRemove: false, completion: completion)
+ self?.openAvatarForEditing(fromGallery: true, completion: completion)
}
galleryController.avatarPhotoEditCompletion = { [weak self] image in
self?.updateProfilePhoto(image)
@@ -3152,12 +3153,30 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
return
}
- let callResult = self.context.sharedContext.callManager?.requestCall(account: self.context.account, peerId: peer.id, isVideo: isVideo, endCurrentIfAny: false)
+ let callResult = self.context.sharedContext.callManager?.requestCall(context: self.context, peerId: peer.id, isVideo: isVideo, endCurrentIfAny: false)
if let callResult = callResult, case let .alreadyInProgress(currentPeerId) = callResult {
if currentPeerId == peer.id {
self.context.sharedContext.navigateToCurrentCall()
} else {
+ let presentationData = self.presentationData
let _ = (self.context.account.postbox.transaction { transaction -> (Peer?, Peer?) in
+ return (transaction.getPeer(peer.id), currentPeerId.flatMap(transaction.getPeer))
+ } |> deliverOnMainQueue).start(next: { [weak self] peer, current in
+ if let peer = peer {
+ if let strongSelf = self, let current = current {
+ strongSelf.controller?.present(textAlertController(context: strongSelf.context, title: presentationData.strings.Call_CallInProgressTitle, text: presentationData.strings.Call_CallInProgressMessage(current.compactDisplayTitle, peer.compactDisplayTitle).0, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: {
+ if let strongSelf = self {
+ let _ = strongSelf.context.sharedContext.callManager?.requestCall(context: strongSelf.context, peerId: peer.id, isVideo: isVideo, endCurrentIfAny: true)
+ }
+ })]), in: .window(.root))
+ } else if let strongSelf = self {
+ strongSelf.controller?.present(textAlertController(context: strongSelf.context, title: presentationData.strings.Call_CallInProgressTitle, text: presentationData.strings.Call_ExternalCallInProgressMessage, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: {
+ })]), in: .window(.root))
+ }
+ }
+ })
+
+ /*let _ = (self.context.account.postbox.transaction { transaction -> (Peer?, Peer?) in
return (transaction.getPeer(peer.id), transaction.getPeer(currentPeerId))
}
|> deliverOnMainQueue).start(next: { [weak self] peer, current in
@@ -3169,10 +3188,10 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
guard let strongSelf = self else {
return
}
- let _ = strongSelf.context.sharedContext.callManager?.requestCall(account: strongSelf.context.account, peerId: peer.id, isVideo: isVideo, endCurrentIfAny: true)
+ let _ = strongSelf.context.sharedContext.callManager?.requestCall(context: strongSelf.context, peerId: peer.id, isVideo: isVideo, endCurrentIfAny: true)
})]), in: .window(.root))
}
- })
+ })*/
}
}
}
@@ -3836,7 +3855,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
}
for childController in tabController.controllers {
if let chatListController = childController as? ChatListController {
- chatListController.maybeAskForPeerChatRemoval(peer: RenderedPeer(peer: peer), deleteGloballyIfPossible: globally, completion: { [weak navigationController] deleted in
+ chatListController.maybeAskForPeerChatRemoval(peer: RenderedPeer(peer: peer), joined: false, deleteGloballyIfPossible: globally, completion: { [weak navigationController] deleted in
if deleted {
navigationController?.popToRoot(animated: true)
}
@@ -3846,18 +3865,6 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
}
}
}
-
- private func setMainAvatar(_ item: PeerInfoAvatarListItem) {
- if self.data?.peer?.id == self.context.account.peerId {
- if case let .image(reference, _, _, _) = item {
- if let reference = reference {
- let _ = updatePeerPhotoExisting(network: self.context.account.network, reference: reference).start()
- self.headerNode.avatarListNode.listContainerNode.setMainItem(item)
-
- }
- }
- }
- }
private func deleteAvatar(_ item: PeerInfoAvatarListItem, remove: Bool = true) {
if self.data?.peer?.id == self.context.account.peerId {
@@ -3879,28 +3886,6 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
}
}
}
-// if entry == self.entries.first {
-// self.dismiss(forceAway: true)
-// } else {
-// if let index = self.entries.firstIndex(of: entry) {
-// self.entries.remove(at: index)
-// self.galleryNode.pager.transaction(GalleryPagerTransaction(deleteItems: [index], insertItems: [], updateItems: [], focusOnItem: index - 1, synchronous: false))
-// }
-// }
- } else {
-// if let messageId = messageId {
-// let _ = deleteMessagesInteractively(account: self.context.account, messageIds: [messageId], type: .forEveryone).start()
-// }
-
-// if entry == self.entries.first {
-// let _ = updatePeerPhoto(postbox: self.context.account.postbox, network: self.context.account.network, stateManager: self.context.account.stateManager, accountPeerId: self.context.account.peerId, peerId: self.peer.id, photo: nil, mapResourceToAvatarSizes: { _, _ in .single([:]) }).start()
-// self.dismiss(forceAway: true)
-// } else {
-// if let index = self.entries.firstIndex(of: entry) {
-// self.entries.remove(at: index)
-// self.galleryNode.pager.transaction(GalleryPagerTransaction(deleteItems: [index], insertItems: [], updateItems: [], focusOnItem: index - 1, synchronous: false))
-// }
-// }
}
}
@@ -3918,7 +3903,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
let resource = LocalFileMediaResource(fileId: arc4random64())
self.context.account.postbox.mediaBox.storeResourceData(resource.id, data: data)
- let representation = TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 640, height: 640), resource: resource)
+ let representation = TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 640, height: 640), resource: resource, progressiveSizes: [])
self.state = self.state.withUpdatingAvatar(.image(representation))
if let (layout, navigationHeight) = self.validLayout {
@@ -3948,18 +3933,6 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
}
}))
}
-
- fileprivate func resetHeaderExpansion() {
- if self.headerNode.isAvatarExpanded {
- self.headerNode.ignoreCollapse = true
- self.headerNode.updateIsAvatarExpanded(false, transition: .immediate)
- self.updateNavigationExpansionPresentation(isExpanded: false, animated: true)
- if let (layout, navigationHeight) = self.validLayout {
- self.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: .immediate, additive: false)
- }
- self.headerNode.ignoreCollapse = false
- }
- }
private func updateProfileVideo(_ image: UIImage, asset: Any?, adjustments: TGVideoEditAdjustments?) {
guard let data = image.jpegData(compressionQuality: 0.6) else {
@@ -3975,7 +3948,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
let photoResource = LocalFileMediaResource(fileId: arc4random64())
self.context.account.postbox.mediaBox.storeResourceData(photoResource.id, data: data)
- let representation = TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 640, height: 640), resource: photoResource)
+ let representation = TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 640, height: 640), resource: photoResource, progressiveSizes: [])
self.state = self.state.withUpdatingAvatar(.image(representation))
if let (layout, navigationHeight) = self.validLayout {
@@ -4077,7 +4050,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
return mapResourceToAvatarSizes(postbox: account.postbox, resource: resource, representations: representations)
})
} else {
- return updatePeerPhoto(postbox: account.postbox, network: account.network, stateManager: account.stateManager, accountPeerId: account.peerId, peerId: peerId, photo: uploadedPeerPhoto(postbox: account.postbox, network: account.network, resource: photoResource), video: uploadedPeerVideo(postbox: account.postbox, network: account.network, messageMediaPreuploadManager: account.messageMediaPreuploadManager, resource: videoResource) |> map(Optional.init), mapResourceToAvatarSizes: { resource, representations in
+ return updatePeerPhoto(postbox: account.postbox, network: account.network, stateManager: account.stateManager, accountPeerId: account.peerId, peerId: peerId, photo: uploadedPeerPhoto(postbox: account.postbox, network: account.network, resource: photoResource), video: uploadedPeerVideo(postbox: account.postbox, network: account.network, messageMediaPreuploadManager: account.messageMediaPreuploadManager, resource: videoResource) |> map(Optional.init), videoStartTimestamp: videoStartTimestamp, mapResourceToAvatarSizes: { resource, representations in
return mapResourceToAvatarSizes(postbox: account.postbox, resource: resource, representations: representations)
})
}
@@ -4098,7 +4071,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
}))
}
- private func openAvatarForEditing(hasRemove: Bool = true, completion: @escaping () -> Void = {}) {
+ private func openAvatarForEditing(fromGallery: Bool = false, completion: @escaping () -> Void = {}) {
guard let peer = self.data?.peer, canEditPeerInfo(context: self.context, peer: peer) else {
return
}
@@ -4150,7 +4123,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
return controller
}
- let mixin = TGMediaAvatarMenuMixin(context: legacyController.context, parentController: emptyController, hasSearchButton: true, hasDeleteButton: hasPhotos && hasRemove, hasViewButton: false, personalPhoto: strongSelf.isSettings, isVideo: currentIsVideo, saveEditedPhotos: false, saveCapturedMedia: false, signup: false)!
+ let mixin = TGMediaAvatarMenuMixin(context: legacyController.context, parentController: emptyController, hasSearchButton: true, hasDeleteButton: hasPhotos && !fromGallery, hasViewButton: false, personalPhoto: strongSelf.isSettings, isVideo: currentIsVideo, saveEditedPhotos: false, saveCapturedMedia: false, signup: false)!
mixin.stickersContext = paintStickersContext
let _ = strongSelf.currentAvatarMixin.swap(mixin)
mixin.requestSearchController = { [weak self] assetsController in
@@ -4163,6 +4136,10 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
}))
controller.navigationPresentation = .modal
strongSelf.controller?.push(controller)
+
+ if fromGallery {
+ completion()
+ }
}
mixin.didFinishWithImage = { [weak self] image in
if let image = image {
@@ -4181,35 +4158,55 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
return
}
- if let item = item {
- strongSelf.deleteAvatar(item, remove: false)
- }
-
- let _ = strongSelf.currentAvatarMixin.swap(nil)
- if let _ = peer.smallProfileImage {
- strongSelf.state = strongSelf.state.withUpdatingAvatar(nil)
- if let (layout, navigationHeight) = strongSelf.validLayout {
- strongSelf.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: .immediate, additive: false)
+ let proceed = {
+ if let item = item {
+ strongSelf.deleteAvatar(item, remove: false)
}
- }
- let postbox = strongSelf.context.account.postbox
- strongSelf.updateAvatarDisposable.set((updatePeerPhoto(postbox: strongSelf.context.account.postbox, network: strongSelf.context.account.network, stateManager: strongSelf.context.account.stateManager, accountPeerId: strongSelf.context.account.peerId, peerId: strongSelf.peerId, photo: nil, mapResourceToAvatarSizes: { resource, representations in
- return mapResourceToAvatarSizes(postbox: postbox, resource: resource, representations: representations)
- })
- |> deliverOnMainQueue).start(next: { result in
- guard let strongSelf = self else {
- return
- }
- switch result {
- case .complete:
+
+ let _ = strongSelf.currentAvatarMixin.swap(nil)
+ if let _ = peer.smallProfileImage {
strongSelf.state = strongSelf.state.withUpdatingAvatar(nil)
if let (layout, navigationHeight) = strongSelf.validLayout {
strongSelf.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: .immediate, additive: false)
}
- case .progress:
- break
}
- }))
+ let postbox = strongSelf.context.account.postbox
+ strongSelf.updateAvatarDisposable.set((updatePeerPhoto(postbox: strongSelf.context.account.postbox, network: strongSelf.context.account.network, stateManager: strongSelf.context.account.stateManager, accountPeerId: strongSelf.context.account.peerId, peerId: strongSelf.peerId, photo: nil, mapResourceToAvatarSizes: { resource, representations in
+ return mapResourceToAvatarSizes(postbox: postbox, resource: resource, representations: representations)
+ })
+ |> deliverOnMainQueue).start(next: { result in
+ guard let strongSelf = self else {
+ return
+ }
+ switch result {
+ case .complete:
+ strongSelf.state = strongSelf.state.withUpdatingAvatar(nil)
+ if let (layout, navigationHeight) = strongSelf.validLayout {
+ strongSelf.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: .immediate, additive: false)
+ }
+ case .progress:
+ break
+ }
+ }))
+ }
+
+ let actionSheet = ActionSheetController(presentationData: presentationData)
+ let items: [ActionSheetItem] = [
+ ActionSheetButtonItem(title: presentationData.strings.Settings_RemoveConfirmation, color: .destructive, action: { [weak actionSheet] in
+ actionSheet?.dismissAnimated()
+ proceed()
+ })
+ ]
+
+ actionSheet.setItemGroups([
+ ActionSheetItemGroup(items: items),
+ ActionSheetItemGroup(items: [
+ ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak actionSheet] in
+ actionSheet?.dismissAnimated()
+ })
+ ])
+ ])
+ strongSelf.controller?.present(actionSheet, in: .window(.root))
}
mixin.didDismiss = { [weak legacyController] in
guard let strongSelf = self else {
@@ -4446,7 +4443,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
if let contactsController = contactsController as? ContactSelectionController {
selectAddMemberDisposable.set((contactsController.result
|> deliverOnMainQueue).start(next: { [weak contactsController] memberPeer in
- guard let memberPeer = memberPeer else {
+ guard let (memberPeer, _) = memberPeer else {
return
}
@@ -5161,6 +5158,9 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
})
}
+ if self.isSettings {
+ contentHeight = max(contentHeight, layout.size.height + 140.0 + (self.headerNode.twoLineInfo ? 17.0 : 0.0) - layout.intrinsicInsets.bottom)
+ }
self.scrollNode.view.contentSize = CGSize(width: layout.size.width, height: contentHeight)
if self.isSettings {
self.scrollNode.view.contentInset = UIEdgeInsets(top: 0.0, left: 0.0, bottom: layout.intrinsicInsets.bottom, right: 0.0)
@@ -5372,6 +5372,18 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
}
}
+ fileprivate func resetHeaderExpansion() {
+ if self.headerNode.isAvatarExpanded {
+ self.headerNode.ignoreCollapse = true
+ self.headerNode.updateIsAvatarExpanded(false, transition: .immediate)
+ self.updateNavigationExpansionPresentation(isExpanded: false, animated: true)
+ if let (layout, navigationHeight) = self.validLayout {
+ self.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: .immediate, additive: false)
+ }
+ self.headerNode.ignoreCollapse = false
+ }
+ }
+
private func updateNavigationExpansionPresentation(isExpanded: Bool, animated: Bool) {
if let controller = self.controller {
controller.setStatusBarStyle(isExpanded ? .White : self.presentationData.theme.rootController.statusBarStyle.style, animated: animated)
@@ -5402,12 +5414,14 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
guard let (_, navigationHeight) = self.validLayout else {
return
}
- if self.state.isEditing && self.isSettings {
- if targetContentOffset.pointee.y < navigationHeight {
- if targetContentOffset.pointee.y < navigationHeight / 2.0 {
- targetContentOffset.pointee.y = 0.0
- } else {
- targetContentOffset.pointee.y = navigationHeight
+ if self.state.isEditing {
+ if self.isSettings {
+ if targetContentOffset.pointee.y < navigationHeight {
+ if targetContentOffset.pointee.y < navigationHeight / 2.0 {
+ targetContentOffset.pointee.y = 0.0
+ } else {
+ targetContentOffset.pointee.y = navigationHeight
+ }
}
}
} else {
diff --git a/submodules/TelegramUI/Sources/PeerMediaCollectionController.swift b/submodules/TelegramUI/Sources/PeerMediaCollectionController.swift
index 77d39591f5..7f9fdcb0dd 100644
--- a/submodules/TelegramUI/Sources/PeerMediaCollectionController.swift
+++ b/submodules/TelegramUI/Sources/PeerMediaCollectionController.swift
@@ -435,6 +435,7 @@ public class PeerMediaCollectionController: TelegramBaseController {
}, animateDiceSuccess: {
}, greetingStickerNode: {
return nil
+ }, openPeerContextMenu: { _, _, _, _ in
}, requestMessageUpdate: { _ in
}, cancelInteractiveKeyboardGestures: {
}, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings,
diff --git a/submodules/TelegramUI/Sources/PeerSelectionController.swift b/submodules/TelegramUI/Sources/PeerSelectionController.swift
index 994cab2b6b..fd6b2fdc66 100644
--- a/submodules/TelegramUI/Sources/PeerSelectionController.swift
+++ b/submodules/TelegramUI/Sources/PeerSelectionController.swift
@@ -190,20 +190,6 @@ public final class PeerSelectionControllerImpl: ViewController, PeerSelectionCon
self._ready.set(self.peerSelectionNode.ready)
}
- override public func viewWillAppear(_ animated: Bool) {
- super.viewWillAppear(animated)
- }
-
- override public func viewDidAppear(_ animated: Bool) {
- super.viewDidAppear(animated)
-
- //self.peerSelectionNode.animateIn()
- }
-
- override public func viewDidDisappear(_ animated: Bool) {
- super.viewDidDisappear(animated)
- }
-
override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
super.containerLayoutUpdated(layout, transition: transition)
diff --git a/submodules/TelegramUI/Sources/PeerSelectionControllerNode.swift b/submodules/TelegramUI/Sources/PeerSelectionControllerNode.swift
index 4ef6f7b9ee..b8cc8236fd 100644
--- a/submodules/TelegramUI/Sources/PeerSelectionControllerNode.swift
+++ b/submodules/TelegramUI/Sources/PeerSelectionControllerNode.swift
@@ -296,20 +296,6 @@ final class PeerSelectionControllerNode: ASDisplayNode {
}
}
- func animateIn() {
- self.layer.animatePosition(from: CGPoint(x: self.layer.position.x, y: self.layer.position.y + self.layer.bounds.size.height), to: self.layer.position, duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring)
- }
-
- func animateOut(completion: (() -> Void)? = nil) {
- self.clipsToBounds = true
- self.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: self.layer.bounds.size.height), duration: 0.2, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, removeOnCompletion: false, additive: true, completion: { [weak self] _ in
- if let strongSelf = self {
- strongSelf.dismiss()
- }
- completion?()
- })
- }
-
private func indexChanged(_ index: Int) {
guard let (layout, navigationHeight, actualNavigationHeight) = self.containerLayout else {
return
@@ -331,7 +317,7 @@ final class PeerSelectionControllerNode: ASDisplayNode {
contactListNode.activateSearch = { [weak self] in
self?.requestActivateSearch?()
}
- contactListNode.openPeer = { [weak self] peer in
+ contactListNode.openPeer = { [weak self] peer, _ in
if case let .peer(peer, _, _) = peer {
self?.requestOpenPeer?(peer.id)
}
diff --git a/submodules/TelegramUI/Sources/ShareExtensionContext.swift b/submodules/TelegramUI/Sources/ShareExtensionContext.swift
index 2148715929..a5cd430eeb 100644
--- a/submodules/TelegramUI/Sources/ShareExtensionContext.swift
+++ b/submodules/TelegramUI/Sources/ShareExtensionContext.swift
@@ -260,10 +260,11 @@ public class ShareRootControllerImpl {
let applicationInterface = account
|> mapToSignal { sharedContext, account, otherAccounts -> Signal<(AccountContext, PostboxAccessChallengeData, [AccountWithInfo]), ShareAuthorizationError> in
- let limitsConfigurationAndContentSettings = account.postbox.transaction { transaction -> (LimitsConfiguration, ContentSettings) in
+ let limitsConfigurationAndContentSettings = account.postbox.transaction { transaction -> (LimitsConfiguration, ContentSettings, AppConfiguration) in
return (
transaction.getPreferencesEntry(key: PreferencesKeys.limitsConfiguration) as? LimitsConfiguration ?? LimitsConfiguration.defaultValue,
- getContentSettings(transaction: transaction)
+ getContentSettings(transaction: transaction),
+ getAppConfiguration(transaction: transaction)
)
}
return combineLatest(sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.presentationPasscodeSettings]), limitsConfigurationAndContentSettings, sharedContext.accountManager.accessChallengeData())
@@ -272,7 +273,7 @@ public class ShareRootControllerImpl {
|> castError(ShareAuthorizationError.self)
|> map { sharedData, limitsConfigurationAndContentSettings, data -> (AccountContext, PostboxAccessChallengeData, [AccountWithInfo]) in
updateLegacyLocalization(strings: sharedContext.currentPresentationData.with({ $0 }).strings)
- let context = AccountContextImpl(sharedContext: sharedContext, account: account/*, tonContext: nil*/, limitsConfiguration: limitsConfigurationAndContentSettings.0, contentSettings: limitsConfigurationAndContentSettings.1)
+ let context = AccountContextImpl(sharedContext: sharedContext, account: account, limitsConfiguration: limitsConfigurationAndContentSettings.0, contentSettings: limitsConfigurationAndContentSettings.1, appConfiguration: limitsConfigurationAndContentSettings.2)
return (context, data.data, otherAccounts)
}
}
diff --git a/submodules/TelegramUI/Sources/SharedAccountContext.swift b/submodules/TelegramUI/Sources/SharedAccountContext.swift
index b8d6dbfb2d..a4c3640344 100644
--- a/submodules/TelegramUI/Sources/SharedAccountContext.swift
+++ b/submodules/TelegramUI/Sources/SharedAccountContext.swift
@@ -574,7 +574,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
})
if let mainWindow = mainWindow, applicationBindings.isMainApp {
- let callManager = PresentationCallManagerImpl(accountManager: self.accountManager, enableVideoCalls: self.immediateExperimentalUISettings.videoCalls, getDeviceAccessData: {
+ let callManager = PresentationCallManagerImpl(accountManager: self.accountManager, getDeviceAccessData: {
return (self.currentPresentationData.with { $0 }, { [weak self] c, a in
self?.presentGlobalController(c, a)
}, {
@@ -634,14 +634,18 @@ public final class SharedAccountContextImpl: SharedAccountContext {
if let strongSelf = self {
let resolvedText: CallStatusText
if let state = state {
- switch state.state {
- case .connecting, .requesting, .terminating, .ringing, .waiting:
- resolvedText = .inProgress(nil)
- case .terminated:
- resolvedText = .none
- case .active(let timestamp, _, _), .reconnecting(let timestamp, _, _):
- resolvedText = .inProgress(timestamp)
- }
+// if [.active, .paused].contains(state.videoState) || [.active, .paused].contains(state.remoteVideoState) {
+// resolvedText = .none
+// } else {
+ switch state.state {
+ case .connecting, .requesting, .terminating, .ringing, .waiting:
+ resolvedText = .inProgress(nil)
+ case .terminated:
+ resolvedText = .none
+ case .active(let timestamp, _, _), .reconnecting(let timestamp, _, _):
+ resolvedText = .inProgress(timestamp)
+ }
+// }
} else {
resolvedText = .none
}
@@ -937,7 +941,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
}
public func makeTempAccountContext(account: Account) -> AccountContext {
- return AccountContextImpl(sharedContext: self, account: account/*, tonContext: nil*/, limitsConfiguration: .defaultValue, contentSettings: .default, temp: true)
+ return AccountContextImpl(sharedContext: self, account: account, limitsConfiguration: .defaultValue, contentSettings: .default, appConfiguration: .defaultValue, temp: true)
}
public func openChatMessage(_ params: OpenChatMessageParams) -> Bool {
@@ -1189,6 +1193,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
}, animateDiceSuccess: {
}, greetingStickerNode: {
return nil
+ }, openPeerContextMenu: { _, _, _, _ in
}, requestMessageUpdate: { _ in
}, cancelInteractiveKeyboardGestures: {
}, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings,
diff --git a/submodules/TelegramUI/Sources/ThemeUpdateManager.swift b/submodules/TelegramUI/Sources/ThemeUpdateManager.swift
index 2371c501a3..2858bc2446 100644
--- a/submodules/TelegramUI/Sources/ThemeUpdateManager.swift
+++ b/submodules/TelegramUI/Sources/ThemeUpdateManager.swift
@@ -104,7 +104,7 @@ final class ThemeUpdateManagerImpl: ThemeUpdateManager {
|> mapToSignal { wallpaper -> Signal<(PresentationThemeReference, PresentationTheme?), NoError> in
if let wallpaper = wallpaper, case let .file(file) = wallpaper {
var convertedRepresentations: [ImageRepresentationWithReference] = []
- convertedRepresentations.append(ImageRepresentationWithReference(representation: TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 100, height: 100), resource: file.file.resource), reference: .wallpaper(wallpaper: .slug(file.slug), resource: file.file.resource)))
+ convertedRepresentations.append(ImageRepresentationWithReference(representation: TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 100, height: 100), resource: file.file.resource, progressiveSizes: []), reference: .wallpaper(wallpaper: .slug(file.slug), resource: file.file.resource)))
return wallpaperDatas(account: account, accountManager: accountManager, fileReference: .standalone(media: file.file), representations: convertedRepresentations, alwaysShowThumbnailFirst: false, thumbnail: false, onlyFullSize: true, autoFetchFullSize: true, synchronousLoad: false)
|> mapToSignal { _, fullSizeData, complete -> Signal<(PresentationThemeReference, PresentationTheme?), NoError> in
guard complete, let fullSizeData = fullSizeData else {
diff --git a/submodules/TelegramUI/Sources/TransformOutgoingMessageMedia.swift b/submodules/TelegramUI/Sources/TransformOutgoingMessageMedia.swift
index 37aa45c842..525586e6dc 100644
--- a/submodules/TelegramUI/Sources/TransformOutgoingMessageMedia.swift
+++ b/submodules/TelegramUI/Sources/TransformOutgoingMessageMedia.swift
@@ -75,7 +75,7 @@ public func transformOutgoingMessageMedia(postbox: Postbox, network: Network, me
}
}
attributes.append(.ImageSize(size: PixelDimensions(imageDimensions)))
- let updatedFile = file.withUpdatedSize(data.size).withUpdatedPreviewRepresentations([TelegramMediaImageRepresentation(dimensions: PixelDimensions(scaledImageSize), resource: thumbnailResource)]).withUpdatedAttributes(attributes)
+ let updatedFile = file.withUpdatedSize(data.size).withUpdatedPreviewRepresentations([TelegramMediaImageRepresentation(dimensions: PixelDimensions(scaledImageSize), resource: thumbnailResource, progressiveSizes: [])]).withUpdatedAttributes(attributes)
subscriber.putNext(.standalone(media: updatedFile))
subscriber.putCompletion()
} else {
@@ -104,7 +104,7 @@ public func transformOutgoingMessageMedia(postbox: Postbox, network: Network, me
let scaledImageSize = CGSize(width: scaledImage.size.width * scaledImage.scale, height: scaledImage.size.height * scaledImage.scale)
- let updatedFile = file.withUpdatedSize(data.size).withUpdatedPreviewRepresentations([TelegramMediaImageRepresentation(dimensions: PixelDimensions(scaledImageSize), resource: thumbnailResource)])
+ let updatedFile = file.withUpdatedSize(data.size).withUpdatedPreviewRepresentations([TelegramMediaImageRepresentation(dimensions: PixelDimensions(scaledImageSize), resource: thumbnailResource, progressiveSizes: [])])
subscriber.putNext(.standalone(media: updatedFile))
subscriber.putCompletion()
} else {
@@ -160,7 +160,7 @@ public func transformOutgoingMessageMedia(postbox: Postbox, network: Network, me
let thumbnailResource = LocalFileMediaResource(fileId: arc4random64())
postbox.mediaBox.storeResourceData(thumbnailResource.id, data: smallestData)
- representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(smallestSize), resource: thumbnailResource))
+ representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(smallestSize), resource: thumbnailResource, progressiveSizes: []))
let updatedImage = TelegramMediaImage(imageId: image.imageId, representations: representations, immediateThumbnailData: image.immediateThumbnailData, reference: image.reference, partialReference: image.partialReference, flags: [])
return .single(.standalone(media: updatedImage))
}
diff --git a/submodules/TelegramUI/Sources/VerticalListContextResultsChatInputPanelItem.swift b/submodules/TelegramUI/Sources/VerticalListContextResultsChatInputPanelItem.swift
index f0173c6023..74b62a277a 100644
--- a/submodules/TelegramUI/Sources/VerticalListContextResultsChatInputPanelItem.swift
+++ b/submodules/TelegramUI/Sources/VerticalListContextResultsChatInputPanelItem.swift
@@ -251,7 +251,7 @@ final class VerticalListContextResultsChatInputPanelItemNode: ListViewItemNode {
if let stickerFile = stickerFile {
updateIconImageSignal = chatMessageSticker(account: item.account, file: stickerFile, small: false, fetched: true)
} else {
- let tmpRepresentation = TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 55, height: 55), resource: imageResource)
+ let tmpRepresentation = TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 55, height: 55), resource: imageResource, progressiveSizes: [])
let tmpImage = TelegramMediaImage(imageId: MediaId(namespace: 0, id: 0), representations: [tmpRepresentation], immediateThumbnailData: nil, reference: nil, partialReference: nil, flags: [])
updateIconImageSignal = chatWebpageSnippetPhoto(account: item.account, photoReference: .standalone(media: tmpImage))
}
diff --git a/submodules/TelegramUI/Sources/WalletContextImpl.swift b/submodules/TelegramUI/Sources/WalletContextImpl.swift
index f4e6630aa5..14c74de5af 100644
--- a/submodules/TelegramUI/Sources/WalletContextImpl.swift
+++ b/submodules/TelegramUI/Sources/WalletContextImpl.swift
@@ -259,7 +259,7 @@ final class WalletContextImpl: WalletContext {
func authorizeAccessToCamera(completion: @escaping () -> Void) {
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
- DeviceAccess.authorizeAccess(to: .camera, presentationData: presentationData, present: { c, a in
+ DeviceAccess.authorizeAccess(to: .camera(.video), presentationData: presentationData, present: { c, a in
c.presentationArguments = a
self.context.sharedContext.mainWindow?.present(c, on: .root)
}, openSettings: { [weak self] in
diff --git a/submodules/TelegramUIPreferences/Sources/ExperimentalUISettings.swift b/submodules/TelegramUIPreferences/Sources/ExperimentalUISettings.swift
index 53d7c35a41..c8a02de15f 100644
--- a/submodules/TelegramUIPreferences/Sources/ExperimentalUISettings.swift
+++ b/submodules/TelegramUIPreferences/Sources/ExperimentalUISettings.swift
@@ -9,9 +9,9 @@ public struct ExperimentalUISettings: Equatable, PreferencesEntry {
public var chatListPhotos: Bool
public var knockoutWallpaper: Bool
public var foldersTabAtBottom: Bool
- public var videoCalls: Bool
public var playerEmbedding: Bool
public var playlistPlayback: Bool
+ public var preferredVideoCodec: String?
public static var defaultSettings: ExperimentalUISettings {
return ExperimentalUISettings(
@@ -21,9 +21,9 @@ public struct ExperimentalUISettings: Equatable, PreferencesEntry {
chatListPhotos: false,
knockoutWallpaper: false,
foldersTabAtBottom: false,
- videoCalls: false,
playerEmbedding: false,
- playlistPlayback: false
+ playlistPlayback: false,
+ preferredVideoCodec: nil
)
}
@@ -34,9 +34,9 @@ public struct ExperimentalUISettings: Equatable, PreferencesEntry {
chatListPhotos: Bool,
knockoutWallpaper: Bool,
foldersTabAtBottom: Bool,
- videoCalls: Bool,
playerEmbedding: Bool,
- playlistPlayback: Bool
+ playlistPlayback: Bool,
+ preferredVideoCodec: String?
) {
self.keepChatNavigationStack = keepChatNavigationStack
self.skipReadHistory = skipReadHistory
@@ -44,9 +44,9 @@ public struct ExperimentalUISettings: Equatable, PreferencesEntry {
self.chatListPhotos = chatListPhotos
self.knockoutWallpaper = knockoutWallpaper
self.foldersTabAtBottom = foldersTabAtBottom
- self.videoCalls = videoCalls
self.playerEmbedding = playerEmbedding
self.playlistPlayback = playlistPlayback
+ self.preferredVideoCodec = preferredVideoCodec
}
public init(decoder: PostboxDecoder) {
@@ -56,9 +56,9 @@ public struct ExperimentalUISettings: Equatable, PreferencesEntry {
self.chatListPhotos = decoder.decodeInt32ForKey("chatListPhotos", orElse: 0) != 0
self.knockoutWallpaper = decoder.decodeInt32ForKey("knockoutWallpaper", orElse: 0) != 0
self.foldersTabAtBottom = decoder.decodeInt32ForKey("foldersTabAtBottom", orElse: 0) != 0
- self.videoCalls = decoder.decodeInt32ForKey("videoCalls", orElse: 0) != 0
self.playerEmbedding = decoder.decodeInt32ForKey("playerEmbedding", orElse: 0) != 0
self.playlistPlayback = decoder.decodeInt32ForKey("playlistPlayback", orElse: 0) != 0
+ self.preferredVideoCodec = decoder.decodeOptionalStringForKey("preferredVideoCodec")
}
public func encode(_ encoder: PostboxEncoder) {
@@ -68,9 +68,11 @@ public struct ExperimentalUISettings: Equatable, PreferencesEntry {
encoder.encodeInt32(self.chatListPhotos ? 1 : 0, forKey: "chatListPhotos")
encoder.encodeInt32(self.knockoutWallpaper ? 1 : 0, forKey: "knockoutWallpaper")
encoder.encodeInt32(self.foldersTabAtBottom ? 1 : 0, forKey: "foldersTabAtBottom")
- encoder.encodeInt32(self.videoCalls ? 1 : 0, forKey: "videoCalls")
encoder.encodeInt32(self.playerEmbedding ? 1 : 0, forKey: "playerEmbedding")
encoder.encodeInt32(self.playlistPlayback ? 1 : 0, forKey: "playlistPlayback")
+ if let preferredVideoCodec = self.preferredVideoCodec {
+ encoder.encodeString(preferredVideoCodec, forKey: "preferredVideoCodec")
+ }
}
public func isEqual(to: PreferencesEntry) -> Bool {
diff --git a/submodules/TelegramUniversalVideoContent/Sources/GenericEmbedImplementation.swift b/submodules/TelegramUniversalVideoContent/Sources/GenericEmbedImplementation.swift
index 436e4161cf..65dae43824 100644
--- a/submodules/TelegramUniversalVideoContent/Sources/GenericEmbedImplementation.swift
+++ b/submodules/TelegramUniversalVideoContent/Sources/GenericEmbedImplementation.swift
@@ -14,7 +14,7 @@ final class GenericEmbedImplementation: WebEmbedImplementation {
init(url: String) {
self.url = url
- self.status = MediaPlayerStatus(generationTimestamp: 0.0, duration: 0.0, dimensions: CGSize(), timestamp: 0.0, baseRate: 1.0, seekId: 0, status: .buffering(initial: true, whilePlaying: true), soundEnabled: true)
+ self.status = MediaPlayerStatus(generationTimestamp: 0.0, duration: 0.0, dimensions: CGSize(), timestamp: 0.0, baseRate: 1.0, seekId: 0, status: .buffering(initial: true, whilePlaying: true, progress: 0.0), soundEnabled: true)
}
func setup(_ webView: WKWebView, userContentController: WKUserContentController, evaluateJavaScript: @escaping (String, ((Any?) -> Void)?) -> Void, updateStatus: @escaping (MediaPlayerStatus) -> Void, onPlaybackStarted: @escaping () -> Void) {
diff --git a/submodules/TelegramUniversalVideoContent/Sources/NativeVideoContent.swift b/submodules/TelegramUniversalVideoContent/Sources/NativeVideoContent.swift
index 7a3ebef4ff..50b894e390 100644
--- a/submodules/TelegramUniversalVideoContent/Sources/NativeVideoContent.swift
+++ b/submodules/TelegramUniversalVideoContent/Sources/NativeVideoContent.swift
@@ -31,13 +31,14 @@ public final class NativeVideoContent: UniversalVideoContent {
public let baseRate: Double
let fetchAutomatically: Bool
let onlyFullSizeThumbnail: Bool
+ let useLargeThumbnail: Bool
let autoFetchFullSizeThumbnail: Bool
let startTimestamp: Double?
let continuePlayingWithoutSoundOnLostAudioSession: Bool
let placeholderColor: UIColor
let tempFilePath: String?
- public init(id: NativeVideoContentId, fileReference: FileMediaReference, imageReference: ImageMediaReference? = nil, streamVideo: MediaPlayerStreaming = .none, loopVideo: Bool = false, enableSound: Bool = true, baseRate: Double = 1.0, fetchAutomatically: Bool = true, onlyFullSizeThumbnail: Bool = false, autoFetchFullSizeThumbnail: Bool = false, startTimestamp: Double? = nil, continuePlayingWithoutSoundOnLostAudioSession: Bool = false, placeholderColor: UIColor = .white, tempFilePath: String? = nil) {
+ public init(id: NativeVideoContentId, fileReference: FileMediaReference, imageReference: ImageMediaReference? = nil, streamVideo: MediaPlayerStreaming = .none, loopVideo: Bool = false, enableSound: Bool = true, baseRate: Double = 1.0, fetchAutomatically: Bool = true, onlyFullSizeThumbnail: Bool = false, useLargeThumbnail: Bool = false, autoFetchFullSizeThumbnail: Bool = false, startTimestamp: Double? = nil, continuePlayingWithoutSoundOnLostAudioSession: Bool = false, placeholderColor: UIColor = .white, tempFilePath: String? = nil) {
self.id = id
self.nativeId = id
self.fileReference = fileReference
@@ -62,6 +63,7 @@ public final class NativeVideoContent: UniversalVideoContent {
self.baseRate = baseRate
self.fetchAutomatically = fetchAutomatically
self.onlyFullSizeThumbnail = onlyFullSizeThumbnail
+ self.useLargeThumbnail = useLargeThumbnail
self.autoFetchFullSizeThumbnail = autoFetchFullSizeThumbnail
self.startTimestamp = startTimestamp
self.continuePlayingWithoutSoundOnLostAudioSession = continuePlayingWithoutSoundOnLostAudioSession
@@ -70,7 +72,7 @@ public final class NativeVideoContent: UniversalVideoContent {
}
public func makeContentNode(postbox: Postbox, audioSession: ManagedAudioSession) -> UniversalVideoContentNode & ASDisplayNode {
- return NativeVideoContentNode(postbox: postbox, audioSessionManager: audioSession, fileReference: self.fileReference, imageReference: self.imageReference, streamVideo: self.streamVideo, loopVideo: self.loopVideo, enableSound: self.enableSound, baseRate: self.baseRate, fetchAutomatically: self.fetchAutomatically, onlyFullSizeThumbnail: self.onlyFullSizeThumbnail, autoFetchFullSizeThumbnail: self.autoFetchFullSizeThumbnail, startTimestamp: self.startTimestamp, continuePlayingWithoutSoundOnLostAudioSession: self.continuePlayingWithoutSoundOnLostAudioSession, placeholderColor: self.placeholderColor, tempFilePath: self.tempFilePath)
+ return NativeVideoContentNode(postbox: postbox, audioSessionManager: audioSession, fileReference: self.fileReference, imageReference: self.imageReference, streamVideo: self.streamVideo, loopVideo: self.loopVideo, enableSound: self.enableSound, baseRate: self.baseRate, fetchAutomatically: self.fetchAutomatically, onlyFullSizeThumbnail: self.onlyFullSizeThumbnail, useLargeThumbnail: self.useLargeThumbnail, autoFetchFullSizeThumbnail: self.autoFetchFullSizeThumbnail, startTimestamp: self.startTimestamp, continuePlayingWithoutSoundOnLostAudioSession: self.continuePlayingWithoutSoundOnLostAudioSession, placeholderColor: self.placeholderColor, tempFilePath: self.tempFilePath)
}
public func isEqual(to other: UniversalVideoContent) -> Bool {
@@ -143,7 +145,7 @@ private final class NativeVideoContentNode: ASDisplayNode, UniversalVideoContent
private var shouldPlay: Bool = false
- init(postbox: Postbox, audioSessionManager: ManagedAudioSession, fileReference: FileMediaReference, imageReference: ImageMediaReference?, streamVideo: MediaPlayerStreaming, loopVideo: Bool, enableSound: Bool, baseRate: Double, fetchAutomatically: Bool, onlyFullSizeThumbnail: Bool, autoFetchFullSizeThumbnail: Bool, startTimestamp: Double?, continuePlayingWithoutSoundOnLostAudioSession: Bool = false, placeholderColor: UIColor, tempFilePath: String?) {
+ init(postbox: Postbox, audioSessionManager: ManagedAudioSession, fileReference: FileMediaReference, imageReference: ImageMediaReference?, streamVideo: MediaPlayerStreaming, loopVideo: Bool, enableSound: Bool, baseRate: Double, fetchAutomatically: Bool, onlyFullSizeThumbnail: Bool, useLargeThumbnail: Bool, autoFetchFullSizeThumbnail: Bool, startTimestamp: Double?, continuePlayingWithoutSoundOnLostAudioSession: Bool = false, placeholderColor: UIColor, tempFilePath: String?) {
self.postbox = postbox
self.fileReference = fileReference
self.placeholderColor = placeholderColor
@@ -180,7 +182,7 @@ private final class NativeVideoContentNode: ASDisplayNode, UniversalVideoContent
self?.performActionAtEnd()
}
- self.imageNode.setSignal(internalMediaGridMessageVideo(postbox: postbox, videoReference: fileReference, imageReference: imageReference, onlyFullSize: onlyFullSizeThumbnail, autoFetchFullSizeThumbnail: autoFetchFullSizeThumbnail || fileReference.media.isInstantVideo) |> map { [weak self] getSize, getData in
+ self.imageNode.setSignal(internalMediaGridMessageVideo(postbox: postbox, videoReference: fileReference, imageReference: imageReference, onlyFullSize: onlyFullSizeThumbnail, useLargeThumbnail: useLargeThumbnail, autoFetchFullSizeThumbnail: autoFetchFullSizeThumbnail || fileReference.media.isInstantVideo) |> map { [weak self] getSize, getData in
Queue.mainQueue().async {
if let strongSelf = self, strongSelf.dimensions == nil {
if let dimensions = getSize() {
diff --git a/submodules/TelegramUniversalVideoContent/Sources/PictureInPictureVideoControlsNode.swift b/submodules/TelegramUniversalVideoContent/Sources/PictureInPictureVideoControlsNode.swift
index bf291fa07e..fd483dae68 100644
--- a/submodules/TelegramUniversalVideoContent/Sources/PictureInPictureVideoControlsNode.swift
+++ b/submodules/TelegramUniversalVideoContent/Sources/PictureInPictureVideoControlsNode.swift
@@ -37,7 +37,7 @@ final class PictureInPictureVideoControlsNode: ASDisplayNode {
case .playing:
self.playButton.isHidden = true
self.pauseButton.isHidden = false
- case let .buffering(_, whilePlaying):
+ case let .buffering(_, whilePlaying, _):
if whilePlaying {
self.playButton.isHidden = true
self.pauseButton.isHidden = false
diff --git a/submodules/TelegramUniversalVideoContent/Sources/PlatformVideoContent.swift b/submodules/TelegramUniversalVideoContent/Sources/PlatformVideoContent.swift
index 4f078b082c..517a7fef05 100644
--- a/submodules/TelegramUniversalVideoContent/Sources/PlatformVideoContent.swift
+++ b/submodules/TelegramUniversalVideoContent/Sources/PlatformVideoContent.swift
@@ -315,7 +315,7 @@ private final class PlatformVideoContentNode: ASDisplayNode, UniversalVideoConte
self.isBuffering = false
}
if self.isBuffering {
- status = .buffering(initial: false, whilePlaying: isPlaying)
+ status = .buffering(initial: false, whilePlaying: isPlaying, progress: 0.0)
} else {
status = isPlaying ? .playing : .paused
}
@@ -326,7 +326,7 @@ private final class PlatformVideoContentNode: ASDisplayNode, UniversalVideoConte
let status: MediaPlayerPlaybackStatus
self.isBuffering = true
if self.isBuffering {
- status = .buffering(initial: false, whilePlaying: isPlaying)
+ status = .buffering(initial: false, whilePlaying: isPlaying, progress: 0.0)
} else {
status = isPlaying ? .playing : .paused
}
@@ -337,7 +337,7 @@ private final class PlatformVideoContentNode: ASDisplayNode, UniversalVideoConte
let status: MediaPlayerPlaybackStatus
self.isBuffering = false
if self.isBuffering {
- status = .buffering(initial: false, whilePlaying: isPlaying)
+ status = .buffering(initial: false, whilePlaying: isPlaying, progress: 0.0)
} else {
status = isPlaying ? .playing : .paused
}
@@ -381,7 +381,7 @@ private final class PlatformVideoContentNode: ASDisplayNode, UniversalVideoConte
func play() {
assert(Queue.mainQueue().isCurrent())
if !self.initializedStatus {
- self._status.set(MediaPlayerStatus(generationTimestamp: 0.0, duration: Double(self.approximateDuration), dimensions: CGSize(), timestamp: 0.0, baseRate: 1.0, seekId: 0, status: .buffering(initial: true, whilePlaying: true), soundEnabled: true))
+ self._status.set(MediaPlayerStatus(generationTimestamp: 0.0, duration: Double(self.approximateDuration), dimensions: CGSize(), timestamp: 0.0, baseRate: 1.0, seekId: 0, status: .buffering(initial: true, whilePlaying: true, progress: 0.0), soundEnabled: true))
}
if !self.hasAudioSession {
self.audioSessionDisposable.set(self.audioSessionManager.push(audioSessionType: .play, activate: { [weak self] _ in
diff --git a/submodules/TelegramUniversalVideoContent/Sources/SystemVideoContent.swift b/submodules/TelegramUniversalVideoContent/Sources/SystemVideoContent.swift
index c3f0eefb31..629adfe05a 100644
--- a/submodules/TelegramUniversalVideoContent/Sources/SystemVideoContent.swift
+++ b/submodules/TelegramUniversalVideoContent/Sources/SystemVideoContent.swift
@@ -44,7 +44,7 @@ private final class SystemVideoContentNode: ASDisplayNode, UniversalVideoContent
private let playbackCompletedListeners = Bag<() -> Void>()
private var initializedStatus = false
- private var statusValue = MediaPlayerStatus(generationTimestamp: 0.0, duration: 0.0, dimensions: CGSize(), timestamp: 0.0, baseRate: 1.0, seekId: 0, status: .buffering(initial: true, whilePlaying: false), soundEnabled: true)
+ private var statusValue = MediaPlayerStatus(generationTimestamp: 0.0, duration: 0.0, dimensions: CGSize(), timestamp: 0.0, baseRate: 1.0, seekId: 0, status: .buffering(initial: true, whilePlaying: false, progress: 0.0), soundEnabled: true)
private var isBuffering = true
private let _status = ValuePromise()
var status: Signal {
@@ -169,7 +169,7 @@ private final class SystemVideoContentNode: ASDisplayNode, UniversalVideoContent
let isPlaying = !self.player.rate.isZero
let status: MediaPlayerPlaybackStatus
if self.isBuffering {
- status = .buffering(initial: false, whilePlaying: isPlaying)
+ status = .buffering(initial: false, whilePlaying: isPlaying, progress: 0.0)
} else {
status = isPlaying ? .playing : .paused
}
@@ -180,7 +180,7 @@ private final class SystemVideoContentNode: ASDisplayNode, UniversalVideoContent
let status: MediaPlayerPlaybackStatus
self.isBuffering = true
if self.isBuffering {
- status = .buffering(initial: false, whilePlaying: isPlaying)
+ status = .buffering(initial: false, whilePlaying: isPlaying, progress: 0.0)
} else {
status = isPlaying ? .playing : .paused
}
@@ -191,7 +191,7 @@ private final class SystemVideoContentNode: ASDisplayNode, UniversalVideoContent
let status: MediaPlayerPlaybackStatus
self.isBuffering = false
if self.isBuffering {
- status = .buffering(initial: false, whilePlaying: isPlaying)
+ status = .buffering(initial: false, whilePlaying: isPlaying, progress: 0.0)
} else {
status = isPlaying ? .playing : .paused
}
@@ -219,7 +219,7 @@ private final class SystemVideoContentNode: ASDisplayNode, UniversalVideoContent
func play() {
assert(Queue.mainQueue().isCurrent())
if !self.initializedStatus {
- self._status.set(MediaPlayerStatus(generationTimestamp: 0.0, duration: Double(self.approximateDuration), dimensions: CGSize(), timestamp: 0.0, baseRate: 1.0, seekId: self.seekId, status: .buffering(initial: true, whilePlaying: true), soundEnabled: true))
+ self._status.set(MediaPlayerStatus(generationTimestamp: 0.0, duration: Double(self.approximateDuration), dimensions: CGSize(), timestamp: 0.0, baseRate: 1.0, seekId: self.seekId, status: .buffering(initial: true, whilePlaying: true, progress: 0.0), soundEnabled: true))
}
if !self.hasAudioSession {
self.audioSessionDisposable.set(self.audioSessionManager.push(audioSessionType: .play, activate: { [weak self] _ in
diff --git a/submodules/TelegramUniversalVideoContent/Sources/TwitchEmbedImplementation.swift b/submodules/TelegramUniversalVideoContent/Sources/TwitchEmbedImplementation.swift
index 994b80b990..4c4041c36e 100644
--- a/submodules/TelegramUniversalVideoContent/Sources/TwitchEmbedImplementation.swift
+++ b/submodules/TelegramUniversalVideoContent/Sources/TwitchEmbedImplementation.swift
@@ -20,7 +20,7 @@ final class TwitchEmbedImplementation: WebEmbedImplementation {
init(url: String) {
self.url = url
- self.status = MediaPlayerStatus(generationTimestamp: 0.0, duration: 0.0, dimensions: CGSize(), timestamp: 0.0, baseRate: 1.0, seekId: 0, status: .buffering(initial: true, whilePlaying: true), soundEnabled: true)
+ self.status = MediaPlayerStatus(generationTimestamp: 0.0, duration: 0.0, dimensions: CGSize(), timestamp: 0.0, baseRate: 1.0, seekId: 0, status: .buffering(initial: true, whilePlaying: true, progress: 0.0), soundEnabled: true)
}
func setup(_ webView: WKWebView, userContentController: WKUserContentController, evaluateJavaScript: @escaping (String, ((Any?) -> Void)?) -> Void, updateStatus: @escaping (MediaPlayerStatus) -> Void, onPlaybackStarted: @escaping () -> Void) {
diff --git a/submodules/TelegramUniversalVideoContent/Sources/VimeoEmbedImplementation.swift b/submodules/TelegramUniversalVideoContent/Sources/VimeoEmbedImplementation.swift
index e0e1cde267..a1af6625ff 100644
--- a/submodules/TelegramUniversalVideoContent/Sources/VimeoEmbedImplementation.swift
+++ b/submodules/TelegramUniversalVideoContent/Sources/VimeoEmbedImplementation.swift
@@ -97,7 +97,7 @@ final class VimeoEmbedImplementation: WebEmbedImplementation {
init(videoId: String, timestamp: Int = 0) {
self.videoId = videoId
self.timestamp = timestamp
- self.status = MediaPlayerStatus(generationTimestamp: 0.0, duration: 0.0, dimensions: CGSize(), timestamp: Double(timestamp), baseRate: 1.0, seekId: 0, status: .buffering(initial: true, whilePlaying: true), soundEnabled: true)
+ self.status = MediaPlayerStatus(generationTimestamp: 0.0, duration: 0.0, dimensions: CGSize(), timestamp: Double(timestamp), baseRate: 1.0, seekId: 0, status: .buffering(initial: true, whilePlaying: true, progress: 0.0), soundEnabled: true)
}
func setup(_ webView: WKWebView, userContentController: WKUserContentController, evaluateJavaScript: @escaping (String, ((Any?) -> Void)?) -> Void, updateStatus: @escaping (MediaPlayerStatus) -> Void, onPlaybackStarted: @escaping () -> Void) {
@@ -221,7 +221,7 @@ final class VimeoEmbedImplementation: WebEmbedImplementation {
playbackStatus = .paused
newTimestamp = 0.0
default:
- playbackStatus = .buffering(initial: true, whilePlaying: false)
+ playbackStatus = .buffering(initial: true, whilePlaying: false, progress: 0.0)
}
self.status = MediaPlayerStatus(generationTimestamp: self.status.generationTimestamp, duration: Double(duration), dimensions: self.status.dimensions, timestamp: newTimestamp, baseRate: 1.0, seekId: self.status.seekId, status: playbackStatus, soundEnabled: true)
diff --git a/submodules/TelegramUniversalVideoContent/Sources/WebEmbedPlayerNode.swift b/submodules/TelegramUniversalVideoContent/Sources/WebEmbedPlayerNode.swift
index aa1329692a..40cf0fec67 100644
--- a/submodules/TelegramUniversalVideoContent/Sources/WebEmbedPlayerNode.swift
+++ b/submodules/TelegramUniversalVideoContent/Sources/WebEmbedPlayerNode.swift
@@ -88,24 +88,24 @@ final class WebEmbedPlayerNode: ASDisplayNode, WKNavigationDelegate {
let userContentController = WKUserContentController()
userContentController.addUserScript(WKUserScript(source: "var meta = document.createElement('meta'); meta.setAttribute('name', 'viewport'); meta.setAttribute('content', 'width=device-width'); document.getElementsByTagName('head')[0].appendChild(meta)", injectionTime: .atDocumentEnd, forMainFrameOnly: true))
- let config = WKWebViewConfiguration()
- config.allowsInlineMediaPlayback = true
- config.userContentController = userContentController
+ let configuration = WKWebViewConfiguration()
+ configuration.allowsInlineMediaPlayback = true
+ configuration.userContentController = userContentController
if #available(iOSApplicationExtension 10.0, iOS 10.0, *) {
- config.mediaTypesRequiringUserActionForPlayback = []
+ configuration.mediaTypesRequiringUserActionForPlayback = []
} else if #available(iOSApplicationExtension 9.0, iOS 9.0, *) {
- config.requiresUserActionForMediaPlayback = false
+ configuration.requiresUserActionForMediaPlayback = false
} else {
- config.mediaPlaybackRequiresUserAction = false
+ configuration.mediaPlaybackRequiresUserAction = false
}
if #available(iOSApplicationExtension 9.0, iOS 9.0, *) {
- config.allowsPictureInPictureMediaPlayback = false
+ configuration.allowsPictureInPictureMediaPlayback = false
}
let frame = CGRect(origin: CGPoint.zero, size: intrinsicDimensions)
- self.webView = WKWebView(frame: frame, configuration: config)
+ self.webView = WKWebView(frame: frame, configuration: configuration)
super.init()
self.frame = frame
diff --git a/submodules/TelegramUniversalVideoContent/Sources/YoutubeEmbedImplementation.swift b/submodules/TelegramUniversalVideoContent/Sources/YoutubeEmbedImplementation.swift
index 6527c0ed16..534a88ed92 100644
--- a/submodules/TelegramUniversalVideoContent/Sources/YoutubeEmbedImplementation.swift
+++ b/submodules/TelegramUniversalVideoContent/Sources/YoutubeEmbedImplementation.swift
@@ -121,7 +121,7 @@ final class YoutubeEmbedImplementation: WebEmbedImplementation {
if self.timestamp > 0 {
self.ignoreEarlierTimestamps = true
}
- self.status = MediaPlayerStatus(generationTimestamp: 0.0, duration: 0.0, dimensions: CGSize(), timestamp: Double(timestamp), baseRate: 1.0, seekId: 0, status: .buffering(initial: true, whilePlaying: true), soundEnabled: true)
+ self.status = MediaPlayerStatus(generationTimestamp: 0.0, duration: 0.0, dimensions: CGSize(), timestamp: Double(timestamp), baseRate: 1.0, seekId: 0, status: .buffering(initial: true, whilePlaying: true, progress: 0.0), soundEnabled: true)
self.benchmarkStartTime = CFAbsoluteTimeGetCurrent()
}
@@ -286,16 +286,16 @@ final class YoutubeEmbedImplementation: WebEmbedImplementation {
playbackStatus = .paused
newTimestamp = 0.0
} else {
- playbackStatus = .buffering(initial: false, whilePlaying: true)
+ playbackStatus = .buffering(initial: false, whilePlaying: true, progress: 0.0)
}
case 1:
playbackStatus = .playing
case 2:
playbackStatus = .paused
case 3:
- playbackStatus = .buffering(initial: false, whilePlaying: true)
+ playbackStatus = .buffering(initial: false, whilePlaying: true, progress: 0.0)
default:
- playbackStatus = .buffering(initial: true, whilePlaying: false)
+ playbackStatus = .buffering(initial: true, whilePlaying: false, progress: 0.0)
}
if case .playing = playbackStatus, !self.started {
diff --git a/submodules/TelegramVoip/Sources/OngoingCallContext.swift b/submodules/TelegramVoip/Sources/OngoingCallContext.swift
index 5665d5d782..3d93fa059a 100644
--- a/submodules/TelegramVoip/Sources/OngoingCallContext.swift
+++ b/submodules/TelegramVoip/Sources/OngoingCallContext.swift
@@ -8,20 +8,32 @@ import TelegramUIPreferences
import TgVoip
import TgVoipWebrtc
-//import TgVoipWebrtcCustom
-private func callConnectionDescription(_ connection: CallSessionConnection) -> OngoingCallConnectionDescription {
- return OngoingCallConnectionDescription(connectionId: connection.id, ip: connection.ip, ipv6: connection.ipv6, port: connection.port, peerTag: connection.peerTag)
+private func callConnectionDescription(_ connection: CallSessionConnection) -> OngoingCallConnectionDescription? {
+ switch connection {
+ case let .reflector(reflector):
+ return OngoingCallConnectionDescription(connectionId: reflector.id, ip: reflector.ip, ipv6: reflector.ipv6, port: reflector.port, peerTag: reflector.peerTag)
+ case .webRtcReflector:
+ return nil
+ }
}
-private func callConnectionDescriptionWebrtc(_ connection: CallSessionConnection) -> OngoingCallConnectionDescriptionWebrtc {
- return OngoingCallConnectionDescriptionWebrtc(connectionId: connection.id, ip: connection.ip, ipv6: connection.ipv6, port: connection.port, peerTag: connection.peerTag)
+private func callConnectionDescriptionsWebrtc(_ connection: CallSessionConnection) -> [OngoingCallConnectionDescriptionWebrtc] {
+ switch connection {
+ case .reflector:
+ return []
+ case let .webRtcReflector(reflector):
+ var result: [OngoingCallConnectionDescriptionWebrtc] = []
+ if !reflector.ip.isEmpty {
+ result.append(OngoingCallConnectionDescriptionWebrtc(connectionId: reflector.id, hasStun: reflector.hasStun, hasTurn: reflector.hasTurn, ip: reflector.ip, port: reflector.port, username: reflector.username, password: reflector.password))
+ }
+ if !reflector.ipv6.isEmpty {
+ result.append(OngoingCallConnectionDescriptionWebrtc(connectionId: reflector.id, hasStun: reflector.hasStun, hasTurn: reflector.hasTurn, ip: reflector.ipv6, port: reflector.port, username: reflector.username, password: reflector.password))
+ }
+ return result
+ }
}
-/*private func callConnectionDescriptionWebrtcCustom(_ connection: CallSessionConnection) -> OngoingCallConnectionDescriptionWebrtcCustom {
- return OngoingCallConnectionDescriptionWebrtcCustom(connectionId: connection.id, ip: connection.ip, ipv6: connection.ipv6, port: connection.port, peerTag: connection.peerTag)
-}*/
-
private let callLogsLimit = 20
public func callLogNameForId(id: Int64, account: Account) -> String? {
@@ -103,20 +115,32 @@ public struct OngoingCallContextState: Equatable {
public enum VideoState: Equatable {
case notAvailable
- case possible
- case outgoingRequested
- case incomingRequested
+ case inactive
case active
+ case paused
}
public enum RemoteVideoState: Equatable {
case inactive
case active
+ case paused
+ }
+
+ public enum RemoteAudioState: Equatable {
+ case active
+ case muted
+ }
+
+ public enum RemoteBatteryLevel: Equatable {
+ case normal
+ case low
}
public let state: State
public let videoState: VideoState
public let remoteVideoState: RemoteVideoState
+ public let remoteAudioState: RemoteAudioState
+ public let remoteBatteryLevel: RemoteBatteryLevel
}
private final class OngoingCallThreadLocalContextQueueImpl: NSObject, OngoingCallThreadLocalContextQueue, OngoingCallThreadLocalContextQueueWebrtc /*, OngoingCallThreadLocalContextQueueWebrtcCustom*/ {
@@ -245,9 +269,11 @@ private func ongoingDataSavingForTypeWebrtc(_ type: VoiceCallDataSaving) -> Ongo
private protocol OngoingCallThreadLocalContextProtocol: class {
func nativeSetNetworkType(_ type: NetworkType)
func nativeSetIsMuted(_ value: Bool)
+ func nativeSetIsLowBatteryLevel(_ value: Bool)
func nativeRequestVideo(_ capturer: OngoingCallVideoCapturer)
- func nativeAcceptVideo(_ capturer: OngoingCallVideoCapturer)
+ func nativeDisableVideo()
func nativeStop(_ completion: @escaping (String?, Int64, Int64, Int64, Int64) -> Void)
+ func nativeBeginTermination()
func nativeDebugInfo() -> String
func nativeVersion() -> String
func nativeGetDerivedState() -> Data
@@ -270,14 +296,20 @@ extension OngoingCallThreadLocalContext: OngoingCallThreadLocalContextProtocol {
self.stop(completion)
}
+ func nativeBeginTermination() {
+ }
+
func nativeSetIsMuted(_ value: Bool) {
self.setIsMuted(value)
}
+ func nativeSetIsLowBatteryLevel(_ value: Bool) {
+ }
+
func nativeRequestVideo(_ capturer: OngoingCallVideoCapturer) {
}
- func nativeAcceptVideo(_ capturer: OngoingCallVideoCapturer) {
+ func nativeDisableVideo() {
}
func nativeSwitchVideoCamera() {
@@ -314,6 +346,14 @@ public final class OngoingCallVideoCapturer {
view: view,
setOnFirstFrameReceived: { [weak view] f in
view?.setOnFirstFrameReceived(f)
+ },
+ getOrientation: {
+ return .rotation0
+ },
+ setOnOrientationUpdated: { _ in
+ },
+ setOnIsMirroredUpdated: { [weak view] f in
+ view?.setOnIsMirroredUpdated(f)
}
))
} else {
@@ -336,16 +376,24 @@ extension OngoingCallThreadLocalContextWebrtc: OngoingCallThreadLocalContextProt
self.stop(completion)
}
+ func nativeBeginTermination() {
+ self.beginTermination()
+ }
+
func nativeSetIsMuted(_ value: Bool) {
self.setIsMuted(value)
}
+ func nativeSetIsLowBatteryLevel(_ value: Bool) {
+ self.setIsLowBatteryLevel(value)
+ }
+
func nativeRequestVideo(_ capturer: OngoingCallVideoCapturer) {
self.requestVideo(capturer.impl)
}
- func nativeAcceptVideo(_ capturer: OngoingCallVideoCapturer) {
- self.acceptVideo(capturer.impl)
+ func nativeDisableVideo() {
+ self.disableVideo()
}
func nativeDebugInfo() -> String {
@@ -395,16 +443,49 @@ private extension OngoingCallContextState.State {
}
}
+public enum OngoingCallVideoOrientation {
+ case rotation0
+ case rotation90
+ case rotation180
+ case rotation270
+}
+
+private extension OngoingCallVideoOrientation {
+ init(_ orientation: OngoingCallVideoOrientationWebrtc) {
+ switch orientation {
+ case .orientation0:
+ self = .rotation0
+ case .orientation90:
+ self = .rotation90
+ case .orientation180:
+ self = .rotation180
+ case .orientation270:
+ self = .rotation270
+ @unknown default:
+ self = .rotation0
+ }
+ }
+}
+
public final class OngoingCallContextPresentationCallVideoView {
public let view: UIView
- public let setOnFirstFrameReceived: ((() -> Void)?) -> Void
+ public let setOnFirstFrameReceived: (((Float) -> Void)?) -> Void
+ public let getOrientation: () -> OngoingCallVideoOrientation
+ public let setOnOrientationUpdated: (((OngoingCallVideoOrientation) -> Void)?) -> Void
+ public let setOnIsMirroredUpdated: (((Bool) -> Void)?) -> Void
public init(
view: UIView,
- setOnFirstFrameReceived: @escaping ((() -> Void)?) -> Void
+ setOnFirstFrameReceived: @escaping (((Float) -> Void)?) -> Void,
+ getOrientation: @escaping () -> OngoingCallVideoOrientation,
+ setOnOrientationUpdated: @escaping (((OngoingCallVideoOrientation) -> Void)?) -> Void,
+ setOnIsMirroredUpdated: @escaping (((Bool) -> Void)?) -> Void
) {
self.view = view
self.setOnFirstFrameReceived = setOnFirstFrameReceived
+ self.getOrientation = getOrientation
+ self.setOnOrientationUpdated = setOnOrientationUpdated
+ self.setOnIsMirroredUpdated = setOnIsMirroredUpdated
}
}
@@ -458,19 +539,19 @@ public final class OngoingCallContext {
public static var maxLayer: Int32 {
return OngoingCallThreadLocalContext.maxLayer()
- //return max(OngoingCallThreadLocalContext.maxLayer(), OngoingCallThreadLocalContextWebrtc.maxLayer())
}
- public static func versions(includeExperimental: Bool) -> [String] {
- var result: [String] = [OngoingCallThreadLocalContext.version()]
+ public static func versions(includeExperimental: Bool, includeReference: Bool) -> [(version: String, supportsVideo: Bool)] {
+ var result: [(version: String, supportsVideo: Bool)] = [(OngoingCallThreadLocalContext.version(), false)]
if includeExperimental {
- result.append(OngoingCallThreadLocalContextWebrtc.version())
- //result.append(OngoingCallThreadLocalContextWebrtcCustom.version())
+ result.append(contentsOf: OngoingCallThreadLocalContextWebrtc.versions(withIncludeReference: includeReference).map { version -> (version: String, supportsVideo: Bool) in
+ return (version, true)
+ })
}
return result
}
- public init(account: Account, callSessionManager: CallSessionManager, internalId: CallSessionInternalId, proxyServer: ProxyServerSettings?, auxiliaryServers: [AuxiliaryServer], initialNetworkType: NetworkType, updatedNetworkType: Signal, serializedData: String?, dataSaving: VoiceCallDataSaving, derivedState: VoipDerivedState, key: Data, isOutgoing: Bool, video: OngoingCallVideoCapturer?, connections: CallSessionConnectionSet, maxLayer: Int32, version: String, allowP2P: Bool, audioSessionActive: Signal, logName: String) {
+ public init(account: Account, callSessionManager: CallSessionManager, internalId: CallSessionInternalId, proxyServer: ProxyServerSettings?, initialNetworkType: NetworkType, updatedNetworkType: Signal, serializedData: String?, dataSaving: VoiceCallDataSaving, derivedState: VoipDerivedState, key: Data, isOutgoing: Bool, video: OngoingCallVideoCapturer?, connections: CallSessionConnectionSet, maxLayer: Int32, version: String, allowP2P: Bool, audioSessionActive: Signal, logName: String, preferredVideoCodec: String?) {
let _ = setupLogs
OngoingCallThreadLocalContext.applyServerConfig(serializedData)
@@ -489,7 +570,7 @@ public final class OngoingCallContext {
|> take(1)
|> deliverOn(queue)).start(next: { [weak self] _ in
if let strongSelf = self {
- if version == OngoingCallThreadLocalContextWebrtc.version() {
+ if OngoingCallThreadLocalContextWebrtc.versions(withIncludeReference: true).contains(version) {
var voipProxyServer: VoipProxyServerWebrtc?
if let proxyServer = proxyServer {
switch proxyServer.connection {
@@ -499,33 +580,29 @@ public final class OngoingCallContext {
break
}
}
- var rtcServers: [VoipRtcServerWebrtc] = []
- for server in auxiliaryServers {
- switch server.connection {
- case .stun:
- rtcServers.append(VoipRtcServerWebrtc(
- host: server.host,
- port: Int32(clamping: server.port),
- username: "",
- password: "",
- isTurn: false
- ))
- case let .turn(username, password):
- rtcServers.append(VoipRtcServerWebrtc(
- host: server.host,
- port: Int32(clamping: server.port),
- username: username,
- password: password,
- isTurn: true
- ))
+
+ let screenSize = UIScreen.main.bounds.size
+ let portraitSize = CGSize(width: min(screenSize.width, screenSize.height), height: max(screenSize.width, screenSize.height))
+ let preferredAspectRatio = portraitSize.width / portraitSize.height
+
+
+ let unfilteredConnections = [connections.primary] + connections.alternatives
+ var processedConnections: [CallSessionConnection] = []
+ var filteredConnections: [OngoingCallConnectionDescriptionWebrtc] = []
+ for connection in unfilteredConnections {
+ if processedConnections.contains(connection) {
+ continue
}
+ processedConnections.append(connection)
+ filteredConnections.append(contentsOf: callConnectionDescriptionsWebrtc(connection))
}
- let context = OngoingCallThreadLocalContextWebrtc(queue: OngoingCallThreadLocalContextQueueImpl(queue: queue), proxy: voipProxyServer, rtcServers: rtcServers, networkType: ongoingNetworkTypeForTypeWebrtc(initialNetworkType), dataSaving: ongoingDataSavingForTypeWebrtc(dataSaving), derivedState: derivedState.data, key: key, isOutgoing: isOutgoing, primaryConnection: callConnectionDescriptionWebrtc(connections.primary), alternativeConnections: connections.alternatives.map(callConnectionDescriptionWebrtc), maxLayer: maxLayer, allowP2P: allowP2P, logPath: logPath, sendSignalingData: { [weak callSessionManager] data in
+
+ let context = OngoingCallThreadLocalContextWebrtc(version: version, queue: OngoingCallThreadLocalContextQueueImpl(queue: queue), proxy: voipProxyServer, networkType: ongoingNetworkTypeForTypeWebrtc(initialNetworkType), dataSaving: ongoingDataSavingForTypeWebrtc(dataSaving), derivedState: derivedState.data, key: key, isOutgoing: isOutgoing, connections: filteredConnections, maxLayer: maxLayer, allowP2P: allowP2P, logPath: logPath, sendSignalingData: { [weak callSessionManager] data in
callSessionManager?.sendSignalingData(internalId: internalId, data: data)
- }, videoCapturer: video?.impl)
+ }, videoCapturer: video?.impl, preferredAspectRatio: Float(preferredAspectRatio), preferredVideoCodec: preferredVideoCodec)
strongSelf.contextRef = Unmanaged.passRetained(OngoingCallThreadLocalContextHolder(context))
- context.stateChanged = { [weak callSessionManager] state, videoState, remoteVideoState in
+ context.stateChanged = { [weak callSessionManager] state, videoState, remoteVideoState, remoteAudioState, remoteBatteryLevel, _ in
queue.async {
guard let strongSelf = self else {
return
@@ -533,14 +610,12 @@ public final class OngoingCallContext {
let mappedState = OngoingCallContextState.State(state)
let mappedVideoState: OngoingCallContextState.VideoState
switch videoState {
- case .possible:
- mappedVideoState = .possible
- case .incomingRequested:
- mappedVideoState = .incomingRequested
- case .outgoingRequested:
- mappedVideoState = .outgoingRequested
+ case .inactive:
+ mappedVideoState = .inactive
case .active:
mappedVideoState = .active
+ case .paused:
+ mappedVideoState = .paused
@unknown default:
mappedVideoState = .notAvailable
}
@@ -550,16 +625,37 @@ public final class OngoingCallContext {
mappedRemoteVideoState = .inactive
case .active:
mappedRemoteVideoState = .active
+ case .paused:
+ mappedRemoteVideoState = .paused
@unknown default:
mappedRemoteVideoState = .inactive
}
+ let mappedRemoteAudioState: OngoingCallContextState.RemoteAudioState
+ switch remoteAudioState {
+ case .active:
+ mappedRemoteAudioState = .active
+ case .muted:
+ mappedRemoteAudioState = .muted
+ @unknown default:
+ mappedRemoteAudioState = .active
+ }
+ let mappedRemoteBatteryLevel: OngoingCallContextState.RemoteBatteryLevel
+ switch remoteBatteryLevel {
+ case .normal:
+ mappedRemoteBatteryLevel = .normal
+ case .low:
+ mappedRemoteBatteryLevel = .low
+ @unknown default:
+ mappedRemoteBatteryLevel = .normal
+ }
if case .active = mappedVideoState, !strongSelf.didReportCallAsVideo {
strongSelf.didReportCallAsVideo = true
callSessionManager?.updateCallType(internalId: internalId, type: .video)
}
- strongSelf.contextState.set(.single(OngoingCallContextState(state: mappedState, videoState: mappedVideoState, remoteVideoState: mappedRemoteVideoState)))
+ strongSelf.contextState.set(.single(OngoingCallContextState(state: mappedState, videoState: mappedVideoState, remoteVideoState: mappedRemoteVideoState, remoteAudioState: mappedRemoteAudioState, remoteBatteryLevel: mappedRemoteBatteryLevel)))
}
}
+ strongSelf.receptionPromise.set(.single(4))
context.signalBarsChanged = { signalBars in
self?.receptionPromise.set(.single(signalBars))
}
@@ -580,11 +676,11 @@ public final class OngoingCallContext {
break
}
}
- let context = OngoingCallThreadLocalContext(queue: OngoingCallThreadLocalContextQueueImpl(queue: queue), proxy: voipProxyServer, networkType: ongoingNetworkTypeForType(initialNetworkType), dataSaving: ongoingDataSavingForType(dataSaving), derivedState: derivedState.data, key: key, isOutgoing: isOutgoing, primaryConnection: callConnectionDescription(connections.primary), alternativeConnections: connections.alternatives.map(callConnectionDescription), maxLayer: maxLayer, allowP2P: allowP2P, logPath: logPath)
+ let context = OngoingCallThreadLocalContext(queue: OngoingCallThreadLocalContextQueueImpl(queue: queue), proxy: voipProxyServer, networkType: ongoingNetworkTypeForType(initialNetworkType), dataSaving: ongoingDataSavingForType(dataSaving), derivedState: derivedState.data, key: key, isOutgoing: isOutgoing, primaryConnection: callConnectionDescription(connections.primary)!, alternativeConnections: connections.alternatives.compactMap(callConnectionDescription), maxLayer: maxLayer, allowP2P: allowP2P, logPath: logPath)
strongSelf.contextRef = Unmanaged.passRetained(OngoingCallThreadLocalContextHolder(context))
context.stateChanged = { state in
- self?.contextState.set(.single(OngoingCallContextState(state: OngoingCallContextState.State(state), videoState: .notAvailable, remoteVideoState: .inactive)))
+ self?.contextState.set(.single(OngoingCallContextState(state: OngoingCallContextState.State(state), videoState: .notAvailable, remoteVideoState: .inactive, remoteAudioState: .active, remoteBatteryLevel: .normal)))
}
context.signalBarsChanged = { signalBars in
self?.receptionPromise.set(.single(signalBars))
@@ -621,7 +717,7 @@ public final class OngoingCallContext {
self.audioSessionDisposable.dispose()
self.networkTypeDisposable?.dispose()
}
-
+
private func withContext(_ f: @escaping (OngoingCallThreadLocalContextProtocol) -> Void) {
self.queue.async {
if let contextRef = self.contextRef {
@@ -631,11 +727,29 @@ public final class OngoingCallContext {
}
}
+ private func withContextThenDeallocate(_ f: @escaping (OngoingCallThreadLocalContextProtocol) -> Void) {
+ self.queue.async {
+ if let contextRef = self.contextRef {
+ let context = contextRef.takeUnretainedValue()
+ f(context.context)
+
+ self.contextRef?.release()
+ self.contextRef = nil
+ }
+ }
+ }
+
+ public func beginTermination() {
+ self.withContext { context in
+ context.nativeBeginTermination()
+ }
+ }
+
public func stop(callId: CallId? = nil, sendDebugLogs: Bool = false, debugLogValue: Promise) {
let account = self.account
let logPath = self.logPath
- self.withContext { context in
+ self.withContextThenDeallocate { context in
context.nativeStop { debugLog, bytesSentWifi, bytesReceivedWifi, bytesSentMobile, bytesReceivedMobile in
debugLogValue.set(.single(debugLog))
let delta = NetworkUsageStatsConnectionsEntry(
@@ -674,15 +788,21 @@ public final class OngoingCallContext {
}
}
+ public func setIsLowBatteryLevel(_ value: Bool) {
+ self.withContext { context in
+ context.nativeSetIsLowBatteryLevel(value)
+ }
+ }
+
public func requestVideo(_ capturer: OngoingCallVideoCapturer) {
self.withContext { context in
context.nativeRequestVideo(capturer)
}
}
- public func acceptVideo(_ capturer: OngoingCallVideoCapturer) {
+ public func disableVideo() {
self.withContext { context in
- context.nativeAcceptVideo(capturer)
+ context.nativeDisableVideo()
}
}
@@ -709,6 +829,23 @@ public final class OngoingCallContext {
view: view,
setOnFirstFrameReceived: { [weak view] f in
view?.setOnFirstFrameReceived(f)
+ },
+ getOrientation: { [weak view] in
+ if let view = view {
+ return OngoingCallVideoOrientation(view.orientation)
+ } else {
+ return .rotation0
+ }
+ },
+ setOnOrientationUpdated: { [weak view] f in
+ view?.setOnOrientationUpdated { value in
+ f?(OngoingCallVideoOrientation(value))
+ }
+ },
+ setOnIsMirroredUpdated: { [weak view] f in
+ view?.setOnIsMirroredUpdated { value in
+ f?(value)
+ }
}
))
} else {
diff --git a/submodules/TemporaryCachedPeerDataManager/Sources/PeerChannelMemberCategoriesContextsManager.swift b/submodules/TemporaryCachedPeerDataManager/Sources/PeerChannelMemberCategoriesContextsManager.swift
index df07f60616..b5e386a3a8 100644
--- a/submodules/TemporaryCachedPeerDataManager/Sources/PeerChannelMemberCategoriesContextsManager.swift
+++ b/submodules/TemporaryCachedPeerDataManager/Sources/PeerChannelMemberCategoriesContextsManager.swift
@@ -43,7 +43,6 @@ private final class ProfileDataPhotoPreloadContext {
let disposable: Disposable
var value: Any?
- var skipNext = false
var emptyTimer: SwiftSignalKit.Timer?
init(disposable: Disposable) {
@@ -211,9 +210,6 @@ private final class PeerChannelMemberCategoriesContextsManagerImpl {
let context: ProfileDataPhotoPreloadContext
if let current = self.profileDataPhotoPreloadContexts[peerId] {
context = current
- if let _ = context.value {
- context.skipNext = true
- }
} else {
let disposable = MetaDisposable()
context = ProfileDataPhotoPreloadContext(disposable: disposable)
@@ -223,10 +219,6 @@ private final class PeerChannelMemberCategoriesContextsManagerImpl {
guard let context = context else {
return
}
- if context.skipNext {
- context.skipNext = false
- return
- }
context.value = value
for f in context.subscribers.copyItems() {
f(value)
diff --git a/submodules/TgVoipWebrtc/BUCK b/submodules/TgVoipWebrtc/BUCK
index 3333a6f854..27191bebbe 100644
--- a/submodules/TgVoipWebrtc/BUCK
+++ b/submodules/TgVoipWebrtc/BUCK
@@ -14,6 +14,7 @@ static_library(
"tgcalls/tgcalls/legacy/**",
"tgcalls/tgcalls/platform/tdesktop/**",
"tgcalls/tgcalls/platform/windows/**",
+ "tgcalls/tgcalls/platform/android/**",
"tgcalls/tgcalls/platform/darwin/VideoCameraCapturerMac.*",
"tgcalls/tgcalls/platform/darwin/VideoMetalViewMac.*",
]),
diff --git a/submodules/TgVoipWebrtc/BUILD b/submodules/TgVoipWebrtc/BUILD
index a71ebfe7a6..edcdb48f07 100644
--- a/submodules/TgVoipWebrtc/BUILD
+++ b/submodules/TgVoipWebrtc/BUILD
@@ -14,6 +14,7 @@ objc_library(
], exclude = [
"tgcalls/tgcalls/legacy/**",
"tgcalls/tgcalls/platform/tdesktop/**",
+ "tgcalls/tgcalls/platform/android/**",
"tgcalls/tgcalls/platform/windows/**",
"tgcalls/tgcalls/platform/darwin/VideoCameraCapturerMac.*",
"tgcalls/tgcalls/platform/darwin/VideoMetalViewMac.*",
diff --git a/submodules/TgVoipWebrtc/PublicHeaders/TgVoip/OngoingCallThreadLocalContext.h b/submodules/TgVoipWebrtc/PublicHeaders/TgVoip/OngoingCallThreadLocalContext.h
index 7915abba8b..13f2a0f46c 100644
--- a/submodules/TgVoipWebrtc/PublicHeaders/TgVoip/OngoingCallThreadLocalContext.h
+++ b/submodules/TgVoipWebrtc/PublicHeaders/TgVoip/OngoingCallThreadLocalContext.h
@@ -13,12 +13,14 @@
@interface OngoingCallConnectionDescriptionWebrtc : NSObject
@property (nonatomic, readonly) int64_t connectionId;
+@property (nonatomic, readonly) bool hasStun;
+@property (nonatomic, readonly) bool hasTurn;
@property (nonatomic, strong, readonly) NSString * _Nonnull ip;
-@property (nonatomic, strong, readonly) NSString * _Nonnull ipv6;
@property (nonatomic, readonly) int32_t port;
-@property (nonatomic, strong, readonly) NSData * _Nonnull peerTag;
+@property (nonatomic, strong, readonly) NSString * _Nonnull username;
+@property (nonatomic, strong, readonly) NSString * _Nonnull password;
-- (instancetype _Nonnull)initWithConnectionId:(int64_t)connectionId ip:(NSString * _Nonnull)ip ipv6:(NSString * _Nonnull)ipv6 port:(int32_t)port peerTag:(NSData * _Nonnull)peerTag;
+- (instancetype _Nonnull)initWithConnectionId:(int64_t)connectionId hasStun:(bool)hasStun hasTurn:(bool)hasTurn ip:(NSString * _Nonnull)ip port:(int32_t)port username:(NSString * _Nonnull)username password:(NSString * _Nonnull)password;
@end
@@ -30,15 +32,32 @@ typedef NS_ENUM(int32_t, OngoingCallStateWebrtc) {
};
typedef NS_ENUM(int32_t, OngoingCallVideoStateWebrtc) {
- OngoingCallVideoStatePossible,
- OngoingCallVideoStateOutgoingRequested,
- OngoingCallVideoStateIncomingRequested,
- OngoingCallVideoStateActive
+ OngoingCallVideoStateInactive,
+ OngoingCallVideoStateActive,
+ OngoingCallVideoStatePaused
};
typedef NS_ENUM(int32_t, OngoingCallRemoteVideoStateWebrtc) {
OngoingCallRemoteVideoStateInactive,
- OngoingCallRemoteVideoStateActive
+ OngoingCallRemoteVideoStateActive,
+ OngoingCallRemoteVideoStatePaused
+};
+
+typedef NS_ENUM(int32_t, OngoingCallRemoteAudioStateWebrtc) {
+ OngoingCallRemoteAudioStateMuted,
+ OngoingCallRemoteAudioStateActive,
+};
+
+typedef NS_ENUM(int32_t, OngoingCallRemoteBatteryLevelWebrtc) {
+ OngoingCallRemoteBatteryLevelNormal,
+ OngoingCallRemoteBatteryLevelLow
+};
+
+typedef NS_ENUM(int32_t, OngoingCallVideoOrientationWebrtc) {
+ OngoingCallVideoOrientation0,
+ OngoingCallVideoOrientation90,
+ OngoingCallVideoOrientation180,
+ OngoingCallVideoOrientation270
};
typedef NS_ENUM(int32_t, OngoingCallNetworkTypeWebrtc) {
@@ -73,22 +92,16 @@ typedef NS_ENUM(int32_t, OngoingCallDataSavingWebrtc) {
@end
-@interface VoipRtcServerWebrtc : NSObject
-
-@property (nonatomic, strong, readonly) NSString * _Nonnull host;
-@property (nonatomic, readonly) int32_t port;
-@property (nonatomic, strong, readonly) NSString * _Nullable username;
-@property (nonatomic, strong, readonly) NSString * _Nullable password;
-@property (nonatomic, readonly) bool isTurn;
-
-- (instancetype _Nonnull)initWithHost:(NSString * _Nonnull)host port:(int32_t)port username:(NSString * _Nullable)username password:(NSString * _Nullable)password isTurn:(bool)isTurn;
-
-@end
-
@protocol OngoingCallThreadLocalContextWebrtcVideoView
-- (void)setOnFirstFrameReceived:(void (^ _Nullable)())onFirstFrameReceived;
+@property (nonatomic, readonly) OngoingCallVideoOrientationWebrtc orientation;
+- (void)setOnFirstFrameReceived:(void (^ _Nullable)(float))onFirstFrameReceived;
+- (void)setOnOrientationUpdated:(void (^ _Nullable)(OngoingCallVideoOrientationWebrtc))onOrientationUpdated;
+- (void)setOnIsMirroredUpdated:(void (^ _Nullable)(bool))onIsMirroredUpdated;
+#ifdef WEBRTC_MAC
+- (void)setVideoContentMode:(CALayerContentsGravity _Nonnull )mode;
+#endif
@end
@interface OngoingCallThreadLocalContextVideoCapturer : NSObject
@@ -107,12 +120,14 @@ typedef NS_ENUM(int32_t, OngoingCallDataSavingWebrtc) {
+ (void)setupLoggingFunction:(void (* _Nullable)(NSString * _Nullable))loggingFunction;
+ (void)applyServerConfig:(NSString * _Nullable)data;
+ (int32_t)maxLayer;
-+ (NSString * _Nonnull)version;
++ (NSArray * _Nonnull)versionsWithIncludeReference:(bool)includeReference;
-@property (nonatomic, copy) void (^ _Nullable stateChanged)(OngoingCallStateWebrtc, OngoingCallVideoStateWebrtc, OngoingCallRemoteVideoStateWebrtc);
+@property (nonatomic, copy) void (^ _Nullable stateChanged)(OngoingCallStateWebrtc, OngoingCallVideoStateWebrtc, OngoingCallRemoteVideoStateWebrtc, OngoingCallRemoteAudioStateWebrtc, OngoingCallRemoteBatteryLevelWebrtc, float);
@property (nonatomic, copy) void (^ _Nullable signalBarsChanged)(int32_t);
-- (instancetype _Nonnull)initWithQueue:(id _Nonnull)queue proxy:(VoipProxyServerWebrtc * _Nullable)proxy rtcServers:(NSArray * _Nonnull)rtcServers networkType:(OngoingCallNetworkTypeWebrtc)networkType dataSaving:(OngoingCallDataSavingWebrtc)dataSaving derivedState:(NSData * _Nonnull)derivedState key:(NSData * _Nonnull)key isOutgoing:(bool)isOutgoing primaryConnection:(OngoingCallConnectionDescriptionWebrtc * _Nonnull)primaryConnection alternativeConnections:(NSArray * _Nonnull)alternativeConnections maxLayer:(int32_t)maxLayer allowP2P:(BOOL)allowP2P logPath:(NSString * _Nonnull)logPath sendSignalingData:(void (^ _Nonnull)(NSData * _Nonnull))sendSignalingData videoCapturer:(OngoingCallThreadLocalContextVideoCapturer * _Nullable)videoCapturer;
+- (instancetype _Nonnull)initWithVersion:(NSString * _Nonnull)version queue:(id _Nonnull)queue proxy:(VoipProxyServerWebrtc * _Nullable)proxy networkType:(OngoingCallNetworkTypeWebrtc)networkType dataSaving:(OngoingCallDataSavingWebrtc)dataSaving derivedState:(NSData * _Nonnull)derivedState key:(NSData * _Nonnull)key isOutgoing:(bool)isOutgoing connections:(NSArray * _Nonnull)connections maxLayer:(int32_t)maxLayer allowP2P:(BOOL)allowP2P logPath:(NSString * _Nonnull)logPath sendSignalingData:(void (^ _Nonnull)(NSData * _Nonnull))sendSignalingData videoCapturer:(OngoingCallThreadLocalContextVideoCapturer * _Nullable)videoCapturer preferredAspectRatio:(float)preferredAspectRatio preferredVideoCodec:(NSString * _Nullable)preferredVideoCodec;
+
+- (void)beginTermination;
- (void)stop:(void (^_Nullable)(NSString * _Nullable debugLog, int64_t bytesSentWifi, int64_t bytesReceivedWifi, int64_t bytesSentMobile, int64_t bytesReceivedMobile))completion;
- (bool)needRate;
@@ -122,10 +137,11 @@ typedef NS_ENUM(int32_t, OngoingCallDataSavingWebrtc) {
- (NSData * _Nonnull)getDerivedState;
- (void)setIsMuted:(bool)isMuted;
+- (void)setIsLowBatteryLevel:(bool)isLowBatteryLevel;
- (void)setNetworkType:(OngoingCallNetworkTypeWebrtc)networkType;
- (void)makeIncomingVideoView:(void (^_Nonnull)(UIView * _Nullable))completion;
- (void)requestVideo:(OngoingCallThreadLocalContextVideoCapturer * _Nullable)videoCapturer;
-- (void)acceptVideo:(OngoingCallThreadLocalContextVideoCapturer * _Nullable)videoCapturer;
+- (void)disableVideo;
- (void)addSignalingData:(NSData * _Nonnull)data;
@end
diff --git a/submodules/TgVoipWebrtc/Sources/OngoingCallThreadLocalContext.mm b/submodules/TgVoipWebrtc/Sources/OngoingCallThreadLocalContext.mm
index 53d28d7bde..5739f30aeb 100644
--- a/submodules/TgVoipWebrtc/Sources/OngoingCallThreadLocalContext.mm
+++ b/submodules/TgVoipWebrtc/Sources/OngoingCallThreadLocalContext.mm
@@ -7,12 +7,16 @@
#import "Instance.h"
#import "InstanceImpl.h"
+#import "reference/InstanceImplReference.h"
+
#import "VideoCaptureInterface.h"
#ifndef WEBRTC_IOS
#import "platform/darwin/VideoMetalViewMac.h"
#define GLVideoView VideoMetalView
#define UIViewContentModeScaleAspectFill kCAGravityResizeAspectFill
+#define UIViewContentModeScaleAspect kCAGravityResizeAspect
+
#else
#import "platform/darwin/VideoMetalView.h"
#import "platform/darwin/GLVideoView.h"
@@ -20,14 +24,16 @@
@implementation OngoingCallConnectionDescriptionWebrtc
-- (instancetype _Nonnull)initWithConnectionId:(int64_t)connectionId ip:(NSString * _Nonnull)ip ipv6:(NSString * _Nonnull)ipv6 port:(int32_t)port peerTag:(NSData * _Nonnull)peerTag {
+- (instancetype _Nonnull)initWithConnectionId:(int64_t)connectionId hasStun:(bool)hasStun hasTurn:(bool)hasTurn ip:(NSString * _Nonnull)ip port:(int32_t)port username:(NSString * _Nonnull)username password:(NSString * _Nonnull)password {
self = [super init];
if (self != nil) {
_connectionId = connectionId;
+ _hasStun = hasStun;
+ _hasTurn = hasTurn;
_ip = ip;
- _ipv6 = ipv6;
_port = port;
- _peerTag = peerTag;
+ _username = username;
+ _password = password;
}
return self;
}
@@ -40,20 +46,86 @@
@end
-@interface VideoMetalView (VideoViewImpl)
+@protocol OngoingCallThreadLocalContextWebrtcVideoViewImpl
+
+@property (nonatomic, readwrite) OngoingCallVideoOrientationWebrtc orientation;
+
+@end
+
+@interface VideoMetalView (VideoViewImpl)
+
+@property (nonatomic, readwrite) OngoingCallVideoOrientationWebrtc orientation;
@end
@implementation VideoMetalView (VideoViewImpl)
+- (OngoingCallVideoOrientationWebrtc)orientation {
+ return (OngoingCallVideoOrientationWebrtc)self.internalOrientation;
+}
+
+- (void)setOrientation:(OngoingCallVideoOrientationWebrtc)orientation {
+ [self setInternalOrientation:(int)orientation];
+}
+
+- (void)setOnOrientationUpdated:(void (^ _Nullable)(OngoingCallVideoOrientationWebrtc))onOrientationUpdated {
+ if (onOrientationUpdated) {
+ [self internalSetOnOrientationUpdated:^(int value) {
+ onOrientationUpdated((OngoingCallVideoOrientationWebrtc)value);
+ }];
+ } else {
+ [self internalSetOnOrientationUpdated:nil];
+ }
+}
+
+- (void)setOnIsMirroredUpdated:(void (^ _Nullable)(bool))onIsMirroredUpdated {
+ if (onIsMirroredUpdated) {
+ [self internalSetOnIsMirroredUpdated:^(bool value) {
+ onIsMirroredUpdated(value);
+ }];
+ } else {
+ [self internalSetOnIsMirroredUpdated:nil];
+ }
+}
+
@end
-@interface GLVideoView (VideoViewImpl)
+@interface GLVideoView (VideoViewImpl)
+
+@property (nonatomic, readwrite) OngoingCallVideoOrientationWebrtc orientation;
@end
@implementation GLVideoView (VideoViewImpl)
+- (OngoingCallVideoOrientationWebrtc)orientation {
+ return (OngoingCallVideoOrientationWebrtc)self.internalOrientation;
+}
+
+- (void)setOrientation:(OngoingCallVideoOrientationWebrtc)orientation {
+ [self setInternalOrientation:(int)orientation];
+}
+
+- (void)setOnOrientationUpdated:(void (^ _Nullable)(OngoingCallVideoOrientationWebrtc))onOrientationUpdated {
+ if (onOrientationUpdated) {
+ [self internalSetOnOrientationUpdated:^(int value) {
+ onOrientationUpdated((OngoingCallVideoOrientationWebrtc)value);
+ }];
+ } else {
+ [self internalSetOnOrientationUpdated:nil];
+ }
+}
+
+- (void)setOnIsMirroredUpdated:(void (^ _Nullable)(bool))onIsMirroredUpdated {
+ if (onIsMirroredUpdated) {
+ [self internalSetOnIsMirroredUpdated:^(bool value) {
+ onIsMirroredUpdated(value);
+ }];
+ } else {
+ [self internalSetOnIsMirroredUpdated:nil];
+ }
+}
+
@end
@implementation OngoingCallThreadLocalContextVideoCapturer
@@ -66,12 +138,15 @@
return self;
}
+- (void)dealloc {
+}
+
- (void)switchVideoCamera {
_interface->switchCamera();
}
- (void)setIsVideoEnabled:(bool)isVideoEnabled {
- _interface->setIsVideoEnabled(isVideoEnabled);
+ _interface->setState(isVideoEnabled ? tgcalls::VideoState::Active : tgcalls::VideoState::Paused);
}
- (std::shared_ptr)getInterface {
@@ -86,14 +161,14 @@
remoteRenderer.videoContentMode = UIViewContentModeScaleAspectFill;
std::shared_ptr> sink = [remoteRenderer getSink];
- interface->setVideoOutput(sink);
+ interface->setOutput(sink);
completion(remoteRenderer);
} else {
GLVideoView *remoteRenderer = [[GLVideoView alloc] initWithFrame:CGRectZero];
std::shared_ptr> sink = [remoteRenderer getSink];
- interface->setVideoOutput(sink);
+ interface->setOutput(sink);
completion(remoteRenderer);
}
@@ -102,7 +177,26 @@
@end
+@interface OngoingCallThreadLocalContextWebrtcTerminationResult : NSObject
+
+@property (nonatomic, readonly) tgcalls::FinalState finalState;
+
+@end
+
+@implementation OngoingCallThreadLocalContextWebrtcTerminationResult
+
+- (instancetype)initWithFinalState:(tgcalls::FinalState)finalState {
+ self = [super init];
+ if (self != nil) {
+ _finalState = finalState;
+ }
+ return self;
+}
+
+@end
+
@interface OngoingCallThreadLocalContextWebrtc () {
+ NSString *_version;
id _queue;
int32_t _contextId;
@@ -117,16 +211,23 @@
OngoingCallStateWebrtc _state;
OngoingCallVideoStateWebrtc _videoState;
bool _connectedOnce;
+ OngoingCallRemoteBatteryLevelWebrtc _remoteBatteryLevel;
OngoingCallRemoteVideoStateWebrtc _remoteVideoState;
+ OngoingCallRemoteAudioStateWebrtc _remoteAudioState;
+ OngoingCallVideoOrientationWebrtc _remoteVideoOrientation;
+ __weak UIView *_currentRemoteVideoRenderer;
OngoingCallThreadLocalContextVideoCapturer *_videoCapturer;
int32_t _signalBars;
NSData *_lastDerivedState;
void (^_sendSignalingData)(NSData *);
+
+ float _remotePreferredAspectRatio;
+
}
-- (void)controllerStateChanged:(tgcalls::State)state videoState:(OngoingCallVideoStateWebrtc)videoState;
+- (void)controllerStateChanged:(tgcalls::State)state;
- (void)signalBarsChanged:(int32_t)signalBars;
@end
@@ -146,22 +247,6 @@
@end
-@implementation VoipRtcServerWebrtc
-
-- (instancetype _Nonnull)initWithHost:(NSString * _Nonnull)host port:(int32_t)port username:(NSString * _Nullable)username password:(NSString * _Nullable)password isTurn:(bool)isTurn {
- self = [super init];
- if (self != nil) {
- _host = host;
- _port = port;
- _username = username;
- _password = password;
- _isTurn = isTurn;
- }
- return self;
-}
-
-@end
-
static tgcalls::NetworkType callControllerNetworkTypeForType(OngoingCallNetworkTypeWebrtc type) {
switch (type) {
case OngoingCallNetworkTypeWifi:
@@ -213,30 +298,46 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL;
return 92;
}
-+ (NSString *)version {
- return @"2.7.7";
++ (NSArray * _Nonnull)versionsWithIncludeReference:(bool)__unused includeReference {
+ return @[@"2.7.7", @"3.0.0"];
}
-- (instancetype _Nonnull)initWithQueue:(id _Nonnull)queue proxy:(VoipProxyServerWebrtc * _Nullable)proxy rtcServers:(NSArray * _Nonnull)rtcServers networkType:(OngoingCallNetworkTypeWebrtc)networkType dataSaving:(OngoingCallDataSavingWebrtc)dataSaving derivedState:(NSData * _Nonnull)derivedState key:(NSData * _Nonnull)key isOutgoing:(bool)isOutgoing primaryConnection:(OngoingCallConnectionDescriptionWebrtc * _Nonnull)primaryConnection alternativeConnections:(NSArray * _Nonnull)alternativeConnections maxLayer:(int32_t)maxLayer allowP2P:(BOOL)allowP2P logPath:(NSString * _Nonnull)logPath sendSignalingData:(void (^)(NSData * _Nonnull))sendSignalingData videoCapturer:(OngoingCallThreadLocalContextVideoCapturer * _Nullable)videoCapturer {
++ (tgcalls::ProtocolVersion)protocolVersionFromLibraryVersion:(NSString *)version {
+ if ([version isEqualToString:@"2.7.7"]) {
+ return tgcalls::ProtocolVersion::V0;
+ } else if ([version isEqualToString:@"3.0.0"]) {
+ return tgcalls::ProtocolVersion::V1;
+ } else {
+ return tgcalls::ProtocolVersion::V0;
+ }
+}
+
+- (instancetype _Nonnull)initWithVersion:(NSString * _Nonnull)version queue:(id _Nonnull)queue proxy:(VoipProxyServerWebrtc * _Nullable)proxy networkType:(OngoingCallNetworkTypeWebrtc)networkType dataSaving:(OngoingCallDataSavingWebrtc)dataSaving derivedState:(NSData * _Nonnull)derivedState key:(NSData * _Nonnull)key isOutgoing:(bool)isOutgoing connections:(NSArray * _Nonnull)connections maxLayer:(int32_t)maxLayer allowP2P:(BOOL)allowP2P logPath:(NSString * _Nonnull)logPath sendSignalingData:(void (^)(NSData * _Nonnull))sendSignalingData videoCapturer:(OngoingCallThreadLocalContextVideoCapturer * _Nullable)videoCapturer preferredAspectRatio:(float)preferredAspectRatio preferredVideoCodec:(NSString * _Nullable)preferredVideoCodec {
self = [super init];
if (self != nil) {
+ _version = version;
_queue = queue;
assert([queue isCurrent]);
+ assert([[OngoingCallThreadLocalContextWebrtc versionsWithIncludeReference:true] containsObject:version]);
+
_callReceiveTimeout = 20.0;
_callRingTimeout = 90.0;
_callConnectTimeout = 30.0;
_callPacketTimeout = 10.0;
+ _remotePreferredAspectRatio = 0;
_networkType = networkType;
_sendSignalingData = [sendSignalingData copy];
_videoCapturer = videoCapturer;
if (videoCapturer != nil) {
- _videoState = OngoingCallVideoStateOutgoingRequested;
- _remoteVideoState = OngoingCallRemoteVideoStateActive;
+ _videoState = OngoingCallVideoStateActive;
} else {
- _videoState = OngoingCallVideoStatePossible;
- _remoteVideoState = OngoingCallRemoteVideoStateInactive;
+ _videoState = OngoingCallVideoStateInactive;
}
+ _remoteVideoState = OngoingCallRemoteVideoStateInactive;
+ _remoteAudioState = OngoingCallRemoteAudioStateActive;
+
+ _remoteVideoOrientation = OngoingCallVideoOrientation0;
std::vector derivedStateValue;
derivedStateValue.resize(derivedState.length);
@@ -253,41 +354,33 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL;
}
std::vector parsedRtcServers;
- for (VoipRtcServerWebrtc *server in rtcServers) {
- parsedRtcServers.push_back((tgcalls::RtcServer){
- .host = server.host.UTF8String,
- .port = (uint16_t)server.port,
- .login = server.username.UTF8String,
- .password = server.password.UTF8String,
- .isTurn = server.isTurn
- });
+ for (OngoingCallConnectionDescriptionWebrtc *connection in connections) {
+ if (connection.hasStun) {
+ parsedRtcServers.push_back((tgcalls::RtcServer){
+ .host = connection.ip.UTF8String,
+ .port = (uint16_t)connection.port,
+ .login = "",
+ .password = "",
+ .isTurn = false
+ });
+ }
+ if (connection.hasTurn) {
+ parsedRtcServers.push_back((tgcalls::RtcServer){
+ .host = connection.ip.UTF8String,
+ .port = (uint16_t)connection.port,
+ .login = connection.username.UTF8String,
+ .password = connection.password.UTF8String,
+ .isTurn = true
+ });
+ }
}
- /*TgVoipCrypto crypto;
- crypto.sha1 = &TGCallSha1;
- crypto.sha256 = &TGCallSha256;
- crypto.rand_bytes = &TGCallRandomBytes;
- crypto.aes_ige_encrypt = &TGCallAesIgeEncrypt;
- crypto.aes_ige_decrypt = &TGCallAesIgeDecrypt;
- crypto.aes_ctr_encrypt = &TGCallAesCtrEncrypt;*/
+ std::vector preferredVideoCodecs;
+ if (preferredVideoCodec != nil) {
+ preferredVideoCodecs.push_back([preferredVideoCodec UTF8String]);
+ }
std::vector endpoints;
- NSArray *connections = [@[primaryConnection] arrayByAddingObjectsFromArray:alternativeConnections];
- for (OngoingCallConnectionDescriptionWebrtc *connection in connections) {
- unsigned char peerTag[16];
- [connection.peerTag getBytes:peerTag length:16];
-
- tgcalls::Endpoint endpoint;
- endpoint.endpointId = connection.connectionId;
- endpoint.host = {
- .ipv4 = std::string(connection.ip.UTF8String),
- .ipv6 = std::string(connection.ipv6.UTF8String)
- };
- endpoint.port = (uint16_t)connection.port;
- endpoint.type = tgcalls::EndpointType::UdpRelay;
- memcpy(endpoint.peerTag, peerTag, 16);
- endpoints.push_back(endpoint);
- }
tgcalls::Config config = {
.initializationTimeout = _callConnectTimeout,
@@ -298,13 +391,16 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL;
.enableNS = true,
.enableAGC = true,
.enableCallUpgrade = false,
- .logPath = logPath.length == 0 ? "" : std::string(logPath.UTF8String),
- .maxApiLayer = [OngoingCallThreadLocalContextWebrtc maxLayer]
+ .logPath = "", //logPath.length == 0 ? "" : std::string(logPath.UTF8String),
+ .maxApiLayer = [OngoingCallThreadLocalContextWebrtc maxLayer],
+ .preferredAspectRatio = preferredAspectRatio,
+ .enableHighBitrateVideo = true,
+ .preferredVideoCodecs = preferredVideoCodecs,
+ .protocolVersion = [OngoingCallThreadLocalContextWebrtc protocolVersionFromLibraryVersion:version]
};
- std::vector encryptionKeyValue;
- encryptionKeyValue.resize(key.length);
- memcpy(encryptionKeyValue.data(), key.bytes, key.length);
+ auto encryptionKeyValue = std::make_shared>();
+ memcpy(encryptionKeyValue->data(), key.bytes, key.length);
tgcalls::EncryptionKey encryptionKey(encryptionKeyValue, isOutgoing);
@@ -313,7 +409,7 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL;
dispatch_once(&onceToken, ^{
tgcalls::Register();
});
- _tgVoip = tgcalls::Meta::Create("2.7.7", (tgcalls::Descriptor){
+ _tgVoip = tgcalls::Meta::Create([version UTF8String], (tgcalls::Descriptor){
.config = config,
.persistentState = (tgcalls::PersistentState){ derivedStateValue },
.endpoints = endpoints,
@@ -322,52 +418,96 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL;
.initialNetworkType = callControllerNetworkTypeForType(networkType),
.encryptionKey = encryptionKey,
.videoCapture = [_videoCapturer getInterface],
- .stateUpdated = [weakSelf, queue](tgcalls::State state, tgcalls::VideoState videoState) {
+ .stateUpdated = [weakSelf, queue](tgcalls::State state) {
[queue dispatch:^{
__strong OngoingCallThreadLocalContextWebrtc *strongSelf = weakSelf;
if (strongSelf) {
- OngoingCallVideoStateWebrtc mappedVideoState;
- switch (videoState) {
- case tgcalls::VideoState::Possible:
- mappedVideoState = OngoingCallVideoStatePossible;
- break;
- case tgcalls::VideoState::OutgoingRequested:
- mappedVideoState = OngoingCallVideoStateOutgoingRequested;
- break;
- case tgcalls::VideoState::IncomingRequested:
- mappedVideoState = OngoingCallVideoStateIncomingRequested;
- break;
- case tgcalls::VideoState::Active:
- mappedVideoState = OngoingCallVideoStateActive;
- break;
- }
-
- [strongSelf controllerStateChanged:state videoState:mappedVideoState];
+ [strongSelf controllerStateChanged:state];
}
}];
},
- .signalBarsUpdated = [](int value) {
-
- },
- .remoteVideoIsActiveUpdated = [weakSelf, queue](bool isActive) {
+ .signalBarsUpdated = [weakSelf, queue](int value) {
[queue dispatch:^{
__strong OngoingCallThreadLocalContextWebrtc *strongSelf = weakSelf;
if (strongSelf) {
- OngoingCallRemoteVideoStateWebrtc remoteVideoState;
- if (isActive) {
- remoteVideoState = OngoingCallRemoteVideoStateActive;
- } else {
- remoteVideoState = OngoingCallRemoteVideoStateInactive;
+ strongSelf->_signalBars = value;
+ if (strongSelf->_signalBarsChanged) {
+ strongSelf->_signalBarsChanged(value);
}
- if (strongSelf->_remoteVideoState != remoteVideoState) {
+ }
+ }];
+ },
+ .remoteMediaStateUpdated = [weakSelf, queue](tgcalls::AudioState audioState, tgcalls::VideoState videoState) {
+ [queue dispatch:^{
+ __strong OngoingCallThreadLocalContextWebrtc *strongSelf = weakSelf;
+ if (strongSelf) {
+ OngoingCallRemoteAudioStateWebrtc remoteAudioState;
+ OngoingCallRemoteVideoStateWebrtc remoteVideoState;
+ switch (audioState) {
+ case tgcalls::AudioState::Muted:
+ remoteAudioState = OngoingCallRemoteAudioStateMuted;
+ break;
+ case tgcalls::AudioState::Active:
+ remoteAudioState = OngoingCallRemoteAudioStateActive;
+ break;
+ default:
+ remoteAudioState = OngoingCallRemoteAudioStateMuted;
+ break;
+ }
+ switch (videoState) {
+ case tgcalls::VideoState::Inactive:
+ remoteVideoState = OngoingCallRemoteVideoStateInactive;
+ break;
+ case tgcalls::VideoState::Paused:
+ remoteVideoState = OngoingCallRemoteVideoStatePaused;
+ break;
+ case tgcalls::VideoState::Active:
+ remoteVideoState = OngoingCallRemoteVideoStateActive;
+ break;
+ default:
+ remoteVideoState = OngoingCallRemoteVideoStateInactive;
+ break;
+ }
+ if (strongSelf->_remoteVideoState != remoteVideoState || strongSelf->_remoteAudioState != remoteAudioState) {
strongSelf->_remoteVideoState = remoteVideoState;
+ strongSelf->_remoteAudioState = remoteAudioState;
if (strongSelf->_stateChanged) {
- strongSelf->_stateChanged(strongSelf->_state, strongSelf->_videoState, strongSelf->_remoteVideoState);
+ strongSelf->_stateChanged(strongSelf->_state, strongSelf->_videoState, strongSelf->_remoteVideoState, strongSelf->_remoteAudioState, strongSelf->_remoteBatteryLevel, strongSelf->_remotePreferredAspectRatio);
}
}
}
}];
},
+ .remoteBatteryLevelIsLowUpdated = [weakSelf, queue](bool isLow) {
+ [queue dispatch:^{
+ __strong OngoingCallThreadLocalContextWebrtc *strongSelf = weakSelf;
+ if (strongSelf) {
+ OngoingCallRemoteBatteryLevelWebrtc remoteBatteryLevel;
+ if (isLow) {
+ remoteBatteryLevel = OngoingCallRemoteBatteryLevelLow;
+ } else {
+ remoteBatteryLevel = OngoingCallRemoteBatteryLevelNormal;
+ }
+ if (strongSelf->_remoteBatteryLevel != remoteBatteryLevel) {
+ strongSelf->_remoteBatteryLevel = remoteBatteryLevel;
+ if (strongSelf->_stateChanged) {
+ strongSelf->_stateChanged(strongSelf->_state, strongSelf->_videoState, strongSelf->_remoteVideoState, strongSelf->_remoteAudioState, strongSelf->_remoteBatteryLevel, strongSelf->_remotePreferredAspectRatio);
+ }
+ }
+ }
+ }];
+ },
+ .remotePrefferedAspectRatioUpdated = [weakSelf, queue](float value) {
+ [queue dispatch:^{
+ __strong OngoingCallThreadLocalContextWebrtc *strongSelf = weakSelf;
+ if (strongSelf) {
+ strongSelf->_remotePreferredAspectRatio = value;
+ if (strongSelf->_stateChanged) {
+ strongSelf->_stateChanged(strongSelf->_state, strongSelf->_videoState, strongSelf->_remoteVideoState, strongSelf->_remoteAudioState, strongSelf->_remoteBatteryLevel, strongSelf->_remotePreferredAspectRatio);
+ }
+ }
+ }];
+ },
.signalingDataEmitted = [weakSelf, queue](const std::vector &data) {
NSData *mappedData = [[NSData alloc] initWithBytes:data.data() length:data.size()];
[queue dispatch:^{
@@ -380,12 +520,16 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL;
});
_state = OngoingCallStateInitializing;
- _signalBars = -1;
+ _signalBars = 4;
}
return self;
}
- (void)dealloc {
+ if (InternalVoipLoggingFunction) {
+ InternalVoipLoggingFunction(@"OngoingCallThreadLocalContext: dealloc");
+ }
+
assert([_queue isCurrent]);
if (_tgVoip != NULL) {
[self stop:nil];
@@ -396,19 +540,55 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL;
return false;
}
-- (void)stop:(void (^)(NSString *, int64_t, int64_t, int64_t, int64_t))completion {
- if (_tgVoip) {
- tgcalls::FinalState finalState = _tgVoip->stop();
-
- NSString *debugLog = [NSString stringWithUTF8String:finalState.debugLog.c_str()];
- _lastDerivedState = [[NSData alloc] initWithBytes:finalState.persistentState.value.data() length:finalState.persistentState.value.size()];
-
- if (completion) {
- completion(debugLog, finalState.trafficStats.bytesSentWifi, finalState.trafficStats.bytesReceivedWifi, finalState.trafficStats.bytesSentMobile, finalState.trafficStats.bytesReceivedMobile);
+- (void)beginTermination {
+}
+
+- (void)stopWithTerminationResult:(OngoingCallThreadLocalContextWebrtcTerminationResult *)terminationResult completion:(void (^)(NSString *, int64_t, int64_t, int64_t, int64_t))completion {
+ _tgVoip.reset();
+
+ if (completion) {
+ if (terminationResult) {
+ NSString *debugLog = [NSString stringWithUTF8String:terminationResult.finalState.debugLog.c_str()];
+ _lastDerivedState = [[NSData alloc] initWithBytes:terminationResult.finalState.persistentState.value.data() length:terminationResult.finalState.persistentState.value.size()];
+
+ if (completion) {
+ completion(debugLog, terminationResult.finalState.trafficStats.bytesSentWifi, terminationResult.finalState.trafficStats.bytesReceivedWifi, terminationResult.finalState.trafficStats.bytesSentMobile, terminationResult.finalState.trafficStats.bytesReceivedMobile);
+ }
+ } else {
+ if (completion) {
+ completion(@"", 0, 0, 0, 0);
+ }
}
}
}
+- (void)stop:(void (^)(NSString *, int64_t, int64_t, int64_t, int64_t))completion {
+ if (!_tgVoip) {
+ return;
+ }
+ if (completion == nil) {
+ _tgVoip->stop([](tgcalls::FinalState finalState) {
+ });
+ _tgVoip.reset();
+ return;
+ }
+
+ __weak OngoingCallThreadLocalContextWebrtc *weakSelf = self;
+ id queue = _queue;
+ _tgVoip->stop([weakSelf, queue, completion = [completion copy]](tgcalls::FinalState finalState) {
+ [queue dispatch:^{
+ __strong OngoingCallThreadLocalContextWebrtc *strongSelf = weakSelf;
+ if (!strongSelf) {
+ return;
+ }
+
+ OngoingCallThreadLocalContextWebrtcTerminationResult *terminationResult = [[OngoingCallThreadLocalContextWebrtcTerminationResult alloc] initWithFinalState:finalState];
+
+ [strongSelf stopWithTerminationResult:terminationResult completion:completion];
+ }];
+ });
+}
+
- (NSString *)debugInfo {
if (_tgVoip != nullptr) {
NSString *version = [self version];
@@ -421,7 +601,7 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL;
}
- (NSString *)version {
- return @"2.7.7";
+ return _version;
}
- (NSData * _Nonnull)getDerivedState {
@@ -435,7 +615,7 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL;
}
}
-- (void)controllerStateChanged:(tgcalls::State)state videoState:(OngoingCallVideoStateWebrtc)videoState {
+- (void)controllerStateChanged:(tgcalls::State)state {
OngoingCallStateWebrtc callState = OngoingCallStateInitializing;
switch (state) {
case tgcalls::State::Established:
@@ -451,12 +631,11 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL;
break;
}
- if (_state != callState || _videoState != videoState) {
+ if (_state != callState) {
_state = callState;
- _videoState = videoState;
if (_stateChanged) {
- _stateChanged(_state, _videoState, _remoteVideoState);
+ _stateChanged(_state, _videoState, _remoteVideoState, _remoteAudioState, _remoteBatteryLevel, _remotePreferredAspectRatio);
}
}
}
@@ -477,6 +656,7 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL;
}
}
+
- (void)addSignalingData:(NSData *)data {
if (_tgVoip) {
std::vector mappedData;
@@ -492,6 +672,12 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL;
}
}
+- (void)setIsLowBatteryLevel:(bool)isLowBatteryLevel {
+ if (_tgVoip) {
+ _tgVoip->setIsLowBatteryLevel(isLowBatteryLevel);
+ }
+}
+
- (void)setNetworkType:(OngoingCallNetworkTypeWebrtc)networkType {
if (_networkType != networkType) {
_networkType = networkType;
@@ -507,11 +693,17 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL;
dispatch_async(dispatch_get_main_queue(), ^{
if ([VideoMetalView isSupported]) {
VideoMetalView *remoteRenderer = [[VideoMetalView alloc] initWithFrame:CGRectZero];
+ #if TARGET_OS_IPHONE
remoteRenderer.videoContentMode = UIViewContentModeScaleAspectFill;
+ #else
+ remoteRenderer.videoContentMode = UIViewContentModeScaleAspect;
+ #endif
std::shared_ptr> sink = [remoteRenderer getSink];
__strong OngoingCallThreadLocalContextWebrtc *strongSelf = weakSelf;
if (strongSelf) {
+ [remoteRenderer setOrientation:strongSelf->_remoteVideoOrientation];
+ strongSelf->_currentRemoteVideoRenderer = remoteRenderer;
strongSelf->_tgVoip->setIncomingVideoOutput(sink);
}
@@ -522,6 +714,8 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL;
std::shared_ptr> sink = [remoteRenderer getSink];
__strong OngoingCallThreadLocalContextWebrtc *strongSelf = weakSelf;
if (strongSelf) {
+ [remoteRenderer setOrientation:strongSelf->_remoteVideoOrientation];
+ strongSelf->_currentRemoteVideoRenderer = remoteRenderer;
strongSelf->_tgVoip->setIncomingVideoOutput(sink);
}
@@ -534,15 +728,29 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL;
- (void)requestVideo:(OngoingCallThreadLocalContextVideoCapturer * _Nullable)videoCapturer {
if (_tgVoip && _videoCapturer == nil) {
_videoCapturer = videoCapturer;
- _tgVoip->requestVideo([_videoCapturer getInterface]);
+ _tgVoip->setVideoCapture([_videoCapturer getInterface]);
+
+ _videoState = OngoingCallVideoStateActive;
+ if (_stateChanged) {
+ _stateChanged(_state, _videoState, _remoteVideoState, _remoteAudioState, _remoteBatteryLevel, _remotePreferredAspectRatio);
+ }
}
}
-- (void)acceptVideo:(OngoingCallThreadLocalContextVideoCapturer * _Nullable)videoCapturer {
- if (_tgVoip && _videoCapturer == nil) {
- _videoCapturer = videoCapturer;
- _tgVoip->requestVideo([_videoCapturer getInterface]);
+- (void)disableVideo {
+ if (_tgVoip) {
+ _videoCapturer = nil;
+ _tgVoip->setVideoCapture(nullptr);
+
+ _videoState = OngoingCallVideoStateInactive;
+ if (_stateChanged) {
+ _stateChanged(_state, _videoState, _remoteVideoState, _remoteAudioState, _remoteBatteryLevel, _remotePreferredAspectRatio);
+ }
}
}
+- (void)remotePrefferedAspectRatioUpdated:(float)remotePrefferedAspectRatio {
+
+}
+
@end
diff --git a/submodules/TgVoipWebrtc/tgcalls b/submodules/TgVoipWebrtc/tgcalls
index 83c85d20cc..a7d9b717fd 160000
--- a/submodules/TgVoipWebrtc/tgcalls
+++ b/submodules/TgVoipWebrtc/tgcalls
@@ -1 +1 @@
-Subproject commit 83c85d20ccdde154acca4b964317de1e695f95d1
+Subproject commit a7d9b717fdf7e8e441b47692dc5771684b2d7970
diff --git a/submodules/TooltipUI/Sources/TooltipScreen.swift b/submodules/TooltipUI/Sources/TooltipScreen.swift
index f84bbbd491..599f3f3dd6 100644
--- a/submodules/TooltipUI/Sources/TooltipScreen.swift
+++ b/submodules/TooltipUI/Sources/TooltipScreen.swift
@@ -25,6 +25,7 @@ public enum TooltipActiveTextAction {
}
private final class TooltipScreenNode: ViewControllerTracingNode {
+ private let tooltipStyle: TooltipScreen.Style
private let icon: TooltipScreen.Icon?
private let location: TooltipScreen.Location
private let displayDuration: TooltipScreen.DisplayDuration
@@ -33,10 +34,12 @@ private final class TooltipScreenNode: ViewControllerTracingNode {
private let scrollingContainer: ASDisplayNode
private let containerNode: ASDisplayNode
+ private let backgroundContainerNode: ASDisplayNode
private let backgroundNode: ASImageNode
private var effectView: UIView?
private let arrowNode: ASImageNode
private let arrowContainer: ASDisplayNode
+ private var arrowEffectView: UIView?
private let animatedStickerNode: AnimatedStickerNode
private let textNode: ImmediateTextNode
@@ -44,7 +47,8 @@ private final class TooltipScreenNode: ViewControllerTracingNode {
private var validLayout: ContainerViewLayout?
- init(text: String, textEntities: [MessageTextEntity], icon: TooltipScreen.Icon?, location: TooltipScreen.Location, displayDuration: TooltipScreen.DisplayDuration, shouldDismissOnTouch: @escaping (CGPoint) -> TooltipScreen.DismissOnTouch, requestDismiss: @escaping () -> Void, openActiveTextItem: @escaping (TooltipActiveTextItem, TooltipActiveTextAction) -> Void) {
+ init(text: String, textEntities: [MessageTextEntity], style: TooltipScreen.Style, icon: TooltipScreen.Icon?, location: TooltipScreen.Location, displayDuration: TooltipScreen.DisplayDuration, shouldDismissOnTouch: @escaping (CGPoint) -> TooltipScreen.DismissOnTouch, requestDismiss: @escaping () -> Void, openActiveTextItem: @escaping (TooltipActiveTextItem, TooltipActiveTextAction) -> Void) {
+ self.tooltipStyle = style
self.icon = icon
self.location = location
self.displayDuration = displayDuration
@@ -52,6 +56,7 @@ private final class TooltipScreenNode: ViewControllerTracingNode {
self.requestDismiss = requestDismiss
self.containerNode = ASDisplayNode()
+ self.backgroundContainerNode = ASDisplayNode()
let fillColor = UIColor(white: 0.0, alpha: 0.8)
@@ -59,14 +64,43 @@ private final class TooltipScreenNode: ViewControllerTracingNode {
self.backgroundNode = ASImageNode()
self.backgroundNode.image = generateAdjustedStretchableFilledCircleImage(diameter: 15.0, color: fillColor)
- if case .top = location {
- self.effectView = UIVisualEffectView(effect: UIBlurEffect(style: .dark))
- self.containerNode.clipsToBounds = true
- self.containerNode.cornerRadius = 9.0
+
+ func svgPath(_ path: StaticString, scale: CGPoint = CGPoint(x: 1.0, y: 1.0), offset: CGPoint = CGPoint()) throws -> UIBezierPath {
+ var index: UnsafePointer = path.utf8Start
+ let end = path.utf8Start.advanced(by: path.utf8CodeUnitCount)
+ let path = UIBezierPath()
+ while index < end {
+ let c = index.pointee
+ index = index.successor()
+
+ if c == 77 { // M
+ let x = try readCGFloat(&index, end: end, separator: 44) * scale.x + offset.x
+ let y = try readCGFloat(&index, end: end, separator: 32) * scale.y + offset.y
+
+ path.move(to: CGPoint(x: x, y: y))
+ } else if c == 76 { // L
+ let x = try readCGFloat(&index, end: end, separator: 44) * scale.x + offset.x
+ let y = try readCGFloat(&index, end: end, separator: 32) * scale.y + offset.y
+
+ path.addLine(to: CGPoint(x: x, y: y))
+ } else if c == 67 { // C
+ let x1 = try readCGFloat(&index, end: end, separator: 44) * scale.x + offset.x
+ let y1 = try readCGFloat(&index, end: end, separator: 32) * scale.y + offset.y
+ let x2 = try readCGFloat(&index, end: end, separator: 44) * scale.x + offset.x
+ let y2 = try readCGFloat(&index, end: end, separator: 32) * scale.y + offset.y
+ let x = try readCGFloat(&index, end: end, separator: 44) * scale.x + offset.x
+ let y = try readCGFloat(&index, end: end, separator: 32) * scale.y + offset.y
+ path.addCurve(to: CGPoint(x: x, y: y), controlPoint1: CGPoint(x: x1, y: y1), controlPoint2: CGPoint(x: x2, y: y2))
+ } else if c == 32 { // space
+ continue
+ }
+ }
+ path.close()
+ return path
}
- self.arrowNode = ASImageNode()
let arrowSize = CGSize(width: 29.0, height: 10.0)
+ self.arrowNode = ASImageNode()
self.arrowNode.image = generateImage(arrowSize, rotatedContext: { size, context in
context.clear(CGRect(origin: CGPoint(), size: size))
context.setFillColor(fillColor.cgColor)
@@ -77,11 +111,42 @@ private final class TooltipScreenNode: ViewControllerTracingNode {
self.arrowContainer = ASDisplayNode()
+ let fontSize: CGFloat
+ if style == .light {
+ self.effectView = UIVisualEffectView(effect: UIBlurEffect(style: .light))
+ self.backgroundContainerNode.clipsToBounds = true
+ self.backgroundContainerNode.cornerRadius = 14.0
+ if #available(iOS 13.0, *) {
+ self.backgroundContainerNode.layer.cornerCurve = .continuous
+ }
+ fontSize = 17.0
+
+ self.arrowEffectView = UIVisualEffectView(effect: UIBlurEffect(style: .light))
+ self.arrowContainer.view.addSubview(self.arrowEffectView!)
+
+ let maskLayer = CAShapeLayer()
+ if let path = try? svgPath("M85.882251,0 C79.5170552,0 73.4125613,2.52817247 68.9116882,7.02834833 L51.4264069,24.5109211 C46.7401154,29.1964866 39.1421356,29.1964866 34.4558441,24.5109211 L16.9705627,7.02834833 C12.4696897,2.52817247 6.36519576,0 0,0 L85.882251,0 ", scale: CGPoint(x: 0.333333, y: 0.333333), offset: CGPoint()) {
+ maskLayer.path = path.cgPath
+ }
+ maskLayer.frame = CGRect(origin: CGPoint(), size: arrowSize)
+ self.arrowContainer.layer.mask = maskLayer
+ } else if case .top = location {
+ self.effectView = UIVisualEffectView(effect: UIBlurEffect(style: .dark))
+ self.containerNode.clipsToBounds = true
+ self.containerNode.cornerRadius = 9.0
+ if #available(iOS 13.0, *) {
+ self.containerNode.layer.cornerCurve = .continuous
+ }
+ fontSize = 14.0
+ } else {
+ fontSize = 14.0
+ }
+
self.textNode = ImmediateTextNode()
self.textNode.displaysAsynchronously = false
self.textNode.maximumNumberOfLines = 0
- self.textNode.attributedText = stringWithAppliedEntities(text, entities: textEntities, baseColor: .white, linkColor: .white, baseFont: Font.regular(14.0), linkFont: Font.regular(14.0), boldFont: Font.semibold(14.0), italicFont: Font.italic(14.0), boldItalicFont: Font.semiboldItalic(14.0), fixedFont: Font.monospace(14.0), blockQuoteFont: Font.regular(14.0), underlineLinks: true, external: false)
+ self.textNode.attributedText = stringWithAppliedEntities(text, entities: textEntities, baseColor: .white, linkColor: .white, baseFont: Font.regular(fontSize), linkFont: Font.regular(fontSize), boldFont: Font.semibold(14.0), italicFont: Font.italic(fontSize), boldItalicFont: Font.semiboldItalic(fontSize), fixedFont: Font.monospace(fontSize), blockQuoteFont: Font.regular(fontSize), underlineLinks: true, external: false)
self.animatedStickerNode = AnimatedStickerNode()
switch icon {
@@ -89,24 +154,29 @@ private final class TooltipScreenNode: ViewControllerTracingNode {
break
case .chatListPress:
if let path = getAppBundle().path(forResource: "ChatListFoldersTooltip", ofType: "json") {
- self.animatedStickerNode.setup(source: AnimatedStickerNodeLocalFileSource(path: path), width: Int(70 * UIScreenScale), height: Int(70 * UIScreenScale), playbackMode: .once, mode: .direct)
+ self.animatedStickerNode.setup(source: AnimatedStickerNodeLocalFileSource(path: path), width: Int(70 * UIScreenScale), height: Int(70 * UIScreenScale), playbackMode: .once, mode: .direct(cachePathPrefix: nil))
self.animatedStickerNode.automaticallyLoadFirstFrame = true
}
case .info:
if let path = getAppBundle().path(forResource: "anim_infotip", ofType: "json") {
- self.animatedStickerNode.setup(source: AnimatedStickerNodeLocalFileSource(path: path), width: Int(70 * UIScreenScale), height: Int(70 * UIScreenScale), playbackMode: .once, mode: .direct)
+ self.animatedStickerNode.setup(source: AnimatedStickerNodeLocalFileSource(path: path), width: Int(70 * UIScreenScale), height: Int(70 * UIScreenScale), playbackMode: .once, mode: .direct(cachePathPrefix: nil))
self.animatedStickerNode.automaticallyLoadFirstFrame = true
}
}
super.init()
+ self.containerNode.addSubnode(self.backgroundContainerNode)
self.arrowContainer.addSubnode(self.arrowNode)
self.backgroundNode.addSubnode(self.arrowContainer)
if let effectView = self.effectView {
- self.containerNode.view.addSubview(effectView)
+ self.backgroundContainerNode.view.addSubview(effectView)
+ if let _ = self.arrowEffectView {
+ self.containerNode.addSubnode(self.arrowContainer)
+ self.arrowNode.removeFromSupernode()
+ }
} else {
- self.containerNode.addSubnode(self.backgroundNode)
+ self.backgroundContainerNode.addSubnode(self.backgroundNode)
}
self.containerNode.addSubnode(self.textNode)
self.containerNode.addSubnode(self.animatedStickerNode)
@@ -207,8 +277,14 @@ private final class TooltipScreenNode: ViewControllerTracingNode {
var backgroundFrame: CGRect
- let backgroundHeight = max(animationSize.height, textSize.height) + contentVerticalInset * 2.0
-
+ let backgroundHeight: CGFloat
+ switch self.tooltipStyle {
+ case .default:
+ backgroundHeight = max(animationSize.height, textSize.height) + contentVerticalInset * 2.0
+ case .light:
+ backgroundHeight = max(28.0, max(animationSize.height, textSize.height) + 4.0 * 2.0)
+ }
+
var invertArrow = false
switch self.location {
case let .point(rect):
@@ -231,6 +307,7 @@ private final class TooltipScreenNode: ViewControllerTracingNode {
}
transition.updateFrame(node: self.containerNode, frame: backgroundFrame)
+ transition.updateFrame(node: self.backgroundContainerNode, frame: CGRect(origin: CGPoint(), size: backgroundFrame.size))
transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(), size: backgroundFrame.size))
if let effectView = self.effectView {
transition.updateFrame(view: effectView, frame: CGRect(origin: CGPoint(), size: backgroundFrame.size))
@@ -252,8 +329,10 @@ private final class TooltipScreenNode: ViewControllerTracingNode {
ContainedViewLayoutTransition.immediate.updateTransformScale(node: self.arrowContainer, scale: CGPoint(x: 1.0, y: invertArrow ? -1.0 : 1.0))
self.arrowNode.frame = CGRect(origin: CGPoint(), size: arrowFrame.size)
+ self.arrowEffectView?.frame = CGRect(origin: CGPoint(), size: arrowFrame.size)
} else {
self.arrowNode.isHidden = true
+ self.arrowEffectView?.isHidden = true
}
transition.updateFrame(node: self.textNode, frame: CGRect(origin: CGPoint(x: contentInset + animationSize.width + animationSpacing, y: floor((backgroundHeight - textSize.height) / 2.0)), size: textSize))
@@ -373,8 +452,14 @@ public final class TooltipScreen: ViewController {
case custom(Double)
}
+ public enum Style {
+ case `default`
+ case light
+ }
+
public let text: String
public let textEntities: [MessageTextEntity]
+ private let style: TooltipScreen.Style
private let icon: TooltipScreen.Icon?
private let location: TooltipScreen.Location
private let displayDuration: DisplayDuration
@@ -393,9 +478,10 @@ public final class TooltipScreen: ViewController {
private var dismissTimer: Foundation.Timer?
- public init(text: String, textEntities: [MessageTextEntity] = [], icon: TooltipScreen.Icon?, location: TooltipScreen.Location, displayDuration: DisplayDuration = .default, shouldDismissOnTouch: @escaping (CGPoint) -> TooltipScreen.DismissOnTouch, openActiveTextItem: @escaping (TooltipActiveTextItem, TooltipActiveTextAction) -> Void = { _, _ in }) {
+ public init(text: String, textEntities: [MessageTextEntity] = [], style: TooltipScreen.Style = .default, icon: TooltipScreen.Icon?, location: TooltipScreen.Location, displayDuration: DisplayDuration = .default, shouldDismissOnTouch: @escaping (CGPoint) -> TooltipScreen.DismissOnTouch, openActiveTextItem: @escaping (TooltipActiveTextItem, TooltipActiveTextAction) -> Void = { _, _ in }) {
self.text = text
self.textEntities = textEntities
+ self.style = style
self.icon = icon
self.location = location
self.displayDuration = displayDuration
@@ -455,7 +541,7 @@ public final class TooltipScreen: ViewController {
}
override public func loadDisplayNode() {
- self.displayNode = TooltipScreenNode(text: self.text, textEntities: self.textEntities, icon: self.icon, location: self.location, displayDuration: self.displayDuration, shouldDismissOnTouch: self.shouldDismissOnTouch, requestDismiss: { [weak self] in
+ self.displayNode = TooltipScreenNode(text: self.text, textEntities: self.textEntities, style: self.style, icon: self.icon, location: self.location, displayDuration: self.displayDuration, shouldDismissOnTouch: self.shouldDismissOnTouch, requestDismiss: { [weak self] in
guard let strongSelf = self else {
return
}
diff --git a/submodules/UndoUI/Sources/UndoOverlayControllerNode.swift b/submodules/UndoUI/Sources/UndoOverlayControllerNode.swift
index 2ce5060ba9..9161de3fab 100644
--- a/submodules/UndoUI/Sources/UndoOverlayControllerNode.swift
+++ b/submodules/UndoUI/Sources/UndoOverlayControllerNode.swift
@@ -206,7 +206,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
self.animationNode = nil
self.animatedStickerNode = AnimatedStickerNode()
self.animatedStickerNode?.visibility = true
- self.animatedStickerNode?.setup(source: AnimatedStickerNodeLocalFileSource(path: path), width: 100, height: 100, playbackMode: .once, mode: .direct)
+ self.animatedStickerNode?.setup(source: AnimatedStickerNodeLocalFileSource(path: path), width: 100, height: 100, playbackMode: .once, mode: .direct(cachePathPrefix: nil))
let body = MarkdownAttributeSet(font: Font.regular(14.0), textColor: .white)
let bold = MarkdownAttributeSet(font: Font.semibold(14.0), textColor: .white)
@@ -255,7 +255,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
thumbnailItem = .animated(item.file.resource)
resourceReference = MediaResourceReference.media(media: .standalone(media: item.file), resource: item.file.resource)
} else if let dimensions = item.file.dimensions, let resource = chatMessageStickerResource(file: item.file, small: true) as? TelegramMediaResource {
- thumbnailItem = .still(TelegramMediaImageRepresentation(dimensions: dimensions, resource: resource))
+ thumbnailItem = .still(TelegramMediaImageRepresentation(dimensions: dimensions, resource: resource, progressiveSizes: []))
resourceReference = MediaResourceReference.media(media: .standalone(media: item.file), resource: resource)
}
}
@@ -349,7 +349,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
case let .result(_, items, _):
let item = items[Int(value)]
if let item = item as? StickerPackItem {
- animatedStickerNode.setup(source: AnimatedStickerResourceSource(account: account, resource: item.file.resource), width: 120, height: 120, playbackMode: .once, mode: .direct)
+ animatedStickerNode.setup(source: AnimatedStickerResourceSource(account: account, resource: item.file.resource), width: 120, height: 120, playbackMode: .once, mode: .direct(cachePathPrefix: nil))
}
default:
break
diff --git a/submodules/WallpaperResources/Sources/WallpaperResources.swift b/submodules/WallpaperResources/Sources/WallpaperResources.swift
index a2e7973982..d98b166d07 100644
--- a/submodules/WallpaperResources/Sources/WallpaperResources.swift
+++ b/submodules/WallpaperResources/Sources/WallpaperResources.swift
@@ -971,7 +971,7 @@ public func themeImage(account: Account, accountManager: AccountManager, source:
|> mapToSignal { wallpaper -> Signal<(PresentationTheme?, UIImage?, Data?), NoError> in
if let wallpaper = wallpaper, case let .file(file) = wallpaper.wallpaper {
var convertedRepresentations: [ImageRepresentationWithReference] = []
- convertedRepresentations.append(ImageRepresentationWithReference(representation: TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 100, height: 100), resource: file.file.resource), reference: .wallpaper(wallpaper: .slug(file.slug), resource: file.file.resource)))
+ convertedRepresentations.append(ImageRepresentationWithReference(representation: TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 100, height: 100), resource: file.file.resource, progressiveSizes: []), reference: .wallpaper(wallpaper: .slug(file.slug), resource: file.file.resource)))
return wallpaperDatas(account: account, accountManager: accountManager, fileReference: .standalone(media: file.file), representations: convertedRepresentations, alwaysShowThumbnailFirst: false, thumbnail: false, onlyFullSize: true, autoFetchFullSize: true, synchronousLoad: false)
|> mapToSignal { _, fullSizeData, complete -> Signal<(PresentationTheme?, UIImage?, Data?), NoError> in
guard complete, let fullSizeData = fullSizeData else {
@@ -1228,7 +1228,7 @@ public func themeIconImage(account: Account, accountManager: AccountManager, the
}
var convertedRepresentations: [ImageRepresentationWithReference] = []
- convertedRepresentations.append(ImageRepresentationWithReference(representation: TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 100, height: 100), resource: file.file.resource), reference: .wallpaper(wallpaper: .slug(file.slug), resource: file.file.resource)))
+ convertedRepresentations.append(ImageRepresentationWithReference(representation: TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 100, height: 100), resource: file.file.resource, progressiveSizes: []), reference: .wallpaper(wallpaper: .slug(file.slug), resource: file.file.resource)))
return wallpaperDatas(account: account, accountManager: accountManager, fileReference: .standalone(media: file.file), representations: convertedRepresentations, alwaysShowThumbnailFirst: false, thumbnail: false, onlyFullSize: true, autoFetchFullSize: true, synchronousLoad: false)
|> mapToSignal { _, fullSizeData, complete -> Signal<((UIColor, UIColor?), (UIColor, UIColor), (UIColor, UIColor), UIImage?, Int32?), NoError> in
guard complete, let fullSizeData = fullSizeData else {
diff --git a/submodules/WebSearchUI/Sources/LegacyWebSearchGallery.swift b/submodules/WebSearchUI/Sources/LegacyWebSearchGallery.swift
index 99e7fab29a..fd93f84bab 100644
--- a/submodules/WebSearchUI/Sources/LegacyWebSearchGallery.swift
+++ b/submodules/WebSearchUI/Sources/LegacyWebSearchGallery.swift
@@ -261,9 +261,9 @@ func legacyWebSearchItem(account: Account, result: ChatContextResult) -> LegacyW
var representations: [TelegramMediaImageRepresentation] = []
if let thumbnailResource = thumbnailResource, let thumbnailDimensions = thumbnailDimensions {
- representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(thumbnailDimensions), resource: thumbnailResource))
+ representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(thumbnailDimensions), resource: thumbnailResource, progressiveSizes: []))
}
- representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(imageDimensions), resource: imageResource))
+ representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(imageDimensions), resource: imageResource, progressiveSizes: []))
let tmpImage = TelegramMediaImage(imageId: MediaId(namespace: 0, id: 0), representations: representations, immediateThumbnailData: immediateThumbnailData, reference: nil, partialReference: nil, flags: [])
thumbnailSignal = chatMessagePhotoDatas(postbox: account.postbox, photoReference: .standalone(media: tmpImage), autoFetchFullSize: false)
|> mapToSignal { value -> Signal in
@@ -459,7 +459,7 @@ public func legacyEnqueueWebSearchMessages(_ selectionState: TGMediaSelectionCon
paintingImage = adjustments.paintingData?.image
}
- let thumbnailImage = TGPhotoEditorVideoExtCrop(image, paintingImage, adjustments.cropOrientation, adjustments.cropRotation, adjustments.cropRect, adjustments.cropMirrored, TGScaleToFill(image.size, CGSize(width: 512.0, height: 512.0)), adjustments.originalSize, true, true, true)
+ let thumbnailImage = TGPhotoEditorVideoExtCrop(image, paintingImage, adjustments.cropOrientation, adjustments.cropRotation, adjustments.cropRect, adjustments.cropMirrored, TGScaleToFill(image.size, CGSize(width: 512.0, height: 512.0)), adjustments.originalSize, true, true, true, false)
if let thumbnailImage = thumbnailImage {
dict["previewImage"] = thumbnailImage
}
diff --git a/submodules/WebSearchUI/Sources/WebSearchGalleryController.swift b/submodules/WebSearchUI/Sources/WebSearchGalleryController.swift
index bd5196a332..d5941381e7 100644
--- a/submodules/WebSearchUI/Sources/WebSearchGalleryController.swift
+++ b/submodules/WebSearchUI/Sources/WebSearchGalleryController.swift
@@ -39,7 +39,7 @@ struct WebSearchGalleryEntry: Equatable {
switch self.result {
case let .externalReference(externalReference):
if let content = externalReference.content, externalReference.type == "gif", let thumbnailResource = externalReference.thumbnail?.resource, let dimensions = content.dimensions {
- let fileReference = FileMediaReference.standalone(media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: content.resource, previewRepresentations: [TelegramMediaImageRepresentation(dimensions: dimensions, resource: thumbnailResource)], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: dimensions, flags: [])]))
+ let fileReference = FileMediaReference.standalone(media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: content.resource, previewRepresentations: [TelegramMediaImageRepresentation(dimensions: dimensions, resource: thumbnailResource, progressiveSizes: [])], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: dimensions, flags: [])]))
return WebSearchVideoGalleryItem(context: context, presentationData: presentationData, index: self.index, result: self.result, content: NativeVideoContent(id: .contextResult(self.result.queryId, self.result.id), fileReference: fileReference, loopVideo: true, enableSound: false, fetchAutomatically: true), controllerInteraction: controllerInteraction)
}
case let .internalReference(internalReference):
diff --git a/submodules/WebSearchUI/Sources/WebSearchItem.swift b/submodules/WebSearchUI/Sources/WebSearchItem.swift
index 2e48090959..62df9d85ed 100644
--- a/submodules/WebSearchUI/Sources/WebSearchItem.swift
+++ b/submodules/WebSearchUI/Sources/WebSearchItem.swift
@@ -132,10 +132,10 @@ final class WebSearchItemNode: GridItemNode {
var representations: [TelegramMediaImageRepresentation] = []
if let thumbnailResource = thumbnailResource, let thumbnailDimensions = thumbnailDimensions {
- representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(thumbnailDimensions), resource: thumbnailResource))
+ representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(thumbnailDimensions), resource: thumbnailResource, progressiveSizes: []))
}
if let imageResource = imageResource, let imageDimensions = imageDimensions {
- representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(imageDimensions), resource: imageResource))
+ representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(imageDimensions), resource: imageResource, progressiveSizes: []))
}
if !representations.isEmpty {
let tmpImage = TelegramMediaImage(imageId: MediaId(namespace: 0, id: 0), representations: representations, immediateThumbnailData: immediateThumbnailData, reference: nil, partialReference: nil, flags: [])
diff --git a/submodules/WebSearchUI/Sources/WebSearchVideoGalleryItem.swift b/submodules/WebSearchUI/Sources/WebSearchVideoGalleryItem.swift
index 201845745c..4ce6a36057 100644
--- a/submodules/WebSearchUI/Sources/WebSearchVideoGalleryItem.swift
+++ b/submodules/WebSearchUI/Sources/WebSearchVideoGalleryItem.swift
@@ -198,7 +198,7 @@ final class WebSearchVideoGalleryItemNode: ZoomableContentGalleryItemNode {
switch value.status {
case .playing:
isPaused = false
- case let .buffering(_, whilePlaying):
+ case let .buffering(_, whilePlaying, _):
initialBuffering = true
isPaused = !whilePlaying
var isStreaming = false
diff --git a/third-party/mozjpeg/BUCK b/third-party/mozjpeg/BUCK
new file mode 100644
index 0000000000..379603ce42
--- /dev/null
+++ b/third-party/mozjpeg/BUCK
@@ -0,0 +1,108 @@
+load("//Config:buck_rule_macros.bzl", "static_library", "gen_header_targets", "merge_maps")
+
+headers = [
+ "mozjpeg/turbojpeg.h",
+ "mozjpeg/jpeglib.h",
+ "mozjpeg/jmorecfg.h",
+ "mozjpeg/jconfig.h",
+]
+
+libs = [
+ "jpeg",
+ "turbojpeg"
+]
+
+genrule(
+ name = "libmozjpeg_build",
+ srcs = glob([
+ "mozjpeg/**/*",
+ ], exclude = ["mozjpeg/**/.*"]) + [
+ "build-mozjpeg-buck.sh",
+ ],
+ bash =
+ """
+ set -ex
+
+ mkdir -p "$OUT"
+ mkdir -p "$OUT/Public/mozjpeg"
+ mkdir -p "$OUT/Public/lib"
+
+ CMAKE_DIR="$(location //third-party/cmake:cmake)/cmake-3.16.0"
+
+ BUILD_ARCH="arm64"
+
+ BUILD_DIR="$OUT/$BUILD_ARCH"
+ rm -rf "$BUILD_DIR"
+ mkdir -p "$BUILD_DIR"
+
+ cp "$SRCDIR/build-mozjpeg-buck.sh" "$BUILD_DIR/"
+
+ cp -R "$SRCDIR/mozjpeg" "$BUILD_DIR/"
+
+ mkdir -p "$BUILD_DIR/Public/mozjpeg"
+
+ PATH="$PATH:$CMAKE_DIR/bin" sh $BUILD_DIR/build-mozjpeg-buck.sh $BUILD_ARCH "$BUILD_DIR/mozjpeg" "$BUILD_DIR"
+
+ cp $BUILD_DIR/mozjpeg/turbojpeg.h "$OUT/Public/mozjpeg/"
+ cp $BUILD_DIR/mozjpeg/jpeglib.h "$OUT/Public/mozjpeg/"
+ cp $BUILD_DIR/mozjpeg/jmorecfg.h "$OUT/Public/mozjpeg/"
+ cp $BUILD_DIR/build/jconfig.h "$OUT/Public/mozjpeg/"
+
+ BUILD_ARCH="armv7"
+
+ BUILD_DIR="$OUT/$BUILD_ARCH"
+ rm -rf "$BUILD_DIR"
+ mkdir -p "$BUILD_DIR"
+
+ cp "$SRCDIR/build-mozjpeg-buck.sh" "$BUILD_DIR/"
+
+ cp -R "$SRCDIR/mozjpeg" "$BUILD_DIR/"
+
+ mkdir -p "$BUILD_DIR/Public/mozjpeg"
+
+ PATH="$PATH:$CMAKE_DIR/bin" sh $BUILD_DIR/build-mozjpeg-buck.sh $BUILD_ARCH "$BUILD_DIR/mozjpeg" "$BUILD_DIR"
+
+ lipo -create $OUT/arm64/build/libjpeg.a $OUT/armv7/build/libjpeg.a -output $OUT/Public/lib/libjpeg.a
+ lipo -create $OUT/arm64/build/libturbojpeg.a $OUT/armv7/build/libturbojpeg.a -output $OUT/Public/lib/libturbojpeg.a
+ """,
+ out = "libmozjpeg",
+ visibility = [
+ "PUBLIC",
+ ]
+)
+
+mozjpeg_header_targets = gen_header_targets(headers, "libmozjpeg_header_", "", "libmozjpeg_build", "Public")
+
+apple_library(
+ name = "mozjpeg_lib",
+ exported_headers = mozjpeg_header_targets,
+ visibility = [
+ "//third-party/mozjpeg:mozjpeg"
+ ],
+ header_namespace = "mozjpeg",
+ linker_flags = [
+ "-L$(location :libmozjpeg_build)/Public/lib",
+ "-ljpeg",
+ "-lturbojpeg",
+ ],
+ exported_linker_flags = [
+ "-L$(location :libmozjpeg_build)/Public/lib",
+ "-ljpeg",
+ "-lturbojpeg",
+ ],
+ deps = [
+ ":libmozjpeg_build",
+ ],
+)
+
+static_library(
+ name = "mozjpeg",
+ exported_headers = mozjpeg_header_targets,
+ deps = [
+ ":libmozjpeg_build",
+ ":mozjpeg_lib",
+ ],
+ frameworks = [
+ "$SDKROOT/System/Library/Frameworks/Foundation.framework",
+ ],
+)
\ No newline at end of file
diff --git a/third-party/mozjpeg/BUILD b/third-party/mozjpeg/BUILD
new file mode 100644
index 0000000000..76dd224534
--- /dev/null
+++ b/third-party/mozjpeg/BUILD
@@ -0,0 +1,98 @@
+
+headers = [
+ "turbojpeg.h",
+ "jpeglib.h",
+ "jmorecfg.h",
+]
+
+libs = [
+ "jpeg",
+ "turbojpeg"
+]
+
+filegroup(
+ name = "mozjpeg_sources",
+ srcs = glob([
+ "mozjpeg/**/*"
+ ]),
+)
+
+genrule(
+ name = "mozjpeg_build",
+ srcs = [
+ "build-mozjpeg-bazel.sh",
+ ":mozjpeg_sources",
+ ],
+ cmd_bash =
+ """
+ set -ex
+
+ if [ "$(TARGET_CPU)" == "ios_armv7" ]; then
+ BUILD_ARCH="armv7"
+ elif [ "$(TARGET_CPU)" == "ios_arm64" ]; then
+ BUILD_ARCH="arm64"
+ elif [ "$(TARGET_CPU)" == "ios_x86_64" ]; then
+ BUILD_ARCH="x86_64"
+ else
+ echo "Unsupported architecture $(TARGET_CPU)"
+ fi
+
+ BUILD_DIR="$(RULEDIR)/build_$${BUILD_ARCH}"
+ rm -rf "$$BUILD_DIR"
+ mkdir -p "$$BUILD_DIR"
+
+ CMAKE_DIR="$$(pwd)/$$BUILD_DIR/cmake"
+ rm -rf "$$CMAKE_DIR"
+ mkdir -p "$$CMAKE_DIR"
+ tar -xf "$(location //third-party/cmake:cmake.tar)" -C "$$CMAKE_DIR"
+
+ cp $(location :build-mozjpeg-bazel.sh) "$$BUILD_DIR/"
+
+ SOURCE_PATH="third-party/mozjpeg/mozjpeg"
+
+ cp -R "$$SOURCE_PATH" "$$BUILD_DIR/"
+
+ mkdir -p "$$BUILD_DIR/Public/mozjpeg"
+
+ PATH="$$PATH:$$CMAKE_DIR/bin" sh $$BUILD_DIR/build-mozjpeg-bazel.sh $$BUILD_ARCH "$$BUILD_DIR/mozjpeg" "$$BUILD_DIR"
+ """ +
+ "\n".join([
+ "cp -f \"$$BUILD_DIR/mozjpeg/{}\" \"$(location Public/mozjpeg/{})\"".format(header, header) for header in headers
+ ]) +
+ "\n" +
+ "\n".join([
+ "cp -f \"$$BUILD_DIR/build/lib{}.a\" \"$(location Public/mozjpeg/lib/lib{}.a)\"".format(lib, lib) for lib in libs
+ ]) +
+ "\n" + "cp -f \"$$BUILD_DIR/build/jconfig.h\" \"$(location Public/mozjpeg/jconfig.h)\"",
+ outs = ["Public/mozjpeg/" + x for x in headers] +
+ ["Public/mozjpeg/jconfig.h"] +
+ ["Public/mozjpeg/lib/lib{}.a".format(x) for x in libs],
+ tools = [
+ "//third-party/cmake:cmake.tar",
+ ],
+ visibility = [
+ "//visibility:public",
+ ]
+)
+
+cc_library(
+ name = "mozjpeg_lib",
+ srcs = [":Public/mozjpeg/lib/lib" + x + ".a" for x in libs],
+)
+
+objc_library(
+ name = "mozjpeg",
+ module_name = "mozjpeg",
+ enable_modules = True,
+ hdrs = [":Public/mozjpeg/" + x for x in headers],
+ includes = [
+ "Public",
+ "Public/mozjpeg",
+ ],
+ deps = [
+ ":mozjpeg_lib",
+ ],
+ visibility = [
+ "//visibility:public",
+ ],
+)
diff --git a/third-party/mozjpeg/build-mozjpeg-bazel.sh b/third-party/mozjpeg/build-mozjpeg-bazel.sh
new file mode 100755
index 0000000000..94a9966479
--- /dev/null
+++ b/third-party/mozjpeg/build-mozjpeg-bazel.sh
@@ -0,0 +1,62 @@
+#! /bin/sh
+
+set -e
+
+ARCH="$1"
+
+SOURCE_DIR="$2"
+BUILD_DIR=$(echo "$(cd "$(dirname "$3")"; pwd -P)/$(basename "$3")")
+
+if [ "$ARCH" = "armv7" ]; then
+ IOS_PLATFORMDIR="$(xcode-select -p)/Platforms/iPhoneOS.platform"
+ IOS_SYSROOT=($IOS_PLATFORMDIR/Developer/SDKs/iPhoneOS*.sdk)
+ export CFLAGS="-mfloat-abi=softfp -arch armv7 -miphoneos-version-min=9.0"
+ export ASMFLAGS="-no-integrated-as"
+
+ cd "$BUILD_DIR"
+ mkdir build
+ cd build
+
+ touch toolchain.cmake
+ echo "set(CMAKE_SYSTEM_NAME Darwin)" >> toolchain.cmake
+ echo "set(CMAKE_SYSTEM_PROCESSOR arm)" >> toolchain.cmake
+ echo "set(CMAKE_C_COMPILER $(xcode-select -p)/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang)" >> toolchain.cmake
+
+ cmake -G"Unix Makefiles" -DCMAKE_TOOLCHAIN_FILE=toolchain.cmake -DCMAKE_OSX_SYSROOT=${IOS_SYSROOT[0]} -DPNG_SUPPORTED=FALSE -DENABLE_SHARED=FALSE -DWITH_JPEG8=1 ../mozjpeg
+ make
+elif [ "$ARCH" = "arm64" ]; then
+ IOS_PLATFORMDIR="$(xcode-select -p)/Platforms/iPhoneOS.platform"
+ IOS_SYSROOT=($IOS_PLATFORMDIR/Developer/SDKs/iPhoneOS*.sdk)
+ export CFLAGS="-Wall -arch arm64 -miphoneos-version-min=9.0 -funwind-tables"
+
+ cd "$BUILD_DIR"
+ mkdir build
+ cd build
+
+ touch toolchain.cmake
+ echo "set(CMAKE_SYSTEM_NAME Darwin)" >> toolchain.cmake
+ echo "set(CMAKE_SYSTEM_PROCESSOR aarch64)" >> toolchain.cmake
+ echo "set(CMAKE_C_COMPILER $(xcode-select -p)/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang)" >> toolchain.cmake
+
+ cmake -G"Unix Makefiles" -DCMAKE_TOOLCHAIN_FILE=toolchain.cmake -DCMAKE_OSX_SYSROOT=${IOS_SYSROOT[0]} -DPNG_SUPPORTED=FALSE -DENABLE_SHARED=FALSE -DWITH_JPEG8=1 ../mozjpeg
+ make
+elif [ "$ARCH" = "x86_64" ]; then
+ IOS_PLATFORMDIR="$(xcode-select -p)/Platforms/iPhoneSimulator.platform"
+ IOS_SYSROOT=($IOS_PLATFORMDIR/Developer/SDKs/iPhoneSimulator*.sdk)
+ export CFLAGS="-Wall -arch x86_64 -miphoneos-version-min=9.0 -funwind-tables"
+
+ cd "$BUILD_DIR"
+ mkdir build
+ cd build
+
+ touch toolchain.cmake
+ echo "set(CMAKE_SYSTEM_NAME Darwin)" >> toolchain.cmake
+ echo "set(CMAKE_SYSTEM_PROCESSOR AMD64)" >> toolchain.cmake
+ echo "set(CMAKE_C_COMPILER $(xcode-select -p)/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang)" >> toolchain.cmake
+
+ cmake -G"Unix Makefiles" -DCMAKE_TOOLCHAIN_FILE=toolchain.cmake -DCMAKE_OSX_SYSROOT=${IOS_SYSROOT[0]} -DPNG_SUPPORTED=FALSE -DENABLE_SHARED=FALSE -DWITH_JPEG8=1 ../mozjpeg
+ make
+else
+ echo "Unsupported architecture $ARCH"
+ exit 1
+fi
diff --git a/third-party/mozjpeg/build-mozjpeg-buck.sh b/third-party/mozjpeg/build-mozjpeg-buck.sh
new file mode 100755
index 0000000000..94a9966479
--- /dev/null
+++ b/third-party/mozjpeg/build-mozjpeg-buck.sh
@@ -0,0 +1,62 @@
+#! /bin/sh
+
+set -e
+
+ARCH="$1"
+
+SOURCE_DIR="$2"
+BUILD_DIR=$(echo "$(cd "$(dirname "$3")"; pwd -P)/$(basename "$3")")
+
+if [ "$ARCH" = "armv7" ]; then
+ IOS_PLATFORMDIR="$(xcode-select -p)/Platforms/iPhoneOS.platform"
+ IOS_SYSROOT=($IOS_PLATFORMDIR/Developer/SDKs/iPhoneOS*.sdk)
+ export CFLAGS="-mfloat-abi=softfp -arch armv7 -miphoneos-version-min=9.0"
+ export ASMFLAGS="-no-integrated-as"
+
+ cd "$BUILD_DIR"
+ mkdir build
+ cd build
+
+ touch toolchain.cmake
+ echo "set(CMAKE_SYSTEM_NAME Darwin)" >> toolchain.cmake
+ echo "set(CMAKE_SYSTEM_PROCESSOR arm)" >> toolchain.cmake
+ echo "set(CMAKE_C_COMPILER $(xcode-select -p)/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang)" >> toolchain.cmake
+
+ cmake -G"Unix Makefiles" -DCMAKE_TOOLCHAIN_FILE=toolchain.cmake -DCMAKE_OSX_SYSROOT=${IOS_SYSROOT[0]} -DPNG_SUPPORTED=FALSE -DENABLE_SHARED=FALSE -DWITH_JPEG8=1 ../mozjpeg
+ make
+elif [ "$ARCH" = "arm64" ]; then
+ IOS_PLATFORMDIR="$(xcode-select -p)/Platforms/iPhoneOS.platform"
+ IOS_SYSROOT=($IOS_PLATFORMDIR/Developer/SDKs/iPhoneOS*.sdk)
+ export CFLAGS="-Wall -arch arm64 -miphoneos-version-min=9.0 -funwind-tables"
+
+ cd "$BUILD_DIR"
+ mkdir build
+ cd build
+
+ touch toolchain.cmake
+ echo "set(CMAKE_SYSTEM_NAME Darwin)" >> toolchain.cmake
+ echo "set(CMAKE_SYSTEM_PROCESSOR aarch64)" >> toolchain.cmake
+ echo "set(CMAKE_C_COMPILER $(xcode-select -p)/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang)" >> toolchain.cmake
+
+ cmake -G"Unix Makefiles" -DCMAKE_TOOLCHAIN_FILE=toolchain.cmake -DCMAKE_OSX_SYSROOT=${IOS_SYSROOT[0]} -DPNG_SUPPORTED=FALSE -DENABLE_SHARED=FALSE -DWITH_JPEG8=1 ../mozjpeg
+ make
+elif [ "$ARCH" = "x86_64" ]; then
+ IOS_PLATFORMDIR="$(xcode-select -p)/Platforms/iPhoneSimulator.platform"
+ IOS_SYSROOT=($IOS_PLATFORMDIR/Developer/SDKs/iPhoneSimulator*.sdk)
+ export CFLAGS="-Wall -arch x86_64 -miphoneos-version-min=9.0 -funwind-tables"
+
+ cd "$BUILD_DIR"
+ mkdir build
+ cd build
+
+ touch toolchain.cmake
+ echo "set(CMAKE_SYSTEM_NAME Darwin)" >> toolchain.cmake
+ echo "set(CMAKE_SYSTEM_PROCESSOR AMD64)" >> toolchain.cmake
+ echo "set(CMAKE_C_COMPILER $(xcode-select -p)/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang)" >> toolchain.cmake
+
+ cmake -G"Unix Makefiles" -DCMAKE_TOOLCHAIN_FILE=toolchain.cmake -DCMAKE_OSX_SYSROOT=${IOS_SYSROOT[0]} -DPNG_SUPPORTED=FALSE -DENABLE_SHARED=FALSE -DWITH_JPEG8=1 ../mozjpeg
+ make
+else
+ echo "Unsupported architecture $ARCH"
+ exit 1
+fi
diff --git a/third-party/mozjpeg/mozjpeg/.gitignore b/third-party/mozjpeg/mozjpeg/.gitignore
new file mode 100644
index 0000000000..ca5976192d
--- /dev/null
+++ b/third-party/mozjpeg/mozjpeg/.gitignore
@@ -0,0 +1,48 @@
+.DS_Store
+Makefile.in
+Makefile
+/CMakeFiles
+/autom4te.cache
+/aclocal.m4
+/compile
+/configure
+/depcomp
+/install-sh
+/libtool
+/missing
+/stamp-h*
+/java/classnoinst.stamp
+/pkgscripts/
+/jconfig.h
+/jconfigint.h
+/config.guess
+/config.h
+/config.h.in
+/config.log
+/config.status
+/config.sub
+/ltmain.sh
+/ar-lib
+/libjpeg.map
+/.libs/
+/simd/.libs/
+/simd/jsimdcfg.inc
+*.o
+*.lo
+*.la
+/cjpeg
+/djpeg
+/jcstest
+/jpegtran
+/jpegyuv
+/md5/md5cmp
+/rdjpgcom
+/test_enc_*
+/tjbench
+/tjbenchtest
+/tjunittest
+/wrjpgcom
+/yuvjpeg
+test_yuv*.jpg
+CMakeCache.txt
+CTestTestfile.cmake
\ No newline at end of file
diff --git a/third-party/mozjpeg/mozjpeg/BUILDING.md b/third-party/mozjpeg/mozjpeg/BUILDING.md
new file mode 100644
index 0000000000..4468ea55c0
--- /dev/null
+++ b/third-party/mozjpeg/mozjpeg/BUILDING.md
@@ -0,0 +1,840 @@
+Building libjpeg-turbo
+======================
+
+
+Build Requirements
+------------------
+
+
+### All Systems
+
+- [CMake](http://www.cmake.org) v2.8.12 or later
+
+- [NASM](http://www.nasm.us) or [YASM](http://yasm.tortall.net)
+ (if building x86 or x86-64 SIMD extensions)
+ * If using NASM, 2.10 or later is required.
+ * If using NASM, 2.10 or later (except 2.11.08) is required for an x86-64 Mac
+ build (2.11.08 does not work properly with libjpeg-turbo's x86-64 SIMD code
+ when building macho64 objects.)
+ * If using YASM, 1.2.0 or later is required.
+ * If building on macOS, NASM or YASM can be obtained from
+ [MacPorts](http://www.macports.org/) or [Homebrew](http://brew.sh/).
+ - NOTE: Currently, if it is desirable to hide the SIMD function symbols in
+ Mac executables or shared libraries that statically link with
+ libjpeg-turbo, then NASM 2.14 or later or YASM must be used when
+ building libjpeg-turbo.
+ * If building on Windows, **nasm.exe**/**yasm.exe** should be in your `PATH`.
+ * NASM and YASM are located in the CRB (Code Ready Builder) repository on
+ Red Hat Enterprise Linux 8 and in the PowerTools repository on CentOS 8,
+ which is not enabled by default.
+
+ The binary RPMs released by the NASM project do not work on older Linux
+ systems, such as Red Hat Enterprise Linux 5. On such systems, you can easily
+ build and install NASM from a source RPM by downloading one of the SRPMs from
+
+
+
+ and executing the following as root:
+
+ ARCH=`uname -m`
+ rpmbuild --rebuild nasm-{version}.src.rpm
+ rpm -Uvh /usr/src/redhat/RPMS/$ARCH/nasm-{version}.$ARCH.rpm
+
+ NOTE: the NASM build will fail if texinfo is not installed.
+
+
+### Un*x Platforms (including Linux, Mac, FreeBSD, Solaris, and Cygwin)
+
+- GCC v4.1 (or later) or Clang recommended for best performance
+
+- If building the TurboJPEG Java wrapper, JDK or OpenJDK 1.5 or later is
+ required. Most modern Linux distributions, as well as Solaris 10 and later,
+ include JDK or OpenJDK. On OS X 10.5 and 10.6, it will be necessary to
+ install the Java Developer Package, which can be downloaded from
+ (Apple ID required.) For other
+ systems, you can obtain the Oracle Java Development Kit from
+ .
+
+ * If using JDK 11 or later, CMake 3.10.x or later must also be used.
+
+### Windows
+
+- Microsoft Visual C++ 2005 or later
+
+ If you don't already have Visual C++, then the easiest way to get it is by
+ installing the
+ [Windows SDK](http://msdn.microsoft.com/en-us/windows/bb980924.aspx).
+ The Windows SDK includes both 32-bit and 64-bit Visual C++ compilers and
+ everything necessary to build libjpeg-turbo.
+
+ * You can also use Microsoft Visual Studio Express/Community Edition, which
+ is a free download. (NOTE: versions prior to 2012 can only be used to
+ build 32-bit code.)
+ * If you intend to build libjpeg-turbo from the command line, then add the
+ appropriate compiler and SDK directories to the `INCLUDE`, `LIB`, and
+ `PATH` environment variables. This is generally accomplished by
+ executing `vcvars32.bat` or `vcvars64.bat` and `SetEnv.cmd`.
+ `vcvars32.bat` and `vcvars64.bat` are part of Visual C++ and are located in
+ the same directory as the compiler. `SetEnv.cmd` is part of the Windows
+ SDK. You can pass optional arguments to `SetEnv.cmd` to specify a 32-bit
+ or 64-bit build environment.
+
+ ... OR ...
+
+- MinGW
+
+ [MSYS2](http://msys2.github.io/) or [tdm-gcc](http://tdm-gcc.tdragon.net/)
+ recommended if building on a Windows machine. Both distributions install a
+ Start Menu link that can be used to launch a command prompt with the
+ appropriate compiler paths automatically set.
+
+- If building the TurboJPEG Java wrapper, JDK 1.5 or later is required. This
+ can be downloaded from
+ .
+
+ * If using JDK 11 or later, CMake 3.10.x or later must also be used.
+
+- Vcpkg
+
+ You can download and install mozjpeg using the [vcpkg](https://github.com/Microsoft/vcpkg) dependency manager:
+
+ git clone https://github.com/Microsoft/vcpkg.git
+ cd vcpkg
+ ./bootstrap-vcpkg.sh
+ ./vcpkg integrate install
+ vcpkg install mozjpeg
+
+ The mozjpeg port in vcpkg is kept up to date by Microsoft team members and community contributors. If the version is out of date, please [create an issue or pull request](https://github.com/Microsoft/vcpkg) on the vcpkg repository.
+
+
+Out-of-Tree Builds
+------------------
+
+Binary objects, libraries, and executables are generated in the directory from
+which CMake is executed (the "binary directory"), and this directory need not
+necessarily be the same as the libjpeg-turbo source directory. You can create
+multiple independent binary directories, in which different versions of
+libjpeg-turbo can be built from the same source tree using different compilers
+or settings. In the sections below, *{build_directory}* refers to the binary
+directory, whereas *{source_directory}* refers to the libjpeg-turbo source
+directory. For in-tree builds, these directories are the same.
+
+
+Build Procedure
+---------------
+
+NOTE: The build procedures below assume that CMake is invoked from the command
+line, but all of these procedures can be adapted to the CMake GUI as
+well.
+
+
+### Un*x
+
+The following procedure will build libjpeg-turbo on Unix and Unix-like systems.
+(On Solaris, this generates a 32-bit build. See "Build Recipes" below for
+64-bit build instructions.)
+
+ cd {build_directory}
+ cmake -G"Unix Makefiles" [additional CMake flags] {source_directory}
+ make
+
+This will generate the following files under *{build_directory}*:
+
+**libjpeg.a**