Merge commit 'a53de39bf56182a6b8acd1fe1d19935b9a070778'

This commit is contained in:
Peter 2019-07-28 00:40:56 +01:00
commit 5e1c4465ca
73 changed files with 4228 additions and 3032 deletions

View File

@ -12,6 +12,24 @@
090E777622A6945900CD99F5 /* BlackClassicIcon@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 090E777222A6945800CD99F5 /* BlackClassicIcon@3x.png */; };
090E777722A6945900CD99F5 /* BlueClassicIcon@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 090E777322A6945800CD99F5 /* BlueClassicIcon@2x.png */; };
092F368521542D6C001A9F49 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 092F368321542D6C001A9F49 /* Localizable.strings */; };
094DDF0822E7A0D3004B0256 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 094DDF0222E7A0D3004B0256 /* Localizable.strings */; };
094DDF0922E7A0D3004B0256 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 094DDF0422E7A0D3004B0256 /* InfoPlist.strings */; };
094DDF0A22E7A0D3004B0256 /* AppIntentVocabulary.plist in Resources */ = {isa = PBXBuildFile; fileRef = 094DDF0622E7A0D3004B0256 /* AppIntentVocabulary.plist */; };
094DDF3222E7A61B004B0256 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 094DDF2C22E7A61B004B0256 /* Localizable.strings */; };
094DDF3322E7A61B004B0256 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 094DDF2E22E7A61B004B0256 /* InfoPlist.strings */; };
094DDF3422E7A61B004B0256 /* AppIntentVocabulary.plist in Resources */ = {isa = PBXBuildFile; fileRef = 094DDF3022E7A61B004B0256 /* AppIntentVocabulary.plist */; };
094DDF3C22E7A98E004B0256 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 094DDF3622E7A98E004B0256 /* Localizable.strings */; };
094DDF3D22E7A98E004B0256 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 094DDF3822E7A98E004B0256 /* InfoPlist.strings */; };
094DDF3E22E7A98E004B0256 /* AppIntentVocabulary.plist in Resources */ = {isa = PBXBuildFile; fileRef = 094DDF3A22E7A98E004B0256 /* AppIntentVocabulary.plist */; };
094DDF4622E7A9A8004B0256 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 094DDF4022E7A9A8004B0256 /* Localizable.strings */; };
094DDF4722E7A9A8004B0256 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 094DDF4222E7A9A8004B0256 /* InfoPlist.strings */; };
094DDF4822E7A9A8004B0256 /* AppIntentVocabulary.plist in Resources */ = {isa = PBXBuildFile; fileRef = 094DDF4422E7A9A8004B0256 /* AppIntentVocabulary.plist */; };
094DDF5722E8C310004B0256 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 094DDF4A22E8C30F004B0256 /* Localizable.strings */; };
094DDF5822E8C310004B0256 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 094DDF4C22E8C30F004B0256 /* InfoPlist.strings */; };
094DDF5922E8C310004B0256 /* AppIntentVocabulary.plist in Resources */ = {isa = PBXBuildFile; fileRef = 094DDF4E22E8C30F004B0256 /* AppIntentVocabulary.plist */; };
094DDF5A22E8C310004B0256 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 094DDF5122E8C30F004B0256 /* Localizable.strings */; };
094DDF5B22E8C310004B0256 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 094DDF5322E8C30F004B0256 /* InfoPlist.strings */; };
094DDF5C22E8C310004B0256 /* AppIntentVocabulary.plist in Resources */ = {isa = PBXBuildFile; fileRef = 094DDF5522E8C30F004B0256 /* AppIntentVocabulary.plist */; };
0956AF2F217B8109008106D0 /* TGNeoUnsupportedMessageViewModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 0956AF2E217B8109008106D0 /* TGNeoUnsupportedMessageViewModel.m */; };
0972C6E021791D950069E98A /* UserNotifications.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0972C6DF21791D950069E98A /* UserNotifications.framework */; };
0972C6E421792D130069E98A /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 0972C6E221792D120069E98A /* InfoPlist.strings */; };
@ -514,6 +532,24 @@
090E777222A6945800CD99F5 /* BlackClassicIcon@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "BlackClassicIcon@3x.png"; sourceTree = "<group>"; };
090E777322A6945800CD99F5 /* BlueClassicIcon@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "BlueClassicIcon@2x.png"; sourceTree = "<group>"; };
092F368421542D6C001A9F49 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = Share/en.lproj/Localizable.strings; sourceTree = SOURCE_ROOT; };
094DDF0322E7A0D3004B0256 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = Localizable.strings; sourceTree = "<group>"; };
094DDF0522E7A0D3004B0256 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = InfoPlist.strings; sourceTree = "<group>"; };
094DDF0722E7A0D3004B0256 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = fr; path = AppIntentVocabulary.plist; sourceTree = "<group>"; };
094DDF2D22E7A61B004B0256 /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = Localizable.strings; sourceTree = "<group>"; };
094DDF2F22E7A61B004B0256 /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = InfoPlist.strings; sourceTree = "<group>"; };
094DDF3122E7A61B004B0256 /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = tr; path = AppIntentVocabulary.plist; sourceTree = "<group>"; };
094DDF3722E7A98E004B0256 /* ca */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ca; path = Localizable.strings; sourceTree = "<group>"; };
094DDF3922E7A98E004B0256 /* ca */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ca; path = InfoPlist.strings; sourceTree = "<group>"; };
094DDF3B22E7A98E004B0256 /* ca */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = ca; path = AppIntentVocabulary.plist; sourceTree = "<group>"; };
094DDF4122E7A9A8004B0256 /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = Localizable.strings; sourceTree = "<group>"; };
094DDF4322E7A9A8004B0256 /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = InfoPlist.strings; sourceTree = "<group>"; };
094DDF4522E7A9A8004B0256 /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = uk; path = AppIntentVocabulary.plist; sourceTree = "<group>"; };
094DDF4B22E8C30F004B0256 /* ms */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ms; path = Localizable.strings; sourceTree = "<group>"; };
094DDF4D22E8C30F004B0256 /* ms */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ms; path = InfoPlist.strings; sourceTree = "<group>"; };
094DDF4F22E8C30F004B0256 /* ms */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = ms; path = AppIntentVocabulary.plist; sourceTree = "<group>"; };
094DDF5222E8C30F004B0256 /* id */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = id; path = Localizable.strings; sourceTree = "<group>"; };
094DDF5422E8C30F004B0256 /* id */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = id; path = InfoPlist.strings; sourceTree = "<group>"; };
094DDF5622E8C30F004B0256 /* id */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = id; path = AppIntentVocabulary.plist; sourceTree = "<group>"; };
0956AF2B217B4642008106D0 /* WatchCommunicationManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WatchCommunicationManager.swift; sourceTree = "<group>"; };
0956AF2D217B8109008106D0 /* TGNeoUnsupportedMessageViewModel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TGNeoUnsupportedMessageViewModel.h; sourceTree = "<group>"; };
0956AF2E217B8109008106D0 /* TGNeoUnsupportedMessageViewModel.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TGNeoUnsupportedMessageViewModel.m; sourceTree = "<group>"; };
@ -1213,6 +1249,66 @@
path = en.lproj;
sourceTree = "<group>";
};
094DDF0122E7A0D3004B0256 /* fr.lproj */ = {
isa = PBXGroup;
children = (
094DDF0222E7A0D3004B0256 /* Localizable.strings */,
094DDF0422E7A0D3004B0256 /* InfoPlist.strings */,
094DDF0622E7A0D3004B0256 /* AppIntentVocabulary.plist */,
);
path = fr.lproj;
sourceTree = "<group>";
};
094DDF2B22E7A61B004B0256 /* tr.lproj */ = {
isa = PBXGroup;
children = (
094DDF2C22E7A61B004B0256 /* Localizable.strings */,
094DDF2E22E7A61B004B0256 /* InfoPlist.strings */,
094DDF3022E7A61B004B0256 /* AppIntentVocabulary.plist */,
);
path = tr.lproj;
sourceTree = "<group>";
};
094DDF3522E7A98E004B0256 /* ca.lproj */ = {
isa = PBXGroup;
children = (
094DDF3622E7A98E004B0256 /* Localizable.strings */,
094DDF3822E7A98E004B0256 /* InfoPlist.strings */,
094DDF3A22E7A98E004B0256 /* AppIntentVocabulary.plist */,
);
path = ca.lproj;
sourceTree = "<group>";
};
094DDF3F22E7A9A8004B0256 /* uk.lproj */ = {
isa = PBXGroup;
children = (
094DDF4022E7A9A8004B0256 /* Localizable.strings */,
094DDF4222E7A9A8004B0256 /* InfoPlist.strings */,
094DDF4422E7A9A8004B0256 /* AppIntentVocabulary.plist */,
);
path = uk.lproj;
sourceTree = "<group>";
};
094DDF4922E8C30F004B0256 /* ms.lproj */ = {
isa = PBXGroup;
children = (
094DDF4A22E8C30F004B0256 /* Localizable.strings */,
094DDF4C22E8C30F004B0256 /* InfoPlist.strings */,
094DDF4E22E8C30F004B0256 /* AppIntentVocabulary.plist */,
);
path = ms.lproj;
sourceTree = "<group>";
};
094DDF5022E8C30F004B0256 /* id.lproj */ = {
isa = PBXGroup;
children = (
094DDF5122E8C30F004B0256 /* Localizable.strings */,
094DDF5322E8C30F004B0256 /* InfoPlist.strings */,
094DDF5522E8C30F004B0256 /* AppIntentVocabulary.plist */,
);
path = id.lproj;
sourceTree = "<group>";
};
0972C6E121792CED0069E98A /* ru.lproj */ = {
isa = PBXGroup;
children = (
@ -1868,13 +1964,19 @@
D0612E481D58B478000C8F02 /* Application.swift */,
D09DCBB41D0C854D00F51FFE /* en.lproj */,
D0CE6F47213EDA4400BCD44B /* ar.lproj */,
094DDF3522E7A98E004B0256 /* ca.lproj */,
D0CE6F4E213EDA4400BCD44B /* de.lproj */,
D0CE6F40213EDA4400BCD44B /* es.lproj */,
094DDF0122E7A0D3004B0256 /* fr.lproj */,
094DDF5022E8C30F004B0256 /* id.lproj */,
D0CE6F24213EDA4300BCD44B /* it.lproj */,
D0CE6F2B213EDA4300BCD44B /* ko.lproj */,
094DDF4922E8C30F004B0256 /* ms.lproj */,
D0CE6F32213EDA4300BCD44B /* nl.lproj */,
D0CE6F1D213EDA4200BCD44B /* pt.lproj */,
D0CE6F39213EDA4300BCD44B /* ru.lproj */,
094DDF2B22E7A61B004B0256 /* tr.lproj */,
094DDF3F22E7A9A8004B0256 /* uk.lproj */,
D00859A01B28189D00EAF753 /* Info.plist */,
D09A59B71B5876B600FC3724 /* Telegram-Bridging-Header.h */,
D02E31221BD803E800CD3F01 /* main.m */,
@ -2225,9 +2327,9 @@
D09DCBB41D0C854D00F51FFE /* en.lproj */ = {
isa = PBXGroup;
children = (
D00ED7581FE94630001F38BD /* AppIntentVocabulary.plist */,
D09DCBB51D0C856B00F51FFE /* Localizable.strings */,
D00ED75B1FE95287001F38BD /* InfoPlist.strings */,
D00ED7581FE94630001F38BD /* AppIntentVocabulary.plist */,
);
name = en.lproj;
sourceTree = "<group>";
@ -2342,15 +2444,15 @@
D0E41A3A1D65A69C00FBFC00 /* Widget */ = {
isa = PBXGroup;
children = (
0972C6E121792CED0069E98A /* ru.lproj */,
D04FA1AE2145E37F0006EF45 /* en.lproj */,
D04FA1B62145E3800006EF45 /* ar.lproj */,
D04FA1B32145E37F0006EF45 /* de.lproj */,
D04FA1AE2145E37F0006EF45 /* en.lproj */,
D04FA1C52145E3810006EF45 /* es.lproj */,
D04FA1BF2145E3800006EF45 /* it.lproj */,
D04FA1BC2145E3800006EF45 /* ko.lproj */,
D04FA1B92145E3800006EF45 /* nl.lproj */,
D04FA1C22145E3810006EF45 /* pt.lproj */,
0972C6E121792CED0069E98A /* ru.lproj */,
D0ADF955212B3B6400310BBC /* Widget-AppStoreLLC.entitlements */,
D0B2F75A204F51E400D3BFB9 /* Widget-AppStore.entitlements */,
D0B2F75B204F51E500D3BFB9 /* Widget-HockeyApp.entitlements */,
@ -2658,6 +2760,12 @@
es,
ar,
de,
fr,
tr,
ca,
uk,
ms,
id,
);
mainGroup = D00859931B28189D00EAF753;
productRefGroup = D008599D1B28189D00EAF753 /* Products */;
@ -2728,6 +2836,9 @@
D04DCC341F71C80000B021D7 /* 7.m4a in Resources */,
09E9600722C23FF200B13673 /* BlackNotificationIcon@3x.png in Resources */,
09EBE2A522B004EA00F670AB /* BlueFilledIconIpad.png in Resources */,
094DDF5C22E8C310004B0256 /* AppIntentVocabulary.plist in Resources */,
094DDF3C22E7A98E004B0256 /* Localizable.strings in Resources */,
094DDF3422E7A61B004B0256 /* AppIntentVocabulary.plist in Resources */,
D0CE6F60213EDA4400BCD44B /* AppIntentVocabulary.plist in Resources */,
D0CE6F63213EDA4400BCD44B /* AppIntentVocabulary.plist in Resources */,
D00ED75D1FE95287001F38BD /* InfoPlist.strings in Resources */,
@ -2747,16 +2858,21 @@
09E9601322C2441000B13673 /* BlackClassicNotificationIcon@2x.png in Resources */,
D0E8C2DE2285EA55009F26E8 /* BlackIcon@2x.png in Resources */,
D09DCBB71D0C856B00F51FFE /* Localizable.strings in Resources */,
094DDF5722E8C310004B0256 /* Localizable.strings in Resources */,
D0CE6F66213EDA4400BCD44B /* AppIntentVocabulary.plist in Resources */,
D08DB0B8213F4D1D00F2ADBF /* powerful_mask@2x.png in Resources */,
D08DB0B4213F4D1D00F2ADBF /* knot_down@2x.png in Resources */,
094DDF4822E7A9A8004B0256 /* AppIntentVocabulary.plist in Resources */,
09EC5CDA22CBBF9600292E42 /* telegram_plane1@2x.png in Resources */,
D08DB0BC213F4D1D00F2ADBF /* start_arrow@2x.png in Resources */,
D08DB0B6213F4D1D00F2ADBF /* powerful_infinity@2x.png in Resources */,
094DDF5B22E8C310004B0256 /* InfoPlist.strings in Resources */,
09EBE2AA22B004EA00F670AB /* BlueIconLargeIpad@2x.png in Resources */,
D0CE6F5B213EDA4400BCD44B /* Localizable.strings in Resources */,
09EBE2AF22B004EA00F670AB /* BlackIconIpad@2x.png in Resources */,
094DDF5922E8C310004B0256 /* AppIntentVocabulary.plist in Resources */,
09A4193422B7A4D500637EB4 /* BlackClassicIconIpad.png in Resources */,
094DDF4622E7A9A8004B0256 /* Localizable.strings in Resources */,
09A4193222B7A4D500637EB4 /* BlueClassicIconIpad.png in Resources */,
D0CE6F62213EDA4400BCD44B /* InfoPlist.strings in Resources */,
D08DB0A8213F4D1D00F2ADBF /* fast_arrow_shadow@2x.png in Resources */,
@ -2767,13 +2883,17 @@
09E9601722C2441000B13673 /* BlackClassicNotificationIcon.png in Resources */,
D0CE6F64213EDA4400BCD44B /* Localizable.strings in Resources */,
D0CE6F6C213EDA4400BCD44B /* AppIntentVocabulary.plist in Resources */,
094DDF3E22E7A98E004B0256 /* AppIntentVocabulary.plist in Resources */,
094DDF3D22E7A98E004B0256 /* InfoPlist.strings in Resources */,
D04DCC231F71C80000B021D7 /* 100.m4a in Resources */,
09E9600A22C23FF200B13673 /* BlueNotificationIcon@2x.png in Resources */,
094DDF3222E7A61B004B0256 /* Localizable.strings in Resources */,
09A218EF22A1570A00DE6898 /* BlueIcon@2x.png in Resources */,
D04DCC281F71C80000B021D7 /* 105.m4a in Resources */,
D08DB0BB213F4D1D00F2ADBF /* private_screw@2x.png in Resources */,
D0CE6F5F213EDA4400BCD44B /* InfoPlist.strings in Resources */,
D04DCC2D1F71C80000B021D7 /* 110.m4a in Resources */,
094DDF5A22E8C310004B0256 /* Localizable.strings in Resources */,
D04DCC2B1F71C80000B021D7 /* 108.m4a in Resources */,
D00859AC1B28189D00EAF753 /* LaunchScreen.xib in Resources */,
D08DB0B5213F4D1D00F2ADBF /* knot_up1@2x.png in Resources */,
@ -2803,6 +2923,7 @@
09EBE2B022B004EA00F670AB /* BlueFilledIconLargeIpad@2x.png in Resources */,
D0CE6F58213EDA4400BCD44B /* Localizable.strings in Resources */,
D08DB0AF213F4D1D00F2ADBF /* ic_pencil@2x.png in Resources */,
094DDF5822E8C310004B0256 /* InfoPlist.strings in Resources */,
09E9601822C2441000B13673 /* BlueClassicNotificationIcon@3x.png in Resources */,
D0CE6F67213EDA4400BCD44B /* Localizable.strings in Resources */,
09A4193322B7A4D500637EB4 /* BlueClassicIconLargeIpad@2x.png in Resources */,
@ -2811,12 +2932,16 @@
D052974622B0073F004ABAF6 /* WhiteFilledIcon@3x.png in Resources */,
D08DB0B7213F4D1D00F2ADBF /* powerful_infinity_white@2x.png in Resources */,
D00859A91B28189D00EAF753 /* Images.xcassets in Resources */,
094DDF0922E7A0D3004B0256 /* InfoPlist.strings in Resources */,
D001D5AA1F878DA300DF975A /* PhoneCountries.txt in Resources */,
094DDF0822E7A0D3004B0256 /* Localizable.strings in Resources */,
094DDF4722E7A9A8004B0256 /* InfoPlist.strings in Resources */,
D0CE6F56213EDA4400BCD44B /* InfoPlist.strings in Resources */,
D0CE6F65213EDA4400BCD44B /* InfoPlist.strings in Resources */,
D0E8B8B12044496C00605593 /* voip_busy.caf in Resources */,
09EBE2A622B004EA00F670AB /* BlueIconIpad@2x.png in Resources */,
D08DB0A7213F4D1D00F2ADBF /* fast_arrow@2x.png in Resources */,
094DDF0A22E7A0D3004B0256 /* AppIntentVocabulary.plist in Resources */,
D0E8B8AF2044496C00605593 /* voip_fail.caf in Resources */,
D0CE6F55213EDA4400BCD44B /* Localizable.strings in Resources */,
D08DB0B2213F4D1D00F2ADBF /* ic_smile_eye@2x.png in Resources */,
@ -2834,6 +2959,7 @@
09EBE2A822B004EA00F670AB /* BlackFilledIconIpad@2x.png in Resources */,
D021D4D9219CAEDD0064BEBA /* Config-Fork.xcconfig in Resources */,
D08DB0AD213F4D1D00F2ADBF /* ic_cam@2x.png in Resources */,
094DDF3322E7A61B004B0256 /* InfoPlist.strings in Resources */,
090E777622A6945900CD99F5 /* BlackClassicIcon@3x.png in Resources */,
D0E8B8B02044496C00605593 /* voip_ringback.caf in Resources */,
09E9600822C23FF200B13673 /* BlackNotificationIcon.png in Resources */,
@ -3168,6 +3294,150 @@
name = Localizable.strings;
sourceTree = "<group>";
};
094DDF0222E7A0D3004B0256 /* Localizable.strings */ = {
isa = PBXVariantGroup;
children = (
094DDF0322E7A0D3004B0256 /* fr */,
);
name = Localizable.strings;
sourceTree = "<group>";
};
094DDF0422E7A0D3004B0256 /* InfoPlist.strings */ = {
isa = PBXVariantGroup;
children = (
094DDF0522E7A0D3004B0256 /* fr */,
);
name = InfoPlist.strings;
sourceTree = "<group>";
};
094DDF0622E7A0D3004B0256 /* AppIntentVocabulary.plist */ = {
isa = PBXVariantGroup;
children = (
094DDF0722E7A0D3004B0256 /* fr */,
);
name = AppIntentVocabulary.plist;
sourceTree = "<group>";
};
094DDF2C22E7A61B004B0256 /* Localizable.strings */ = {
isa = PBXVariantGroup;
children = (
094DDF2D22E7A61B004B0256 /* tr */,
);
name = Localizable.strings;
sourceTree = "<group>";
};
094DDF2E22E7A61B004B0256 /* InfoPlist.strings */ = {
isa = PBXVariantGroup;
children = (
094DDF2F22E7A61B004B0256 /* tr */,
);
name = InfoPlist.strings;
sourceTree = "<group>";
};
094DDF3022E7A61B004B0256 /* AppIntentVocabulary.plist */ = {
isa = PBXVariantGroup;
children = (
094DDF3122E7A61B004B0256 /* tr */,
);
name = AppIntentVocabulary.plist;
sourceTree = "<group>";
};
094DDF3622E7A98E004B0256 /* Localizable.strings */ = {
isa = PBXVariantGroup;
children = (
094DDF3722E7A98E004B0256 /* ca */,
);
name = Localizable.strings;
sourceTree = "<group>";
};
094DDF3822E7A98E004B0256 /* InfoPlist.strings */ = {
isa = PBXVariantGroup;
children = (
094DDF3922E7A98E004B0256 /* ca */,
);
name = InfoPlist.strings;
sourceTree = "<group>";
};
094DDF3A22E7A98E004B0256 /* AppIntentVocabulary.plist */ = {
isa = PBXVariantGroup;
children = (
094DDF3B22E7A98E004B0256 /* ca */,
);
name = AppIntentVocabulary.plist;
sourceTree = "<group>";
};
094DDF4022E7A9A8004B0256 /* Localizable.strings */ = {
isa = PBXVariantGroup;
children = (
094DDF4122E7A9A8004B0256 /* uk */,
);
name = Localizable.strings;
sourceTree = "<group>";
};
094DDF4222E7A9A8004B0256 /* InfoPlist.strings */ = {
isa = PBXVariantGroup;
children = (
094DDF4322E7A9A8004B0256 /* uk */,
);
name = InfoPlist.strings;
sourceTree = "<group>";
};
094DDF4422E7A9A8004B0256 /* AppIntentVocabulary.plist */ = {
isa = PBXVariantGroup;
children = (
094DDF4522E7A9A8004B0256 /* uk */,
);
name = AppIntentVocabulary.plist;
sourceTree = "<group>";
};
094DDF4A22E8C30F004B0256 /* Localizable.strings */ = {
isa = PBXVariantGroup;
children = (
094DDF4B22E8C30F004B0256 /* ms */,
);
name = Localizable.strings;
sourceTree = "<group>";
};
094DDF4C22E8C30F004B0256 /* InfoPlist.strings */ = {
isa = PBXVariantGroup;
children = (
094DDF4D22E8C30F004B0256 /* ms */,
);
name = InfoPlist.strings;
sourceTree = "<group>";
};
094DDF4E22E8C30F004B0256 /* AppIntentVocabulary.plist */ = {
isa = PBXVariantGroup;
children = (
094DDF4F22E8C30F004B0256 /* ms */,
);
name = AppIntentVocabulary.plist;
sourceTree = "<group>";
};
094DDF5122E8C30F004B0256 /* Localizable.strings */ = {
isa = PBXVariantGroup;
children = (
094DDF5222E8C30F004B0256 /* id */,
);
name = Localizable.strings;
sourceTree = "<group>";
};
094DDF5322E8C30F004B0256 /* InfoPlist.strings */ = {
isa = PBXVariantGroup;
children = (
094DDF5422E8C30F004B0256 /* id */,
);
name = InfoPlist.strings;
sourceTree = "<group>";
};
094DDF5522E8C30F004B0256 /* AppIntentVocabulary.plist */ = {
isa = PBXVariantGroup;
children = (
094DDF5622E8C30F004B0256 /* id */,
);
name = AppIntentVocabulary.plist;
sourceTree = "<group>";
};
0972C6E221792D120069E98A /* InfoPlist.strings */ = {
isa = PBXVariantGroup;
children = (

View File

@ -33,5 +33,7 @@
<string>merchant.privatbank.test.telergramios</string>
<string>merchant.privatbank.prod.telergram</string>
</array>
<key>com.apple.developer.carplay-messaging</key><true/>
<key>com.apple.developer.carplay-calling</key><true/>
</dict>
</plist>

View File

@ -1,16 +1,12 @@
/* Localized versions of Info.plist keys */
"CFBundleDisplayName" = "تيليجرام";
"NSContactsUsageDescription" = "سيقوم تيليجرام برفع جهات الاتصال الخاصة بك باستمرار إلى خوادم التخزين السحابية ذات التشفير العالي لتتمكن من التواصل مع أصدقائك من خلال جميع أجهزتك.";
"NSLocationWhenInUseUsageDescription" = "عندما ترغب في مشاركة مكانك مع أصدقائك، تيليجرام يحتاج لصلاحيات لعرض الخريطة لهم.";
"NSLocationAlwaysAndWhenInUseUsageDescription" = "عندما تختار أن تشارك مكانك بشكل حي مع أصدقائك في المحادثة، يحتاج تيليجرام إلى الوصول لموقعك في الخلفية حتى بعد إغلاق تيليجرام خلال فترة المشاركة.";
"NSLocationAlwaysUsageDescription" = "عندما تقوم بمشاركة موقعك مع أصدقائك، تيليجرام يحتاج إلى الصلاحية ليعرض لهم الخريطة. كما تحتاج لإعطاء تيليجرام الصلاحية لتتمكن من إرسال موقعك من ساعة آبل.";
"NSCameraUsageDescription" = "نحتاج ذلك لتتمكن من التقاط وإرسال الصور والفيديوهات.";
"NSPhotoLibraryUsageDescription" = "نحتاج ذلك لتتمكن من إرسال الصور والفيديوهات من ألبوم الصور.";
"NSPhotoLibraryAddUsageDescription" = "نحتاج هذه الصلاحية لتتمكن من حفظ وسائطك في مكتبة الصور الخاصة بك.";
"NSMicrophoneUsageDescription" = "نحتاج ذلك لتتمكن من تسجيل رسائل صوتية وفيديوهات بالصوت لترسلها..";
"NSSiriUsageDescription" = "يمكنك استخدام سيري لإرسال رسائلك.";
"NSLocationAlwaysAndWhenInUseUsageDescription" = "عندما تختار أن تشارك مكانك بشكل حي مع أصدقائك في المحادثة، يحتاج تيليجرام إلى الوصول لموقعك في الخلفية حتى بعد إغلاق تيليجرام خلال فترة المشاركة.";
"NSLocationAlwaysUsageDescription" = "عندما تقوم بمشاركة موقعك مع أصدقائك، تيليجرام يحتاج إلى الصلاحية ليعرض لهم الخريطة. كما تحتاج لإعطاء تيليجرام الصلاحية لتتمكن من إرسال موقعك من ساعة آبل.";
"NSLocationWhenInUseUsageDescription" = "عندما ترغب في مشاركة مكانك مع أصدقائك، تيليجرام يحتاج لصلاحيات لعرض الخريطة لهم.";
"NSFaceIDUsageDescription" = "يمكنك استخدام Face ID لفتح قفل التطبيق.";

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IntentPhrases</key>
<array>
<dict>
<key>IntentName</key>
<string>INSendMessageIntent</string>
<key>IntentExamples</key>
<array>
<string>Send a Telegram message to Alex saying I&apos;ll be there in 10 minutes</string>
</array>
</dict>
</array>
</dict>
</plist>

View File

@ -0,0 +1,12 @@
/* Localized versions of Info.plist keys */
"NSContactsUsageDescription" = "Telegram pujarà automàticament els vostres contactes als seus servidors xifrats, perquè pugueu connectar-vos amb els amics des de qualsevol dispositiu.";
"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.";
"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.";
"NSSiriUsageDescription" = "Podeu usar Siri per a enviar missatges.";
"NSFaceIDUsageDescription" = "Podeu emprar Face ID per a desblocar l'aplicació.";

View File

@ -0,0 +1 @@

View File

@ -2,13 +2,11 @@
"NSContactsUsageDescription" = "Telegram lädt deine Kontakte durchgehend auf die stark verschlüsselten Cloud Server, damit du dich mit deinen Freunden auf all deinen Geräten verbinden kannst.";
"NSLocationWhenInUseUsageDescription" = "Wenn du Freunden deinen Standort mitteilen willst, musst du Telegram den Zugriff darauf erlauben.";
"NSLocationAlwaysAndWhenInUseUsageDescription" = "Wenn du deinen Live-Standort mit Freunden im Chat teilen möchtest, benötigt Telegram so lange im Hintergrund Zugriff auf deinen Standort, bis du ihn nicht mehr teilen willst.";
"NSLocationAlwaysUsageDescription" = "Wenn du Freunden deinen Standort mitteilen willst, musst du Telegram den Zugriff darauf erlauben. Diese Bereichtigung wird auch für die Apple Watch benötigt.";
"NSCameraUsageDescription" = "Brauchen wir, damit du Bilder und Videos aufnehmen und teilen kannst.";
"NSPhotoLibraryUsageDescription" = "Brauchen wir, damit du Bilder und Videos aus deiner Fotomediathek teilen kannst.";
"NSPhotoLibraryAddUsageDescription" = "Brauchen wir, damit du Bilder und Videos in deiner Fotomediathek speichern kannst.";
"NSMicrophoneUsageDescription" = "Brauchen wir, damit du Sprachnachrichten aufnehmen und Videos mit Ton teilen kannst.";
"NSSiriUsageDescription" = "Mit Siri kannst du Nachrichten senden.";
"NSLocationAlwaysAndWhenInUseUsageDescription" = "Wenn du deinen Live-Standort mit Freunden im Chat teilen möchtest, benötigt Telegram so lange im Hintergrund Zugriff auf deinen Standort, bis du ihn nicht mehr teilen willst.";
"NSLocationAlwaysUsageDescription" = "Wenn du Freunden deinen Standort mitteilen willst, musst du Telegram den Zugriff darauf erlauben. Diese Bereichtigung wird auch für die Apple Watch benötigt.";
"NSLocationWhenInUseUsageDescription" = "Wenn du Freunden deinen Standort mitteilen willst, musst du Telegram den Zugriff darauf erlauben.";
"NSFaceIDUsageDescription" = "Mit Face ID kannst du die App entsperren.";

View File

@ -2,12 +2,11 @@
"NSContactsUsageDescription" = "Telegram will continuously upload your contacts to its heavily encrypted cloud servers to let you connect with your friends across all your devices.";
"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.";
"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.";
"NSSiriUsageDescription" = "You can use Siri to send messages.";
"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.";
"NSLocationWhenInUseUsageDescription" = "When you send your location to your friends, Telegram needs access to show them a map.";
"NSFaceIDUsageDescription" = "You can use Face ID to unlock the app.";

View File

@ -4526,3 +4526,5 @@ Any member of this group will be able to see messages in the channel.";
"Group.EditAdmin.RankInfo" = "A title that will be shown instead of '%@'.";
"Group.EditAdmin.RankOwnerPlaceholder" = "owner";
"Group.EditAdmin.RankAdminPlaceholder" = "admin";
"Conversation.SendMessage.SendSilently" = "Send Without Sound";

View File

@ -2,13 +2,11 @@
"NSContactsUsageDescription" = "Telegram subirá continuamente tus contactos a sus servidores fuertemente cifrados, para permitirte interactuar con tus amigos en todos tus dispositivos.";
"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.";
"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.";
"NSSiriUsageDescription" = "Puedes usar Siri para enviar mensajes.";
"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.";
"NSLocationWhenInUseUsageDescription" = "Cuando envías tu ubicación a tus amigos, Telegram necesita acceso para mostrarles un mapa.";
"NSFaceIDUsageDescription" = "Puedes usar Face ID para desbloquear la app.";

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IntentPhrases</key>
<array>
<dict>
<key>IntentName</key>
<string>INSendMessageIntent</string>
<key>IntentExamples</key>
<array>
<string>Send a Telegram message to Alex saying I&apos;ll be there in 10 minutes</string>
</array>
</dict>
</array>
</dict>
</plist>

View File

@ -0,0 +1,12 @@
/* Localized versions of Info.plist keys */
"NSContactsUsageDescription" = "Telegram va synchroniser en continu vos contacts sur ses serveurs chiffrés pour vous permettre de joindre vos amis sur tous vos appareils.";
"NSLocationWhenInUseUsageDescription" = "Quand vous envoyez votre position à vos amis, Telegram doit accéder à votre position pour leur montrer une carte.";
"NSLocationAlwaysAndWhenInUseUsageDescription" = "Quand vous décidez de partager votre position en temps réel dans un échange, Telegram doit y accéder en arrière-plan pour l'actualiser pendant la durée du partage.";
"NSLocationAlwaysUsageDescription" = "Quand vous décidez de partager votre position en temps réel dans un échange, Telegram doit y accéder en arrière-plan pour l'actualiser pendant la durée du partage. Cela sert aussi à envoyer une position depuis l'Apple Watch.";
"NSCameraUsageDescription" = "Nous en avons besoin pour que vous puissiez prendre et partager des photos et des vidéos.";
"NSPhotoLibraryUsageDescription" = "Nous en avons besoin pour que vous puissiez partager des photos et des vidéos de votre photothèque.";
"NSPhotoLibraryAddUsageDescription" = "Nous en avons besoin pour que vous puissiez enregistrer des photos et des vidéos dans votre photothèque.";
"NSMicrophoneUsageDescription" = "Nous en avons besoin pour que vous puissiez enregistrer et partager des messages vocaux et des vidéos avec du son.";
"NSSiriUsageDescription" = "Vous pouvez utiliser Siri pour envoyer des messages.";
"NSFaceIDUsageDescription" = "Vous pouvez déverrouiller Telegram avec Face ID.";

View File

@ -0,0 +1 @@

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IntentPhrases</key>
<array>
<dict>
<key>IntentName</key>
<string>INSendMessageIntent</string>
<key>IntentExamples</key>
<array>
<string>Send a Telegram message to Alex saying I&apos;ll be there in 10 minutes</string>
</array>
</dict>
</array>
</dict>
</plist>

View File

@ -0,0 +1,12 @@
/* Localized versions of Info.plist keys */
"NSContactsUsageDescription" = "Telegram akan terus mengunggah kontak Anda ke penyimpanan awan yang dienkripsi penuh, sehingga Anda dapat terhubung dengan teman Anda di semua perangkat Anda.";
"NSLocationWhenInUseUsageDescription" = "Ketika Anda mengirim lokasi untuk teman, Telegram membutuhkan akses untuk berbagi peta.";
"NSLocationAlwaysAndWhenInUseUsageDescription" = "Jika Anda memilih untuk membagikan Live Location dengan teman dalam obrolan, Telegram memerlukan akses latar belakang ke lokasi Anda agar lokasi tetap diperbarui selama lokasi langsung dibagikan.";
"NSLocationAlwaysUsageDescription" = "Jika Anda ingin berbagi Live Location dengan teman dalam chat, Telegram perlu akses latar belakang ke lokasi Anda agar lokasi tetap diperbarui selama lokasi langsung dibagikan. Hal yang sama perlu dilakukan untuk berbagi lokasi dari Apple Watch.";
"NSCameraUsageDescription" = "Kami membutuhkan hal ini agar Anda dapat mengambil dan membagikan foto dan video.";
"NSPhotoLibraryUsageDescription" = "Kami membutuhkan hal ini agar Anda dapat berbagi foto dan video dari galeri foto Anda.";
"NSPhotoLibraryAddUsageDescription" = "Kami membutuhkan hal ini agar Anda dapat menyimpan foto dan video ke galeri foto.";
"NSMicrophoneUsageDescription" = "Kami butuh hal ini agar Anda dapat merekam dan berbagi pesan audio dan video dengan suara.";
"NSSiriUsageDescription" = "Anda dapat menggunakan Siri untuk mengirim pesan.";
"NSFaceIDUsageDescription" = "Anda dapat menggunakan Face ID untuk membuka kunci aplikasi.";

View File

@ -0,0 +1 @@

View File

@ -3,12 +3,10 @@
"NSContactsUsageDescription" = "Telegram caricherà continuamente i tuoi contatti sui suoi server cloud altamente criptati per farti connettere con i tuoi amici da tutti i tuoi dispositivi.";
"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.";
"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.";
"NSSiriUsageDescription" = "Puoi usare Siri per inviare messaggi.";
"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.";
"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.";
"NSLocationWhenInUseUsageDescription" = "Quando invii la tua posizione ai tuoi amici, Telegram ha bisogno di accedere per mostrare loro la mappa.";
"NSFaceIDUsageDescription" = "Puoi usare il Face ID per sbloccare l'app.";

View File

@ -1,16 +1,12 @@
/* Localized versions of Info.plist keys */
"CFBundleDisplayName" = "텔레그램";
"NSContactsUsageDescription" = "Telegram will continuously upload your contacts to its heavily encrypted cloud servers to let you connect with your friends across all your devices.";
"NSLocationWhenInUseUsageDescription" = "친구에게 회원님의 위치를 전송할 경우 위치를 지도에 표시하기 위해 텔레그램이 위치 정보에 접근할 수 있어야 합니다.";
"NSLocationAlwaysUsageDescription" = "친구에게 회원님의 위치를 전송할 경우 위치를 지도에 표시하기 위해 텔레그램이 위치 정보에 접근할 수 있어야 합니다. 애플워치에 위치 전송을 위해서도 필요합니다.";
"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.";
"NSCameraUsageDescription" = "사진과 비디오 촬영을 위하여 필요합니다.";
"NSPhotoLibraryUsageDescription" = "촬영한 사진과 비디오를 공유하기 위하여 필요합니다.";
"NSPhotoLibraryAddUsageDescription" = "사진과 동영상을 갤러리에 저장하기 위해 이 권한이 필요합니다.";
"NSMicrophoneUsageDescription" = "음성메시지와 비디오 촬영시 음성 녹음을 위하여 필요합니다.";
"NSSiriUsageDescription" = "시리를 통하여 메시지 전송이 가능합니다.";
"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" = "친구에게 회원님의 위치를 전송할 경우 위치를 지도에 표시하기 위해 텔레그램이 위치 정보에 접근할 수 있어야 합니다. 애플워치에 위치 전송을 위해서도 필요합니다.";
"NSLocationWhenInUseUsageDescription" = "친구에게 회원님의 위치를 전송할 경우 위치를 지도에 표시하기 위해 텔레그램이 위치 정보에 접근할 수 있어야 합니다.";
"NSFaceIDUsageDescription" = "Face ID 를 사용하여 앱 잠금을 해제할 수 있습니다";

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IntentPhrases</key>
<array>
<dict>
<key>IntentName</key>
<string>INSendMessageIntent</string>
<key>IntentExamples</key>
<array>
<string>Send a Telegram message to Alex saying I&apos;ll be there in 10 minutes</string>
</array>
</dict>
</array>
</dict>
</plist>

View File

@ -0,0 +1,12 @@
/* Localized versions of Info.plist keys */
"NSContactsUsageDescription" = "Telegram akan sentiasa muat naik kenalan anda ke pelayan awan yang berenkripsi tinggi agar anda boleh berhubung dengan rakan anda di semua peranti anda.";
"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.";
"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.";
"NSSiriUsageDescription" = "Anda boleh guna Siri untuk kirim mesej.";
"NSFaceIDUsageDescription" = "Anda boleh guna ID Muka untuk guna aplikasi.";

View File

@ -0,0 +1 @@

View File

@ -2,13 +2,11 @@
"NSContactsUsageDescription" = "Telegram synchroniseert je contacten continu naar onze zwaar versleutelde Cloud-servers, zodat je contact kunt opnemen met je vrienden vanaf al je apparaten.";
"NSLocationWhenInUseUsageDescription" = "Als je je locatie met vrienden wilt delen heeft Telegram toegang nodig om ze een kaart te kunnen tonen.";
"NSLocationAlwaysAndWhenInUseUsageDescription" = "Als je ervoor kiest om je huidige locatie te delen met vrienden in een chat heeft Telegram achtergrondtoegang tot je locatie nodig om deze bij te werken tijdens het live-delen.";
"NSLocationAlwaysUsageDescription" = "Telegram heeft toegang nodig om een kaart aan je vrienden te tonen als je jouw locatie met ze deelt. Je hebt dit ook nodig om locaties te sturen vanaf een Apple Watch.";
"NSCameraUsageDescription" = "We hebben dit nodig zodat je foto's en video's kunt maken en delen.";
"NSPhotoLibraryUsageDescription" = "We hebben dit nodig zodat je foto's en video's kunt delen vanuit je fotobibliotheek.";
"NSPhotoLibraryAddUsageDescription" = "We hebben dit nodig zodat je foto's en video's kunt opslaan in je fotobibliotheek.";
"NSMicrophoneUsageDescription" = "We hebben dit nodig zodat je spraakberichten en video's met geluid kunt opnemen en delen.";
"NSSiriUsageDescription" = "Je kunt Siri gebruiken om berichten te sturen";
"NSLocationAlwaysAndWhenInUseUsageDescription" = "Als je ervoor kiest om je huidige locatie te delen met vrienden in een chat heeft Telegram achtergrondtoegang tot je locatie nodig om deze bij te werken tijdens het live-delen.";
"NSLocationAlwaysUsageDescription" = "Telegram heeft toegang nodig om een kaart aan je vrienden te tonen als je jouw locatie met ze deelt. Je hebt dit ook nodig om locaties te sturen vanaf een Apple Watch.";
"NSLocationWhenInUseUsageDescription" = "Als je je locatie met vrienden wilt delen heeft Telegram toegang nodig om ze een kaart te kunnen tonen.";
"NSFaceIDUsageDescription" = "Je kunt Face ID gebruiken om de app te ontgrendelen.";

View File

@ -2,13 +2,11 @@
"NSContactsUsageDescription" = "Telegram atualizará continuamente os seus contatos em servidores na nuvem fortemente criptografados para que você se conecte com seus amigos através de todos os seus dispositivos.";
"NSLocationWhenInUseUsageDescription" = "Para enviar sua localização para seus amigos o Telegram precisa de permissão para mostrá-los o mapa.";
"NSLocationAlwaysAndWhenInUseUsageDescription" = "Quando você escolhe compartilhar sua Localização em Tempo Real com amigos no chat, o Telegram precisa de acesso à sua localização em segundo plano para mantê-los atualizados durante o compartilhamento.";
"NSLocationAlwaysUsageDescription" = "Para enviar sua localização para seus amigos, o Telegram precisa de permissão para mostrá-los o mapa. Você também precisará disso para enviar localizações de um Apple Watch.";
"NSCameraUsageDescription" = "Precisamos acessar sua câmera para que você possa capturar fotos e vídeos.";
"NSPhotoLibraryUsageDescription" = "Precisamos disso para que você possa compartilhar fotos e vídeos de sua galeria.";
"NSPhotoLibraryAddUsageDescription" = "Precisamos disso para que você possa salvar fotos e vídeos em sua galeria de fotos.";
"NSMicrophoneUsageDescription" = "Precisamos disso para que você possa gravar mensagens de voz e vídeos com som.";
"NSSiriUsageDescription" = "Você pode usar a Siri para enviar mensagens.";
"NSLocationAlwaysAndWhenInUseUsageDescription" = "Quando você escolhe compartilhar sua Localização em Tempo Real com amigos no chat, o Telegram precisa de acesso à sua localização em segundo plano para mantê-los atualizados durante o compartilhamento.";
"NSLocationAlwaysUsageDescription" = "Para enviar sua localização para seus amigos, o Telegram precisa de permissão para mostrá-los o mapa. Você também precisará disso para enviar localizações de um Apple Watch.";
"NSLocationWhenInUseUsageDescription" = "Para enviar sua localização para seus amigos o Telegram precisa de permissão para mostrá-los o mapa.";
"NSFaceIDUsageDescription" = "Você pode usar o Face ID para desbloquear o app.";

View File

@ -2,12 +2,11 @@
"NSContactsUsageDescription" = "Актуальная информация о ваших контактах будет храниться зашифрованной в облаке Telegram, чтобы вы могли связаться с друзьями с любого устройства.";
"NSLocationWhenInUseUsageDescription" = "Когда вы отправляете друзьям геопозицию, Telegram нужно разрешение, чтобы показать им карту.";
"NSLocationAlwaysAndWhenInUseUsageDescription" = "Фоновый доступ к геопозиции требуется, чтобы обновлять вашу геопозицию, когда вы транслируете её в чат с друзьями. ";
"NSLocationAlwaysUsageDescription" = "Фоновый доступ к геопозиции требуется, чтобы обновлять вашу геопозицию, когда вы транслируете её в чат с друзьями. Он также необходим для отправки геопозиции с Apple Watch.";
"NSCameraUsageDescription" = "Это необходимо, чтобы вы могли делиться снятыми фотографиями и видео.";
"NSPhotoLibraryUsageDescription" = "Это необходимо, чтобы вы могли делиться фото и видео из библиотеки устройства.";
"NSPhotoLibraryAddUsageDescription" = "Это необходимо, чтобы вы могли сохранять фото и видео в библиотеку устройства.";
"NSMicrophoneUsageDescription" = "Это необходимо, чтобы вы могли делиться голосовыми сообщениями и видео со звуком.";
"NSSiriUsageDescription" = "Вы можете использовать Siri для отправки сообщений";
"NSLocationAlwaysAndWhenInUseUsageDescription" = "Фоновый доступ к геопозиции требуется, чтобы обновлять вашу геопозицию, когда вы транслируете её в чат с друзьями. ";
"NSLocationAlwaysUsageDescription" = "Фоновый доступ к геопозиции требуется, чтобы обновлять вашу геопозицию, когда вы транслируете её в чат с друзьями. Он также необходим для отправки геопозиции с Apple Watch.";
"NSLocationWhenInUseUsageDescription" = "Когда вы отправляете друзьям геопозицию, Telegram нужно разрешение, чтобы показать им карту.";
"NSFaceIDUsageDescription" = "Вы можете разблокировать приложение с помощью Face ID.";

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IntentPhrases</key>
<array>
<dict>
<key>IntentName</key>
<string>INSendMessageIntent</string>
<key>IntentExamples</key>
<array>
<string>Send a Telegram message to Alex saying I&apos;ll be there in 10 minutes</string>
</array>
</dict>
</array>
</dict>
</plist>

View File

@ -0,0 +1,12 @@
/* Localized versions of Info.plist keys */
"NSContactsUsageDescription" = "Telegram, arkadaşlarınızla tüm cihazlarınız arasında bağlantı kurmanızı sağlamak için kişilerinizi sürekli olarak şifreli bulut sunucularına yükleyecek.";
"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.";
"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.";
"NSSiriUsageDescription" = "Mesaj göndermek için Siri'yi kullanabilirsiniz.";
"NSFaceIDUsageDescription" = "Uygulamanın kilidini açmak için Face ID'yi kullanabilirsiniz.";

View File

@ -0,0 +1 @@

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IntentPhrases</key>
<array>
<dict>
<key>IntentName</key>
<string>INSendMessageIntent</string>
<key>IntentExamples</key>
<array>
<string>Send a Telegram message to Alex saying I&apos;ll be there in 10 minutes</string>
</array>
</dict>
</array>
</dict>
</plist>

View File

@ -0,0 +1,12 @@
/* Localized versions of Info.plist keys */
"NSContactsUsageDescription" = "Telegram завантажуватиме ваші контакти до зашифрованих хмарних серверів, щоб ви могли зʼєднуватися з друзями на всіх пристроях.";
"NSLocationWhenInUseUsageDescription" = "Коли ви надсилаєте розташування друзям, Telegram потребує доступу, щоб показати їм мапу.";
"NSLocationAlwaysAndWhenInUseUsageDescription" = "Коли ви захочете поділитися Маячком із друзями у чаті, Telegram потребує фонового доступу до розташування, щоб воно оновлювалося на час трансляції маячка.";
"NSLocationAlwaysUsageDescription" = "Коли ви захочете поділитися Маячком із друзями у чаті, Telegram потребує фонового доступу до розташування, щоб воно оновлювалося на час трансляції маячка. Також вам це потрібно для надсилання розташувань з Apple Watch.";
"NSCameraUsageDescription" = "Нам це потрібно, щоб ви могли знімати та ділитися фото й відео.";
"NSPhotoLibraryUsageDescription" = "Нам це потрібно, щоб ви могли ділитися фото та відео з вашої бібліотеки фото.";
"NSPhotoLibraryAddUsageDescription" = "Нам це потрібно, щоб ви могли зберігати фото та відео до вашої бібліотеки фото.";
"NSMicrophoneUsageDescription" = "Нам це потрібно, щоб ви могли записувати та ділитися голосовими повідомленнями та відео зі звуком.";
"NSSiriUsageDescription" = "Надсилайте повідомлення з Siri.";
"NSFaceIDUsageDescription" = "Розблоковуйте застосунок із Face ID.";

View File

@ -0,0 +1 @@

View File

@ -16,3 +16,20 @@ public class EditableTextNode: ASEditableTextNode {
}
}
}
public extension UITextView {
var numberOfLines: Int {
let layoutManager = self.layoutManager
let numberOfGlyphs = layoutManager.numberOfGlyphs
var lineRange: NSRange = NSMakeRange(0, 1)
var index = 0
var numberOfLines = 0
while index < numberOfGlyphs {
layoutManager.lineFragmentRect(forGlyphAt: index, effectiveRange: &lineRange)
index = NSMaxRange(lineRange)
numberOfLines += 1
}
return numberOfLines
}
}

View File

@ -51,6 +51,14 @@ private func maxSubviewBounds(_ view: UIView) -> CGRect {
return bounds
}
private let formatter: DateFormatter? = {
let formatter = DateFormatter()
formatter.timeStyle = .short
formatter.locale = Locale.current
return formatter
}()
private class StatusBarItemNode: ASDisplayNode {
var statusBarStyle: StatusBarStyle
var targetView: UIView
@ -103,11 +111,42 @@ private class StatusBarItemNode: ASDisplayNode {
}
}
} else {
context.withContext { c in
c.translateBy(x: containingBounds.minX, y: -containingBounds.minY)
UIGraphicsPushContext(c)
self.targetView.layer.render(in: c)
UIGraphicsPopContext()
if self.targetView.checkIsKind(of: timeViewClass) {
context.withContext { c in
c.translateBy(x: containingBounds.minX, y: -containingBounds.minY)
UIGraphicsPushContext(c)
let color: UIColor
switch self.statusBarStyle {
case .Black, .Ignore, .Hide:
color = UIColor.black
case .White:
color = UIColor.white
}
formatter?.locale = Locale.current
if let string = formatter?.string(from: Date()) {
let attributedString = NSAttributedString(string: string, attributes: [NSAttributedStringKey.font: UIFont.boldSystemFont(ofSize: 12.0), NSAttributedStringKey.foregroundColor: color])
let line = CTLineCreateWithAttributedString(attributedString)
c.translateBy(x: containingBounds.width / 2.0, y: containingBounds.height / 2.0)
c.scaleBy(x: 1.0, y: -1.0)
c.translateBy(x: -containingBounds.width / 2.0, y: -containingBounds.height / 2.0)
c.translateBy(x: 0.0, y: 5.0 + UIScreenPixel)
CTLineDraw(line, c)
}
UIGraphicsPopContext()
}
} else {
context.withContext { c in
c.translateBy(x: containingBounds.minX, y: -containingBounds.minY)
UIGraphicsPushContext(c)
self.targetView.layer.render(in: c)
UIGraphicsPopContext()
}
}
}
//dumpViews(self.targetView)
@ -136,8 +175,9 @@ private class StatusBarItemNode: ASDisplayNode {
type = .Activity
}
}
tintStatusBarItem(context, type: type, style: statusBarStyle)
self.contentNode.contents = context.generateImage()?.cgImage
tintStatusBarItem(context, type: type, style: self.statusBarStyle)
let image = context.generateImage()?.cgImage
self.contentNode.contents = image
let mappedFrame = self.targetView.convert(self.targetView.bounds, to: self.rootView)
self.frame = mappedFrame
@ -356,6 +396,14 @@ private let stringClass: AnyClass? = {
return NSClassFromString("_UI" + nameString)
}()
private let timeViewClass: AnyClass? = {
var nameString = "StatusBar"
if CFAbsoluteTimeGetCurrent() > 0 {
nameString += "TimeItemView"
}
return NSClassFromString("UI" + nameString)
}()
private func containsSubviewOfClass(view: UIView, of subviewClass: AnyClass?) -> Bool {
guard let subviewClass = subviewClass else {
return false

View File

@ -46,6 +46,4 @@
- (instancetype)initWithContext:(id<LegacyComponentsContext>)context items:(NSArray *)items focusItem:(id<TGModernGalleryItem>)focusItem selectionContext:(TGMediaSelectionContext *)selectionContext editingContext:(TGMediaEditingContext *)editingContext hasCaptions:(bool)hasCaptions allowCaptionEntities:(bool)allowCaptionEntities hasTimer:(bool)hasTimer onlyCrop:(bool)onlyCrop inhibitDocumentCaptions:(bool)inhibitDocumentCaptions hasSelectionPanel:(bool)hasSelectionPanel hasCamera:(bool)hasCamera recipientName:(NSString *)recipientName;
//- (void)setCurrentItem:(id<TGMediaSelectableItem>)item direction:(TGModernGalleryScrollAnimationDirection)direction;
@end

View File

@ -100,7 +100,7 @@ public func addChannelMember(account: Account, peerId: PeerId, memberId: PeerId)
|> map { [$0] }
|> `catch` { error -> Signal<[Api.Updates], AddChannelMemberError> in
switch error.errorDescription {
case "CHANNELS_TOO_MUCH":
case "USER_CHANNELS_TOO_MUCH":
return .fail(.tooMuchJoined)
case "USERS_TOO_MUCH":
return .fail(.limitExceeded)
@ -195,7 +195,6 @@ public func addChannelMembers(account: Account, peerId: PeerId, memberIds: [Peer
}
if let peer = transaction.getPeer(peerId), let channel = peer as? TelegramChannel, let inputChannel = apiInputChannel(channel) {
let signal = account.network.request(Api.functions.channels.inviteToChannel(channel: inputChannel, users: inputUsers))
|> mapError { error -> AddChannelMemberError in
switch error.errorDescription {

View File

@ -0,0 +1,9 @@
{
"info" : {
"version" : 1,
"author" : "xcode"
},
"properties" : {
"provides-namespace" : true
}
}

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "Mute@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "Mute@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 868 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -1843,7 +1843,7 @@ final class SharedApplicationContext {
notificationCenter.getNotificationSettings(completionHandler: { settings in
switch (settings.authorizationStatus, authorize) {
case (.authorized, _), (.notDetermined, true):
notificationCenter.requestAuthorization(options: [.badge, .sound, .alert], completionHandler: { result, _ in
notificationCenter.requestAuthorization(options: [.badge, .sound, .alert, .carPlay], completionHandler: { result, _ in
completion(result)
if result {
Queue.mainQueue().async {
@ -1857,26 +1857,32 @@ final class SharedApplicationContext {
let legacyChannelMessageCategory: UNNotificationCategory
let muteMessageCategory: UNNotificationCategory
let muteMediaMessageCategory: UNNotificationCategory
if #available(iOS 11.0, *) {
var options: UNNotificationCategoryOptions = []
if includeNames {
options.insert(.hiddenPreviewsShowTitle)
}
var carPlayOptions = options
carPlayOptions.insert(.allowInCarPlay)
unknownMessageCategory = UNNotificationCategory(identifier: "unknown", actions: [], intentIdentifiers: [], hiddenPreviewsBodyPlaceholder: hiddenContentString, options: options)
replyMessageCategory = UNNotificationCategory(identifier: "withReply", actions: [reply], intentIdentifiers: [], hiddenPreviewsBodyPlaceholder: hiddenContentString, options: options)
replyLegacyMessageCategory = UNNotificationCategory(identifier: "r", actions: [reply], intentIdentifiers: [], hiddenPreviewsBodyPlaceholder: hiddenContentString, options: options)
replyMessageCategory = UNNotificationCategory(identifier: "withReply", actions: [reply], intentIdentifiers: [INSearchForMessagesIntentIdentifier], hiddenPreviewsBodyPlaceholder: hiddenContentString, options: carPlayOptions)
replyLegacyMessageCategory = UNNotificationCategory(identifier: "r", actions: [reply], intentIdentifiers: [INSearchForMessagesIntentIdentifier], hiddenPreviewsBodyPlaceholder: hiddenContentString, options: carPlayOptions)
replyLegacyMediaMessageCategory = UNNotificationCategory(identifier: "m", actions: [reply], intentIdentifiers: [], hiddenPreviewsBodyPlaceholder: hiddenContentString, options: options)
replyMediaMessageCategory = UNNotificationCategory(identifier: "withReplyMedia", actions: [reply], intentIdentifiers: [], hiddenPreviewsBodyPlaceholder: hiddenContentString, options: options)
legacyChannelMessageCategory = UNNotificationCategory(identifier: "c", actions: [], intentIdentifiers: [], hiddenPreviewsBodyPlaceholder: hiddenContentString, options: options)
muteMessageCategory = UNNotificationCategory(identifier: "withMute", actions: [], intentIdentifiers: [], hiddenPreviewsBodyPlaceholder: hiddenContentString, options: options)
muteMediaMessageCategory = UNNotificationCategory(identifier: "withMuteMedia", actions: [], intentIdentifiers: [], hiddenPreviewsBodyPlaceholder: hiddenContentString, options: options)
} else {
let carPlayOptions: UNNotificationCategoryOptions = [.allowInCarPlay]
unknownMessageCategory = UNNotificationCategory(identifier: "unknown", actions: [], intentIdentifiers: [], options: [])
replyMessageCategory = UNNotificationCategory(identifier: "withReply", actions: [reply], intentIdentifiers: [], options: [])
replyLegacyMessageCategory = UNNotificationCategory(identifier: "r", actions: [reply], intentIdentifiers: [], options: [])
replyMessageCategory = UNNotificationCategory(identifier: "withReply", actions: [reply], intentIdentifiers: [INSearchForMessagesIntentIdentifier], options: carPlayOptions)
replyLegacyMessageCategory = UNNotificationCategory(identifier: "r", actions: [reply], intentIdentifiers: [INSearchForMessagesIntentIdentifier], options: carPlayOptions)
replyLegacyMediaMessageCategory = UNNotificationCategory(identifier: "m", actions: [reply], intentIdentifiers: [], options: [])
replyMediaMessageCategory = UNNotificationCategory(identifier: "withReplyMedia", actions: [reply], intentIdentifiers: [], options: [])
replyMediaMessageCategory = UNNotificationCategory(identifier: "withReplyMedia", actions: [reply], intentIdentifiers: [], options: [])
legacyChannelMessageCategory = UNNotificationCategory(identifier: "c", actions: [], intentIdentifiers: [], options: [])
muteMessageCategory = UNNotificationCategory(identifier: "withMute", actions: [], intentIdentifiers: [], options: [])
muteMediaMessageCategory = UNNotificationCategory(identifier: "withMuteMedia", actions: [], intentIdentifiers: [], options: [])

View File

@ -165,7 +165,7 @@ final class AuthorizationSequenceCountrySelectionControllerNode: ASDisplayNode,
self.searchTableView.reloadData()
self.searchTableView.isHidden = true
} else {
let normalizedQuery = query.lowercased().trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
let normalizedQuery = query.lowercased().trimmingCharacters(in: .whitespacesAndNewlines)
var results: [((String, String), String, Int)] = []
for (_, items) in self.sections {

View File

@ -26,7 +26,7 @@ public final class CallController: ViewController {
public let call: PresentationCall
private var presentationData: PresentationData
private var animatedAppearance = false
private var didPlayPresentationAnimation = false
private var peer: Peer?
@ -217,7 +217,7 @@ public final class CallController: ViewController {
}
self.controllerNode.dismissedInteractively = { [weak self] in
self?.animatedAppearance = false
self?.didPlayPresentationAnimation = false
self?.presentingViewController?.dismiss(animated: false, completion: nil)
}
@ -242,8 +242,8 @@ public final class CallController: ViewController {
override public func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if !self.animatedAppearance {
self.animatedAppearance = true
if !self.didPlayPresentationAnimation {
self.didPlayPresentationAnimation = true
self.controllerNode.animateIn()
}
@ -257,7 +257,7 @@ public final class CallController: ViewController {
override public func dismiss(completion: (() -> Void)? = nil) {
self.controllerNode.animateOut(completion: { [weak self] in
self?.animatedAppearance = false
self?.didPlayPresentationAnimation = false
self?.presentingViewController?.dismiss(animated: false, completion: nil)
completion?()

View File

@ -13,14 +13,16 @@ private final class ChannelAdminControllerArguments {
let toggleRight: (TelegramChatAdminRightsFlags, TelegramChatAdminRightsFlags) -> Void
let transferOwnership: () -> Void
let updateRank: (String, String) -> Void
let updateFocusedOnRank: (Bool) -> Void
let dismissAdmin: () -> Void
let dismissInput: () -> Void
init(account: Account, toggleRight: @escaping (TelegramChatAdminRightsFlags, TelegramChatAdminRightsFlags) -> Void, transferOwnership: @escaping () -> Void, updateRank: @escaping (String, String) -> Void, dismissAdmin: @escaping () -> Void, dismissInput: @escaping () -> Void) {
init(account: Account, toggleRight: @escaping (TelegramChatAdminRightsFlags, TelegramChatAdminRightsFlags) -> Void, transferOwnership: @escaping () -> Void, updateRank: @escaping (String, String) -> Void, updateFocusedOnRank: @escaping (Bool) -> Void, dismissAdmin: @escaping () -> Void, dismissInput: @escaping () -> Void) {
self.account = account
self.toggleRight = toggleRight
self.transferOwnership = transferOwnership
self.updateRank = updateRank
self.updateFocusedOnRank = updateFocusedOnRank
self.dismissAdmin = dismissAdmin
self.dismissInput = dismissInput
}
@ -142,7 +144,7 @@ private enum ChannelAdminEntryStableId: Hashable {
private enum ChannelAdminEntry: ItemListNodeEntry {
case info(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, Peer, TelegramUserPresence?)
case rankTitle(PresentationTheme, String, Int32, Int32)
case rankTitle(PresentationTheme, String, Int32?, Int32)
case rank(PresentationTheme, String, String, Bool)
case rankInfo(PresentationTheme, String)
case rightsTitle(PresentationTheme, String)
@ -357,11 +359,16 @@ private enum ChannelAdminEntry: ItemListNodeEntry {
}, avatarTapped: {
})
case let .rankTitle(theme, text, count, limit):
let accessoryText = count > 0 ? ItemListSectionHeaderAccessoryText(value: "\(limit - count)", color: count > limit ? .destructive : .generic) : nil
var accessoryText: ItemListSectionHeaderAccessoryText?
if let count = count {
accessoryText = ItemListSectionHeaderAccessoryText(value: "\(limit - count)", color: count > limit ? .destructive : .generic)
}
return ItemListSectionHeaderItem(theme: theme, text: text, accessoryText: accessoryText, sectionId: self.section)
case let .rank(theme, placeholder, text, enabled):
return ItemListSingleLineInputItem(theme: theme, title: NSAttributedString(string: "", textColor: .black), text: text, placeholder: placeholder, type: .regular(capitalization: false, autocorrection: true), spacing: 0.0, clearButton: enabled, enabled: enabled, tag: ChannelAdminEntryTag.rank, sectionId: self.section, textUpdated: { updatedText in
arguments.updateRank(text, updatedText)
}, updatedFocus: { focus in
arguments.updateFocusedOnRank(focus)
}, action: {
arguments.dismissInput()
})
@ -391,11 +398,13 @@ private struct ChannelAdminControllerState: Equatable {
let updatedFlags: TelegramChatAdminRightsFlags?
let updatedRank: String?
let updating: Bool
let focusedOnRank: Bool
init(updatedFlags: TelegramChatAdminRightsFlags? = nil, updatedRank: String? = nil, updating: Bool = false) {
init(updatedFlags: TelegramChatAdminRightsFlags? = nil, updatedRank: String? = nil, updating: Bool = false, focusedOnRank: Bool = false) {
self.updatedFlags = updatedFlags
self.updatedRank = updatedRank
self.updating = updating
self.focusedOnRank = focusedOnRank
}
static func ==(lhs: ChannelAdminControllerState, rhs: ChannelAdminControllerState) -> Bool {
@ -408,19 +417,26 @@ private struct ChannelAdminControllerState: Equatable {
if lhs.updating != rhs.updating {
return false
}
if lhs.focusedOnRank != rhs.focusedOnRank {
return false
}
return true
}
func withUpdatedUpdatedFlags(_ updatedFlags: TelegramChatAdminRightsFlags?) -> ChannelAdminControllerState {
return ChannelAdminControllerState(updatedFlags: updatedFlags, updatedRank: self.updatedRank, updating: self.updating)
return ChannelAdminControllerState(updatedFlags: updatedFlags, updatedRank: self.updatedRank, updating: self.updating, focusedOnRank: self.focusedOnRank)
}
func withUpdatedUpdatedRank(_ updatedRank: String?) -> ChannelAdminControllerState {
return ChannelAdminControllerState(updatedFlags: self.updatedFlags, updatedRank: updatedRank, updating: self.updating)
return ChannelAdminControllerState(updatedFlags: self.updatedFlags, updatedRank: updatedRank, updating: self.updating, focusedOnRank: self.focusedOnRank)
}
func withUpdatedUpdating(_ updating: Bool) -> ChannelAdminControllerState {
return ChannelAdminControllerState(updatedFlags: self.updatedFlags, updatedRank: self.updatedRank, updating: updating)
return ChannelAdminControllerState(updatedFlags: self.updatedFlags, updatedRank: self.updatedRank, updating: updating, focusedOnRank: self.focusedOnRank)
}
func withUpdatedFocusedOnRank(_ focusedOnRank: Bool) -> ChannelAdminControllerState {
return ChannelAdminControllerState(updatedFlags: self.updatedFlags, updatedRank: self.updatedRank, updating: self.updating, focusedOnRank: focusedOnRank)
}
}
@ -513,7 +529,7 @@ private func areAllAdminRightsEnabled(_ flags: TelegramChatAdminRightsFlags, gro
}
}
private func channelAdminControllerEntries(presentationData: PresentationData, state: ChannelAdminControllerState, accountPeerId: PeerId, channelView: PeerView, adminView: PeerView, initialParticipant: ChannelParticipant?) -> [ChannelAdminEntry] {
private func channelAdminControllerEntries(presentationData: PresentationData, state: ChannelAdminControllerState, accountPeerId: PeerId, channelView: PeerView, adminView: PeerView, initialParticipant: ChannelParticipant?, canEdit: Bool) -> [ChannelAdminEntry] {
var entries: [ChannelAdminEntry] = []
if let channel = channelView.peers[channelView.peerId] as? TelegramChannel, let admin = adminView.peers[adminView.peerId] {
@ -536,8 +552,8 @@ private func channelAdminControllerEntries(presentationData: PresentationData, s
currentRank = nil
}
let enabled = !state.updating && (admin.id != accountPeerId || isCreator)
entries.append(.rankTitle(presentationData.theme, presentationData.strings.Group_EditAdmin_RankTitle.uppercased(), enabled ? Int32(currentRank?.count ?? 0) : 0, rankMaxLength))
let enabled = !state.updating && canEdit
entries.append(.rankTitle(presentationData.theme, presentationData.strings.Group_EditAdmin_RankTitle.uppercased(), enabled && state.focusedOnRank ? Int32(currentRank?.count ?? 0) : nil, rankMaxLength))
entries.append(.rank(presentationData.theme, isCreator ? presentationData.strings.Group_EditAdmin_RankOwnerPlaceholder : presentationData.strings.Group_EditAdmin_RankAdminPlaceholder, currentRank ?? "", enabled))
entries.append(.rankInfo(presentationData.theme, presentationData.strings.Group_EditAdmin_RankInfo(placeholder).0))
}
@ -653,8 +669,8 @@ private func channelAdminControllerEntries(presentationData: PresentationData, s
currentRank = nil
}
let enabled = !state.updating && (admin.id != accountPeerId || isCreator)
entries.append(.rankTitle(presentationData.theme, presentationData.strings.Group_EditAdmin_RankTitle.uppercased(), enabled ? Int32(currentRank?.count ?? 0) : 0, rankMaxLength))
let enabled = !state.updating && canEdit
entries.append(.rankTitle(presentationData.theme, presentationData.strings.Group_EditAdmin_RankTitle.uppercased(), enabled && state.focusedOnRank ? Int32(currentRank?.count ?? 0) : nil, rankMaxLength))
entries.append(.rank(presentationData.theme, isCreator ? presentationData.strings.Group_EditAdmin_RankOwnerPlaceholder : presentationData.strings.Group_EditAdmin_RankAdminPlaceholder, currentRank ?? "", enabled))
if isCreator {
@ -773,6 +789,8 @@ public func channelAdminController(context: AccountContext, peerId: PeerId, admi
if updatedRank != previousRank {
updateState { $0.withUpdatedUpdatedRank(updatedRank) }
}
}, updateFocusedOnRank: { focusedOnRank in
updateState { $0.withUpdatedFocusedOnRank(focusedOnRank) }
}, dismissAdmin: {
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
let actionSheet = ActionSheetController(presentationTheme: presentationData.theme)
@ -830,7 +848,6 @@ public func channelAdminController(context: AccountContext, peerId: PeerId, admi
}
var focusItemTag: ItemListItemTag?
var isCreator = false
if let initialParticipant = initialParticipant, case .creator = initialParticipant {
focusItemTag = ChannelAdminEntryTag.rank
}
@ -846,7 +863,7 @@ public func channelAdminController(context: AccountContext, peerId: PeerId, admi
var updateRank: String?
updateState { current in
updateFlags = current.updatedFlags
updateRank = current.updatedRank
updateRank = current.updatedRank?.trimmingCharacters(in: .whitespacesAndNewlines)
return current
}
@ -915,7 +932,7 @@ public func channelAdminController(context: AccountContext, peerId: PeerId, admi
var updateRank: String?
updateState { current in
updateFlags = current.updatedFlags
updateRank = current.updatedRank
updateRank = current.updatedRank?.trimmingCharacters(in: .whitespacesAndNewlines)
return current
}
@ -946,7 +963,7 @@ public func channelAdminController(context: AccountContext, peerId: PeerId, admi
updateState { current in
return current.withUpdatedUpdating(true)
}
updateRightsDisposable.set((context.peerChannelMemberCategoriesContextsManager.updateMemberAdminRights(account: context.account, peerId: peerId, memberId: adminId, adminRights: TelegramChatAdminRights(flags: updateFlags), rank: "") |> deliverOnMainQueue).start(error: { error in
updateRightsDisposable.set((context.peerChannelMemberCategoriesContextsManager.updateMemberAdminRights(account: context.account, peerId: peerId, memberId: adminId, adminRights: TelegramChatAdminRights(flags: updateFlags), rank: updateRank) |> deliverOnMainQueue).start(error: { error in
if case let .addMemberError(error) = error, case .restricted = error, let admin = adminView.peers[adminView.peerId] {
var text = presentationData.strings.Privacy_GroupsAndChannels_InviteToChannelError(admin.compactDisplayTitle, admin.compactDisplayTitle).0
if case .group = channel.info {
@ -967,7 +984,7 @@ public func channelAdminController(context: AccountContext, peerId: PeerId, admi
updateState { current in
updateFlags = current.updatedFlags
if let updatedRank = current.updatedRank, !updatedRank.isEmpty {
updateRank = updatedRank
updateRank = updatedRank.trimmingCharacters(in: .whitespacesAndNewlines)
}
return current
}
@ -1045,7 +1062,7 @@ public func channelAdminController(context: AccountContext, peerId: PeerId, admi
let controllerState = ItemListControllerState(theme: presentationData.theme, title: .text(presentationData.strings.Channel_Management_LabelEditor), leftNavigationButton: leftNavigationButton, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: false)
let listState = ItemListNodeState(entries: channelAdminControllerEntries(presentationData: presentationData, state: state, accountPeerId: context.account.peerId, channelView: channelView, adminView: adminView, initialParticipant: initialParticipant), style: .blocks, focusItemTag: focusItemTag, emptyStateItem: nil, animateChanges: true)
let listState = ItemListNodeState(entries: channelAdminControllerEntries(presentationData: presentationData, state: state, accountPeerId: context.account.peerId, channelView: channelView, adminView: adminView, initialParticipant: initialParticipant, canEdit: canEdit), style: .blocks, focusItemTag: focusItemTag, emptyStateItem: nil, animateChanges: true)
return (controllerState, (listState, arguments))
}

View File

@ -317,8 +317,9 @@ final class ChannelMembersSearchContainerNode: SearchDisplayControllerContentNod
}
let removeMemberDisposable = self.removeMemberDisposable
let interaction = ChannelMembersSearchContainerInteraction(peerSelected: { peer, participant in
let interaction = ChannelMembersSearchContainerInteraction(peerSelected: { [weak self] peer, participant in
openPeer(peer, participant)
self?.listNode.clearHighlightAnimated(true)
}, setPeerIdWithRevealedOptions: { peerId, fromPeerId in
updateState { state in
var state = state

View File

@ -129,6 +129,7 @@ class ChannelMembersSearchControllerNode: ASDisplayNode {
let interaction = ChannelMembersSearchInteraction(openPeer: { [weak self] peer, participant in
self?.requestOpenPeerFromSearch?(peer, participant)
self?.listNode.clearHighlightAnimated(true)
})
let previousEntries = Atomic<[ChannelMembersSearchEntry]?>(value: nil)

View File

@ -282,8 +282,10 @@ private enum ChannelVisibilityEntry: ItemListNodeEntry {
case let .editablePublicLink(theme, placeholder, currentText):
return ItemListSingleLineInputItem(theme: theme, title: NSAttributedString(string: "t.me/", textColor: theme.list.itemPrimaryTextColor), text: currentText, placeholder: placeholder, type: .regular(capitalization: false, autocorrection: false), clearButton: true, tag: ChannelVisibilityEntryTag.publicLink, sectionId: self.section, textUpdated: { updatedText in
arguments.updatePublicLinkText(currentText, updatedText)
}, receivedFocus: {
arguments.scrollToPublicLinkText()
}, updatedFocus: { focus in
if focus {
arguments.scrollToPublicLinkText()
}
}, action: {
})
case let .privateLinkInfo(theme, text):

View File

@ -559,6 +559,10 @@ public final class ChatController: TelegramController, GalleryHiddenMediaTarget,
UIAccessibilityPostNotification(UIAccessibilityAnnouncementNotification, text as NSString)
})
}
}, sendCurrentMessage: { [weak self] silentPosting in
if let strongSelf = self {
strongSelf.chatDisplayNode.sendCurrentMessage(silentPosting: silentPosting)
}
}, sendMessage: { [weak self] text in
guard let strongSelf = self, canSendMessagesToChat(strongSelf.presentationInterfaceState) else {
return
@ -2391,7 +2395,7 @@ public final class ChatController: TelegramController, GalleryHiddenMediaTarget,
}
}
self.chatDisplayNode.sendMessages = { [weak self] messages, isAnyMessageTextPartitioned in
self.chatDisplayNode.sendMessages = { [weak self] messages, silentPosting, isAnyMessageTextPartitioned in
if let strongSelf = self, case let .peer(peerId) = strongSelf.chatLocation {
strongSelf.commitPurposefulAction()
@ -2417,7 +2421,14 @@ public final class ChatController: TelegramController, GalleryHiddenMediaTarget,
}
}
let _ = (enqueueMessages(account: strongSelf.context.account, peerId: peerId, messages: strongSelf.transformEnqueueMessages(messages))
let transformedMessages: [EnqueueMessage]
if let silentPosting = silentPosting {
transformedMessages = strongSelf.transformEnqueueMessages(messages, silentPosting: silentPosting)
} else {
transformedMessages = strongSelf.transformEnqueueMessages(messages)
}
let _ = (enqueueMessages(account: strongSelf.context.account, peerId: peerId, messages: transformedMessages)
|> deliverOnMainQueue).start(next: { _ in
if let strongSelf = self {
strongSelf.chatDisplayNode.historyNode.scrollToEndOfHistory()
@ -3631,7 +3642,12 @@ public final class ChatController: TelegramController, GalleryHiddenMediaTarget,
strongSelf.slowmodeTooltipController = slowmodeTooltipController
strongSelf.window?.presentInGlobalOverlay(slowmodeTooltipController)
}, statuses: ChatPanelInterfaceInteractionStatuses(editingMessage: self.editingMessage.get(), startingBot: self.startingBot.get(), unblockingPeer: self.unblockingPeer.get(), searching: self.searching.get(), loadingMessage: self.loadingMessage.get()))
}, displaySendMessageOptions: { [weak self] in
if let strongSelf = self, let sendButtonFrame = strongSelf.chatDisplayNode.sendButtonFrame(), let textInputNode = strongSelf.chatDisplayNode.textInputNode() {
let controller = ChatSendMessageActionSheetController(context: strongSelf.context, controllerInteraction: strongSelf.controllerInteraction, sendButtonFrame: sendButtonFrame, textInputNode: textInputNode)
strongSelf.presentInGlobalOverlay(controller, with: nil)
}
}, statuses: ChatPanelInterfaceInteractionStatuses(editingMessage: self.editingMessage.get(), startingBot: self.startingBot.get(), unblockingPeer: self.unblockingPeer.get(), searching: self.searching.get(), loadingMessage: self.loadingMessage.get()))
switch self.chatLocation {
case let .peer(peerId):
@ -5081,8 +5097,12 @@ public final class ChatController: TelegramController, GalleryHiddenMediaTarget,
}
}
private func transformEnqueueMessages(_ messages: [EnqueueMessage]) -> [EnqueueMessage] {
func transformEnqueueMessages(_ messages: [EnqueueMessage]) -> [EnqueueMessage] {
let silentPosting = self.presentationInterfaceState.interfaceState.silentPosting
return transformEnqueueMessages(messages, silentPosting: silentPosting)
}
private func transformEnqueueMessages(_ messages: [EnqueueMessage], silentPosting: Bool) -> [EnqueueMessage] {
return messages.map { message in
if silentPosting {
return message.withUpdatedAttributes { attributes in

View File

@ -77,6 +77,7 @@ public final class ChatControllerInteraction {
let navigateToMessage: (MessageId, MessageId) -> Void
let clickThroughMessage: () -> Void
let toggleMessagesSelection: ([MessageId], Bool) -> Void
let sendCurrentMessage: (Bool) -> Void
let sendMessage: (String) -> Void
let sendSticker: (FileMediaReference, Bool, ASDisplayNode, CGRect) -> Bool
let sendGif: (FileMediaReference, ASDisplayNode, CGRect) -> Bool
@ -123,7 +124,7 @@ public final class ChatControllerInteraction {
var stickerSettings: ChatInterfaceStickerSettings
var searchTextHighightState: String?
init(openMessage: @escaping (Message, ChatControllerInteractionOpenMessageMode) -> Bool, openPeer: @escaping (PeerId?, ChatControllerInteractionNavigateToPeer, Message?) -> Void, openPeerMention: @escaping (String) -> Void, openMessageContextMenu: @escaping (Message, Bool, ASDisplayNode, CGRect) -> Void, navigateToMessage: @escaping (MessageId, MessageId) -> Void, clickThroughMessage: @escaping () -> Void, toggleMessagesSelection: @escaping ([MessageId], Bool) -> Void, sendMessage: @escaping (String) -> Void, sendSticker: @escaping (FileMediaReference, Bool, ASDisplayNode, CGRect) -> Bool, sendGif: @escaping (FileMediaReference, ASDisplayNode, CGRect) -> Bool, requestMessageActionCallback: @escaping (MessageId, MemoryBuffer?, Bool) -> Void, requestMessageActionUrlAuth: @escaping (String, MessageId, Int32) -> Void, activateSwitchInline: @escaping (PeerId?, String) -> Void, openUrl: @escaping (String, Bool, Bool?) -> Void, shareCurrentLocation: @escaping () -> Void, shareAccountContact: @escaping () -> Void, sendBotCommand: @escaping (MessageId?, String) -> Void, openInstantPage: @escaping (Message, ChatMessageItemAssociatedData?) -> Void, openWallpaper: @escaping (Message) -> Void, openHashtag: @escaping (String?, String) -> Void, updateInputState: @escaping ((ChatTextInputState) -> ChatTextInputState) -> Void, updateInputMode: @escaping ((ChatInputMode) -> ChatInputMode) -> Void, openMessageShareMenu: @escaping (MessageId) -> Void, presentController: @escaping (ViewController, Any?) -> Void, navigationController: @escaping () -> NavigationController?, presentGlobalOverlayController: @escaping (ViewController, Any?) -> Void, callPeer: @escaping (PeerId) -> Void, longTap: @escaping (ChatControllerInteractionLongTapAction, Message?) -> Void, openCheckoutOrReceipt: @escaping (MessageId) -> Void, openSearch: @escaping () -> Void, setupReply: @escaping (MessageId) -> Void, canSetupReply: @escaping (Message) -> Bool, navigateToFirstDateMessage: @escaping(Int32) ->Void, requestRedeliveryOfFailedMessages: @escaping (MessageId) -> Void, addContact: @escaping (String) -> Void, rateCall: @escaping (Message, CallId) -> Void, requestSelectMessagePollOption: @escaping (MessageId, Data) -> Void, openAppStorePage: @escaping () -> Void, displayMessageTooltip: @escaping (MessageId, String, ASDisplayNode?, CGRect?) -> Void, seekToTimecode: @escaping (Message, Double, Bool) -> Void, 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) -> Void, navigateToMessage: @escaping (MessageId, MessageId) -> Void, clickThroughMessage: @escaping () -> Void, toggleMessagesSelection: @escaping ([MessageId], Bool) -> Void, sendCurrentMessage: @escaping (Bool) -> Void, sendMessage: @escaping (String) -> Void, sendSticker: @escaping (FileMediaReference, Bool, ASDisplayNode, CGRect) -> Bool, sendGif: @escaping (FileMediaReference, ASDisplayNode, CGRect) -> Bool, requestMessageActionCallback: @escaping (MessageId, MemoryBuffer?, Bool) -> Void, requestMessageActionUrlAuth: @escaping (String, MessageId, Int32) -> Void, activateSwitchInline: @escaping (PeerId?, String) -> Void, openUrl: @escaping (String, Bool, Bool?) -> Void, shareCurrentLocation: @escaping () -> Void, shareAccountContact: @escaping () -> Void, sendBotCommand: @escaping (MessageId?, String) -> Void, openInstantPage: @escaping (Message, ChatMessageItemAssociatedData?) -> Void, openWallpaper: @escaping (Message) -> Void, openHashtag: @escaping (String?, String) -> Void, updateInputState: @escaping ((ChatTextInputState) -> ChatTextInputState) -> Void, updateInputMode: @escaping ((ChatInputMode) -> ChatInputMode) -> Void, openMessageShareMenu: @escaping (MessageId) -> Void, presentController: @escaping (ViewController, Any?) -> Void, navigationController: @escaping () -> NavigationController?, presentGlobalOverlayController: @escaping (ViewController, Any?) -> Void, callPeer: @escaping (PeerId) -> Void, longTap: @escaping (ChatControllerInteractionLongTapAction, Message?) -> Void, openCheckoutOrReceipt: @escaping (MessageId) -> Void, openSearch: @escaping () -> Void, setupReply: @escaping (MessageId) -> Void, canSetupReply: @escaping (Message) -> Bool, navigateToFirstDateMessage: @escaping(Int32) ->Void, requestRedeliveryOfFailedMessages: @escaping (MessageId) -> Void, addContact: @escaping (String) -> Void, rateCall: @escaping (Message, CallId) -> Void, requestSelectMessagePollOption: @escaping (MessageId, Data) -> Void, openAppStorePage: @escaping () -> Void, displayMessageTooltip: @escaping (MessageId, String, ASDisplayNode?, CGRect?) -> Void, seekToTimecode: @escaping (Message, Double, Bool) -> Void, requestMessageUpdate: @escaping (MessageId) -> Void, cancelInteractiveKeyboardGestures: @escaping () -> Void, automaticMediaDownloadSettings: MediaAutoDownloadSettings, pollActionState: ChatInterfacePollActionState, stickerSettings: ChatInterfaceStickerSettings) {
self.openMessage = openMessage
self.openPeer = openPeer
self.openPeerMention = openPeerMention
@ -131,6 +132,7 @@ public final class ChatControllerInteraction {
self.navigateToMessage = navigateToMessage
self.clickThroughMessage = clickThroughMessage
self.toggleMessagesSelection = toggleMessagesSelection
self.sendCurrentMessage = sendCurrentMessage
self.sendMessage = sendMessage
self.sendSticker = sendSticker
self.sendGif = sendGif
@ -176,7 +178,7 @@ public final class ChatControllerInteraction {
static var `default`: ChatControllerInteraction {
return ChatControllerInteraction(openMessage: { _, _ in
return false }, openPeer: { _, _, _ in }, openPeerMention: { _ in }, openMessageContextMenu: { _, _, _, _ in }, navigateToMessage: { _, _ in }, clickThroughMessage: { }, toggleMessagesSelection: { _, _ in }, sendMessage: { _ in }, sendSticker: { _, _, _, _ in return false }, sendGif: { _, _, _ in return false }, requestMessageActionCallback: { _, _, _ in }, requestMessageActionUrlAuth: { _, _, _ in }, activateSwitchInline: { _, _ in }, openUrl: { _, _, _ in }, shareCurrentLocation: {}, shareAccountContact: {}, sendBotCommand: { _, _ in }, openInstantPage: { _, _ in }, openWallpaper: { _ in }, openHashtag: { _, _ in }, updateInputState: { _ in }, updateInputMode: { _ in }, openMessageShareMenu: { _ in
return false }, openPeer: { _, _, _ in }, openPeerMention: { _ in }, openMessageContextMenu: { _, _, _, _ in }, navigateToMessage: { _, _ in }, clickThroughMessage: { }, toggleMessagesSelection: { _, _ in }, sendCurrentMessage: { _ in }, sendMessage: { _ in }, sendSticker: { _, _, _, _ in return false }, sendGif: { _, _, _ in return false }, requestMessageActionCallback: { _, _, _ in }, requestMessageActionUrlAuth: { _, _, _ in }, activateSwitchInline: { _, _ in }, openUrl: { _, _, _ in }, shareCurrentLocation: {}, shareAccountContact: {}, sendBotCommand: { _, _ in }, openInstantPage: { _, _ in }, openWallpaper: { _ in }, openHashtag: { _, _ in }, updateInputState: { _ in }, updateInputMode: { _ in }, openMessageShareMenu: { _ in
}, presentController: { _, _ in }, navigationController: {
return nil
}, presentGlobalOverlayController: { _, _ in }, callPeer: { _ in }, longTap: { _, _ in }, openCheckoutOrReceipt: { _ in }, openSearch: { }, setupReply: { _ in

View File

@ -127,7 +127,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
var requestUpdateChatInterfaceState: (Bool, Bool, (ChatInterfaceState) -> ChatInterfaceState) -> Void = { _, _, _ in }
var requestUpdateInterfaceState: (ContainedViewLayoutTransition, Bool, (ChatPresentationInterfaceState) -> ChatPresentationInterfaceState) -> Void = { _, _, _ in }
var sendMessages: ([EnqueueMessage], Bool) -> Void = { _, _ in }
var sendMessages: ([EnqueueMessage], Bool?, Bool) -> Void = { _, _, _ in }
var displayAttachmentMenu: () -> Void = { }
var paste: (ChatTextInputPanelPasteData) -> Void = { _ in }
var updateTypingActivity: (Bool) -> Void = { _ in }
@ -172,6 +172,8 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
}
}
private var lastSendTimestamp = 0.0
private var openStickersDisposable: Disposable?
private var displayVideoUnmuteTipDisposable: Disposable?
@ -277,78 +279,10 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
strongSelf.requestLayout(.animated(duration: 0.1, curve: .easeInOut))
}
}
var lastSendTimestamp = 0.0
self.textInputPanelNode?.sendMessage = { [weak self] in
if let strongSelf = self, let textInputPanelNode = strongSelf.inputPanelNode as? ChatTextInputPanelNode {
if textInputPanelNode.textInputNode?.isFirstResponder() ?? false {
Keyboard.applyAutocorrection()
}
var effectivePresentationInterfaceState = strongSelf.chatPresentationInterfaceState
if let textInputPanelNode = strongSelf.textInputPanelNode {
effectivePresentationInterfaceState = effectivePresentationInterfaceState.updatedInterfaceState { $0.withUpdatedEffectiveInputState(textInputPanelNode.inputTextState) }
}
if let _ = effectivePresentationInterfaceState.interfaceState.editMessage {
strongSelf.interfaceInteraction?.editMessage()
} else {
if let _ = effectivePresentationInterfaceState.slowmodeState {
if let rect = strongSelf.frameForInputActionButton() {
strongSelf.interfaceInteraction?.displaySlowmodeTooltip(strongSelf, rect)
}
return
}
let timestamp = CACurrentMediaTime()
if lastSendTimestamp + 0.15 > timestamp {
return
}
lastSendTimestamp = timestamp
strongSelf.updateTypingActivity(false)
var messages: [EnqueueMessage] = []
let inputText = convertMarkdownToAttributes(effectivePresentationInterfaceState.interfaceState.composeInputState.inputText)
for text in breakChatInputText(trimChatInputText(inputText)) {
if text.length != 0 {
var attributes: [MessageAttribute] = []
let entities = generateTextEntities(text.string, enabledTypes: .all, currentEntities: generateChatInputTextEntities(text))
if !entities.isEmpty {
attributes.append(TextEntitiesMessageAttribute(entities: entities))
}
var webpage: TelegramMediaWebpage?
if strongSelf.chatPresentationInterfaceState.interfaceState.composeDisableUrlPreview != nil {
attributes.append(OutgoingContentInfoMessageAttribute(flags: [.disableLinkPreviews]))
} else {
webpage = strongSelf.chatPresentationInterfaceState.urlPreview?.1
}
messages.append(.message(text: text.string, attributes: attributes, mediaReference: webpage.flatMap(AnyMediaReference.standalone), replyToMessageId: strongSelf.chatPresentationInterfaceState.interfaceState.replyMessageId, localGroupingKey: nil))
}
}
if !messages.isEmpty || strongSelf.chatPresentationInterfaceState.interfaceState.forwardMessageIds != nil {
strongSelf.setupSendActionOnViewUpdate({ [weak strongSelf] in
if let strongSelf = strongSelf, let textInputPanelNode = strongSelf.inputPanelNode as? ChatTextInputPanelNode {
strongSelf.ignoreUpdateHeight = true
textInputPanelNode.text = ""
strongSelf.requestUpdateChatInterfaceState(false, true, { $0.withUpdatedReplyMessageId(nil).withUpdatedForwardMessageIds(nil).withUpdatedComposeDisableUrlPreview(nil) })
strongSelf.ignoreUpdateHeight = false
}
})
if let forwardMessageIds = strongSelf.chatPresentationInterfaceState.interfaceState.forwardMessageIds {
for id in forwardMessageIds {
messages.append(.forward(source: id, grouping: .auto))
}
}
if case .peer = strongSelf.chatLocation {
strongSelf.sendMessages(messages, messages.count > 1)
}
}
}
if let strongSelf = self {
strongSelf.sendCurrentMessage()
}
}
@ -1602,6 +1536,18 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
return self.inputPanelNode?.frame
}
func sendButtonFrame() -> CGRect? {
if let frame = self.textInputPanelNode?.actionButtons.frame {
return self.textInputPanelNode?.convert(frame, to: self)
} else {
return nil
}
}
func textInputNode() -> EditableTextNode? {
return self.textInputPanelNode?.textInputNode
}
func frameForInputPanelAccessoryButton(_ item: ChatTextInputAccessoryItem) -> CGRect? {
if let textInputPanelNode = self.textInputPanelNode, self.inputPanelNode === textInputPanelNode {
return textInputPanelNode.frameForAccessoryButton(item).flatMap {
@ -2053,4 +1999,78 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
})
}
}
func sendCurrentMessage(silentPosting: Bool? = nil) {
if let textInputPanelNode = self.inputPanelNode as? ChatTextInputPanelNode {
if textInputPanelNode.textInputNode?.isFirstResponder() ?? false {
Keyboard.applyAutocorrection()
}
var effectivePresentationInterfaceState = self.chatPresentationInterfaceState
if let textInputPanelNode = self.textInputPanelNode {
effectivePresentationInterfaceState = effectivePresentationInterfaceState.updatedInterfaceState { $0.withUpdatedEffectiveInputState(textInputPanelNode.inputTextState) }
}
if let _ = effectivePresentationInterfaceState.interfaceState.editMessage {
self.interfaceInteraction?.editMessage()
} else {
if let _ = effectivePresentationInterfaceState.slowmodeState {
if let rect = self.frameForInputActionButton() {
self.interfaceInteraction?.displaySlowmodeTooltip(self, rect)
}
return
}
let timestamp = CACurrentMediaTime()
if self.lastSendTimestamp + 0.15 > timestamp {
return
}
self.lastSendTimestamp = timestamp
self.updateTypingActivity(false)
var messages: [EnqueueMessage] = []
let inputText = convertMarkdownToAttributes(effectivePresentationInterfaceState.interfaceState.composeInputState.inputText)
for text in breakChatInputText(trimChatInputText(inputText)) {
if text.length != 0 {
var attributes: [MessageAttribute] = []
let entities = generateTextEntities(text.string, enabledTypes: .all, currentEntities: generateChatInputTextEntities(text))
if !entities.isEmpty {
attributes.append(TextEntitiesMessageAttribute(entities: entities))
}
var webpage: TelegramMediaWebpage?
if self.chatPresentationInterfaceState.interfaceState.composeDisableUrlPreview != nil {
attributes.append(OutgoingContentInfoMessageAttribute(flags: [.disableLinkPreviews]))
} else {
webpage = self.chatPresentationInterfaceState.urlPreview?.1
}
messages.append(.message(text: text.string, attributes: attributes, mediaReference: webpage.flatMap(AnyMediaReference.standalone), replyToMessageId: self.chatPresentationInterfaceState.interfaceState.replyMessageId, localGroupingKey: nil))
}
}
if !messages.isEmpty || self.chatPresentationInterfaceState.interfaceState.forwardMessageIds != nil {
self.setupSendActionOnViewUpdate({ [weak self] in
if let strongSelf = self, let textInputPanelNode = strongSelf.inputPanelNode as? ChatTextInputPanelNode {
strongSelf.ignoreUpdateHeight = true
textInputPanelNode.text = ""
strongSelf.requestUpdateChatInterfaceState(false, true, { $0.withUpdatedReplyMessageId(nil).withUpdatedForwardMessageIds(nil).withUpdatedComposeDisableUrlPreview(nil) })
strongSelf.ignoreUpdateHeight = false
}
})
if let forwardMessageIds = self.chatPresentationInterfaceState.interfaceState.forwardMessageIds {
for id in forwardMessageIds {
messages.append(.forward(source: id, grouping: .auto))
}
}
if case .peer = self.chatLocation {
self.sendMessages(messages, silentPosting, messages.count > 1)
}
}
}
}
}
}

View File

@ -40,7 +40,7 @@ enum ChatHistoryEntry: Identifiable, Comparable {
switch self {
case let .MessageEntry(message, presentationData, _, _, _, _):
var type = 2
if presentationData.largeEmoji && message.elligibleForLargeEmoji && messageTextIsElligibleForLargeEmoji(message.text) {
if presentationData.largeEmoji && message.elligibleForLargeEmoji && messageIsElligibleForLargeEmoji(message) {
type = 3
}
return UInt64(message.stableId) | ((UInt64(type) << 40))

View File

@ -58,7 +58,7 @@ final class ChatMediaInputPeerSpecificItem: ListViewItem {
}
}
private let avatarFont = UIFont(name: ".SFCompactRounded-Semibold", size: 10.0)!
private let avatarFont = UIFont(name: ".SFCompactRounded-Semibold", size: 12.0)!
private let boundingSize = CGSize(width: 41.0, height: 41.0)
private let boundingImageSize = CGSize(width: 28.0, height: 28.0)
private let highlightSize = CGSize(width: 35.0, height: 35.0)
@ -83,7 +83,7 @@ final class ChatMediaInputPeerSpecificItemNode: ListViewItemNode {
self.avatarNode.isLayerBacked = !smartInvertColorsEnabled()
self.avatarNode.transform = CATransform3DMakeRotation(CGFloat.pi / 2.0, 0.0, 0.0, 1.0)
let imageSize = CGSize(width: 32.0, height: 32.0)
let imageSize = CGSize(width: 26.0, height: 26.0)
self.avatarNode.frame = CGRect(origin: CGPoint(x: floor((boundingSize.width - imageSize.width) / 2.0) + verticalOffset, y: floor((boundingSize.height - imageSize.height) / 2.0)), size: imageSize)
self.highlightNode.frame = CGRect(origin: CGPoint(x: floor((boundingSize.width - highlightSize.width) / 2.0) + verticalOffset, y: floor((boundingSize.height - highlightSize.height) / 2.0)), size: highlightSize)

View File

@ -385,7 +385,7 @@ public final class ChatMessageItem: ListViewItem, CustomStringConvertible {
}
}
if viewClassName == ChatMessageBubbleItemNode.self && self.presentationData.largeEmoji && self.message.elligibleForLargeEmoji && messageTextIsElligibleForLargeEmoji(message.text) {
if viewClassName == ChatMessageBubbleItemNode.self && self.presentationData.largeEmoji && self.message.elligibleForLargeEmoji && messageIsElligibleForLargeEmoji(self.message) {
viewClassName = ChatMessageStickerItemNode.self
}

View File

@ -106,9 +106,10 @@ final class ChatPanelInterfaceInteraction {
let openLinkEditing: () -> Void
let reportPeerIrrelevantGeoLocation: () -> Void
let displaySlowmodeTooltip: (ASDisplayNode, CGRect) -> Void
let displaySendMessageOptions: () -> Void
let statuses: ChatPanelInterfaceInteractionStatuses?
init(setupReplyMessage: @escaping (MessageId) -> Void, setupEditMessage: @escaping (MessageId?) -> Void, beginMessageSelection: @escaping ([MessageId]) -> Void, deleteSelectedMessages: @escaping () -> Void, reportSelectedMessages: @escaping () -> Void, reportMessages: @escaping ([Message]) -> Void, deleteMessages: @escaping ([Message]) -> Void, forwardSelectedMessages: @escaping () -> Void, forwardCurrentForwardMessages: @escaping () -> Void, forwardMessages: @escaping ([Message]) -> Void, shareSelectedMessages: @escaping () -> Void, updateTextInputStateAndMode: @escaping ((ChatTextInputState, ChatInputMode) -> (ChatTextInputState, ChatInputMode)) -> Void, updateInputModeAndDismissedButtonKeyboardMessageId: @escaping ((ChatPresentationInterfaceState) -> (ChatInputMode, MessageId?)) -> Void, openStickers: @escaping () -> Void, editMessage: @escaping () -> Void, beginMessageSearch: @escaping (ChatSearchDomain, String) -> Void, dismissMessageSearch: @escaping () -> Void, updateMessageSearch: @escaping (String) -> Void, navigateMessageSearch: @escaping (ChatPanelSearchNavigationAction) -> Void, openCalendarSearch: @escaping () -> Void, toggleMembersSearch: @escaping (Bool) -> Void, navigateToMessage: @escaping (MessageId) -> Void, navigateToChat: @escaping (PeerId) -> Void, openPeerInfo: @escaping () -> Void, togglePeerNotifications: @escaping () -> Void, sendContextResult: @escaping (ChatContextResultCollection, ChatContextResult, ASDisplayNode, CGRect) -> Bool, sendBotCommand: @escaping (Peer, String) -> Void, sendBotStart: @escaping (String?) -> Void, botSwitchChatWithPayload: @escaping (PeerId, String) -> Void, beginMediaRecording: @escaping (Bool) -> Void, finishMediaRecording: @escaping (ChatFinishMediaRecordingAction) -> Void, stopMediaRecording: @escaping () -> Void, lockMediaRecording: @escaping () -> Void, deleteRecordedMedia: @escaping () -> Void, sendRecordedMedia: @escaping () -> Void, displayRestrictedInfo: @escaping (ChatPanelRestrictionInfoSubject, ChatPanelRestrictionInfoDisplayType) -> Void, displayVideoUnmuteTip: @escaping (CGPoint?) -> Void, switchMediaRecordingMode: @escaping () -> Void, setupMessageAutoremoveTimeout: @escaping () -> Void, sendSticker: @escaping (FileMediaReference, ASDisplayNode, CGRect) -> Bool, unblockPeer: @escaping () -> Void, pinMessage: @escaping (MessageId) -> Void, unpinMessage: @escaping () -> Void, shareAccountContact: @escaping () -> Void, reportPeer: @escaping () -> Void, presentPeerContact: @escaping () -> Void, dismissReportPeer: @escaping () -> Void, deleteChat: @escaping () -> Void, beginCall: @escaping () -> Void, toggleMessageStickerStarred: @escaping (MessageId) -> Void, presentController: @escaping (ViewController, Any?) -> Void, getNavigationController: @escaping () -> NavigationController?, presentGlobalOverlayController: @escaping (ViewController, Any?) -> Void, navigateFeed: @escaping () -> Void, openGrouping: @escaping () -> Void, toggleSilentPost: @escaping () -> Void, requestUnvoteInMessage: @escaping (MessageId) -> Void, requestStopPollInMessage: @escaping (MessageId) -> Void, updateInputLanguage: @escaping ((String?) -> String?) -> Void, unarchiveChat: @escaping () -> Void, openLinkEditing: @escaping () -> Void, reportPeerIrrelevantGeoLocation: @escaping () -> Void, displaySlowmodeTooltip: @escaping (ASDisplayNode, CGRect) -> Void, statuses: ChatPanelInterfaceInteractionStatuses?) {
init(setupReplyMessage: @escaping (MessageId) -> Void, setupEditMessage: @escaping (MessageId?) -> Void, beginMessageSelection: @escaping ([MessageId]) -> Void, deleteSelectedMessages: @escaping () -> Void, reportSelectedMessages: @escaping () -> Void, reportMessages: @escaping ([Message]) -> Void, deleteMessages: @escaping ([Message]) -> Void, forwardSelectedMessages: @escaping () -> Void, forwardCurrentForwardMessages: @escaping () -> Void, forwardMessages: @escaping ([Message]) -> Void, shareSelectedMessages: @escaping () -> Void, updateTextInputStateAndMode: @escaping ((ChatTextInputState, ChatInputMode) -> (ChatTextInputState, ChatInputMode)) -> Void, updateInputModeAndDismissedButtonKeyboardMessageId: @escaping ((ChatPresentationInterfaceState) -> (ChatInputMode, MessageId?)) -> Void, openStickers: @escaping () -> Void, editMessage: @escaping () -> Void, beginMessageSearch: @escaping (ChatSearchDomain, String) -> Void, dismissMessageSearch: @escaping () -> Void, updateMessageSearch: @escaping (String) -> Void, navigateMessageSearch: @escaping (ChatPanelSearchNavigationAction) -> Void, openCalendarSearch: @escaping () -> Void, toggleMembersSearch: @escaping (Bool) -> Void, navigateToMessage: @escaping (MessageId) -> Void, navigateToChat: @escaping (PeerId) -> Void, openPeerInfo: @escaping () -> Void, togglePeerNotifications: @escaping () -> Void, sendContextResult: @escaping (ChatContextResultCollection, ChatContextResult, ASDisplayNode, CGRect) -> Bool, sendBotCommand: @escaping (Peer, String) -> Void, sendBotStart: @escaping (String?) -> Void, botSwitchChatWithPayload: @escaping (PeerId, String) -> Void, beginMediaRecording: @escaping (Bool) -> Void, finishMediaRecording: @escaping (ChatFinishMediaRecordingAction) -> Void, stopMediaRecording: @escaping () -> Void, lockMediaRecording: @escaping () -> Void, deleteRecordedMedia: @escaping () -> Void, sendRecordedMedia: @escaping () -> Void, displayRestrictedInfo: @escaping (ChatPanelRestrictionInfoSubject, ChatPanelRestrictionInfoDisplayType) -> Void, displayVideoUnmuteTip: @escaping (CGPoint?) -> Void, switchMediaRecordingMode: @escaping () -> Void, setupMessageAutoremoveTimeout: @escaping () -> Void, sendSticker: @escaping (FileMediaReference, ASDisplayNode, CGRect) -> Bool, unblockPeer: @escaping () -> Void, pinMessage: @escaping (MessageId) -> Void, unpinMessage: @escaping () -> Void, shareAccountContact: @escaping () -> Void, reportPeer: @escaping () -> Void, presentPeerContact: @escaping () -> Void, dismissReportPeer: @escaping () -> Void, deleteChat: @escaping () -> Void, beginCall: @escaping () -> Void, toggleMessageStickerStarred: @escaping (MessageId) -> Void, presentController: @escaping (ViewController, Any?) -> Void, getNavigationController: @escaping () -> NavigationController?, presentGlobalOverlayController: @escaping (ViewController, Any?) -> Void, navigateFeed: @escaping () -> Void, openGrouping: @escaping () -> Void, toggleSilentPost: @escaping () -> Void, requestUnvoteInMessage: @escaping (MessageId) -> Void, requestStopPollInMessage: @escaping (MessageId) -> Void, updateInputLanguage: @escaping ((String?) -> String?) -> Void, unarchiveChat: @escaping () -> Void, openLinkEditing: @escaping () -> Void, reportPeerIrrelevantGeoLocation: @escaping () -> Void, displaySlowmodeTooltip: @escaping (ASDisplayNode, CGRect) -> Void, displaySendMessageOptions: @escaping () -> Void, statuses: ChatPanelInterfaceInteractionStatuses?) {
self.setupReplyMessage = setupReplyMessage
self.setupEditMessage = setupEditMessage
self.beginMessageSelection = beginMessageSelection
@ -172,6 +173,7 @@ final class ChatPanelInterfaceInteraction {
self.openLinkEditing = openLinkEditing
self.reportPeerIrrelevantGeoLocation = reportPeerIrrelevantGeoLocation
self.displaySlowmodeTooltip = displaySlowmodeTooltip
self.displaySendMessageOptions = displaySendMessageOptions
self.statuses = statuses
}
}

View File

@ -105,6 +105,7 @@ final class ChatRecentActionsController: TelegramController {
}, openLinkEditing: {
}, reportPeerIrrelevantGeoLocation: {
}, displaySlowmodeTooltip: { _, _ in
}, displaySendMessageOptions: {
}, statuses: nil)
self.navigationItem.titleView = self.titleView

View File

@ -93,6 +93,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
self.panelButtonNode.setTitle(self.presentationData.strings.Channel_AdminLog_InfoPanelTitle, with: Font.regular(17.0), with: self.presentationData.theme.chat.inputPanel.panelControlAccentColor, for: [])
self.listNode = ListView()
self.listNode.dynamicBounceEnabled = !self.presentationData.disableAnimations
self.listNode.transform = CATransform3DMakeRotation(CGFloat(Double.pi), 0.0, 0.0, 1.0)
self.loadingNode = ChatLoadingNode(theme: self.presentationData.theme, chatWallpaper: self.presentationData.chatWallpaper)
self.emptyNode = ChatRecentActionsEmptyNode(theme: self.presentationData.theme, chatWallpaper: self.presentationData.chatWallpaper)
@ -181,7 +182,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
self?.openPeerMention(name)
}, openMessageContextMenu: { [weak self] message, selectAll, node, frame in
self?.openMessageContextMenu(message: message, selectAll: selectAll, node: node, frame: frame)
}, navigateToMessage: { _, _ in }, clickThroughMessage: { }, toggleMessagesSelection: { _, _ in }, sendMessage: { _ in }, sendSticker: { _, _, _, _ in return false }, sendGif: { _, _, _ in return false }, requestMessageActionCallback: { _, _, _ in }, requestMessageActionUrlAuth: { _, _, _ in }, activateSwitchInline: { _, _ in }, openUrl: { [weak self] url, _, _ in
}, navigateToMessage: { _, _ in }, clickThroughMessage: { }, toggleMessagesSelection: { _, _ in }, sendCurrentMessage: { _ in }, sendMessage: { _ in }, sendSticker: { _, _, _, _ in return false }, sendGif: { _, _, _ in return false }, requestMessageActionCallback: { _, _, _ in }, requestMessageActionUrlAuth: { _, _, _ in }, activateSwitchInline: { _, _ in }, openUrl: { [weak self] url, _, _ in
self?.openUrl(url)
}, shareCurrentLocation: {}, shareAccountContact: {}, sendBotCommand: { _, _ in }, openInstantPage: { [weak self] message, associatedData in
if let strongSelf = self, let navigationController = strongSelf.getNavigationController() {
@ -441,7 +442,6 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
self.historyDisposable = appliedTransition.start()
let mediaManager = self.context.sharedContext.mediaManager
self.galleryHiddenMesageAndMediaDisposable.set(mediaManager.galleryHiddenMediaManager.hiddenIds().start(next: { [weak self] ids in
if let strongSelf = self, let controllerInteraction = strongSelf.controllerInteraction {

View File

@ -0,0 +1,79 @@
import Foundation
import UIKit
import Display
import AsyncDisplayKit
import TelegramPresentationData
final class ChatSendMessageActionSheetController: ViewController {
var controllerNode: ChatSendMessageActionSheetControllerNode {
return self.displayNode as! ChatSendMessageActionSheetControllerNode
}
private let context: AccountContext
private let controllerInteraction: ChatControllerInteraction?
private let sendButtonFrame: CGRect
private let textInputNode: EditableTextNode
private var didPlayPresentationAnimation = false
private let hapticFeedback = HapticFeedback()
init(context: AccountContext, controllerInteraction: ChatControllerInteraction?, sendButtonFrame: CGRect, textInputNode: EditableTextNode) {
self.context = context
self.controllerInteraction = controllerInteraction
self.sendButtonFrame = sendButtonFrame
self.textInputNode = textInputNode
super.init(navigationBarPresentationData: nil)
self.statusBar.statusBarStyle = .Hide
self.statusBar.ignoreInCall = true
self.lockOrientation = true
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func loadDisplayNode() {
self.displayNode = ChatSendMessageActionSheetControllerNode(context: self.context, sendButtonFrame: self.sendButtonFrame, textInputNode: self.textInputNode, send: { [weak self] in
self?.controllerInteraction?.sendCurrentMessage(false)
self?.dismiss(cancel: false)
}, sendSilently: { [weak self] in
self?.controllerInteraction?.sendCurrentMessage(true)
self?.dismiss(cancel: false)
}, cancel: { [weak self] in
self?.dismiss(cancel: true)
})
self.displayNodeDidLoad()
}
override public func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if !self.didPlayPresentationAnimation {
self.didPlayPresentationAnimation = true
self.hapticFeedback.impact()
self.controllerNode.animateIn()
}
}
override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
super.containerLayoutUpdated(layout, transition: transition)
self.controllerNode.containerLayoutUpdated(layout, transition: transition)
}
override public func dismiss(completion: (() -> Void)? = nil) {
self.dismiss(cancel: true)
}
private func dismiss(cancel: Bool) {
self.controllerNode.animateOut(cancel: cancel, completion: { [weak self] in
self?.didPlayPresentationAnimation = false
self?.presentingViewController?.dismiss(animated: false, completion: nil)
})
}
}

View File

@ -0,0 +1,453 @@
import Foundation
import UIKit
import AsyncDisplayKit
import Display
import Postbox
import TelegramCore
import TelegramPresentationData
private final class ActionSheetItemNode: ASDisplayNode {
private let action: () -> Void
private let separatorNode: ASDisplayNode
private let highlightedBackgroundNode: ASDisplayNode
private let buttonNode: HighlightTrackingButtonNode
private let iconNode: ASImageNode
private let titleNode: ImmediateTextNode
init(theme: PresentationTheme, title: String, action: @escaping () -> Void) {
self.action = action
self.separatorNode = ASDisplayNode()
self.separatorNode.backgroundColor = theme.actionSheet.opaqueItemSeparatorColor
self.highlightedBackgroundNode = ASDisplayNode()
self.highlightedBackgroundNode.backgroundColor = theme.actionSheet.opaqueItemHighlightedBackgroundColor
self.highlightedBackgroundNode.alpha = 0.0
self.buttonNode = HighlightTrackingButtonNode()
self.titleNode = ImmediateTextNode()
self.titleNode.maximumNumberOfLines = 1
self.titleNode.attributedText = NSAttributedString(string: title, font: Font.regular(17.0), textColor: theme.actionSheet.primaryTextColor)
self.iconNode = ASImageNode()
self.iconNode.image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Menu/SilentIcon"), color: theme.actionSheet.primaryTextColor)
self.iconNode.contentMode = .center
super.init()
self.addSubnode(self.separatorNode)
self.addSubnode(self.highlightedBackgroundNode)
self.addSubnode(self.titleNode)
self.addSubnode(self.iconNode)
self.addSubnode(self.buttonNode)
self.buttonNode.addTarget(self, action: #selector(self.buttonPressed), forControlEvents: .touchUpInside)
self.buttonNode.highligthedChanged = { [weak self] highlighted in
if let strongSelf = self {
if highlighted {
strongSelf.highlightedBackgroundNode.layer.removeAnimation(forKey: "opacity")
strongSelf.highlightedBackgroundNode.alpha = 1.0
} else {
strongSelf.highlightedBackgroundNode.alpha = 0.0
strongSelf.highlightedBackgroundNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3)
}
}
}
}
func updateLayout(maxWidth: CGFloat) -> (CGFloat, CGFloat, (CGFloat) -> Void) {
let leftInset: CGFloat = 16.0
let rightInset: CGFloat = 76.0
let titleSize = self.titleNode.updateLayout(CGSize(width: maxWidth - leftInset - rightInset, height: .greatestFiniteMagnitude))
let height: CGFloat = 44.0
return (titleSize.width + leftInset + rightInset, height, { width in
self.titleNode.frame = CGRect(origin: CGPoint(x: leftInset, y: floor((height - titleSize.height) / 2.0)), size: titleSize)
if let image = self.iconNode.image {
self.iconNode.frame = CGRect(origin: CGPoint(x: width - floor((rightInset - image.size.width) / 2.0) - 10.0, y: floor((height - image.size.height) / 2.0)), size: image.size)
}
self.separatorNode.frame = CGRect(origin: CGPoint(x: 0.0, y: height - UIScreenPixel), size: CGSize(width: width, height: UIScreenPixel))
self.highlightedBackgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: width, height: height))
self.buttonNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: width, height: height))
})
}
@objc private func buttonPressed() {
self.action()
}
}
final class ChatSendMessageActionSheetControllerNode: ViewControllerTracingNode, UIScrollViewDelegate {
private let presentationData: PresentationData
private let sendButtonFrame: CGRect
private let textFieldFrame: CGRect
private let textInputNode: EditableTextNode
private let send: (() -> Void)?
private let cancel: (() -> Void)?
private let coverNode: ASDisplayNode
private let effectView: UIVisualEffectView
private let dimNode: ASDisplayNode
private let contentContainerNode: ASDisplayNode
private let contentNodes: [ActionSheetItemNode]
private let sendButtonNode: HighlightableButtonNode
private let messageClipNode: ASDisplayNode
private let messageBackgroundNode: ASImageNode
private let messageTextNode: EditableTextNode
private let scrollNode: ASScrollNode
private var validLayout: ContainerViewLayout?
init(context: AccountContext, sendButtonFrame: CGRect, textInputNode: EditableTextNode, send: (() -> Void)?, sendSilently: (() -> Void)?, cancel: (() -> Void)?) {
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
self.sendButtonFrame = sendButtonFrame
self.textFieldFrame = textInputNode.convert(textInputNode.bounds, to: nil)
self.textInputNode = textInputNode
self.send = send
self.cancel = cancel
self.coverNode = ASDisplayNode()
self.effectView = UIVisualEffectView()
if #available(iOS 9.0, *) {
} else {
if presentationData.theme.chatList.searchBarKeyboardColor == .dark {
self.effectView.effect = UIBlurEffect(style: .dark)
} else {
self.effectView.effect = UIBlurEffect(style: .light)
}
self.effectView.alpha = 0.0
}
self.dimNode = ASDisplayNode()
self.dimNode.alpha = 1.0
if self.presentationData.theme.chatList.searchBarKeyboardColor == .light {
self.dimNode.backgroundColor = UIColor(white: 0.0, alpha: 0.04)
} else {
self.dimNode.backgroundColor = presentationData.theme.chatList.backgroundColor.withAlphaComponent(0.2)
}
self.sendButtonNode = HighlightableButtonNode()
self.sendButtonNode.setImage(PresentationResourcesChat.chatInputPanelSendButtonImage(self.presentationData.theme), for: [])
self.messageClipNode = ASDisplayNode()
self.messageClipNode.clipsToBounds = true
self.messageClipNode.transform = CATransform3DMakeScale(1.0, -1.0, 1.0)
self.messageBackgroundNode = ASImageNode()
self.messageBackgroundNode.isUserInteractionEnabled = true
self.messageTextNode = EditableTextNode()
self.messageTextNode.isUserInteractionEnabled = false
self.scrollNode = ASScrollNode()
self.scrollNode.transform = CATransform3DMakeScale(1.0, -1.0, 1.0)
self.contentContainerNode = ASDisplayNode()
self.contentContainerNode.backgroundColor = self.presentationData.theme.actionSheet.opaqueItemBackgroundColor
self.contentContainerNode.cornerRadius = 12.0
self.contentContainerNode.clipsToBounds = true
var contentNodes: [ActionSheetItemNode] = []
contentNodes.append(ActionSheetItemNode(theme: self.presentationData.theme, title: self.presentationData.strings.Conversation_SendMessage_SendSilently, action: {
sendSilently?()
}))
self.contentNodes = contentNodes
super.init()
self.coverNode.backgroundColor = self.presentationData.theme.chat.inputPanel.inputBackgroundColor
self.addSubnode(self.coverNode)
self.sendButtonNode.setImage(PresentationResourcesChat.chatInputPanelSendButtonImage(self.presentationData.theme), for: [])
self.sendButtonNode.addTarget(self, action: #selector(sendButtonPressed), forControlEvents: .touchUpInside)
self.messageTextNode.attributedText = textInputNode.attributedText
self.messageBackgroundNode.contentMode = .scaleToFill
let graphics = PresentationResourcesChat.principalGraphics(self.presentationData.theme, wallpaper: self.presentationData.chatWallpaper)
self.messageBackgroundNode.image = graphics.chatMessageBackgroundOutgoingImage
self.view.addSubview(self.effectView)
self.addSubnode(self.dimNode)
self.addSubnode(self.contentContainerNode)
self.addSubnode(self.scrollNode)
self.addSubnode(self.sendButtonNode)
self.scrollNode.addSubnode(self.messageClipNode)
self.messageClipNode.addSubnode(self.messageBackgroundNode)
self.messageClipNode.addSubnode(self.messageTextNode)
self.contentNodes.forEach(self.contentContainerNode.addSubnode)
}
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
let result = super.hitTest(point, with: event)
if result != self.scrollNode.view {
return result
} else {
return self.dimNode.view
}
}
override func didLoad() {
super.didLoad()
self.dimNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.dimTapGesture(_:))))
self.scrollNode.view.showsVerticalScrollIndicator = false
self.scrollNode.view.delaysContentTouches = false
self.scrollNode.view.delegate = self
self.scrollNode.view.alwaysBounceVertical = true
if #available(iOSApplicationExtension 11.0, iOS 11.0, *) {
self.scrollNode.view.contentInsetAdjustmentBehavior = .never
}
}
func animateIn() {
UIView.animate(withDuration: 0.4, animations: {
if #available(iOS 9.0, *) {
if self.presentationData.theme.chatList.searchBarKeyboardColor == .dark {
if #available(iOSApplicationExtension 10.0, iOS 10.0, *) {
self.effectView.effect = UIBlurEffect(style: .regular)
if self.effectView.subviews.count == 2 {
self.effectView.subviews[1].isHidden = true
}
} else {
self.effectView.effect = UIBlurEffect(style: .dark)
}
} else {
if #available(iOSApplicationExtension 10.0, iOS 10.0, *) {
self.effectView.effect = UIBlurEffect(style: .regular)
} else {
self.effectView.effect = UIBlurEffect(style: .light)
}
}
} else {
self.effectView.alpha = 1.0
}
}, completion: { [weak self] _ in
guard let strongSelf = self else {
return
}
if strongSelf.presentationData.theme.chatList.searchBarKeyboardColor == .dark {
if strongSelf.effectView.subviews.count == 2 {
strongSelf.effectView.subviews[1].isHidden = true
}
}
})
self.effectView.subviews[1].layer.removeAnimation(forKey: "backgroundColor")
self.dimNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.4)
self.contentContainerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
self.messageBackgroundNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
if let layout = self.validLayout {
let duration = 0.6
self.sendButtonNode.layer.animateScale(from: 0.75, to: 1.0, duration: 0.2, timingFunction: kCAMediaTimingFunctionLinear, removeOnCompletion: false)
self.sendButtonNode.layer.animatePosition(from: self.sendButtonFrame.center, to: self.sendButtonNode.position, duration: duration, timingFunction: kCAMediaTimingFunctionSpring)
let initialWidth = self.textFieldFrame.width + 32.0
let fromFrame = CGRect(origin: CGPoint(), size: CGSize(width: initialWidth, height: self.textFieldFrame.height + 2.0))
let delta = (fromFrame.height - self.messageClipNode.bounds.height) / 2.0
let inputHeight = layout.inputHeight ?? 0.0
var clipDelta = delta
if inputHeight.isZero {
clipDelta -= 60.0
}
self.messageClipNode.layer.animateBounds(from: fromFrame, to: self.messageClipNode.bounds, duration: duration, timingFunction: kCAMediaTimingFunctionSpring)
self.messageClipNode.layer.animatePosition(from: CGPoint(x: (self.messageClipNode.bounds.width - initialWidth) / 2.0, y: clipDelta), to: CGPoint(), duration: duration, timingFunction: kCAMediaTimingFunctionSpring, additive: true, completion: { [weak self] _ in
if let strongSelf = self {
strongSelf.insertSubnode(strongSelf.contentContainerNode, aboveSubnode: strongSelf.scrollNode)
}
})
self.messageBackgroundNode.layer.animateBounds(from: fromFrame, to: self.messageBackgroundNode.bounds, duration: duration, timingFunction: kCAMediaTimingFunctionSpring)
self.messageBackgroundNode.layer.animatePosition(from: CGPoint(x: (initialWidth - self.messageClipNode.bounds.width) / 2.0, y: delta), to: CGPoint(), duration: duration, timingFunction: kCAMediaTimingFunctionSpring, additive: true)
self.messageTextNode.layer.animatePosition(from: CGPoint(x: 0.0, y: delta * 2.0), to: CGPoint(), duration: duration, timingFunction: kCAMediaTimingFunctionSpring, additive: true)
self.contentContainerNode.layer.animatePosition(from: CGPoint(x: 160.0, y: 0.0), to: CGPoint(), duration: duration, timingFunction: kCAMediaTimingFunctionSpring, additive: true)
self.contentContainerNode.layer.animateScale(from: 0.45, to: 1.0, duration: duration, timingFunction: kCAMediaTimingFunctionSpring)
}
}
func animateOut(cancel: Bool, completion: @escaping () -> Void) {
self.isUserInteractionEnabled = false
var completedEffect = false
var completedButton = false
var completedBubble = false
var completedAlpha = false
let intermediateCompletion: () -> Void = { [weak self] in
if completedEffect && completedButton && completedBubble && completedAlpha {
self?.coverNode.isHidden = true
completion()
}
}
UIView.animate(withDuration: 0.4, animations: {
if #available(iOS 9.0, *) {
self.effectView.effect = nil
} else {
self.effectView.alpha = 0.0
}
}, completion: { _ in
completedEffect = true
intermediateCompletion()
})
self.dimNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false)
self.contentContainerNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { _ in })
if cancel {
self.messageBackgroundNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, delay: 0.15, removeOnCompletion: false, completion: { _ in
completedAlpha = true
intermediateCompletion()
})
} else {
self.coverNode.isHidden = true
self.messageClipNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { _ in
completedAlpha = true
intermediateCompletion()
})
}
if let layout = self.validLayout {
let duration = 0.6
self.sendButtonNode.layer.animatePosition(from: self.sendButtonNode.position, to: self.sendButtonFrame.center, duration: duration, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, completion: { _ in
completedButton = true
intermediateCompletion()
})
if !cancel {
self.sendButtonNode.layer.animateScale(from: 1.0, to: 0.2, duration: 0.2, timingFunction: kCAMediaTimingFunctionLinear, removeOnCompletion: false)
self.sendButtonNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, timingFunction: kCAMediaTimingFunctionLinear, removeOnCompletion: false)
}
let initialWidth = self.textFieldFrame.width + 32.0
let toFrame = CGRect(origin: CGPoint(), size: CGSize(width: initialWidth, height: self.textFieldFrame.height + 1.0))
let delta = (toFrame.height - self.messageClipNode.bounds.height) / 2.0
let inputHeight = layout.inputHeight ?? 0.0
var clipDelta = delta
if inputHeight.isZero {
clipDelta -= 60.0
}
if cancel {
self.messageClipNode.layer.animateBounds(from: self.messageClipNode.bounds, to: toFrame, duration: duration, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, completion: { _ in
completedBubble = true
intermediateCompletion()
})
self.messageClipNode.layer.animatePosition(from: CGPoint(), to: CGPoint(x: (self.messageClipNode.bounds.width - initialWidth) / 2.0, y: clipDelta), duration: duration, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, additive: true)
self.messageBackgroundNode.layer.animateBounds(from: self.messageBackgroundNode.bounds, to: toFrame, duration: duration, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false)
self.messageBackgroundNode.layer.animatePosition(from: CGPoint(), to: CGPoint(x: (initialWidth - self.messageClipNode.bounds.width) / 2.0, y: delta), duration: duration, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, additive: true)
self.messageTextNode.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: delta * 2.0), duration: duration, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, additive: true)
}
self.contentContainerNode.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 160.0, y: 0.0), duration: duration, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, additive: true)
self.contentContainerNode.layer.animateScale(from: 1.0, to: 0.4, duration: duration, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false)
}
}
func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
self.validLayout = layout
transition.updateFrame(node: self.coverNode, frame: self.textFieldFrame)
transition.updateFrame(view: self.effectView, frame: CGRect(origin: CGPoint(), size: layout.size))
transition.updateFrame(node: self.dimNode, frame: CGRect(origin: CGPoint(), size: layout.size))
let sideInset: CGFloat = 43.0
var contentSize = CGSize()
contentSize.width = min(layout.size.width - 40.0, 240.0)
var applyNodes: [(ASDisplayNode, CGFloat, (CGFloat) -> Void)] = []
for itemNode in self.contentNodes {
let (width, height, apply) = itemNode.updateLayout(maxWidth: layout.size.width - sideInset * 2.0)
applyNodes.append((itemNode, height, apply))
contentSize.width = max(contentSize.width, width)
contentSize.height += height
}
let insets = layout.insets(options: [.statusBar, .input])
let inputHeight = layout.inputHeight ?? 0.0
var contentOrigin = CGPoint(x: layout.size.width - sideInset - contentSize.width, y: layout.size.height - 6.0 - insets.bottom - contentSize.height)
if inputHeight > 0.0 {
contentOrigin.y += 60.0
}
transition.updateFrame(node: self.contentContainerNode, frame: CGRect(origin: contentOrigin, size: contentSize))
var nextY: CGFloat = 0.0
for (itemNode, height, apply) in applyNodes {
transition.updateFrame(node: itemNode, frame: CGRect(origin: CGPoint(x: 0.0, y: nextY), size: CGSize(width: contentSize.width, height: height)))
apply(contentSize.width)
nextY += height
}
let initialSendButtonFrame = self.sendButtonFrame
var sendButtonFrame = CGRect(origin: CGPoint(x: layout.size.width - initialSendButtonFrame.width + 1.0 - UIScreenPixel, y: layout.size.height - insets.bottom - initialSendButtonFrame.height), size: initialSendButtonFrame.size)
if inputHeight.isZero {
sendButtonFrame.origin.y -= 60.0
}
transition.updateFrame(node: self.sendButtonNode, frame: sendButtonFrame)
var messageFrame = self.textFieldFrame
messageFrame.size.width += 32.0
messageFrame.origin.x -= 13.0
messageFrame.origin.y = layout.size.height - messageFrame.origin.y - messageFrame.size.height - 1.0
if inputHeight.isZero {
messageFrame.origin.y += 60.0
}
if self.textInputNode.textView.numberOfLines == 1 {
let textWidth = min(self.textInputNode.textView.sizeThatFits(layout.size).width + 36.0, messageFrame.width)
messageFrame.origin.x += messageFrame.width - textWidth
messageFrame.size.width = textWidth
}
let messageHeight = max(messageFrame.size.height, self.textInputNode.textView.contentSize.height + 2.0)
messageFrame.size.height = messageHeight
transition.updateFrame(node: self.scrollNode, frame: CGRect(origin: CGPoint(), size: layout.size))
var scrollContentSize = CGSize(width: layout.size.width, height: messageHeight + max(0.0, messageFrame.origin.y))
if messageHeight > layout.size.height - messageFrame.origin.y {
scrollContentSize.height += insets.top + 16.0
}
self.scrollNode.view.contentSize = scrollContentSize
let clipFrame = messageFrame
transition.updateFrame(node: self.messageClipNode, frame: clipFrame)
let backgroundFrame = CGRect(origin: CGPoint(), size: messageFrame.size)
transition.updateFrame(node: self.messageBackgroundNode, frame: backgroundFrame)
var textFrame = self.textFieldFrame
textFrame.origin = CGPoint(x: 13.0, y: 6.0 - UIScreenPixel)
textFrame.size.height = self.textInputNode.textView.contentSize.height
self.messageTextNode.frame = textFrame
}
@objc private func dimTapGesture(_ recognizer: UITapGestureRecognizer) {
if case .ended = recognizer.state {
self.cancel?()
}
}
@objc private func sendButtonPressed() {
self.send?()
}
}

View File

@ -6,15 +6,20 @@ import TelegramPresentationData
final class ChatTextInputActionButtonsNode: ASDisplayNode {
let micButton: ChatTextInputMediaRecordingButton
let sendButton: HighlightableButton
let sendButton: HighlightTrackingButton
var sendButtonRadialStatusNode: ChatSendButtonRadialStatusNode?
var sendButtonHasApplyIcon = false
var animatingSendButton = false
let expandMediaInputButton: HighlightableButtonNode
var sendButtonLongPressed: (() -> Void)?
init(theme: PresentationTheme, presentController: @escaping (ViewController) -> Void) {
self.micButton = ChatTextInputMediaRecordingButton(theme: theme, presentController: presentController)
self.sendButton = HighlightableButton()
self.sendButton = HighlightTrackingButton()
self.sendButton.adjustsImageWhenHighlighted = false
self.sendButton.adjustsImageWhenDisabled = false
self.expandMediaInputButton = HighlightableButtonNode()
super.init()
@ -22,11 +27,36 @@ final class ChatTextInputActionButtonsNode: ASDisplayNode {
self.isAccessibilityElement = true
self.accessibilityTraits = UIAccessibilityTraitButton | UIAccessibilityTraitNotEnabled
self.sendButton.highligthedChanged = { [weak self] highlighted in
if let strongSelf = self {
if highlighted {
strongSelf.sendButton.layer.animateScale(from: 1.0, to: 0.75, duration: 0.7, removeOnCompletion: false)
} else if let presentationLayer = strongSelf.sendButton.layer.presentation() {
strongSelf.sendButton.layer.animateScale(from: CGFloat((presentationLayer.value(forKeyPath: "transform.scale.y") as? NSNumber)?.floatValue ?? 1.0), to: 1.0, duration: 0.25, removeOnCompletion: false)
}
}
}
self.view.addSubview(self.micButton)
self.view.addSubview(self.sendButton)
self.addSubnode(self.expandMediaInputButton)
}
override func didLoad() {
super.didLoad()
let gestureRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(self.handleLongPress(_:)))
gestureRecognizer.minimumPressDuration = 0.7
self.sendButton.addGestureRecognizer(gestureRecognizer)
}
@objc func handleLongPress(_ gestureRecognizer: UILongPressGestureRecognizer) {
if gestureRecognizer.state == .began {
self.sendButtonLongPressed?()
}
}
func updateTheme(theme: PresentationTheme) {
self.micButton.updateTheme(theme: theme)
self.expandMediaInputButton.setImage(PresentationResourcesChat.chatInputPanelExpandButtonImage(theme), for: [])

View File

@ -352,6 +352,10 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
self.addSubnode(self.actionButtons)
self.actionButtons.sendButtonLongPressed = { [weak self] in
self?.interfaceInteraction?.displaySendMessageOptions()
}
self.actionButtons.micButton.recordingDisabled = { [weak self] in
self?.interfaceInteraction?.displayRestrictedInfo(.mediaRecording, .tooltip)
}

View File

@ -197,8 +197,20 @@ private func matchingEmojiEntry(_ emoji: String) -> (UInt8, UInt8, UInt8)? {
return nil
}
func messageTextIsElligibleForLargeEmoji(_ emoji: String) -> Bool {
for emoji in emoji.emojis {
func messageIsElligibleForLargeEmoji(_ message: Message) -> Bool {
var messageEntities: [MessageTextEntity]?
for attribute in message.attributes {
if let attribute = attribute as? TextEntitiesMessageAttribute {
messageEntities = attribute.entities
break
}
}
if !(messageEntities?.isEmpty ?? true) {
return false
}
for emoji in message.text.emojis {
if let _ = matchingEmojiEntry(emoji) {
} else {
return false

View File

@ -1823,6 +1823,8 @@ public func groupInfoController(context: AccountContext, peerId originalPeerId:
default:
break
}
} else if case .tooMuchJoined = error {
presentControllerImpl?(textAlertController(context: context, title: nil, text: presentationData.strings.Invite_ChannelsTooMuch, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: {})]), nil)
}
contactsController?.dismiss()

View File

@ -181,7 +181,7 @@ private let titleBoldFont = Font.medium(17.0)
private let statusFont = Font.regular(14.0)
private let labelFont = Font.regular(13.0)
private let labelDisclosureFont = Font.regular(17.0)
private let avatarFont = UIFont(name: ".SFCompactRounded-Semibold", size: 17.0)!
private let avatarFont = UIFont(name: ".SFCompactRounded-Semibold", size: 15.0)!
private let badgeFont = Font.regular(15.0)
class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNode {

View File

@ -27,10 +27,10 @@ class ItemListSingleLineInputItem: ListViewItem, ItemListItem {
let action: () -> Void
let textUpdated: (String) -> Void
let processPaste: ((String) -> String)?
let receivedFocus: (() -> Void)?
let updatedFocus: ((Bool) -> Void)?
let tag: ItemListItemTag?
init(theme: PresentationTheme, title: NSAttributedString, text: String, placeholder: String, type: ItemListSingleLineInputItemType = .regular(capitalization: true, autocorrection: true), returnKeyType: UIReturnKeyType = .`default`, spacing: CGFloat = 0.0, clearButton: Bool = false, enabled: Bool = true, tag: ItemListItemTag? = nil, sectionId: ItemListSectionId, textUpdated: @escaping (String) -> Void, processPaste: ((String) -> String)? = nil, receivedFocus: (() -> Void)? = nil, action: @escaping () -> Void) {
init(theme: PresentationTheme, title: NSAttributedString, text: String, placeholder: String, type: ItemListSingleLineInputItemType = .regular(capitalization: true, autocorrection: true), returnKeyType: UIReturnKeyType = .`default`, spacing: CGFloat = 0.0, clearButton: Bool = false, enabled: Bool = true, tag: ItemListItemTag? = nil, sectionId: ItemListSectionId, textUpdated: @escaping (String) -> Void, processPaste: ((String) -> String)? = nil, updatedFocus: ((Bool) -> Void)? = nil, action: @escaping () -> Void) {
self.theme = theme
self.title = title
self.text = text
@ -44,7 +44,7 @@ class ItemListSingleLineInputItem: ListViewItem, ItemListItem {
self.sectionId = sectionId
self.textUpdated = textUpdated
self.processPaste = processPaste
self.receivedFocus = receivedFocus
self.updatedFocus = updatedFocus
self.action = action
}
@ -376,7 +376,11 @@ class ItemListSingleLineInputItemNode: ListViewItemNode, UITextFieldDelegate, It
}
@objc internal func textFieldDidBeginEditing(_ textField: UITextField) {
self.item?.receivedFocus?()
self.item?.updatedFocus?(true)
}
@objc internal func textFieldDidEndEditing(_ textField: UITextField) {
self.item?.updatedFocus?(false)
}
func animateError() {

View File

@ -64,6 +64,7 @@ final class OverlayPlayerControllerNode: ViewControllerTracingNode, UIGestureRec
}, navigateToMessage: { _, _ in
}, clickThroughMessage: {
}, toggleMessagesSelection: { _, _ in
}, sendCurrentMessage: { _ in
}, sendMessage: { _ in
}, sendSticker: { _, _, _, _ in
return false

View File

@ -180,6 +180,7 @@ public class PeerMediaCollectionController: TelegramController {
if let strongSelf = self, strongSelf.isNodeLoaded {
strongSelf.updateInterfaceState(animated: true, { $0.withToggledSelectedMessages(ids, value: value) })
}
}, sendCurrentMessage: { _ in
}, sendMessage: { _ in
}, sendSticker: { _, _, _, _ in
return false
@ -379,6 +380,7 @@ public class PeerMediaCollectionController: TelegramController {
}, openLinkEditing: {
}, reportPeerIrrelevantGeoLocation: {
}, displaySlowmodeTooltip: { _, _ in
}, displaySendMessageOptions: {
}, statuses: nil)
self.updateInterfaceState(animated: false, { return $0 })

View File

@ -241,7 +241,7 @@ final class PhoneInputNode: ASDisplayNode, UITextFieldDelegate {
}
if self.previousCountryCodeText != realRegionPrefix || forceNotifyCountryCodeUpdated {
self.previousCountryCodeText = realRegionPrefix
let code = removePlus(realRegionPrefix).trimmingCharacters(in: CharacterSet.whitespaces)
let code = removePlus(realRegionPrefix).trimmingCharacters(in: .whitespaces)
var countryName: String?
if self.countryNameForCode?.0 == Int32(code) {
countryName = self.countryNameForCode?.1

View File

@ -1378,7 +1378,6 @@ public func settingsController(context: AccountContext, accountManager: AccountM
let accountContext = AccountContext(sharedContext: sharedContext, account: selectedAccount, limitsConfiguration: LimitsConfiguration.defaultValue)
let chatListController = ChatListController(context: accountContext, groupId: .root, controlsHistoryPreload: false, hideNetworkActivityStatus: true)
return chatListController
}
}
return nil

View File

@ -25,7 +25,7 @@ public final class TabBarAccountSwitchController: ViewController {
private let sourceNodes: [ASDisplayNode]
private var presentationData: PresentationData
private var animatedAppearance = false
private var didPlayPresentationAnimation = false
private var changedAccount = false
private let hapticFeedback = HapticFeedback()
@ -76,8 +76,8 @@ public final class TabBarAccountSwitchController: ViewController {
override public func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if !self.animatedAppearance {
self.animatedAppearance = true
if !self.didPlayPresentationAnimation {
self.didPlayPresentationAnimation = true
self.hapticFeedback.impact()
self.controllerNode.animateIn()
@ -97,7 +97,7 @@ public final class TabBarAccountSwitchController: ViewController {
public func dismiss(sourceNodes: [ASDisplayNode]) {
self.controllerNode.animateOut(sourceNodes: sourceNodes, changedAccount: self.changedAccount, completion: { [weak self] in
self?.animatedAppearance = false
self?.didPlayPresentationAnimation = false
self?.presentingViewController?.dismiss(animated: false, completion: nil)
})
}

View File

@ -24,10 +24,12 @@ private func generateSwatchImage(color: PresentationThemeAccentColor, selected:
context.fillEllipse(in: bounds.insetBy(dx: 4.0, dy: 4.0))
context.strokeEllipse(in: bounds.insetBy(dx: 1.0, dy: 1.0))
context.setFillColor(UIColor.white.cgColor)
context.fillEllipse(in: CGRect(x: 11.0, y: 18.0, width: 4.0, height: 4.0))
context.fillEllipse(in: CGRect(x: 18.0, y: 18.0, width: 4.0, height: 4.0))
context.fillEllipse(in: CGRect(x: 25.0, y: 18.0, width: 4.0, height: 4.0))
if color.baseColor != .white && color.baseColor != .black {
context.setFillColor(UIColor.white.cgColor)
context.fillEllipse(in: CGRect(x: 11.0, y: 18.0, width: 4.0, height: 4.0))
context.fillEllipse(in: CGRect(x: 18.0, y: 18.0, width: 4.0, height: 4.0))
context.fillEllipse(in: CGRect(x: 25.0, y: 18.0, width: 4.0, height: 4.0))
}
} else {
context.fillEllipse(in: bounds)
}

View File

@ -257,7 +257,7 @@ private enum ThemeSettingsControllerEntry: ItemListNodeEntry {
return ThemeSettingsAccentColorItem(theme: theme, sectionId: self.section, colors: colors, currentColor: color ?? PresentationThemeAccentColor(baseColor: .blue, value: 0.5), updated: { color in
arguments.selectAccentColor(color)
}, toggleSlider: { baseColor in
arguments.toggleColorSlider(baseColor == .white)
arguments.toggleColorSlider(baseColor == .white || baseColor == .black)
}, tag: ThemeSettingsEntryTag.accentColor)
case let .autoNightTheme(theme, text, value):
return ItemListDisclosureItem(theme: theme, icon: nil, title: text, label: value, labelStyle: .text, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: {
@ -416,7 +416,7 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
themeSpecificChatWallpapers[current.theme.index] = chatWallpaper
}
if color.baseColor == .white {
if color.baseColor == .white || color.baseColor == .black {
updateState { $0.withDisplayColorSlider(false) }
}

View File

@ -38,6 +38,8 @@
092F36902157AB46001A9F49 /* ItemListCallListItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 092F368F2157AB46001A9F49 /* ItemListCallListItem.swift */; };
09310D32213ED5FC0020033A /* anim_ungroup.json in Resources */ = {isa = PBXBuildFile; fileRef = 09310D1A213BC5DE0020033A /* anim_ungroup.json */; };
09310D33213ED5FC0020033A /* anim_group.json in Resources */ = {isa = PBXBuildFile; fileRef = 09310D1B213BC5DE0020033A /* anim_group.json */; };
0940932422E73DFB003846A3 /* ChatSendMessageActionSheetController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0940932322E73DFB003846A3 /* ChatSendMessageActionSheetController.swift */; };
0940932622E73E12003846A3 /* ChatSendMessageActionSheetControllerNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0940932522E73E12003846A3 /* ChatSendMessageActionSheetControllerNode.swift */; };
0941A9A0210B057200EBE194 /* OpenInActionSheetController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0941A99F210B057200EBE194 /* OpenInActionSheetController.swift */; };
0941A9A4210B0E2E00EBE194 /* OpenInAppIconResources.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0941A9A3210B0E2E00EBE194 /* OpenInAppIconResources.swift */; };
0941A9A6210B822D00EBE194 /* OpenInOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0941A9A5210B822D00EBE194 /* OpenInOptions.swift */; };
@ -1268,6 +1270,8 @@
092F368F2157AB46001A9F49 /* ItemListCallListItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemListCallListItem.swift; sourceTree = "<group>"; };
09310D1A213BC5DE0020033A /* anim_ungroup.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = anim_ungroup.json; sourceTree = "<group>"; };
09310D1B213BC5DE0020033A /* anim_group.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = anim_group.json; sourceTree = "<group>"; };
0940932322E73DFB003846A3 /* ChatSendMessageActionSheetController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatSendMessageActionSheetController.swift; sourceTree = "<group>"; };
0940932522E73E12003846A3 /* ChatSendMessageActionSheetControllerNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatSendMessageActionSheetControllerNode.swift; sourceTree = "<group>"; };
0941A99F210B057200EBE194 /* OpenInActionSheetController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenInActionSheetController.swift; sourceTree = "<group>"; };
0941A9A3210B0E2E00EBE194 /* OpenInAppIconResources.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenInAppIconResources.swift; sourceTree = "<group>"; };
0941A9A5210B822D00EBE194 /* OpenInOptions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenInOptions.swift; sourceTree = "<group>"; };
@ -4708,6 +4712,8 @@
D0AF32391FB1D8D60097362B /* ChatOverlayNavigationBar.swift */,
D0BCC3D320404CC7008126C2 /* ChatMessageActionSheetController.swift */,
D0BCC3D520404CD8008126C2 /* ChatMessageActionSheetControllerNode.swift */,
0940932322E73DFB003846A3 /* ChatSendMessageActionSheetController.swift */,
0940932522E73E12003846A3 /* ChatSendMessageActionSheetControllerNode.swift */,
D0F69E181D6B8AD10046BCD6 /* Items */,
D03ADB461D703250005A521C /* Interface State */,
D03ADB491D704427005A521C /* Accessory Panels */,
@ -6184,6 +6190,7 @@
09F215A422649C2200AEDF6D /* PasscodeEntryLabelNode.swift in Sources */,
D099D74D1EEFEE1500A3128C /* GameController.swift in Sources */,
09749BCF21F236F2008FDDE9 /* ModernCheckNode.swift in Sources */,
0940932422E73DFB003846A3 /* ChatSendMessageActionSheetController.swift in Sources */,
D0EC6E2C1EB9F58900EBF1C3 /* ComposeControllerNode.swift in Sources */,
D0EC6E2D1EB9F58900EBF1C3 /* CounterContollerTitleView.swift in Sources */,
D0AEAE292080FD660013176E /* StickerPaneSearchGlobaltem.swift in Sources */,
@ -6223,6 +6230,7 @@
D0EC6E3F1EB9F58900EBF1C3 /* ItemListTextItem.swift in Sources */,
D0EC6E401EB9F58900EBF1C3 /* ItemListActivityTextItem.swift in Sources */,
0958FBB9218AD6AF00E0CBD8 /* InstantPageFeedbackItem.swift in Sources */,
0940932622E73E12003846A3 /* ChatSendMessageActionSheetControllerNode.swift in Sources */,
D00817D022B47A14008A895F /* WakeupManager.swift in Sources */,
D0AF798822C2E26500CECCB8 /* matrix.cc in Sources */,
D0EC6E411EB9F58900EBF1C3 /* ItemListEditableItem.swift in Sources */,

View File

@ -211,21 +211,21 @@ public enum PresentationThemeBaseColor: Int32, CaseIterable {
let values: (UIColor, UIColor)
switch self {
case .blue:
values = (UIColor(rgb: 0x003d80), UIColor(rgb: 0x66afff))
values = (UIColor(rgb: 0x394cb5), UIColor(rgb: 0x7fd3fb))
case .cyan:
values = (UIColor(rgb: 0x00c2ed), UIColor(rgb: 0x00c2ed))
case .green:
values = (UIColor(rgb: 0x29b327), UIColor(rgb: 0x29b327))
values = (UIColor(rgb: 0x608236), UIColor(rgb: 0xb1e786))
case .pink:
values = (UIColor(rgb: 0xeb6ca4), UIColor(rgb: 0xeb6ca4))
values = (UIColor(rgb: 0xad4974), UIColor(rgb: 0xeca2d0))
case .orange:
values = (UIColor(rgb: 0xf08200), UIColor(rgb: 0xf08200))
values = (UIColor(rgb: 0xbe5d29), UIColor(rgb: 0xf3ae68))
case .purple:
values = (UIColor(rgb: 0x9472ee), UIColor(rgb: 0x9472ee))
values = (UIColor(rgb: 0x544292), UIColor(rgb: 0xb2a3e3))
case .red:
values = (UIColor(rgb: 0xd33213), UIColor(rgb: 0xd33213))
values = (UIColor(rgb: 0x94211b), UIColor(rgb: 0xe47e66))
case .yellow:
values = (UIColor(rgb: 0xedb400), UIColor(rgb: 0xedb400))
values = (UIColor(rgb: 0xdda23a), UIColor(rgb: 0xfbe589))
case .gray:
values = (UIColor(rgb: 0x6d839e), UIColor(rgb: 0x6d839e))
case .black: