diff --git a/Lock.json b/Lock.json
deleted file mode 100644
index f56c2c101f..0000000000
--- a/Lock.json
+++ /dev/null
@@ -1 +0,0 @@
-{"v":"5.5.9","fr":60,"ip":0,"op":30,"w":240,"h":360,"nm":"Lock2","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Path","parent":2,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.333],"y":[0]},"t":15,"s":[100]},{"t":25,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":15,"s":[100,100,100]},{"t":25,"s":[0,0,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-1.29,0],[0,0],[0,-1.29],[0,0],[1.28,0],[0,0],[0,1.28],[0,0]],"o":[[0,0],[1.28,0],[0,0],[0,1.28],[0,0],[-1.29,0],[0,0],[0,-1.29]],"v":[[-4.995,-6.335],[5.005,-6.335],[7.335,-3.995],[7.335,4.005],[5.005,6.335],[-4.995,6.335],[-7.335,4.005],[-7.335,-3.995]],"c":true},"ix":2},"nm":"Контур 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.968627510819,0.968627510819,0.968627510819,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Заливка 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Преобразовать"}],"nm":"Path","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":30,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Rectangle","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":15,"s":[0]},{"t":25,"s":[90]}],"ix":10},"p":{"s":true,"x":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[120]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":15,"s":[120]},{"t":25,"s":[120]}],"ix":3},"y":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[150]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":15,"s":[200]},{"t":25,"s":[180]}],"ix":4}},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":15,"s":[{"i":[[-1.66,0],[0,0],[0,-1.66],[0,0],[1.66,0],[0,0],[0,1.66],[0,0]],"o":[[0,0],[1.66,0],[0,0],[0,1.66],[0,0],[-1.66,0],[0,0],[0,-1.66]],"v":[[-5,-7],[5,-7],[8,-4],[8,4],[5,7],[-5,7],[-8,4],[-8,-4]],"c":true}]},{"t":25,"s":[{"i":[[-1.66,0],[0,0],[0,-1.66],[0,0],[1.66,0],[0,0],[0,1.66],[0,0]],"o":[[0,0],[1.66,0],[0,0],[0,1.66],[0,0],[-1.66,0],[0,0],[0,-1.66]],"v":[[-5,-8],[5,-8],[8,-5],[8,5],[5,8],[-5,8],[-8,5],[-8,-5]],"c":true}]}],"ix":2},"nm":"Контур 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.592000007629,0.592000007629,0.592000007629,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1.33,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Обводка 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Преобразовать"}],"nm":"Rectangle","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.592156862745,0.592156862745,0.592156862745,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Заливка 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":30,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Path","parent":2,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[10]},{"t":15,"s":[0]}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":1,"y":0},"t":0,"s":[30,-32,0],"to":[0,2.5,0],"ti":[0,-11.667,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":15,"s":[30,-17,0],"to":[0,11.667,0],"ti":[0,-9.167,0]},{"t":25,"s":[30,38,0]}],"ix":2},"a":{"a":0,"k":[30,36,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[-2.76,0],[0,-2.76],[0,0]],"o":[[0,0],[0,-2.76],[2.76,0],[0,0],[0,0]],"v":[[-5,2],[-5,-1],[0,-6],[5,-1],[5,6]],"c":false},"ix":2},"nm":"Контур 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.592000007629,0.592000007629,0.592000007629,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1.33,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Обводка 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Преобразовать"}],"nm":"Path","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":30,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Path 4","parent":2,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":1,"y":0},"t":0,"s":[0,132,0],"to":[0,-19.667,0],"ti":[0,19.667,0]},{"t":15,"s":[0,14,0]}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":1,"y":0},"t":0,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-8,3],[0,-3],[8,3]],"c":false}]},{"t":12,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-0.753,3],[0,-3],[0.753,3]],"c":false}]}],"ix":2},"nm":"Контур 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.592000007629,0.592000007629,0.592000007629,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1.33,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Обводка 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Преобразовать"}],"nm":"Path 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":30,"st":0,"bm":0}],"markers":[]}
\ No newline at end of file
diff --git a/LockWait.json b/LockWait.json
deleted file mode 100644
index c46209d092..0000000000
--- a/LockWait.json
+++ /dev/null
@@ -1 +0,0 @@
-{"v":"5.5.9","fr":60,"ip":0,"op":120,"w":240,"h":360,"nm":"Lock1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Path 4","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[120,282,0],"to":[0,-1.667,0],"ti":[0,0.833,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":45,"s":[120,272,0],"to":[0,-0.833,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":59,"s":[120,277,0],"to":[0,0,0],"ti":[0,-0.833,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":75,"s":[120,272,0],"to":[0,0.833,0],"ti":[0,-1.667,0]},{"t":120,"s":[120,282,0]}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-8,3],[0,-3],[8,3]],"c":false},"ix":2},"nm":"Контур 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.592000007629,0.592000007629,0.592000007629,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1.33,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Обводка 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Преобразовать"}],"nm":"Path 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":120,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Rectangle","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[120,150,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-1.66,0],[0,0],[0,-1.66],[0,0],[1.66,0],[0,0],[0,1.66],[0,0]],"o":[[0,0],[1.66,0],[0,0],[0,1.66],[0,0],[-1.66,0],[0,0],[0,-1.66]],"v":[[-5,-7],[5,-7],[8,-4],[8,4],[5,7],[-5,7],[-8,4],[-8,-4]],"c":true},"ix":2},"nm":"Контур 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.592000007629,0.592000007629,0.592000007629,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1.33,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Обводка 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Преобразовать"}],"nm":"Rectangle","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.96862745098,0.96862745098,0.96862745098,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Заливка 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":120,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Path","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[10]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":45,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":60,"s":[5]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":75,"s":[0]},{"t":120,"s":[10]}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[150,118,0],"to":[0,1.667,0],"ti":[0,-0.833,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":45,"s":[150,128,0],"to":[0,0.833,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":60,"s":[150,123,0],"to":[0,0,0],"ti":[0,0.833,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":75,"s":[150,128,0],"to":[0,-0.833,0],"ti":[0,1.667,0]},{"t":120,"s":[150,118,0]}],"ix":2},"a":{"a":0,"k":[30,36,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[-2.76,0],[0,-2.76],[0,0]],"o":[[0,0],[0,-2.76],[2.76,0],[0,0],[0,0]],"v":[[-5,2],[-5,-1],[0,-6],[5,-1],[5,6]],"c":false},"ix":2},"nm":"Контур 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.592000007629,0.592000007629,0.592000007629,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1.33,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Обводка 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Преобразовать"}],"nm":"Path","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":120,"st":0,"bm":0}],"markers":[]}
\ No newline at end of file
diff --git a/Telegram/BUILD b/Telegram/BUILD
index 26d56c83f1..4526e31da5 100644
--- a/Telegram/BUILD
+++ b/Telegram/BUILD
@@ -730,6 +730,7 @@ plist_fragment(
onionhttp
ucbrowser
dolphin
+ instagram-stories
LSRequiresIPhoneOS
diff --git a/Telegram/NotificationService/Serialization.m b/Telegram/NotificationService/Serialization.m
index 3d92dca7b9..ddfa890a93 100644
--- a/Telegram/NotificationService/Serialization.m
+++ b/Telegram/NotificationService/Serialization.m
@@ -3,7 +3,7 @@
@implementation Serialization
- (NSUInteger)currentLayer {
- return 117;
+ return 118;
}
- (id _Nullable)parseMessage:(NSData * _Nullable)data {
diff --git a/Telegram/Telegram-iOS/Info.plist b/Telegram/Telegram-iOS/Info.plist
index f66daefe49..a34aee90c5 100644
--- a/Telegram/Telegram-iOS/Info.plist
+++ b/Telegram/Telegram-iOS/Info.plist
@@ -264,6 +264,7 @@
onionhttp
ucbrowser
dolphin
+ instagram-stories
LSRequiresIPhoneOS
diff --git a/Telegram/Telegram-iOS/InfoBazel.plist b/Telegram/Telegram-iOS/InfoBazel.plist
index e7ca119cd8..ae1b92de45 100644
--- a/Telegram/Telegram-iOS/InfoBazel.plist
+++ b/Telegram/Telegram-iOS/InfoBazel.plist
@@ -98,6 +98,7 @@
onionhttp
ucbrowser
dolphin
+ instagram-stories
LSRequiresIPhoneOS
diff --git a/Telegram/Telegram-iOS/Resources/Slot_Back_Idle.tgs b/Telegram/Telegram-iOS/Resources/Slot_Back_Idle.tgs
new file mode 100644
index 0000000000..5eaa214a3a
Binary files /dev/null and b/Telegram/Telegram-iOS/Resources/Slot_Back_Idle.tgs differ
diff --git a/Telegram/Telegram-iOS/Resources/Slot_Back_Win.tgs b/Telegram/Telegram-iOS/Resources/Slot_Back_Win.tgs
new file mode 100644
index 0000000000..9426a934ac
Binary files /dev/null and b/Telegram/Telegram-iOS/Resources/Slot_Back_Win.tgs differ
diff --git a/Telegram/Telegram-iOS/Resources/Slot_Front_Pull.tgs b/Telegram/Telegram-iOS/Resources/Slot_Front_Pull.tgs
new file mode 100644
index 0000000000..478b690679
Binary files /dev/null and b/Telegram/Telegram-iOS/Resources/Slot_Front_Pull.tgs differ
diff --git a/Telegram/Telegram-iOS/Resources/Slot_L_7.tgs b/Telegram/Telegram-iOS/Resources/Slot_L_7.tgs
new file mode 100644
index 0000000000..6efa982b70
Binary files /dev/null and b/Telegram/Telegram-iOS/Resources/Slot_L_7.tgs differ
diff --git a/Telegram/Telegram-iOS/Resources/Slot_L_7_Win.tgs b/Telegram/Telegram-iOS/Resources/Slot_L_7_Win.tgs
new file mode 100644
index 0000000000..eee9e62328
Binary files /dev/null and b/Telegram/Telegram-iOS/Resources/Slot_L_7_Win.tgs differ
diff --git a/Telegram/Telegram-iOS/Resources/Slot_L_Bar.tgs b/Telegram/Telegram-iOS/Resources/Slot_L_Bar.tgs
new file mode 100644
index 0000000000..b7ff8be2e6
Binary files /dev/null and b/Telegram/Telegram-iOS/Resources/Slot_L_Bar.tgs differ
diff --git a/Telegram/Telegram-iOS/Resources/Slot_L_Berries.tgs b/Telegram/Telegram-iOS/Resources/Slot_L_Berries.tgs
new file mode 100644
index 0000000000..942ac87c98
Binary files /dev/null and b/Telegram/Telegram-iOS/Resources/Slot_L_Berries.tgs differ
diff --git a/Telegram/Telegram-iOS/Resources/Slot_L_Lemon.tgs b/Telegram/Telegram-iOS/Resources/Slot_L_Lemon.tgs
new file mode 100644
index 0000000000..77b5147076
Binary files /dev/null and b/Telegram/Telegram-iOS/Resources/Slot_L_Lemon.tgs differ
diff --git a/Telegram/Telegram-iOS/Resources/Slot_L_Spinning.tgs b/Telegram/Telegram-iOS/Resources/Slot_L_Spinning.tgs
new file mode 100644
index 0000000000..1d8a0955f1
Binary files /dev/null and b/Telegram/Telegram-iOS/Resources/Slot_L_Spinning.tgs differ
diff --git a/Telegram/Telegram-iOS/Resources/Slot_M_7.tgs b/Telegram/Telegram-iOS/Resources/Slot_M_7.tgs
new file mode 100644
index 0000000000..a234c73ec1
Binary files /dev/null and b/Telegram/Telegram-iOS/Resources/Slot_M_7.tgs differ
diff --git a/Telegram/Telegram-iOS/Resources/Slot_M_7_Win.tgs b/Telegram/Telegram-iOS/Resources/Slot_M_7_Win.tgs
new file mode 100644
index 0000000000..2caef207f9
Binary files /dev/null and b/Telegram/Telegram-iOS/Resources/Slot_M_7_Win.tgs differ
diff --git a/Telegram/Telegram-iOS/Resources/Slot_M_Bar.tgs b/Telegram/Telegram-iOS/Resources/Slot_M_Bar.tgs
new file mode 100644
index 0000000000..e478739edd
Binary files /dev/null and b/Telegram/Telegram-iOS/Resources/Slot_M_Bar.tgs differ
diff --git a/Telegram/Telegram-iOS/Resources/Slot_M_Berries.tgs b/Telegram/Telegram-iOS/Resources/Slot_M_Berries.tgs
new file mode 100644
index 0000000000..cbfa50774b
Binary files /dev/null and b/Telegram/Telegram-iOS/Resources/Slot_M_Berries.tgs differ
diff --git a/Telegram/Telegram-iOS/Resources/Slot_M_Lemon.tgs b/Telegram/Telegram-iOS/Resources/Slot_M_Lemon.tgs
new file mode 100644
index 0000000000..fa618d5936
Binary files /dev/null and b/Telegram/Telegram-iOS/Resources/Slot_M_Lemon.tgs differ
diff --git a/Telegram/Telegram-iOS/Resources/Slot_M_Spinning.tgs b/Telegram/Telegram-iOS/Resources/Slot_M_Spinning.tgs
new file mode 100644
index 0000000000..b6549225f7
Binary files /dev/null and b/Telegram/Telegram-iOS/Resources/Slot_M_Spinning.tgs differ
diff --git a/Telegram/Telegram-iOS/Resources/Slot_R_7.tgs b/Telegram/Telegram-iOS/Resources/Slot_R_7.tgs
new file mode 100644
index 0000000000..49f00f17cf
Binary files /dev/null and b/Telegram/Telegram-iOS/Resources/Slot_R_7.tgs differ
diff --git a/Telegram/Telegram-iOS/Resources/Slot_R_7_Win.tgs b/Telegram/Telegram-iOS/Resources/Slot_R_7_Win.tgs
new file mode 100644
index 0000000000..884a485834
Binary files /dev/null and b/Telegram/Telegram-iOS/Resources/Slot_R_7_Win.tgs differ
diff --git a/Telegram/Telegram-iOS/Resources/Slot_R_Bar.tgs b/Telegram/Telegram-iOS/Resources/Slot_R_Bar.tgs
new file mode 100644
index 0000000000..6204c6c4f1
Binary files /dev/null and b/Telegram/Telegram-iOS/Resources/Slot_R_Bar.tgs differ
diff --git a/Telegram/Telegram-iOS/Resources/Slot_R_Berries.tgs b/Telegram/Telegram-iOS/Resources/Slot_R_Berries.tgs
new file mode 100644
index 0000000000..93264afa4e
Binary files /dev/null and b/Telegram/Telegram-iOS/Resources/Slot_R_Berries.tgs differ
diff --git a/Telegram/Telegram-iOS/Resources/Slot_R_Lemon.tgs b/Telegram/Telegram-iOS/Resources/Slot_R_Lemon.tgs
new file mode 100644
index 0000000000..37173e01d7
Binary files /dev/null and b/Telegram/Telegram-iOS/Resources/Slot_R_Lemon.tgs differ
diff --git a/Telegram/Telegram-iOS/Resources/Slot_R_Spinning.tgs b/Telegram/Telegram-iOS/Resources/Slot_R_Spinning.tgs
new file mode 100644
index 0000000000..c13193e81a
Binary files /dev/null and b/Telegram/Telegram-iOS/Resources/Slot_R_Spinning.tgs differ
diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings
index 0a21b66c03..2f1cba8cc0 100644
--- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings
+++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings
@@ -5739,3 +5739,5 @@ Any member of this group will be able to see messages in the channel.";
"Call.AudioRouteMute" = "Mute Yourself";
"AccessDenied.VideoCallCamera" = "Telegram needs access to your camera to make video calls.\n\nPlease go to Settings > Privacy > Camera and set Telegram to ON.";
+
+"Call.AccountIsLoggedOnCurrentDevice" = "Sorry, you can't call %@ because that account is logged in to Telegram on the device you're using for the call.";
diff --git a/submodules/AccountContext/Sources/AccountContext.swift b/submodules/AccountContext/Sources/AccountContext.swift
index ed6306da0d..4bc3fe02d3 100644
--- a/submodules/AccountContext/Sources/AccountContext.swift
+++ b/submodules/AccountContext/Sources/AccountContext.swift
@@ -531,7 +531,7 @@ public protocol SharedAccountContext: class {
func makeComposeController(context: AccountContext) -> ViewController
func makeChatListController(context: AccountContext, groupId: PeerGroupId, controlsHistoryPreload: Bool, hideNetworkActivityStatus: Bool, previewing: Bool, enableDebugActions: Bool) -> ChatListController
func makeChatController(context: AccountContext, chatLocation: ChatLocation, subject: ChatControllerSubject?, botStart: ChatControllerInitialBotStart?, mode: ChatControllerPresentationMode) -> ChatController
- func makeChatMessagePreviewItem(context: AccountContext, message: Message, theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, chatBubbleCorners: PresentationChatBubbleCorners, dateTimeFormat: PresentationDateTimeFormat, nameOrder: PresentationPersonNameOrder, forcedResourceStatus: FileMediaResourceStatus?, tapMessage: ((Message) -> Void)?, clickThroughMessage: (() -> Void)?) -> ListViewItem
+ func makeChatMessagePreviewItem(context: AccountContext, messages: [Message], theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, chatBubbleCorners: PresentationChatBubbleCorners, dateTimeFormat: PresentationDateTimeFormat, nameOrder: PresentationPersonNameOrder, forcedResourceStatus: FileMediaResourceStatus?, tapMessage: ((Message) -> Void)?, clickThroughMessage: (() -> Void)?) -> ListViewItem
func makeChatMessageDateHeaderItem(context: AccountContext, timestamp: Int32, theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, chatBubbleCorners: PresentationChatBubbleCorners, dateTimeFormat: PresentationDateTimeFormat, nameOrder: PresentationPersonNameOrder) -> ListViewItemHeader
func makePeerSharedMediaController(context: AccountContext, peerId: PeerId) -> ViewController?
func makeContactSelectionController(_ params: ContactSelectionControllerParams) -> ContactSelectionController
diff --git a/submodules/AnimatedStickerNode/BUCK b/submodules/AnimatedStickerNode/BUCK
index bdc95d5051..1e6a948afa 100644
--- a/submodules/AnimatedStickerNode/BUCK
+++ b/submodules/AnimatedStickerNode/BUCK
@@ -12,6 +12,7 @@ static_library(
"//submodules/YuvConversion:YuvConversion",
"//submodules/GZip:GZip",
"//submodules/rlottie:RLottieBinding",
+ "//submodules/MediaResources:MediaResources",
],
frameworks = [
"$SDKROOT/System/Library/Frameworks/Foundation.framework",
diff --git a/submodules/AnimatedStickerNode/BUILD b/submodules/AnimatedStickerNode/BUILD
index bf1ba42bdd..655f34543c 100644
--- a/submodules/AnimatedStickerNode/BUILD
+++ b/submodules/AnimatedStickerNode/BUILD
@@ -13,6 +13,7 @@ swift_library(
"//submodules/YuvConversion:YuvConversion",
"//submodules/GZip:GZip",
"//submodules/rlottie:RLottieBinding",
+ "//submodules/MediaResources:MediaResources",
],
visibility = [
"//visibility:public",
diff --git a/submodules/AnimatedStickerNode/Sources/AnimatedStickerNode.swift b/submodules/AnimatedStickerNode/Sources/AnimatedStickerNode.swift
index ebbf1292e8..b8f4989a31 100644
--- a/submodules/AnimatedStickerNode/Sources/AnimatedStickerNode.swift
+++ b/submodules/AnimatedStickerNode/Sources/AnimatedStickerNode.swift
@@ -6,6 +6,7 @@ import AsyncDisplayKit
import RLottieBinding
import GZip
import YuvConversion
+import MediaResources
private let sharedQueue = Queue()
private let sharedStoreQueue = Queue.concurrentDefaultQueue()
@@ -438,7 +439,7 @@ private final class AnimatedStickerDirectFrameSourceCache {
private var scratchBuffer: Data
private var decodeBuffer: Data
- init?(queue: Queue, pathPrefix: String, width: Int, height: Int, frameCount: Int) {
+ init?(queue: Queue, pathPrefix: String, width: Int, height: Int, frameCount: Int, fitzModifier: EmojiFitzModifier?) {
self.queue = queue
self.storeQueue = sharedStoreQueue
@@ -446,7 +447,13 @@ private final class AnimatedStickerDirectFrameSourceCache {
self.width = width
self.height = height
- let path = "\(pathPrefix)_\(width):\(height).stickerframecache"
+ let suffix : String
+ if let fitzModifier = fitzModifier {
+ suffix = "_fitz\(fitzModifier.rawValue)"
+ } else {
+ suffix = ""
+ }
+ let path = "\(pathPrefix)_\(width):\(height)\(suffix).stickerframecache"
var file = ManagedFileImpl(queue: queue, path: path, mode: .readwrite)
if let file = file {
self.file = file
@@ -594,7 +601,7 @@ private final class AnimatedStickerDirectFrameSource: AnimatedStickerFrameSource
return self.currentFrame % self.frameCount
}
- init?(queue: Queue, data: Data, width: Int, height: Int, cachePathPrefix: String?) {
+ init?(queue: Queue, data: Data, width: Int, height: Int, cachePathPrefix: String?, fitzModifier: EmojiFitzModifier?) {
self.queue = queue
self.data = data
self.width = width
@@ -602,7 +609,9 @@ private final class AnimatedStickerDirectFrameSource: AnimatedStickerFrameSource
self.bytesPerRow = (4 * Int(width) + 15) & (~15)
self.currentFrame = 0
let rawData = TGGUnzipData(data, 8 * 1024 * 1024) ?? data
- guard let animation = LottieInstance(data: rawData, cacheKey: "") else {
+ let decompressedData = transformedWithFitzModifier(data: rawData, fitzModifier: fitzModifier)
+
+ guard let animation = LottieInstance(data: decompressedData, cacheKey: "") else {
return nil
}
self.animation = animation
@@ -611,7 +620,7 @@ private final class AnimatedStickerDirectFrameSource: AnimatedStickerFrameSource
self.frameRate = Int(animation.frameRate)
self.cache = cachePathPrefix.flatMap { cachePathPrefix in
- AnimatedStickerDirectFrameSourceCache(queue: queue, pathPrefix: cachePathPrefix, width: width, height: height, frameCount: frameCount)
+ AnimatedStickerDirectFrameSourceCache(queue: queue, pathPrefix: cachePathPrefix, width: width, height: height, frameCount: frameCount, fitzModifier: fitzModifier)
}
}
@@ -698,11 +707,15 @@ public struct AnimatedStickerStatus: Equatable {
}
public protocol AnimatedStickerNodeSource {
+ var fitzModifier: EmojiFitzModifier? { get }
+
func cachedDataPath(width: Int, height: Int) -> Signal<(String, Bool), NoError>
func directDataPath() -> Signal
}
public final class AnimatedStickerNodeLocalFileSource: AnimatedStickerNodeSource {
+ public var fitzModifier: EmojiFitzModifier? = nil
+
public let path: String
public init(path: String) {
@@ -733,8 +746,8 @@ public final class AnimatedStickerNode: ASDisplayNode {
private let timer = Atomic(value: nil)
private let frameSource = Atomic?>(value: nil)
- private var directData: (Data, String, Int, Int, String?)?
- private var cachedData: (Data, Bool)?
+ private var directData: (Data, String, Int, Int, String?, EmojiFitzModifier?)?
+ private var cachedData: (Data, Bool, EmojiFitzModifier?)?
private var renderer: (AnimationRenderer & ASDisplayNode)?
@@ -809,7 +822,7 @@ public final class AnimatedStickerNode: ASDisplayNode {
return
}
if let directData = try? Data(contentsOf: URL(fileURLWithPath: path), options: [.mappedRead]) {
- strongSelf.directData = (directData, path, width, height, cachePathPrefix)
+ strongSelf.directData = (directData, path, width, height, cachePathPrefix, source.fitzModifier)
}
if case let .still(position) = playbackMode {
strongSelf.seekTo(position)
@@ -830,9 +843,9 @@ public final class AnimatedStickerNode: ASDisplayNode {
return
}
if let data = try? Data(contentsOf: URL(fileURLWithPath: path), options: [.mappedRead]) {
- if let (_, currentComplete) = strongSelf.cachedData {
+ if let (_, currentComplete, _) = strongSelf.cachedData {
if !currentComplete {
- strongSelf.cachedData = (data, complete)
+ strongSelf.cachedData = (data, complete, source.fitzModifier)
strongSelf.frameSource.with { frameSource in
frameSource?.with { frameSource in
if let frameSource = frameSource.value as? AnimatedStickerCachedFrameSource {
@@ -842,7 +855,7 @@ public final class AnimatedStickerNode: ASDisplayNode {
}
}
} else {
- strongSelf.cachedData = (data, complete)
+ strongSelf.cachedData = (data, complete, source.fitzModifier)
if strongSelf.isPlaying {
strongSelf.play()
} else if strongSelf.canDisplayFirstFrame {
@@ -892,8 +905,8 @@ public final class AnimatedStickerNode: ASDisplayNode {
if maybeFrameSource == nil {
let notifyUpdated: (() -> Void)? = nil
if let directData = directData {
- maybeFrameSource = AnimatedStickerDirectFrameSource(queue: queue, data: directData.0, width: directData.2, height: directData.3, cachePathPrefix: directData.4)
- } else if let (cachedData, cachedDataComplete) = cachedData {
+ maybeFrameSource = AnimatedStickerDirectFrameSource(queue: queue, data: directData.0, width: directData.2, height: directData.3, cachePathPrefix: directData.4, fitzModifier: directData.5)
+ } else if let (cachedData, cachedDataComplete, _) = cachedData {
if #available(iOS 9.0, *) {
maybeFrameSource = AnimatedStickerCachedFrameSource(queue: queue, data: cachedData, complete: cachedDataComplete, notifyUpdated: {
notifyUpdated?()
@@ -964,8 +977,8 @@ public final class AnimatedStickerNode: ASDisplayNode {
var maybeFrameSource: AnimatedStickerFrameSource?
let notifyUpdated: (() -> Void)? = nil
if let directData = directData {
- maybeFrameSource = AnimatedStickerDirectFrameSource(queue: queue, data: directData.0, width: directData.2, height: directData.3, cachePathPrefix: directData.4)
- } else if let (cachedData, cachedDataComplete) = cachedData {
+ maybeFrameSource = AnimatedStickerDirectFrameSource(queue: queue, data: directData.0, width: directData.2, height: directData.3, cachePathPrefix: directData.4, fitzModifier: directData.5)
+ } else if let (cachedData, cachedDataComplete, _) = cachedData {
if #available(iOS 9.0, *) {
maybeFrameSource = AnimatedStickerCachedFrameSource(queue: queue, data: cachedData, complete: cachedDataComplete, notifyUpdated: {
notifyUpdated?()
@@ -1054,11 +1067,11 @@ public final class AnimatedStickerNode: ASDisplayNode {
} else {
var maybeFrameSource: AnimatedStickerFrameSource?
if let directData = directData {
- maybeFrameSource = AnimatedStickerDirectFrameSource(queue: queue, data: directData.0, width: directData.2, height: directData.3, cachePathPrefix: directData.4)
+ maybeFrameSource = AnimatedStickerDirectFrameSource(queue: queue, data: directData.0, width: directData.2, height: directData.3, cachePathPrefix: directData.4, fitzModifier: directData.5)
if case .end = position {
maybeFrameSource?.skipToEnd()
}
- } else if let (cachedData, cachedDataComplete) = cachedData {
+ } else if let (cachedData, cachedDataComplete, _) = cachedData {
if #available(iOS 9.0, *) {
maybeFrameSource = AnimatedStickerCachedFrameSource(queue: queue, data: cachedData, complete: cachedDataComplete, notifyUpdated: {})
}
diff --git a/submodules/AnimatedStickerNode/Sources/FitzModifier.swift b/submodules/AnimatedStickerNode/Sources/FitzModifier.swift
new file mode 100644
index 0000000000..b033905a2c
--- /dev/null
+++ b/submodules/AnimatedStickerNode/Sources/FitzModifier.swift
@@ -0,0 +1,76 @@
+import Foundation
+import UIKit
+import MediaResources
+
+let colorKeyRegex = try? NSRegularExpression(pattern: "\"k\":\\[[\\d\\.]+\\,[\\d\\.]+\\,[\\d\\.]+\\,[\\d\\.]+\\]")
+
+public func transformedWithFitzModifier(data: Data, fitzModifier: EmojiFitzModifier?) -> Data {
+ if let fitzModifier = fitzModifier, var string = String(data: data, encoding: .utf8) {
+ let colors: [UIColor] = [0xf77e41, 0xffb139, 0xffd140, 0xffdf79].map { UIColor(rgb: $0) }
+ let replacementColors: [UIColor]
+ switch fitzModifier {
+ case .type12:
+ replacementColors = [0xca907a, 0xedc5a5, 0xf7e3c3, 0xfbefd6].map { UIColor(rgb: $0) }
+ case .type3:
+ replacementColors = [0xaa7c60, 0xc8a987, 0xddc89f, 0xe6d6b2].map { UIColor(rgb: $0) }
+ case .type4:
+ replacementColors = [0x8c6148, 0xad8562, 0xc49e76, 0xd4b188].map { UIColor(rgb: $0) }
+ case .type5:
+ replacementColors = [0x6e3c2c, 0x925a34, 0xa16e46, 0xac7a52].map { UIColor(rgb: $0) }
+ case .type6:
+ replacementColors = [0x291c12, 0x472a22, 0x573b30, 0x68493c].map { UIColor(rgb: $0) }
+ }
+
+ func colorToString(_ color: UIColor) -> String {
+ var r: CGFloat = 0.0
+ var g: CGFloat = 0.0
+ var b: CGFloat = 0.0
+ if color.getRed(&r, green: &g, blue: &b, alpha: nil) {
+ return "\"k\":[\(r),\(g),\(b),1]"
+ }
+ return ""
+ }
+
+ func match(_ a: Double, _ b: Double, eps: Double) -> Bool {
+ return abs(a - b) < eps
+ }
+
+ var replacements: [(NSTextCheckingResult, String)] = []
+
+ if let colorKeyRegex = colorKeyRegex {
+ let results = colorKeyRegex.matches(in: string, range: NSRange(string.startIndex..., in: string))
+ for result in results.reversed() {
+ if let range = Range(result.range, in: string) {
+ let substring = String(string[range])
+ let color = substring[substring.index(string.startIndex, offsetBy: "\"k\":[".count) ..< substring.index(before: substring.endIndex)]
+ let components = color.split(separator: ",")
+ if components.count == 4, let r = Double(components[0]), let g = Double(components[1]), let b = Double(components[2]), let a = Double(components[3]) {
+ if match(a, 1.0, eps: 0.01) {
+ for i in 0 ..< colors.count {
+ let color = colors[i]
+ var cr: CGFloat = 0.0
+ var cg: CGFloat = 0.0
+ var cb: CGFloat = 0.0
+ if color.getRed(&cr, green: &cg, blue: &cb, alpha: nil) {
+ if match(r, Double(cr), eps: 0.01) && match(g, Double(cg), eps: 0.01) && match(b, Double(cb), eps: 0.01) {
+ replacements.append((result, colorToString(replacementColors[i])))
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ for (result, text) in replacements {
+ if let range = Range(result.range, in: string) {
+ string = string.replacingCharacters(in: range, with: text)
+ }
+ }
+
+ return string.data(using: .utf8) ?? data
+ } else {
+ return data
+ }
+}
diff --git a/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift b/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift
index 96189597db..95ba00a378 100644
--- a/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift
+++ b/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift
@@ -762,7 +762,30 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
)
)
let location: SearchMessagesLocation
- location = .general
+ let messageTags: MessageTags?
+ if query.hasPrefix("%media ") {
+ messageTags = .photoOrVideo
+ } else if query.hasPrefix("%photo ") {
+ messageTags = .photo
+ } else if query.hasPrefix("%video ") {
+ messageTags = .video
+ } else if query.hasPrefix("%file ") {
+ messageTags = .file
+ } else if query.hasPrefix("%music ") {
+ messageTags = .music
+ } else if query.hasPrefix("%link ") {
+ messageTags = .webPage
+ } else if query.hasPrefix("%gif ") {
+ messageTags = .gif
+ } else {
+ messageTags = nil
+ }
+ location = .general(tags: messageTags)
+
+ var finalQuery = query
+ if let _ = messageTags, let index = finalQuery.firstIndex(of: " ") {
+ finalQuery = String(finalQuery.suffix(from: finalQuery.index(after: index)))
+ }
updateSearchContext { _ in
return (nil, true)
@@ -771,22 +794,22 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
if filter.contains(.doNotSearchMessages) {
foundRemoteMessages = .single((([], [:], 0), false))
} else {
- if !query.isEmpty {
+ if !finalQuery.isEmpty {
addAppLogEvent(postbox: context.account.postbox, time: Date().timeIntervalSince1970, type: "search_global_query", peerId: nil, data: .dictionary([:]))
}
- let searchSignal = searchMessages(account: context.account, location: location, query: query, state: nil, limit: 50)
+ let searchSignal = searchMessages(account: context.account, location: location, query: finalQuery, state: nil, limit: 50)
|> map { result, updatedState -> ChatListSearchMessagesResult in
- return ChatListSearchMessagesResult(query: query, messages: result.messages.sorted(by: { $0.index > $1.index }), readStates: result.readStates, hasMore: !result.completed, state: updatedState)
+ return ChatListSearchMessagesResult(query: finalQuery, messages: result.messages.sorted(by: { $0.index > $1.index }), readStates: result.readStates, hasMore: !result.completed, state: updatedState)
}
let loadMore = searchContext.get()
|> mapToSignal { searchContext -> Signal<(([Message], [PeerId: CombinedPeerReadState], Int32), Bool), NoError> in
if let searchContext = searchContext {
if let _ = searchContext.loadMoreIndex {
- return searchMessages(account: context.account, location: location, query: query, state: searchContext.result.state, limit: 80)
+ return searchMessages(account: context.account, location: location, query: finalQuery, state: searchContext.result.state, limit: 80)
|> map { result, updatedState -> ChatListSearchMessagesResult in
- return ChatListSearchMessagesResult(query: query, messages: result.messages.sorted(by: { $0.index > $1.index }), readStates: result.readStates, hasMore: !result.completed, state: updatedState)
+ return ChatListSearchMessagesResult(query: finalQuery, messages: result.messages.sorted(by: { $0.index > $1.index }), readStates: result.readStates, hasMore: !result.completed, state: updatedState)
}
|> mapToSignal { foundMessages -> Signal<(([Message], [PeerId: CombinedPeerReadState], Int32), Bool), NoError> in
updateSearchContext { previous in
@@ -818,7 +841,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
}
let resolvedMessage = .single(nil)
- |> then(context.sharedContext.resolveUrl(account: context.account, url: query)
+ |> then(context.sharedContext.resolveUrl(account: context.account, url: finalQuery)
|> mapToSignal { resolvedUrl -> Signal in
if case let .channelMessage(peerId, messageId) = resolvedUrl {
return downloadMessage(postbox: context.account.postbox, network: context.account.network, messageId: messageId)
@@ -904,60 +927,63 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
globalExpandType = .none
}
- let lowercasedQuery = query.lowercased()
- if presentationData.strings.DialogList_SavedMessages.lowercased().hasPrefix(lowercasedQuery) || "saved messages".hasPrefix(lowercasedQuery) {
- if !existingPeerIds.contains(accountPeer.id), filteredPeer(accountPeer, accountPeer) {
- existingPeerIds.insert(accountPeer.id)
- entries.append(.localPeer(accountPeer, nil, nil, index, presentationData.theme, presentationData.strings, presentationData.nameSortOrder, presentationData.nameDisplayOrder, localExpandType))
- index += 1
- }
- }
-
- var numberOfLocalPeers = 0
- for renderedPeer in foundLocalPeers.peers {
- if case .expand = localExpandType, numberOfLocalPeers >= 5 {
- break
+ if let _ = messageTags {
+ } else {
+ let lowercasedQuery = finalQuery.lowercased()
+ if presentationData.strings.DialogList_SavedMessages.lowercased().hasPrefix(lowercasedQuery) || "saved messages".hasPrefix(lowercasedQuery) {
+ if !existingPeerIds.contains(accountPeer.id), filteredPeer(accountPeer, accountPeer) {
+ existingPeerIds.insert(accountPeer.id)
+ entries.append(.localPeer(accountPeer, nil, nil, index, presentationData.theme, presentationData.strings, presentationData.nameSortOrder, presentationData.nameDisplayOrder, localExpandType))
+ index += 1
+ }
}
- if let peer = renderedPeer.peers[renderedPeer.peerId], peer.id != context.account.peerId, filteredPeer(peer, accountPeer) {
- if !existingPeerIds.contains(peer.id) {
- existingPeerIds.insert(peer.id)
- var associatedPeer: Peer?
- if let associatedPeerId = peer.associatedPeerId {
- associatedPeer = renderedPeer.peers[associatedPeerId]
+ var numberOfLocalPeers = 0
+ for renderedPeer in foundLocalPeers.peers {
+ if case .expand = localExpandType, numberOfLocalPeers >= 5 {
+ break
+ }
+
+ if let peer = renderedPeer.peers[renderedPeer.peerId], peer.id != context.account.peerId, filteredPeer(peer, accountPeer) {
+ if !existingPeerIds.contains(peer.id) {
+ existingPeerIds.insert(peer.id)
+ var associatedPeer: Peer?
+ if let associatedPeerId = peer.associatedPeerId {
+ associatedPeer = renderedPeer.peers[associatedPeerId]
+ }
+ entries.append(.localPeer(peer, associatedPeer, foundLocalPeers.unread[peer.id], index, presentationData.theme, presentationData.strings, presentationData.nameSortOrder, presentationData.nameDisplayOrder, localExpandType))
+ index += 1
+ numberOfLocalPeers += 1
}
- entries.append(.localPeer(peer, associatedPeer, foundLocalPeers.unread[peer.id], index, presentationData.theme, presentationData.strings, presentationData.nameSortOrder, presentationData.nameDisplayOrder, localExpandType))
+ }
+ }
+
+ for peer in foundRemotePeers.0 {
+ if case .expand = localExpandType, numberOfLocalPeers >= 5 {
+ break
+ }
+
+ if !existingPeerIds.contains(peer.peer.id), filteredPeer(peer.peer, accountPeer) {
+ existingPeerIds.insert(peer.peer.id)
+ entries.append(.localPeer(peer.peer, nil, nil, index, presentationData.theme, presentationData.strings, presentationData.nameSortOrder, presentationData.nameDisplayOrder, localExpandType))
index += 1
numberOfLocalPeers += 1
}
}
- }
-
- for peer in foundRemotePeers.0 {
- if case .expand = localExpandType, numberOfLocalPeers >= 5 {
- break
- }
-
- if !existingPeerIds.contains(peer.peer.id), filteredPeer(peer.peer, accountPeer) {
- existingPeerIds.insert(peer.peer.id)
- entries.append(.localPeer(peer.peer, nil, nil, index, presentationData.theme, presentationData.strings, presentationData.nameSortOrder, presentationData.nameDisplayOrder, localExpandType))
- index += 1
- numberOfLocalPeers += 1
- }
- }
- var numberOfGlobalPeers = 0
- index = 0
- for peer in foundRemotePeers.1 {
- if case .expand = globalExpandType, numberOfGlobalPeers >= 3 {
- break
- }
-
- if !existingPeerIds.contains(peer.peer.id), filteredPeer(peer.peer, accountPeer) {
- existingPeerIds.insert(peer.peer.id)
- entries.append(.globalPeer(peer, nil, index, presentationData.theme, presentationData.strings, presentationData.nameSortOrder, presentationData.nameDisplayOrder, globalExpandType))
- index += 1
- numberOfGlobalPeers += 1
+ var numberOfGlobalPeers = 0
+ index = 0
+ for peer in foundRemotePeers.1 {
+ if case .expand = globalExpandType, numberOfGlobalPeers >= 3 {
+ break
+ }
+
+ if !existingPeerIds.contains(peer.peer.id), filteredPeer(peer.peer, accountPeer) {
+ existingPeerIds.insert(peer.peer.id)
+ entries.append(.globalPeer(peer, nil, index, presentationData.theme, presentationData.strings, presentationData.nameSortOrder, presentationData.nameDisplayOrder, globalExpandType))
+ index += 1
+ numberOfGlobalPeers += 1
+ }
}
}
@@ -986,8 +1012,8 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
}
}
- if addContact != nil && isViablePhoneNumber(query) {
- entries.append(.addContact(query, presentationData.theme, presentationData.strings))
+ if addContact != nil && isViablePhoneNumber(finalQuery) {
+ entries.append(.addContact(finalQuery, presentationData.theme, presentationData.strings))
}
return (entries, isSearching)
diff --git a/submodules/CountrySelectionUI/BUCK b/submodules/CountrySelectionUI/BUCK
index d42c4ed704..03763af9f3 100644
--- a/submodules/CountrySelectionUI/BUCK
+++ b/submodules/CountrySelectionUI/BUCK
@@ -9,6 +9,7 @@ static_library(
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit#shared",
"//submodules/AsyncDisplayKit:AsyncDisplayKit#shared",
"//submodules/Display:Display#shared",
+ "//submodules/TelegramCore:TelegramCore#shared",
"//submodules/TelegramPresentationData:TelegramPresentationData",
"//submodules/TelegramStringFormatting:TelegramStringFormatting",
"//submodules/SearchBarNode:SearchBarNode",
diff --git a/submodules/CountrySelectionUI/BUILD b/submodules/CountrySelectionUI/BUILD
index 7fcc859b8b..368cd0b914 100644
--- a/submodules/CountrySelectionUI/BUILD
+++ b/submodules/CountrySelectionUI/BUILD
@@ -10,6 +10,7 @@ swift_library(
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit",
"//submodules/AsyncDisplayKit:AsyncDisplayKit",
"//submodules/Display:Display",
+ "//submodules/TelegramCore:TelegramCore",
"//submodules/TelegramPresentationData:TelegramPresentationData",
"//submodules/TelegramStringFormatting:TelegramStringFormatting",
"//submodules/SearchBarNode:SearchBarNode",
diff --git a/submodules/CountrySelectionUI/Sources/AuthorizationSequenceCountrySelectionController.swift b/submodules/CountrySelectionUI/Sources/AuthorizationSequenceCountrySelectionController.swift
index 13e2fdbe3c..79b65b718f 100644
--- a/submodules/CountrySelectionUI/Sources/AuthorizationSequenceCountrySelectionController.swift
+++ b/submodules/CountrySelectionUI/Sources/AuthorizationSequenceCountrySelectionController.swift
@@ -2,12 +2,15 @@ import Foundation
import UIKit
import Display
import AsyncDisplayKit
+import SwiftSignalKit
+import Postbox
import TelegramPresentationData
import TelegramStringFormatting
import SearchBarNode
import AppBundle
+import TelegramCore
-private func loadCountryCodes() -> [(String, Int)] {
+private func loadCountryCodes() -> [Country] {
guard let filePath = getAppBundle().path(forResource: "PhoneCountries", ofType: "txt") else {
return []
}
@@ -21,10 +24,12 @@ private func loadCountryCodes() -> [(String, Int)] {
let delimiter = ";"
let endOfLine = "\n"
- var result: [(String, Int)] = []
+ var result: [Country] = []
var currentLocation = data.startIndex
+ let locale = Locale(identifier: "en-US")
+
while true {
guard let codeRange = data.range(of: delimiter, options: [], range: currentLocation ..< data.endIndex) else {
break
@@ -40,8 +45,9 @@ private func loadCountryCodes() -> [(String, Int)] {
let maybeNameRange = data.range(of: endOfLine, options: [], range: idRange.upperBound ..< data.endIndex)
+ let countryName = locale.localizedString(forIdentifier: countryId) ?? ""
if let countryCodeInt = Int(countryCode) {
- result.append((countryId, countryCodeInt))
+ result.append(Country(code: countryId, name: countryName, localizedName: nil, countryCodes: [Country.CountryCode(code: countryCode, prefixes: [], patterns: [])], hidden: false))
}
if let maybeNameRange = maybeNameRange {
@@ -54,7 +60,14 @@ private func loadCountryCodes() -> [(String, Int)] {
return result
}
-private let countryCodes: [(String, Int)] = loadCountryCodes()
+private var countryCodes: [Country] = loadCountryCodes()
+
+public func loadServerCountryCodes(accountManager: AccountManager, network: Network) {
+ let _ = (getCountriesList(accountManager: accountManager, network: network, langCode: nil)
+ |> deliverOnMainQueue).start(next: { countries in
+ countryCodes = countries
+ })
+}
private final class AuthorizationSequenceCountrySelectionNavigationContentNode: NavigationBarContentNode {
private let theme: PresentationTheme
@@ -117,8 +130,8 @@ private final class AuthorizationSequenceCountrySelectionNavigationContentNode:
public final class AuthorizationSequenceCountrySelectionController: ViewController {
public static func lookupCountryNameById(_ id: String, strings: PresentationStrings) -> String? {
- for (itemId, _) in countryCodes {
- if id == itemId {
+ for country in countryCodes {
+ if id == country.code {
let locale = localeWithStrings(strings)
if let countryName = locale.localizedString(forRegionCode: id) {
return countryName
@@ -131,9 +144,22 @@ public final class AuthorizationSequenceCountrySelectionController: ViewControll
}
public static func lookupCountryIdByCode(_ code: Int) -> String? {
- for (itemId, itemCode) in countryCodes {
- if itemCode == code {
- return itemId
+ for country in countryCodes {
+ for countryCode in country.countryCodes {
+ if countryCode.code == "\(code)" {
+ return country.code
+ }
+ }
+ }
+ return nil
+ }
+
+ public static func lookupPatternByCode(_ code: Int) -> String? {
+ for country in countryCodes {
+ for countryCode in country.countryCodes {
+ if countryCode.code == "\(code)" {
+ return countryCode.patterns.first
+ }
}
}
return nil
diff --git a/submodules/DeviceAccess/Sources/DeviceAccess.swift b/submodules/DeviceAccess/Sources/DeviceAccess.swift
index 7c16e2247d..d598849965 100644
--- a/submodules/DeviceAccess/Sources/DeviceAccess.swift
+++ b/submodules/DeviceAccess/Sources/DeviceAccess.swift
@@ -277,7 +277,12 @@ public final class DeviceAccess {
if status == PGCameraAuthorizationStatusRestricted {
text = presentationData.strings.AccessDenied_CameraRestricted
} else {
- text = presentationData.strings.AccessDenied_Camera
+ switch cameraSubject {
+ case .video:
+ text = presentationData.strings.AccessDenied_Camera
+ case .videoCall:
+ text = presentationData.strings.AccessDenied_VideoCallCamera
+ }
}
completion(false)
present(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: presentationData.strings.AccessDenied_Title, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_NotNow, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.AccessDenied_Settings, action: {
diff --git a/submodules/Display/Source/Font.swift b/submodules/Display/Source/Font.swift
index 0a9779afb6..674197bfea 100644
--- a/submodules/Display/Source/Font.swift
+++ b/submodules/Display/Source/Font.swift
@@ -22,6 +22,7 @@ public struct Font {
public static let bold = Traits(rawValue: 1 << 0)
public static let italic = Traits(rawValue: 1 << 1)
+ public static let monospacedNumbers = Traits(rawValue: 1 << 2)
}
public enum Weight {
@@ -43,6 +44,15 @@ public struct Font {
symbolicTraits.insert(.traitItalic)
}
var updatedDescriptor: UIFontDescriptor? = descriptor.withSymbolicTraits(symbolicTraits)
+ if traits.contains(.monospacedNumbers) {
+ updatedDescriptor = descriptor.addingAttributes([
+ UIFontDescriptor.AttributeName.featureSettings: [
+ [UIFontDescriptor.FeatureKey.featureIdentifier:
+ kNumberSpacingType,
+ UIFontDescriptor.FeatureKey.typeIdentifier:
+ kMonospacedNumbersSelector]
+ ]])
+ }
switch design {
case .serif:
updatedDescriptor = updatedDescriptor?.withDesign(.serif)
diff --git a/submodules/Emoji/Sources/EmojiUtils.swift b/submodules/Emoji/Sources/EmojiUtils.swift
index da06ccc2bb..893bae47e8 100644
--- a/submodules/Emoji/Sources/EmojiUtils.swift
+++ b/submodules/Emoji/Sources/EmojiUtils.swift
@@ -5,7 +5,7 @@ import AVFoundation
public extension UnicodeScalar {
var isEmoji: Bool {
switch self.value {
- case 0x1F600...0x1F64F, 0x1F300...0x1F5FF, 0x1F680...0x1F6FF, 0x1F1E6...0x1F1FF, 0xE0020...0xE007F, 0xFE00...0xFE0F, 0x1F900...0x1F9FF, 0x1F018...0x1F0F5, 0x1F200...0x1F270, 65024...65039, 9100...9300, 8400...8447, 0x1F004, 0x1F18E, 0x1F191...0x1F19A, 0x1F5E8:
+ case 0x1F600...0x1F64F, 0x1F300...0x1F5FF, 0x1F680...0x1F6FF, 0x1F1E6...0x1F1FF, 0xE0020...0xE007F, 0xFE00...0xFE0F, 0x1F900...0x1F9FF, 0x1F018...0x1F0F5, 0x1F200...0x1F270, 65024...65039, 9100...9300, 8400...8447, 0x1F004, 0x1F18E, 0x1F191...0x1F19A, 0x1F5E8, 0x1FA70...0x1FA73, 0x1FA78...0x1FA7A, 0x1FA80...0x1FA82, 0x1FA90...0x1FA95:
return true
case 0x2603, 0x265F, 0x267E, 0x2692, 0x26C4, 0x26C8, 0x26CE, 0x26CF, 0x26D1...0x26D3, 0x26E9, 0x26F0...0x26F9, 0x2705, 0x270A, 0x270B, 0x2728, 0x274E, 0x2753...0x2755, 0x274C, 0x2795...0x2797, 0x27B0, 0x27BF:
return true
diff --git a/submodules/HashtagSearchUI/Sources/HashtagSearchController.swift b/submodules/HashtagSearchUI/Sources/HashtagSearchController.swift
index 78442b6a65..538fa413c0 100644
--- a/submodules/HashtagSearchUI/Sources/HashtagSearchController.swift
+++ b/submodules/HashtagSearchUI/Sources/HashtagSearchController.swift
@@ -41,7 +41,7 @@ public final class HashtagSearchController: TelegramBaseController {
let chatListPresentationData = ChatListPresentationData(theme: self.presentationData.theme, fontSize: self.presentationData.listsFontSize, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameSortOrder: self.presentationData.nameSortOrder, nameDisplayOrder: self.presentationData.nameDisplayOrder, disableAnimations: self.presentationData.disableAnimations)
- let location: SearchMessagesLocation = .general
+ let location: SearchMessagesLocation = .general(tags: nil)
let search = searchMessages(account: context.account, location: location, query: query, state: nil)
let foundMessages: Signal<[ChatListSearchEntry], NoError> = search
|> map { result, _ in
diff --git a/submodules/LegacyComponents/Sources/TGMediaPickerGalleryVideoItemView.m b/submodules/LegacyComponents/Sources/TGMediaPickerGalleryVideoItemView.m
index 84c7f381d7..208a6df4a3 100644
--- a/submodules/LegacyComponents/Sources/TGMediaPickerGalleryVideoItemView.m
+++ b/submodules/LegacyComponents/Sources/TGMediaPickerGalleryVideoItemView.m
@@ -1461,13 +1461,13 @@
NSMutableArray *actions = [[NSMutableArray alloc] init];
NSString *text = [self itemIsLivePhoto] ? TGLocalized(@"MediaPicker.LivePhotoDescription") : TGLocalized(@"MediaPicker.VideoMuteDescription");
[actions addObject:@{@"title":text}];
- _tooltipContainerView.menuView.forceArrowOnTop = true;
+ _tooltipContainerView.menuView.forceArrowOnTop = false;
_tooltipContainerView.menuView.multiline = true;
[_tooltipContainerView.menuView setButtonsAndActions:actions watcherHandle:nil];
_tooltipContainerView.menuView.buttonHighlightDisabled = true;
[_tooltipContainerView.menuView sizeToFit];
- CGRect iconViewFrame = CGRectMake(12, 188 + _safeAreaInset.top, 40, 40);
+ CGRect iconViewFrame = CGRectMake(12, self.frame.size.height - 192.0 - _safeAreaInset.bottom, 40, 40);
[_tooltipContainerView showMenuFromRect:iconViewFrame animated:false];
}
diff --git a/submodules/PeerInfoUI/Sources/ChannelOwnershipTransferController.swift b/submodules/PeerInfoUI/Sources/ChannelOwnershipTransferController.swift
index b8ff2dde96..2dc4cd0c0f 100644
--- a/submodules/PeerInfoUI/Sources/ChannelOwnershipTransferController.swift
+++ b/submodules/PeerInfoUI/Sources/ChannelOwnershipTransferController.swift
@@ -173,12 +173,12 @@ private final class ChannelOwnershipTransferPasswordFieldNode: ASDisplayNode, UI
}
}
-private final class ChannelOwnershipTransferAlertContentNode: AlertContentNode {
+public final class ChannelOwnershipTransferAlertContentNode: AlertContentNode {
private let strings: PresentationStrings
private let titleNode: ASTextNode
private let textNode: ASTextNode
- let inputFieldNode: ChannelOwnershipTransferPasswordFieldNode
+ fileprivate let inputFieldNode: ChannelOwnershipTransferPasswordFieldNode
private let actionNodesSeparator: ASDisplayNode
private let actionNodes: [TextAlertContentActionNode]
@@ -190,18 +190,25 @@ private final class ChannelOwnershipTransferAlertContentNode: AlertContentNode {
private let hapticFeedback = HapticFeedback()
- var complete: (() -> Void)? {
+ public var complete: (() -> Void)? {
didSet {
self.inputFieldNode.complete = self.complete
}
}
- override var dismissOnOutsideTap: Bool {
+ public var theme: PresentationTheme {
+ didSet {
+ self.inputFieldNode.updateTheme(self.theme)
+ }
+ }
+
+ public override var dismissOnOutsideTap: Bool {
return self.isUserInteractionEnabled
}
- init(theme: AlertControllerTheme, ptheme: PresentationTheme, strings: PresentationStrings, actions: [TextAlertAction]) {
+ public init(theme: AlertControllerTheme, ptheme: PresentationTheme, strings: PresentationStrings, actions: [TextAlertAction]) {
self.strings = strings
+ self.theme = ptheme
self.titleNode = ASTextNode()
self.titleNode.maximumNumberOfLines = 2
@@ -258,19 +265,19 @@ private final class ChannelOwnershipTransferAlertContentNode: AlertContentNode {
self.disposable.dispose()
}
- func dismissInput() {
+ public func dismissInput() {
self.inputFieldNode.deactivateInput()
}
- var password: String {
+ public var password: String {
return self.inputFieldNode.password
}
- func updateIsChecking(_ checking: Bool) {
+ public func updateIsChecking(_ checking: Bool) {
self.inputFieldNode.updateIsChecking(checking)
}
- override func updateTheme(_ theme: AlertControllerTheme) {
+ public override func updateTheme(_ theme: AlertControllerTheme) {
self.titleNode.attributedText = NSAttributedString(string: self.strings.Channel_OwnershipTransfer_EnterPassword, font: Font.bold(17.0), textColor: theme.primaryColor, paragraphAlignment: .center)
self.textNode.attributedText = NSAttributedString(string: self.strings.Channel_OwnershipTransfer_EnterPasswordText, font: Font.regular(13.0), textColor: theme.primaryColor, paragraphAlignment: .center)
@@ -281,13 +288,13 @@ private final class ChannelOwnershipTransferAlertContentNode: AlertContentNode {
for separatorNode in self.actionVerticalSeparators {
separatorNode.backgroundColor = theme.separatorColor
}
-
+
if let size = self.validLayout {
_ = self.updateLayout(size: size, transition: .immediate)
}
}
- override func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize {
+ public override func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize {
var size = size
size.width = min(size.width, 270.0)
let measureSize = CGSize(width: size.width - 16.0 * 2.0, height: CGFloat.greatestFiniteMagnitude)
@@ -400,7 +407,7 @@ private final class ChannelOwnershipTransferAlertContentNode: AlertContentNode {
return resultSize
}
- func animateError() {
+ public func animateError() {
self.inputFieldNode.updateIsInvalid()
self.inputFieldNode.layer.addShakeAnimation()
self.hapticFeedback.error()
diff --git a/submodules/PhoneInputNode/Sources/PhoneInputNode.swift b/submodules/PhoneInputNode/Sources/PhoneInputNode.swift
index 47bdad1865..f68af967e8 100644
--- a/submodules/PhoneInputNode/Sources/PhoneInputNode.swift
+++ b/submodules/PhoneInputNode/Sources/PhoneInputNode.swift
@@ -76,9 +76,24 @@ private func cleanSuffix(_ text: String) -> String {
return result
}
+extension String {
+ func applyPatternOnNumbers(pattern: String, replacementCharacter: Character) -> String {
+ var pureNumber = self.replacingOccurrences( of: "[^0-9]", with: "", options: .regularExpression)
+ for index in 0 ..< pattern.count {
+ guard index < pureNumber.count else { return pureNumber }
+ let stringIndex = String.Index(encodedOffset: index)
+ let patternCharacter = pattern[stringIndex]
+ guard patternCharacter != replacementCharacter else { continue }
+ pureNumber.insert(patternCharacter, at: stringIndex)
+ }
+ return pureNumber
+ }
+}
+
public final class PhoneInputNode: ASDisplayNode, UITextFieldDelegate {
public let countryCodeField: TextFieldNode
public let numberField: TextFieldNode
+ public let placeholderNode: ImmediateTextNode
public var previousCountryCodeText = "+"
public var previousNumberText = ""
@@ -151,13 +166,33 @@ public final class PhoneInputNode: ASDisplayNode, UITextFieldDelegate {
private let phoneFormatter = InteractivePhoneFormatter()
+ public var mask: NSAttributedString? {
+ didSet {
+ self.updatePlaceholder()
+ }
+ }
+
+ private func updatePlaceholder() {
+ if let mask = self.mask {
+ let mutableMask = NSMutableAttributedString(attributedString: mask)
+ mutableMask.replaceCharacters(in: NSRange(location: 0, length: mask.string.count), with: mask.string.replacingOccurrences(of: "X", with: "-"))
+ mutableMask.addAttribute(.foregroundColor, value: UIColor.clear, range: NSRange(location: 0, length: min(self.numberField.textField.text?.count ?? 0, mask.string.count)))
+ self.placeholderNode.attributedText = mutableMask
+ } else {
+ self.placeholderNode.attributedText = NSAttributedString(string: "")
+ }
+ let _ = self.placeholderNode.updateLayout(CGSize(width: self.frame.size.width, height: CGFloat.greatestFiniteMagnitude))
+ }
+
private let fontSize: CGFloat
public init(fontSize: CGFloat = 20.0) {
self.fontSize = fontSize
+ let font = Font.with(size: fontSize, design: .regular, traits: [.monospacedNumbers])
+
self.countryCodeField = TextFieldNode()
- self.countryCodeField.textField.font = Font.regular(fontSize)
+ self.countryCodeField.textField.font = font
self.countryCodeField.textField.textAlignment = .center
self.countryCodeField.textField.returnKeyType = .next
if #available(iOSApplicationExtension 10.0, iOS 10.0, *) {
@@ -167,18 +202,21 @@ public final class PhoneInputNode: ASDisplayNode, UITextFieldDelegate {
self.countryCodeField.textField.keyboardType = .numberPad
}
+ self.placeholderNode = ImmediateTextNode()
+
self.numberField = TextFieldNode()
- self.numberField.textField.font = Font.regular(fontSize)
+ self.numberField.textField.font = font
if #available(iOSApplicationExtension 10.0, iOS 10.0, *) {
self.numberField.textField.keyboardType = .asciiCapableNumberPad
self.numberField.textField.textContentType = .telephoneNumber
} else {
self.numberField.textField.keyboardType = .numberPad
}
-
+ self.numberField.textField.defaultTextAttributes = [NSAttributedString.Key.font: font, NSAttributedString.Key.kern: 2.0]
super.init()
self.addSubnode(self.countryCodeField)
+ self.addSubnode(self.placeholderNode)
self.addSubnode(self.numberField)
self.numberField.textField.didDeleteBackwardWhileEmpty = { [weak self] in
@@ -223,8 +261,9 @@ public final class PhoneInputNode: ASDisplayNode, UITextFieldDelegate {
private func updateNumber(_ inputText: String, tryRestoringInputPosition: Bool = true, forceNotifyCountryCodeUpdated: Bool = false) {
let (regionPrefix, text) = self.phoneFormatter.updateText(inputText)
+
var realRegionPrefix: String
- let numberText: String
+ var numberText: String
if let regionPrefix = regionPrefix, !regionPrefix.isEmpty, regionPrefix != "+" {
realRegionPrefix = cleanSuffix(regionPrefix)
if !realRegionPrefix.hasPrefix("+") {
@@ -239,6 +278,10 @@ public final class PhoneInputNode: ASDisplayNode, UITextFieldDelegate {
numberText = ""
}
+ if let mask = self.mask {
+ numberText = numberText.applyPatternOnNumbers(pattern: mask.string, replacementCharacter: "X")
+ }
+
var focusOnNumber = false
if realRegionPrefix != self.countryCodeField.textField.text {
self.countryCodeField.textField.text = realRegionPrefix
@@ -296,5 +339,7 @@ public final class PhoneInputNode: ASDisplayNode, UITextFieldDelegate {
if focusOnNumber && !self.numberField.textField.isFirstResponder {
self.numberField.textField.becomeFirstResponder()
}
+
+ self.updatePlaceholder()
}
}
diff --git a/submodules/SettingsUI/Sources/BubbleSettings/BubbleSettingsController.swift b/submodules/SettingsUI/Sources/BubbleSettings/BubbleSettingsController.swift
index 18984dbed5..600f8741f7 100644
--- a/submodules/SettingsUI/Sources/BubbleSettings/BubbleSettingsController.swift
+++ b/submodules/SettingsUI/Sources/BubbleSettings/BubbleSettingsController.swift
@@ -173,20 +173,20 @@ private final class BubbleSettingsControllerNode: ASDisplayNode, UIScrollViewDel
messages[replyMessageId] = Message(stableId: 3, stableVersion: 0, id: replyMessageId, globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66000, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_1_Text, attributes: [], media: [], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [])
let message1 = Message(stableId: 4, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 4), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66003, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_3_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])
- items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: message1, theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil))
+ items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message1], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil))
let message2 = Message(stableId: 3, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 3), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66002, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_2_Text, attributes: [ReplyMessageAttribute(messageId: replyMessageId)], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])
- items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: message2, theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil))
+ items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message2], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil))
let waveformBase64 = "DAAOAAkACQAGAAwADwAMABAADQAPABsAGAALAA0AGAAfABoAHgATABgAGQAYABQADAAVABEAHwANAA0ACQAWABkACQAOAAwACQAfAAAAGQAVAAAAEwATAAAACAAfAAAAHAAAABwAHwAAABcAGQAAABQADgAAABQAHwAAAB8AHwAAAAwADwAAAB8AEwAAABoAFwAAAB8AFAAAAAAAHwAAAAAAHgAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAAAA="
let voiceAttributes: [TelegramMediaFileAttribute] = [.Audio(isVoice: true, duration: 23, title: nil, performer: nil, waveform: MemoryBuffer(data: Data(base64Encoded: waveformBase64)!))]
let voiceMedia = TelegramMediaFile(fileId: MediaId(namespace: 0, id: 0), partialReference: nil, resource: LocalFileMediaResource(fileId: 0), previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "audio/ogg", size: 0, attributes: voiceAttributes)
let message3 = Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66001, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [voiceMedia], peers: peers, associatedMessages: messages, associatedMessageIds: [])
- items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: message3, theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: FileMediaResourceStatus(mediaStatus: .playbackStatus(.paused), fetchStatus: .Local), tapMessage: nil, clickThroughMessage: nil))
+ items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message3], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: FileMediaResourceStatus(mediaStatus: .playbackStatus(.paused), fetchStatus: .Local), tapMessage: nil, clickThroughMessage: nil))
let message4 = Message(stableId: 2, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 2), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66001, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_1_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])
- items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: message4, theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil))
+ items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message4], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil))
let width: CGFloat
if case .regular = layout.metrics.widthClass {
diff --git a/submodules/SettingsUI/Sources/Privacy and Security/ForwardPrivacyChatPreviewItem.swift b/submodules/SettingsUI/Sources/Privacy and Security/ForwardPrivacyChatPreviewItem.swift
index 25ad1884d2..db6abc4344 100644
--- a/submodules/SettingsUI/Sources/Privacy and Security/ForwardPrivacyChatPreviewItem.swift
+++ b/submodules/SettingsUI/Sources/Privacy and Security/ForwardPrivacyChatPreviewItem.swift
@@ -159,7 +159,7 @@ class ForwardPrivacyChatPreviewItemNode: ListViewItemNode {
let forwardInfo = MessageForwardInfo(author: item.linkEnabled ? peers[peerId] : nil, source: nil, sourceMessageId: nil, date: 0, authorSignature: item.linkEnabled ? nil : item.peerName, psaType: nil)
- let messageItem = item.context.sharedContext.makeChatMessagePreviewItem(context: item.context, message: Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66000, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: forwardInfo, author: nil, text: item.strings.Privacy_Forwards_PreviewMessageText, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), theme: item.theme, strings: item.strings, wallpaper: item.wallpaper, fontSize: item.fontSize, chatBubbleCorners: item.chatBubbleCorners, dateTimeFormat: item.dateTimeFormat, nameOrder: item.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil)
+ let messageItem = item.context.sharedContext.makeChatMessagePreviewItem(context: item.context, messages: [Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66000, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: forwardInfo, author: nil, text: item.strings.Privacy_Forwards_PreviewMessageText, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])], theme: item.theme, strings: item.strings, wallpaper: item.wallpaper, fontSize: item.fontSize, chatBubbleCorners: item.chatBubbleCorners, dateTimeFormat: item.dateTimeFormat, nameOrder: item.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil)
var node: ListViewItemNode?
if let current = currentNode {
diff --git a/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift b/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift
index 9c01ad1baa..862a27987f 100644
--- a/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift
+++ b/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift
@@ -319,20 +319,20 @@ private final class TextSizeSelectionControllerNode: ASDisplayNode, UIScrollView
messages[replyMessageId] = Message(stableId: 3, stableVersion: 0, id: replyMessageId, globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66000, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_1_Text, attributes: [], media: [], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [])
let message1 = Message(stableId: 4, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 4), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66003, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_3_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])
- items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: message1, theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil))
+ items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message1], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil))
let message2 = Message(stableId: 3, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 3), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66002, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_2_Text, attributes: [ReplyMessageAttribute(messageId: replyMessageId)], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])
- items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: message2, theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil))
+ items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message2], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil))
let waveformBase64 = "DAAOAAkACQAGAAwADwAMABAADQAPABsAGAALAA0AGAAfABoAHgATABgAGQAYABQADAAVABEAHwANAA0ACQAWABkACQAOAAwACQAfAAAAGQAVAAAAEwATAAAACAAfAAAAHAAAABwAHwAAABcAGQAAABQADgAAABQAHwAAAB8AHwAAAAwADwAAAB8AEwAAABoAFwAAAB8AFAAAAAAAHwAAAAAAHgAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAAAA="
let voiceAttributes: [TelegramMediaFileAttribute] = [.Audio(isVoice: true, duration: 23, title: nil, performer: nil, waveform: MemoryBuffer(data: Data(base64Encoded: waveformBase64)!))]
let voiceMedia = TelegramMediaFile(fileId: MediaId(namespace: 0, id: 0), partialReference: nil, resource: LocalFileMediaResource(fileId: 0), previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "audio/ogg", size: 0, attributes: voiceAttributes)
let message3 = Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66001, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [voiceMedia], peers: peers, associatedMessages: messages, associatedMessageIds: [])
- items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: message3, theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: FileMediaResourceStatus(mediaStatus: .playbackStatus(.paused), fetchStatus: .Local), tapMessage: nil, clickThroughMessage: nil))
+ items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message3], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: FileMediaResourceStatus(mediaStatus: .playbackStatus(.paused), fetchStatus: .Local), tapMessage: nil, clickThroughMessage: nil))
let message4 = Message(stableId: 2, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 2), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66001, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_1_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])
- items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: message4, theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil))
+ items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message4], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil))
let width: CGFloat
if case .regular = layout.metrics.widthClass {
diff --git a/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift b/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift
index 5d38dc26fd..2830ef49ba 100644
--- a/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift
+++ b/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift
@@ -882,7 +882,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
sampleMessages.append(message8)
items = sampleMessages.reversed().map { message in
- let item = self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: message, theme: self.theme, strings: self.presentationData.strings, wallpaper: self.wallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: !message.media.isEmpty ? FileMediaResourceStatus(mediaStatus: .playbackStatus(.paused), fetchStatus: .Local) : nil, tapMessage: { [weak self] message in
+ let item = self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message], theme: self.theme, strings: self.presentationData.strings, wallpaper: self.wallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: !message.media.isEmpty ? FileMediaResourceStatus(mediaStatus: .playbackStatus(.paused), fetchStatus: .Local) : nil, tapMessage: { [weak self] message in
if message.flags.contains(.Incoming) {
self?.updateSection(.accent)
self?.requestSectionUpdate?(.accent)
diff --git a/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift b/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift
index 86079f02cf..e6a3736b1f 100644
--- a/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift
+++ b/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift
@@ -487,7 +487,7 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
sampleMessages.append(message8)
items = sampleMessages.reversed().map { message in
- self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: message, theme: self.previewTheme, strings: self.presentationData.strings, wallpaper: self.previewTheme.chat.defaultWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: !message.media.isEmpty ? FileMediaResourceStatus(mediaStatus: .playbackStatus(.paused), fetchStatus: .Local) : nil, tapMessage: nil, clickThroughMessage: nil)
+ self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message], theme: self.previewTheme, strings: self.presentationData.strings, wallpaper: self.previewTheme.chat.defaultWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: !message.media.isEmpty ? FileMediaResourceStatus(mediaStatus: .playbackStatus(.paused), fetchStatus: .Local) : nil, tapMessage: nil, clickThroughMessage: nil)
}
let width: CGFloat
diff --git a/submodules/SettingsUI/Sources/Themes/ThemeSettingsChatPreviewItem.swift b/submodules/SettingsUI/Sources/Themes/ThemeSettingsChatPreviewItem.swift
index 47b4bbda37..df4f629682 100644
--- a/submodules/SettingsUI/Sources/Themes/ThemeSettingsChatPreviewItem.swift
+++ b/submodules/SettingsUI/Sources/Themes/ThemeSettingsChatPreviewItem.swift
@@ -165,7 +165,7 @@ class ThemeSettingsChatPreviewItemNode: ListViewItemNode {
}
let message = Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: messageItem.outgoing ? otherPeerId : peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66000, flags: messageItem.outgoing ? [] : [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: messageItem.outgoing ? TelegramUser(id: otherPeerId, accessHash: nil, firstName: "", lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: []) : nil, text: messageItem.text, attributes: messageItem.reply != nil ? [ReplyMessageAttribute(messageId: replyMessageId)] : [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])
- items.append(item.context.sharedContext.makeChatMessagePreviewItem(context: item.context, message: message, theme: item.componentTheme, strings: item.strings, wallpaper: item.wallpaper, fontSize: item.fontSize, chatBubbleCorners: item.chatBubbleCorners, dateTimeFormat: item.dateTimeFormat, nameOrder: item.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil))
+ items.append(item.context.sharedContext.makeChatMessagePreviewItem(context: item.context, messages: [message], theme: item.componentTheme, strings: item.strings, wallpaper: item.wallpaper, fontSize: item.fontSize, chatBubbleCorners: item.chatBubbleCorners, dateTimeFormat: item.dateTimeFormat, nameOrder: item.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil))
}
var nodes: [ListViewItemNode] = []
diff --git a/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift b/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift
index 932101a4f4..d489119662 100644
--- a/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift
+++ b/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift
@@ -840,28 +840,13 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
let theme = self.presentationData.theme.withUpdated(preview: true)
let message1 = Message(stableId: 2, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 2), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66001, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: bottomMessageText, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])
- items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: message1, theme: theme, strings: self.presentationData.strings, wallpaper: currentWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil))
+ items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message1], theme: theme, strings: self.presentationData.strings, wallpaper: currentWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil))
let message2 = Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66000, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: topMessageText, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])
- items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: message2, theme: theme, strings: self.presentationData.strings, wallpaper: currentWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil))
+ items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message2], theme: theme, strings: self.presentationData.strings, wallpaper: currentWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil))
let params = ListViewItemLayoutParams(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, availableHeight: layout.size.height)
- if let messageNodes = self.messageNodes {
-// for i in 0 ..< items.count {
-// let itemNode = messageNodes[i]
-// items[i].updateNode(async: { $0() }, node: {
-// return itemNode
-// }, params: params, previousItem: i == 0 ? nil : items[i - 1], nextItem: i == (items.count - 1) ? nil : items[i + 1], animation: .None, completion: { (layout, apply) in
-// let nodeFrame = CGRect(origin: itemNode.frame.origin, size: CGSize(width: layout.size.width, height: layout.size.height))
-//
-// itemNode.contentSize = layout.contentSize
-// itemNode.insets = layout.insets
-// itemNode.frame = nodeFrame
-// itemNode.isUserInteractionEnabled = false
-//
-// apply(ListViewItemApply(isOnScreen: true))
-// })
-// }
+ if let _ = self.messageNodes {
} else {
var messageNodes: [ListViewItemNode] = []
for i in 0 ..< items.count {
diff --git a/submodules/ShareController/Sources/ShareController.swift b/submodules/ShareController/Sources/ShareController.swift
index c87d6f1d32..907362c78b 100644
--- a/submodules/ShareController/Sources/ShareController.swift
+++ b/submodules/ShareController/Sources/ShareController.swift
@@ -593,6 +593,27 @@ public final class ShareController: ViewController {
}
self.controllerNode.shareExternal = { [weak self] in
if let strongSelf = self {
+ if case let .messages(messages) = strongSelf.subject, let message = messages.first, let peer = message.peers[message.id.peerId] {
+ let renderer = MessageStoryRenderer(context: strongSelf.currentContext, messages: messages)
+
+ let layout = ContainerViewLayout(size: CGSize(width: 414.0, height: 896.0), metrics: LayoutMetrics(widthClass: .compact, heightClass: .compact), deviceMetrics: .iPhoneX, intrinsicInsets: UIEdgeInsets(), safeInsets: UIEdgeInsets(), statusBarHeight: 0.0, inputHeight: nil, inputHeightIsInteractivellyChanging: false, inVoiceOver: false)
+ renderer.update(layout: layout) { image in
+ if let data = image?.pngData() {
+ let pasteboardItems: [[String: Any]] = [["com.instagram.sharedSticker.backgroundImage": data,
+ "com.instagram.sharedSticker.contentURL": "https://t.me/\(peer.addressName ?? "")/\(message.id.id)"]]
+ if #available(iOS 10.0, *) {
+ UIPasteboard.general.setItems(pasteboardItems, options: [.expirationDate: Date().addingTimeInterval(5 * 60)])
+ } else {
+// UIPasteboard.general.setItems(pasteboardItems)
+ }
+ strongSelf.sharedContext.applicationBindings.openUrl("instagram-stories://share")
+ }
+ }
+
+ return .complete()
+ }
+
+
var collectableItems: [CollectableExternalShareItem] = []
switch strongSelf.subject {
case let .url(text):
@@ -865,3 +886,132 @@ public final class ShareController: ViewController {
}))
}
}
+
+
+final class MessageStoryRenderer {
+ private let context: AccountContext
+ private let presentationData: PresentationData
+ private let messages: [Message]
+
+ let containerNode: ASDisplayNode
+ private let instantChatBackgroundNode: WallpaperBackgroundNode
+ private let messagesContainerNode: ASDisplayNode
+ private var dateHeaderNode: ListViewItemHeaderNode?
+ private var messageNodes: [ListViewItemNode]?
+ private let addressNode: ImmediateTextNode
+
+ init(context: AccountContext, messages: [Message]) {
+ self.context = context
+ self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
+ self.messages = messages
+
+ self.containerNode = ASDisplayNode()
+
+ self.instantChatBackgroundNode = WallpaperBackgroundNode()
+ self.instantChatBackgroundNode.displaysAsynchronously = false
+ self.instantChatBackgroundNode.image = chatControllerBackgroundImage(theme: self.presentationData.theme, wallpaper: self.presentationData.chatWallpaper, mediaBox: context.sharedContext.accountManager.mediaBox, knockoutMode: context.sharedContext.immediateExperimentalUISettings.knockoutWallpaper)
+
+ self.messagesContainerNode = ASDisplayNode()
+ self.messagesContainerNode.clipsToBounds = true
+ self.messagesContainerNode.transform = CATransform3DMakeScale(1.0, -1.0, 1.0)
+
+ let message = messages.first!
+ let addressName = message.peers[message.id.peerId]?.addressName ?? ""
+
+ self.addressNode = ImmediateTextNode()
+ self.addressNode.displaysAsynchronously = false
+ self.addressNode.attributedText = NSAttributedString(string: "t.me/\(addressName)/\(message.id.id)", font: Font.medium(14.0), textColor: UIColor(rgb: 0xffffff))
+ self.addressNode.textShadowColor = UIColor(rgb: 0x929292, alpha: 0.8)
+
+ self.containerNode.addSubnode(self.instantChatBackgroundNode)
+ self.containerNode.addSubnode(self.messagesContainerNode)
+ self.containerNode.addSubnode(self.addressNode)
+ }
+
+ func update(layout: ContainerViewLayout, completion: @escaping (UIImage?) -> Void) {
+ self.updateMessagesLayout(layout: layout)
+
+ Queue.mainQueue().after(0.01) {
+ UIGraphicsBeginImageContextWithOptions(layout.size, false, 3.0)
+ self.containerNode.view.drawHierarchy(in: CGRect(origin: CGPoint(), size: layout.size), afterScreenUpdates: true)
+ let img = UIGraphicsGetImageFromCurrentImageContext()
+ UIGraphicsEndImageContext()
+ completion(img)
+ }
+ }
+
+ private func updateMessagesLayout(layout: ContainerViewLayout) {
+ let size = layout.size
+ self.containerNode.frame = CGRect(origin: CGPoint(), size: layout.size)
+ self.instantChatBackgroundNode.frame = CGRect(origin: CGPoint(), size: layout.size)
+ self.instantChatBackgroundNode.updateLayout(size: size, transition: .immediate)
+ self.messagesContainerNode.frame = CGRect(origin: CGPoint(), size: layout.size)
+
+ let addressLayout = self.addressNode.updateLayout(size)
+
+ let theme = self.presentationData.theme.withUpdated(preview: true)
+ let headerItem = self.context.sharedContext.makeChatMessageDateHeaderItem(context: self.context, timestamp: self.messages.first?.timestamp ?? 0, theme: theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder)
+
+ let items: [ListViewItem] = [self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: self.messages, theme: theme, strings: self.presentationData.strings, wallpaper: self.presentationData.theme.chat.defaultWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil)]
+
+ let inset: CGFloat = 16.0
+ let width = layout.size.width - inset * 2.0
+ let params = ListViewItemLayoutParams(width: width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, availableHeight: layout.size.height)
+ if let messageNodes = self.messageNodes {
+ for i in 0 ..< items.count {
+ let itemNode = messageNodes[i]
+ items[i].updateNode(async: { $0() }, node: {
+ return itemNode
+ }, params: params, previousItem: i == 0 ? nil : items[i - 1], nextItem: i == (items.count - 1) ? nil : items[i + 1], animation: .None, completion: { (layout, apply) in
+ let nodeFrame = CGRect(origin: CGPoint(x: 0.0, y: floor((size.height - layout.size.height) / 2.0)), size: CGSize(width: width, height: layout.size.height))
+
+ itemNode.contentSize = layout.contentSize
+ itemNode.insets = layout.insets
+ itemNode.frame = nodeFrame
+ itemNode.isUserInteractionEnabled = false
+
+ apply(ListViewItemApply(isOnScreen: true))
+ })
+ }
+ } else {
+ var messageNodes: [ListViewItemNode] = []
+ for i in 0 ..< items.count {
+ var itemNode: ListViewItemNode?
+ items[i].nodeConfiguredForParams(async: { $0() }, params: params, synchronousLoads: true, previousItem: i == 0 ? nil : items[i - 1], nextItem: i == (items.count - 1) ? nil : items[i + 1], completion: { node, apply in
+ itemNode = node
+ apply().1(ListViewItemApply(isOnScreen: true))
+ })
+ itemNode!.subnodeTransform = CATransform3DMakeScale(-1.0, 1.0, 1.0)
+ itemNode!.isUserInteractionEnabled = false
+ messageNodes.append(itemNode!)
+ self.messagesContainerNode.addSubnode(itemNode!)
+ }
+ self.messageNodes = messageNodes
+ }
+
+ var bottomOffset: CGFloat = 0.0
+ if let messageNodes = self.messageNodes {
+ for itemNode in messageNodes {
+ itemNode.frame = CGRect(origin: CGPoint(x: inset, y: floor((size.height - itemNode.frame.height) / 2.0)), size: itemNode.frame.size)
+ bottomOffset += itemNode.frame.maxY
+ itemNode.updateFrame(itemNode.frame, within: layout.size)
+ }
+ }
+
+ self.addressNode.frame = CGRect(origin: CGPoint(x: inset + 16.0, y: bottomOffset + 3.0), size: CGSize(width: addressLayout.width, height: addressLayout.height + 3.0))
+
+ let dateHeaderNode: ListViewItemHeaderNode
+ if let currentDateHeaderNode = self.dateHeaderNode {
+ dateHeaderNode = currentDateHeaderNode
+ headerItem.updateNode(dateHeaderNode, previous: nil, next: headerItem)
+ } else {
+ dateHeaderNode = headerItem.node()
+ dateHeaderNode.subnodeTransform = CATransform3DMakeScale(-1.0, 1.0, 1.0)
+ self.messagesContainerNode.addSubnode(dateHeaderNode)
+ self.dateHeaderNode = dateHeaderNode
+ }
+
+ dateHeaderNode.frame = CGRect(origin: CGPoint(x: 0.0, y: bottomOffset), size: CGSize(width: layout.size.width, height: headerItem.height))
+ dateHeaderNode.updateLayout(size: self.containerNode.frame.size, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right)
+ }
+}
diff --git a/submodules/SyncCore/Sources/Namespaces.swift b/submodules/SyncCore/Sources/Namespaces.swift
index c678f7d3ab..6a7ddf90d5 100644
--- a/submodules/SyncCore/Sources/Namespaces.swift
+++ b/submodules/SyncCore/Sources/Namespaces.swift
@@ -96,8 +96,10 @@ public extension MessageTags {
static let unseenPersonalMessage = MessageTags(rawValue: 1 << 5)
static let liveLocation = MessageTags(rawValue: 1 << 6)
static let gif = MessageTags(rawValue: 1 << 7)
+ static let photo = MessageTags(rawValue: 1 << 8)
+ static let video = MessageTags(rawValue: 1 << 9)
- static let all: MessageTags = [.photoOrVideo, .file, .music, .webPage, .voiceOrInstantVideo, .unseenPersonalMessage, .liveLocation, .gif]
+ static let all: MessageTags = [.photoOrVideo, .file, .music, .webPage, .voiceOrInstantVideo, .unseenPersonalMessage, .liveLocation, .gif, .photo, .video]
}
public extension GlobalMessageTags {
@@ -348,6 +350,7 @@ private enum SharedDataKeyValues: Int32 {
case proxySettings = 4
case autodownloadSettings = 5
case themeSettings = 6
+ case countriesList = 7
}
public struct SharedDataKeys {
@@ -386,6 +389,12 @@ public struct SharedDataKeys {
key.setInt32(0, value: SharedDataKeyValues.themeSettings.rawValue)
return key
}()
+
+ public static let countriesList: ValueBoxKey = {
+ let key = ValueBoxKey(length: 4)
+ key.setInt32(0, value: SharedDataKeyValues.countriesList.rawValue)
+ return key
+ }()
}
public func applicationSpecificItemCacheCollectionId(_ value: Int8) -> Int8 {
diff --git a/submodules/SyncCore/Sources/ReplyMarkupMessageAttribute.swift b/submodules/SyncCore/Sources/ReplyMarkupMessageAttribute.swift
index c72fdced40..d1eb42c27c 100644
--- a/submodules/SyncCore/Sources/ReplyMarkupMessageAttribute.swift
+++ b/submodules/SyncCore/Sources/ReplyMarkupMessageAttribute.swift
@@ -3,7 +3,7 @@ import Postbox
public enum ReplyMarkupButtonAction: PostboxCoding, Equatable {
case text
case url(String)
- case callback(MemoryBuffer)
+ case callback(requiresPassword: Bool, data: MemoryBuffer)
case requestPhone
case requestMap
case switchInline(samePeer: Bool, query: String)
@@ -19,7 +19,7 @@ public enum ReplyMarkupButtonAction: PostboxCoding, Equatable {
case 1:
self = .url(decoder.decodeStringForKey("u", orElse: ""))
case 2:
- self = .callback(decoder.decodeBytesForKey("d") ?? MemoryBuffer())
+ self = .callback(requiresPassword: decoder.decodeInt32ForKey("p", orElse: 0) != 0, data: decoder.decodeBytesForKey("d") ?? MemoryBuffer())
case 3:
self = .requestPhone
case 4:
@@ -46,8 +46,9 @@ public enum ReplyMarkupButtonAction: PostboxCoding, Equatable {
case let .url(url):
encoder.encodeInt32(1, forKey: "v")
encoder.encodeString(url, forKey: "u")
- case let .callback(data):
+ case let .callback(requiresPassword, data):
encoder.encodeInt32(2, forKey: "v")
+ encoder.encodeInt32(requiresPassword ? 1 : 0, forKey: "p")
encoder.encodeBytes(data, forKey: "d")
case .requestPhone:
encoder.encodeInt32(3, forKey: "v")
diff --git a/submodules/TelegramAnimatedStickerNode/Sources/AnimatedStickerUtils.swift b/submodules/TelegramAnimatedStickerNode/Sources/AnimatedStickerUtils.swift
index 206574e171..d39c63244e 100644
--- a/submodules/TelegramAnimatedStickerNode/Sources/AnimatedStickerUtils.swift
+++ b/submodules/TelegramAnimatedStickerNode/Sources/AnimatedStickerUtils.swift
@@ -12,79 +12,7 @@ import MediaResources
import MobileCoreServices
import MediaResources
import YuvConversion
-
-let colorKeyRegex = try? NSRegularExpression(pattern: "\"k\":\\[[\\d\\.]+\\,[\\d\\.]+\\,[\\d\\.]+\\,[\\d\\.]+\\]")
-
-private func transformedWithFitzModifier(data: Data, fitzModifier: EmojiFitzModifier?) -> Data {
- if let fitzModifier = fitzModifier, var string = String(data: data, encoding: .utf8) {
- var colors: [UIColor] = [0xf77e41, 0xffb139, 0xffd140, 0xffdf79].map { UIColor(rgb: $0) }
- let replacementColors: [UIColor]
- switch fitzModifier {
- case .type12:
- replacementColors = [0xca907a, 0xedc5a5, 0xf7e3c3, 0xfbefd6].map { UIColor(rgb: $0) }
- case .type3:
- replacementColors = [0xaa7c60, 0xc8a987, 0xddc89f, 0xe6d6b2].map { UIColor(rgb: $0) }
- case .type4:
- replacementColors = [0x8c6148, 0xad8562, 0xc49e76, 0xd4b188].map { UIColor(rgb: $0) }
- case .type5:
- replacementColors = [0x6e3c2c, 0x925a34, 0xa16e46, 0xac7a52].map { UIColor(rgb: $0) }
- case .type6:
- replacementColors = [0x291c12, 0x472a22, 0x573b30, 0x68493c].map { UIColor(rgb: $0) }
- }
-
- func colorToString(_ color: UIColor) -> String {
- var r: CGFloat = 0.0
- var g: CGFloat = 0.0
- var b: CGFloat = 0.0
- if color.getRed(&r, green: &g, blue: &b, alpha: nil) {
- return "\"k\":[\(r),\(g),\(b),1]"
- }
- return ""
- }
-
- func match(_ a: Double, _ b: Double, eps: Double) -> Bool {
- return abs(a - b) < eps
- }
-
- var replacements: [(NSTextCheckingResult, String)] = []
-
- if let colorKeyRegex = colorKeyRegex {
- let results = colorKeyRegex.matches(in: string, range: NSRange(string.startIndex..., in: string))
- for result in results.reversed() {
- if let range = Range(result.range, in: string) {
- let substring = String(string[range])
- let color = substring[substring.index(string.startIndex, offsetBy: "\"k\":[".count) ..< substring.index(before: substring.endIndex)]
- let components = color.split(separator: ",")
- if components.count == 4, let r = Double(components[0]), let g = Double(components[1]), let b = Double(components[2]), let a = Double(components[3]) {
- if match(a, 1.0, eps: 0.01) {
- for i in 0 ..< colors.count {
- let color = colors[i]
- var cr: CGFloat = 0.0
- var cg: CGFloat = 0.0
- var cb: CGFloat = 0.0
- if color.getRed(&cr, green: &cg, blue: &cb, alpha: nil) {
- if match(r, Double(cr), eps: 0.01) && match(g, Double(cg), eps: 0.01) && match(b, Double(cb), eps: 0.01) {
- replacements.append((result, colorToString(replacementColors[i])))
- }
- }
- }
- }
- }
- }
- }
- }
-
- for (result, text) in replacements {
- if let range = Range(result.range, in: string) {
- string = string.replacingCharacters(in: range, with: text)
- }
- }
-
- return string.data(using: .utf8) ?? data
- } else {
- return data
- }
-}
+import AnimatedStickerNode
public func fetchCompressedLottieFirstFrameAJpeg(data: Data, size: CGSize, fitzModifier: EmojiFitzModifier? = nil, cacheKey: String) -> Signal {
return Signal({ subscriber in
diff --git a/submodules/TelegramApi/Sources/Api0.swift b/submodules/TelegramApi/Sources/Api0.swift
index f36922f701..9d6c66583c 100644
--- a/submodules/TelegramApi/Sources/Api0.swift
+++ b/submodules/TelegramApi/Sources/Api0.swift
@@ -255,6 +255,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[889491791] = { return Api.Update.parse_updateDialogFilters($0) }
dict[643940105] = { return Api.Update.parse_updatePhoneCallSignalingData($0) }
dict[1708307556] = { return Api.Update.parse_updateChannelParticipant($0) }
+ dict[1854571743] = { return Api.Update.parse_updateChannelMessageForwards($0) }
dict[136574537] = { return Api.messages.VotesList.parse_votesList($0) }
dict[1558266229] = { return Api.PopularContact.parse_popularContact($0) }
dict[-373643672] = { return Api.FolderPeer.parse_folderPeer($0) }
@@ -273,7 +274,6 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-994444869] = { return Api.Error.parse_error($0) }
dict[-1560655744] = { return Api.KeyboardButton.parse_keyboardButton($0) }
dict[629866245] = { return Api.KeyboardButton.parse_keyboardButtonUrl($0) }
- dict[1748655686] = { return Api.KeyboardButton.parse_keyboardButtonCallback($0) }
dict[-1318425559] = { return Api.KeyboardButton.parse_keyboardButtonRequestPhone($0) }
dict[-59151553] = { return Api.KeyboardButton.parse_keyboardButtonRequestGeoLocation($0) }
dict[90744648] = { return Api.KeyboardButton.parse_keyboardButtonSwitchInline($0) }
@@ -282,6 +282,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[280464681] = { return Api.KeyboardButton.parse_keyboardButtonUrlAuth($0) }
dict[-802258988] = { return Api.KeyboardButton.parse_inputKeyboardButtonUrlAuth($0) }
dict[-1144565411] = { return Api.KeyboardButton.parse_keyboardButtonRequestPoll($0) }
+ dict[901503851] = { return Api.KeyboardButton.parse_keyboardButtonCallback($0) }
dict[-748155807] = { return Api.ContactStatus.parse_contactStatus($0) }
dict[1679398724] = { return Api.SecureFile.parse_secureFileEmpty($0) }
dict[-534283678] = { return Api.SecureFile.parse_secureFile($0) }
@@ -387,6 +388,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[1694474197] = { return Api.messages.Chats.parse_chats($0) }
dict[-1663561404] = { return Api.messages.Chats.parse_chatsSlice($0) }
dict[482797855] = { return Api.InputSingleMedia.parse_inputSingleMedia($0) }
+ dict[1831138451] = { return Api.MessageViews.parse_messageViews($0) }
dict[218751099] = { return Api.InputPrivacyRule.parse_inputPrivacyValueAllowContacts($0) }
dict[407582158] = { return Api.InputPrivacyRule.parse_inputPrivacyValueAllowAll($0) }
dict[320652927] = { return Api.InputPrivacyRule.parse_inputPrivacyValueAllowUsers($0) }
@@ -447,6 +449,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-524237339] = { return Api.PageTableRow.parse_pageTableRow($0) }
dict[-40996577] = { return Api.DraftMessage.parse_draftMessage($0) }
dict[453805082] = { return Api.DraftMessage.parse_draftMessageEmpty($0) }
+ dict[-1014526429] = { return Api.help.Country.parse_country($0) }
dict[418631927] = { return Api.StatsGroupTopPoster.parse_statsGroupTopPoster($0) }
dict[-2128640689] = { return Api.account.SentEmailCode.parse_sentEmailCode($0) }
dict[-1038136962] = { return Api.EncryptedFile.parse_encryptedFileEmpty($0) }
@@ -519,6 +522,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-914167110] = { return Api.CdnPublicKey.parse_cdnPublicKey($0) }
dict[53231223] = { return Api.InputGame.parse_inputGameID($0) }
dict[-1020139510] = { return Api.InputGame.parse_inputGameShortName($0) }
+ dict[1107543535] = { return Api.help.CountryCode.parse_countryCode($0) }
dict[-1502174430] = { return Api.InputMessage.parse_inputMessageID($0) }
dict[-1160215659] = { return Api.InputMessage.parse_inputMessageReplyTo($0) }
dict[-2037963464] = { return Api.InputMessage.parse_inputMessagePinned($0) }
@@ -598,7 +602,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-1820043071] = { return Api.User.parse_user($0) }
dict[-2082087340] = { return Api.Message.parse_messageEmpty($0) }
dict[-1642487306] = { return Api.Message.parse_messageService($0) }
- dict[1160515173] = { return Api.Message.parse_message($0) }
+ dict[-181507201] = { return Api.Message.parse_message($0) }
dict[831924812] = { return Api.StatsGroupTopInviter.parse_statsGroupTopInviter($0) }
dict[186120336] = { return Api.messages.RecentStickers.parse_recentStickersNotModified($0) }
dict[586395571] = { return Api.messages.RecentStickers.parse_recentStickers($0) }
@@ -683,6 +687,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[364538944] = { return Api.messages.Dialogs.parse_dialogs($0) }
dict[1910543603] = { return Api.messages.Dialogs.parse_dialogsSlice($0) }
dict[-253500010] = { return Api.messages.Dialogs.parse_dialogsNotModified($0) }
+ dict[-1986399595] = { return Api.stats.MessageStats.parse_messageStats($0) }
dict[-709641735] = { return Api.EmojiKeyword.parse_emojiKeyword($0) }
dict[594408994] = { return Api.EmojiKeyword.parse_emojiKeywordDeleted($0) }
dict[-290921362] = { return Api.upload.CdnFile.parse_cdnFileReuploadNeeded($0) }
@@ -789,6 +794,8 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[1041346555] = { return Api.updates.ChannelDifference.parse_channelDifferenceEmpty($0) }
dict[543450958] = { return Api.updates.ChannelDifference.parse_channelDifference($0) }
dict[-1531132162] = { return Api.updates.ChannelDifference.parse_channelDifferenceTooLong($0) }
+ dict[-1815339214] = { return Api.help.CountriesList.parse_countriesListNotModified($0) }
+ dict[-2016381538] = { return Api.help.CountriesList.parse_countriesList($0) }
dict[-309659827] = { return Api.channels.AdminLogResults.parse_adminLogResults($0) }
dict[-264117680] = { return Api.ChatOnlines.parse_chatOnlines($0) }
dict[488313413] = { return Api.InputAppEvent.parse_inputAppEvent($0) }
@@ -1106,6 +1113,8 @@ public struct Api {
_1.serialize(buffer, boxed)
case let _1 as Api.InputSingleMedia:
_1.serialize(buffer, boxed)
+ case let _1 as Api.MessageViews:
+ _1.serialize(buffer, boxed)
case let _1 as Api.InputPrivacyRule:
_1.serialize(buffer, boxed)
case let _1 as Api.messages.DhConfig:
@@ -1148,6 +1157,8 @@ public struct Api {
_1.serialize(buffer, boxed)
case let _1 as Api.DraftMessage:
_1.serialize(buffer, boxed)
+ case let _1 as Api.help.Country:
+ _1.serialize(buffer, boxed)
case let _1 as Api.StatsGroupTopPoster:
_1.serialize(buffer, boxed)
case let _1 as Api.account.SentEmailCode:
@@ -1226,6 +1237,8 @@ public struct Api {
_1.serialize(buffer, boxed)
case let _1 as Api.InputGame:
_1.serialize(buffer, boxed)
+ case let _1 as Api.help.CountryCode:
+ _1.serialize(buffer, boxed)
case let _1 as Api.InputMessage:
_1.serialize(buffer, boxed)
case let _1 as Api.PhoneCallProtocol:
@@ -1366,6 +1379,8 @@ public struct Api {
_1.serialize(buffer, boxed)
case let _1 as Api.messages.Dialogs:
_1.serialize(buffer, boxed)
+ case let _1 as Api.stats.MessageStats:
+ _1.serialize(buffer, boxed)
case let _1 as Api.EmojiKeyword:
_1.serialize(buffer, boxed)
case let _1 as Api.upload.CdnFile:
@@ -1442,6 +1457,8 @@ public struct Api {
_1.serialize(buffer, boxed)
case let _1 as Api.updates.ChannelDifference:
_1.serialize(buffer, boxed)
+ case let _1 as Api.help.CountriesList:
+ _1.serialize(buffer, boxed)
case let _1 as Api.channels.AdminLogResults:
_1.serialize(buffer, boxed)
case let _1 as Api.ChatOnlines:
diff --git a/submodules/TelegramApi/Sources/Api1.swift b/submodules/TelegramApi/Sources/Api1.swift
index 025d00a4aa..5a30fc3b91 100644
--- a/submodules/TelegramApi/Sources/Api1.swift
+++ b/submodules/TelegramApi/Sources/Api1.swift
@@ -6038,6 +6038,7 @@ public extension Api {
case updateDialogFilters
case updatePhoneCallSignalingData(phoneCallId: Int64, data: Buffer)
case updateChannelParticipant(flags: Int32, channelId: Int32, date: Int32, userId: Int32, prevParticipant: Api.ChannelParticipant?, newParticipant: Api.ChannelParticipant?, qts: Int32)
+ case updateChannelMessageForwards(channelId: Int32, id: Int32, forwards: Int32)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
@@ -6730,6 +6731,14 @@ public extension Api {
if Int(flags) & Int(1 << 1) != 0 {newParticipant!.serialize(buffer, true)}
serializeInt32(qts, buffer: buffer, boxed: false)
break
+ case .updateChannelMessageForwards(let channelId, let id, let forwards):
+ if boxed {
+ buffer.appendInt32(1854571743)
+ }
+ serializeInt32(channelId, buffer: buffer, boxed: false)
+ serializeInt32(id, buffer: buffer, boxed: false)
+ serializeInt32(forwards, buffer: buffer, boxed: false)
+ break
}
}
@@ -6899,6 +6908,8 @@ public extension Api {
return ("updatePhoneCallSignalingData", [("phoneCallId", phoneCallId), ("data", data)])
case .updateChannelParticipant(let flags, let channelId, let date, let userId, let prevParticipant, let newParticipant, let qts):
return ("updateChannelParticipant", [("flags", flags), ("channelId", channelId), ("date", date), ("userId", userId), ("prevParticipant", prevParticipant), ("newParticipant", newParticipant), ("qts", qts)])
+ case .updateChannelMessageForwards(let channelId, let id, let forwards):
+ return ("updateChannelMessageForwards", [("channelId", channelId), ("id", id), ("forwards", forwards)])
}
}
@@ -8277,6 +8288,23 @@ public extension Api {
return nil
}
}
+ public static func parse_updateChannelMessageForwards(_ reader: BufferReader) -> Update? {
+ var _1: Int32?
+ _1 = reader.readInt32()
+ var _2: Int32?
+ _2 = reader.readInt32()
+ var _3: Int32?
+ _3 = reader.readInt32()
+ let _c1 = _1 != nil
+ let _c2 = _2 != nil
+ let _c3 = _3 != nil
+ if _c1 && _c2 && _c3 {
+ return Api.Update.updateChannelMessageForwards(channelId: _1!, id: _2!, forwards: _3!)
+ }
+ else {
+ return nil
+ }
+ }
}
public enum PopularContact: TypeConstructorDescription {
@@ -8732,7 +8760,6 @@ public extension Api {
public enum KeyboardButton: TypeConstructorDescription {
case keyboardButton(text: String)
case keyboardButtonUrl(text: String, url: String)
- case keyboardButtonCallback(text: String, data: Buffer)
case keyboardButtonRequestPhone(text: String)
case keyboardButtonRequestGeoLocation(text: String)
case keyboardButtonSwitchInline(flags: Int32, text: String, query: String)
@@ -8741,6 +8768,7 @@ public extension Api {
case keyboardButtonUrlAuth(flags: Int32, text: String, fwdText: String?, url: String, buttonId: Int32)
case inputKeyboardButtonUrlAuth(flags: Int32, text: String, fwdText: String?, url: String, bot: Api.InputUser)
case keyboardButtonRequestPoll(flags: Int32, quiz: Api.Bool?, text: String)
+ case keyboardButtonCallback(flags: Int32, text: String, data: Buffer)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
@@ -8757,13 +8785,6 @@ public extension Api {
serializeString(text, buffer: buffer, boxed: false)
serializeString(url, buffer: buffer, boxed: false)
break
- case .keyboardButtonCallback(let text, let data):
- if boxed {
- buffer.appendInt32(1748655686)
- }
- serializeString(text, buffer: buffer, boxed: false)
- serializeBytes(data, buffer: buffer, boxed: false)
- break
case .keyboardButtonRequestPhone(let text):
if boxed {
buffer.appendInt32(-1318425559)
@@ -8824,6 +8845,14 @@ public extension Api {
if Int(flags) & Int(1 << 0) != 0 {quiz!.serialize(buffer, true)}
serializeString(text, buffer: buffer, boxed: false)
break
+ case .keyboardButtonCallback(let flags, let text, let data):
+ if boxed {
+ buffer.appendInt32(901503851)
+ }
+ serializeInt32(flags, buffer: buffer, boxed: false)
+ serializeString(text, buffer: buffer, boxed: false)
+ serializeBytes(data, buffer: buffer, boxed: false)
+ break
}
}
@@ -8833,8 +8862,6 @@ public extension Api {
return ("keyboardButton", [("text", text)])
case .keyboardButtonUrl(let text, let url):
return ("keyboardButtonUrl", [("text", text), ("url", url)])
- case .keyboardButtonCallback(let text, let data):
- return ("keyboardButtonCallback", [("text", text), ("data", data)])
case .keyboardButtonRequestPhone(let text):
return ("keyboardButtonRequestPhone", [("text", text)])
case .keyboardButtonRequestGeoLocation(let text):
@@ -8851,6 +8878,8 @@ public extension Api {
return ("inputKeyboardButtonUrlAuth", [("flags", flags), ("text", text), ("fwdText", fwdText), ("url", url), ("bot", bot)])
case .keyboardButtonRequestPoll(let flags, let quiz, let text):
return ("keyboardButtonRequestPoll", [("flags", flags), ("quiz", quiz), ("text", text)])
+ case .keyboardButtonCallback(let flags, let text, let data):
+ return ("keyboardButtonCallback", [("flags", flags), ("text", text), ("data", data)])
}
}
@@ -8879,20 +8908,6 @@ public extension Api {
return nil
}
}
- public static func parse_keyboardButtonCallback(_ reader: BufferReader) -> KeyboardButton? {
- var _1: String?
- _1 = parseString(reader)
- var _2: Buffer?
- _2 = parseBytes(reader)
- let _c1 = _1 != nil
- let _c2 = _2 != nil
- if _c1 && _c2 {
- return Api.KeyboardButton.keyboardButtonCallback(text: _1!, data: _2!)
- }
- else {
- return nil
- }
- }
public static func parse_keyboardButtonRequestPhone(_ reader: BufferReader) -> KeyboardButton? {
var _1: String?
_1 = parseString(reader)
@@ -9021,6 +9036,23 @@ public extension Api {
return nil
}
}
+ public static func parse_keyboardButtonCallback(_ reader: BufferReader) -> KeyboardButton? {
+ var _1: Int32?
+ _1 = reader.readInt32()
+ var _2: String?
+ _2 = parseString(reader)
+ var _3: Buffer?
+ _3 = parseBytes(reader)
+ let _c1 = _1 != nil
+ let _c2 = _2 != nil
+ let _c3 = _3 != nil
+ if _c1 && _c2 && _c3 {
+ return Api.KeyboardButton.keyboardButtonCallback(flags: _1!, text: _2!, data: _3!)
+ }
+ else {
+ return nil
+ }
+ }
}
public enum ContactStatus: TypeConstructorDescription {
@@ -11718,6 +11750,44 @@ public extension Api {
}
}
+ }
+ public enum MessageViews: TypeConstructorDescription {
+ case messageViews(views: Int32, forwards: Int32)
+
+ public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
+ switch self {
+ case .messageViews(let views, let forwards):
+ if boxed {
+ buffer.appendInt32(1831138451)
+ }
+ serializeInt32(views, buffer: buffer, boxed: false)
+ serializeInt32(forwards, buffer: buffer, boxed: false)
+ break
+ }
+ }
+
+ public func descriptionFields() -> (String, [(String, Any)]) {
+ switch self {
+ case .messageViews(let views, let forwards):
+ return ("messageViews", [("views", views), ("forwards", forwards)])
+ }
+ }
+
+ public static func parse_messageViews(_ reader: BufferReader) -> MessageViews? {
+ var _1: Int32?
+ _1 = reader.readInt32()
+ var _2: Int32?
+ _2 = reader.readInt32()
+ let _c1 = _1 != nil
+ let _c2 = _2 != nil
+ if _c1 && _c2 {
+ return Api.MessageViews.messageViews(views: _1!, forwards: _2!)
+ }
+ else {
+ return nil
+ }
+ }
+
}
public enum InputPrivacyRule: TypeConstructorDescription {
case inputPrivacyValueAllowContacts
@@ -17092,7 +17162,7 @@ public extension Api {
public enum Message: TypeConstructorDescription {
case messageEmpty(id: Int32)
case messageService(flags: Int32, id: Int32, fromId: Int32?, toId: Api.Peer, replyToMsgId: Int32?, date: Int32, action: Api.MessageAction)
- case message(flags: Int32, id: Int32, fromId: Int32?, toId: Api.Peer, fwdFrom: Api.MessageFwdHeader?, viaBotId: Int32?, replyToMsgId: Int32?, date: Int32, message: String, media: Api.MessageMedia?, replyMarkup: Api.ReplyMarkup?, entities: [Api.MessageEntity]?, views: Int32?, editDate: Int32?, postAuthor: String?, groupedId: Int64?, restrictionReason: [Api.RestrictionReason]?)
+ case message(flags: Int32, id: Int32, fromId: Int32?, toId: Api.Peer, fwdFrom: Api.MessageFwdHeader?, viaBotId: Int32?, replyToMsgId: Int32?, date: Int32, message: String, media: Api.MessageMedia?, replyMarkup: Api.ReplyMarkup?, entities: [Api.MessageEntity]?, views: Int32?, forwards: Int32?, editDate: Int32?, postAuthor: String?, groupedId: Int64?, restrictionReason: [Api.RestrictionReason]?)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
@@ -17114,9 +17184,9 @@ public extension Api {
serializeInt32(date, buffer: buffer, boxed: false)
action.serialize(buffer, true)
break
- case .message(let flags, let id, let fromId, let toId, let fwdFrom, let viaBotId, let replyToMsgId, let date, let message, let media, let replyMarkup, let entities, let views, let editDate, let postAuthor, let groupedId, let restrictionReason):
+ case .message(let flags, let id, let fromId, let toId, let fwdFrom, let viaBotId, let replyToMsgId, let date, let message, let media, let replyMarkup, let entities, let views, let forwards, let editDate, let postAuthor, let groupedId, let restrictionReason):
if boxed {
- buffer.appendInt32(1160515173)
+ buffer.appendInt32(-181507201)
}
serializeInt32(flags, buffer: buffer, boxed: false)
serializeInt32(id, buffer: buffer, boxed: false)
@@ -17135,6 +17205,7 @@ public extension Api {
item.serialize(buffer, true)
}}
if Int(flags) & Int(1 << 10) != 0 {serializeInt32(views!, buffer: buffer, boxed: false)}
+ if Int(flags) & Int(1 << 10) != 0 {serializeInt32(forwards!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 15) != 0 {serializeInt32(editDate!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 16) != 0 {serializeString(postAuthor!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 17) != 0 {serializeInt64(groupedId!, buffer: buffer, boxed: false)}
@@ -17153,8 +17224,8 @@ public extension Api {
return ("messageEmpty", [("id", id)])
case .messageService(let flags, let id, let fromId, let toId, let replyToMsgId, let date, let action):
return ("messageService", [("flags", flags), ("id", id), ("fromId", fromId), ("toId", toId), ("replyToMsgId", replyToMsgId), ("date", date), ("action", action)])
- case .message(let flags, let id, let fromId, let toId, let fwdFrom, let viaBotId, let replyToMsgId, let date, let message, let media, let replyMarkup, let entities, let views, let editDate, let postAuthor, let groupedId, let restrictionReason):
- return ("message", [("flags", flags), ("id", id), ("fromId", fromId), ("toId", toId), ("fwdFrom", fwdFrom), ("viaBotId", viaBotId), ("replyToMsgId", replyToMsgId), ("date", date), ("message", message), ("media", media), ("replyMarkup", replyMarkup), ("entities", entities), ("views", views), ("editDate", editDate), ("postAuthor", postAuthor), ("groupedId", groupedId), ("restrictionReason", restrictionReason)])
+ case .message(let flags, let id, let fromId, let toId, let fwdFrom, let viaBotId, let replyToMsgId, let date, let message, let media, let replyMarkup, let entities, let views, let forwards, let editDate, let postAuthor, let groupedId, let restrictionReason):
+ return ("message", [("flags", flags), ("id", id), ("fromId", fromId), ("toId", toId), ("fwdFrom", fwdFrom), ("viaBotId", viaBotId), ("replyToMsgId", replyToMsgId), ("date", date), ("message", message), ("media", media), ("replyMarkup", replyMarkup), ("entities", entities), ("views", views), ("forwards", forwards), ("editDate", editDate), ("postAuthor", postAuthor), ("groupedId", groupedId), ("restrictionReason", restrictionReason)])
}
}
@@ -17240,14 +17311,16 @@ public extension Api {
var _13: Int32?
if Int(_1!) & Int(1 << 10) != 0 {_13 = reader.readInt32() }
var _14: Int32?
- if Int(_1!) & Int(1 << 15) != 0 {_14 = reader.readInt32() }
- var _15: String?
- if Int(_1!) & Int(1 << 16) != 0 {_15 = parseString(reader) }
- var _16: Int64?
- if Int(_1!) & Int(1 << 17) != 0 {_16 = reader.readInt64() }
- var _17: [Api.RestrictionReason]?
+ if Int(_1!) & Int(1 << 10) != 0 {_14 = reader.readInt32() }
+ var _15: Int32?
+ if Int(_1!) & Int(1 << 15) != 0 {_15 = reader.readInt32() }
+ var _16: String?
+ if Int(_1!) & Int(1 << 16) != 0 {_16 = parseString(reader) }
+ var _17: Int64?
+ if Int(_1!) & Int(1 << 17) != 0 {_17 = reader.readInt64() }
+ var _18: [Api.RestrictionReason]?
if Int(_1!) & Int(1 << 22) != 0 {if let _ = reader.readInt32() {
- _17 = Api.parseVector(reader, elementSignature: 0, elementType: Api.RestrictionReason.self)
+ _18 = Api.parseVector(reader, elementSignature: 0, elementType: Api.RestrictionReason.self)
} }
let _c1 = _1 != nil
let _c2 = _2 != nil
@@ -17262,12 +17335,13 @@ public extension Api {
let _c11 = (Int(_1!) & Int(1 << 6) == 0) || _11 != nil
let _c12 = (Int(_1!) & Int(1 << 7) == 0) || _12 != nil
let _c13 = (Int(_1!) & Int(1 << 10) == 0) || _13 != nil
- let _c14 = (Int(_1!) & Int(1 << 15) == 0) || _14 != nil
- let _c15 = (Int(_1!) & Int(1 << 16) == 0) || _15 != nil
- let _c16 = (Int(_1!) & Int(1 << 17) == 0) || _16 != nil
- let _c17 = (Int(_1!) & Int(1 << 22) == 0) || _17 != nil
- if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 {
- return Api.Message.message(flags: _1!, id: _2!, fromId: _3, toId: _4!, fwdFrom: _5, viaBotId: _6, replyToMsgId: _7, date: _8!, message: _9!, media: _10, replyMarkup: _11, entities: _12, views: _13, editDate: _14, postAuthor: _15, groupedId: _16, restrictionReason: _17)
+ let _c14 = (Int(_1!) & Int(1 << 10) == 0) || _14 != nil
+ let _c15 = (Int(_1!) & Int(1 << 15) == 0) || _15 != nil
+ let _c16 = (Int(_1!) & Int(1 << 16) == 0) || _16 != nil
+ let _c17 = (Int(_1!) & Int(1 << 17) == 0) || _17 != nil
+ let _c18 = (Int(_1!) & Int(1 << 22) == 0) || _18 != nil
+ if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 {
+ return Api.Message.message(flags: _1!, id: _2!, fromId: _3, toId: _4!, fwdFrom: _5, viaBotId: _6, replyToMsgId: _7, date: _8!, message: _9!, media: _10, replyMarkup: _11, entities: _12, views: _13, forwards: _14, editDate: _15, postAuthor: _16, groupedId: _17, restrictionReason: _18)
}
else {
return nil
diff --git a/submodules/TelegramApi/Sources/Api2.swift b/submodules/TelegramApi/Sources/Api2.swift
index c24dccef69..a47b7c7b83 100644
--- a/submodules/TelegramApi/Sources/Api2.swift
+++ b/submodules/TelegramApi/Sources/Api2.swift
@@ -810,6 +810,42 @@ public struct stats {
}
}
+ public enum MessageStats: TypeConstructorDescription {
+ case messageStats(viewsGraph: Api.StatsGraph)
+
+ public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
+ switch self {
+ case .messageStats(let viewsGraph):
+ if boxed {
+ buffer.appendInt32(-1986399595)
+ }
+ viewsGraph.serialize(buffer, true)
+ break
+ }
+ }
+
+ public func descriptionFields() -> (String, [(String, Any)]) {
+ switch self {
+ case .messageStats(let viewsGraph):
+ return ("messageStats", [("viewsGraph", viewsGraph)])
+ }
+ }
+
+ public static func parse_messageStats(_ reader: BufferReader) -> MessageStats? {
+ var _1: Api.StatsGraph?
+ if let signature = reader.readInt32() {
+ _1 = Api.parse(reader, signature: signature) as? Api.StatsGraph
+ }
+ let _c1 = _1 != nil
+ if _c1 {
+ return Api.stats.MessageStats.messageStats(viewsGraph: _1!)
+ }
+ else {
+ return nil
+ }
+ }
+
+ }
}
}
public extension Api {
@@ -1965,6 +2001,62 @@ public struct help {
}
}
+ }
+ public enum Country: TypeConstructorDescription {
+ case country(flags: Int32, iso2: String, defaultName: String, name: String?, countryCodes: [Api.help.CountryCode])
+
+ public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
+ switch self {
+ case .country(let flags, let iso2, let defaultName, let name, let countryCodes):
+ if boxed {
+ buffer.appendInt32(-1014526429)
+ }
+ serializeInt32(flags, buffer: buffer, boxed: false)
+ serializeString(iso2, buffer: buffer, boxed: false)
+ serializeString(defaultName, buffer: buffer, boxed: false)
+ if Int(flags) & Int(1 << 1) != 0 {serializeString(name!, buffer: buffer, boxed: false)}
+ buffer.appendInt32(481674261)
+ buffer.appendInt32(Int32(countryCodes.count))
+ for item in countryCodes {
+ item.serialize(buffer, true)
+ }
+ break
+ }
+ }
+
+ public func descriptionFields() -> (String, [(String, Any)]) {
+ switch self {
+ case .country(let flags, let iso2, let defaultName, let name, let countryCodes):
+ return ("country", [("flags", flags), ("iso2", iso2), ("defaultName", defaultName), ("name", name), ("countryCodes", countryCodes)])
+ }
+ }
+
+ public static func parse_country(_ reader: BufferReader) -> Country? {
+ var _1: Int32?
+ _1 = reader.readInt32()
+ var _2: String?
+ _2 = parseString(reader)
+ var _3: String?
+ _3 = parseString(reader)
+ var _4: String?
+ if Int(_1!) & Int(1 << 1) != 0 {_4 = parseString(reader) }
+ var _5: [Api.help.CountryCode]?
+ if let _ = reader.readInt32() {
+ _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.help.CountryCode.self)
+ }
+ let _c1 = _1 != nil
+ let _c2 = _2 != nil
+ let _c3 = _3 != nil
+ let _c4 = (Int(_1!) & Int(1 << 1) == 0) || _4 != nil
+ let _c5 = _5 != nil
+ if _c1 && _c2 && _c3 && _c4 && _c5 {
+ return Api.help.Country.country(flags: _1!, iso2: _2!, defaultName: _3!, name: _4, countryCodes: _5!)
+ }
+ else {
+ return nil
+ }
+ }
+
}
public enum PromoData: TypeConstructorDescription {
case promoDataEmpty(expires: Int32)
@@ -2117,6 +2209,64 @@ public struct help {
}
}
+ }
+ public enum CountryCode: TypeConstructorDescription {
+ case countryCode(flags: Int32, countryCode: String, prefixes: [String]?, patterns: [String]?)
+
+ public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
+ switch self {
+ case .countryCode(let flags, let countryCode, let prefixes, let patterns):
+ if boxed {
+ buffer.appendInt32(1107543535)
+ }
+ serializeInt32(flags, buffer: buffer, boxed: false)
+ serializeString(countryCode, buffer: buffer, boxed: false)
+ if Int(flags) & Int(1 << 0) != 0 {buffer.appendInt32(481674261)
+ buffer.appendInt32(Int32(prefixes!.count))
+ for item in prefixes! {
+ serializeString(item, buffer: buffer, boxed: false)
+ }}
+ if Int(flags) & Int(1 << 1) != 0 {buffer.appendInt32(481674261)
+ buffer.appendInt32(Int32(patterns!.count))
+ for item in patterns! {
+ serializeString(item, buffer: buffer, boxed: false)
+ }}
+ break
+ }
+ }
+
+ public func descriptionFields() -> (String, [(String, Any)]) {
+ switch self {
+ case .countryCode(let flags, let countryCode, let prefixes, let patterns):
+ return ("countryCode", [("flags", flags), ("countryCode", countryCode), ("prefixes", prefixes), ("patterns", patterns)])
+ }
+ }
+
+ public static func parse_countryCode(_ reader: BufferReader) -> CountryCode? {
+ var _1: Int32?
+ _1 = reader.readInt32()
+ var _2: String?
+ _2 = parseString(reader)
+ var _3: [String]?
+ if Int(_1!) & Int(1 << 0) != 0 {if let _ = reader.readInt32() {
+ _3 = Api.parseVector(reader, elementSignature: -1255641564, elementType: String.self)
+ } }
+ var _4: [String]?
+ if Int(_1!) & Int(1 << 1) != 0 {if let _ = reader.readInt32() {
+ _4 = Api.parseVector(reader, elementSignature: -1255641564, elementType: String.self)
+ } }
+ let _c1 = _1 != nil
+ let _c2 = _2 != nil
+ let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil
+ let _c4 = (Int(_1!) & Int(1 << 1) == 0) || _4 != nil
+ if _c1 && _c2 && _c3 && _c4 {
+ return Api.help.CountryCode.countryCode(flags: _1!, countryCode: _2!, prefixes: _3, patterns: _4)
+ }
+ else {
+ return nil
+ }
+ }
+
}
public enum Support: TypeConstructorDescription {
case support(phoneNumber: String, user: Api.User)
@@ -2316,5 +2466,61 @@ public struct help {
}
}
+ public enum CountriesList: TypeConstructorDescription {
+ case countriesListNotModified
+ case countriesList(countries: [Api.help.Country], hash: Int32)
+
+ public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
+ switch self {
+ case .countriesListNotModified:
+ if boxed {
+ buffer.appendInt32(-1815339214)
+ }
+
+ break
+ case .countriesList(let countries, let hash):
+ if boxed {
+ buffer.appendInt32(-2016381538)
+ }
+ buffer.appendInt32(481674261)
+ buffer.appendInt32(Int32(countries.count))
+ for item in countries {
+ item.serialize(buffer, true)
+ }
+ serializeInt32(hash, buffer: buffer, boxed: false)
+ break
+ }
+ }
+
+ public func descriptionFields() -> (String, [(String, Any)]) {
+ switch self {
+ case .countriesListNotModified:
+ return ("countriesListNotModified", [])
+ case .countriesList(let countries, let hash):
+ return ("countriesList", [("countries", countries), ("hash", hash)])
+ }
+ }
+
+ public static func parse_countriesListNotModified(_ reader: BufferReader) -> CountriesList? {
+ return Api.help.CountriesList.countriesListNotModified
+ }
+ public static func parse_countriesList(_ reader: BufferReader) -> CountriesList? {
+ var _1: [Api.help.Country]?
+ if let _ = reader.readInt32() {
+ _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.help.Country.self)
+ }
+ var _2: Int32?
+ _2 = reader.readInt32()
+ let _c1 = _1 != nil
+ let _c2 = _2 != nil
+ if _c1 && _c2 {
+ return Api.help.CountriesList.countriesList(countries: _1!, hash: _2!)
+ }
+ else {
+ return nil
+ }
+ }
+
+ }
}
}
diff --git a/submodules/TelegramApi/Sources/Api3.swift b/submodules/TelegramApi/Sources/Api3.swift
index c79744cf01..67999cfce8 100644
--- a/submodules/TelegramApi/Sources/Api3.swift
+++ b/submodules/TelegramApi/Sources/Api3.swift
@@ -2046,39 +2046,6 @@ public extension Api {
})
}
- public static func sendEncrypted(peer: Api.InputEncryptedChat, randomId: Int64, data: Buffer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) {
- let buffer = Buffer()
- buffer.appendInt32(-1451792525)
- peer.serialize(buffer, true)
- serializeInt64(randomId, buffer: buffer, boxed: false)
- serializeBytes(data, buffer: buffer, boxed: false)
- return (FunctionDescription(name: "messages.sendEncrypted", parameters: [("peer", peer), ("randomId", randomId), ("data", data)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.SentEncryptedMessage? in
- let reader = BufferReader(buffer)
- var result: Api.messages.SentEncryptedMessage?
- if let signature = reader.readInt32() {
- result = Api.parse(reader, signature: signature) as? Api.messages.SentEncryptedMessage
- }
- return result
- })
- }
-
- public static func sendEncryptedFile(peer: Api.InputEncryptedChat, randomId: Int64, data: Buffer, file: Api.InputEncryptedFile) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) {
- let buffer = Buffer()
- buffer.appendInt32(-1701831834)
- peer.serialize(buffer, true)
- serializeInt64(randomId, buffer: buffer, boxed: false)
- serializeBytes(data, buffer: buffer, boxed: false)
- file.serialize(buffer, true)
- return (FunctionDescription(name: "messages.sendEncryptedFile", parameters: [("peer", peer), ("randomId", randomId), ("data", data), ("file", file)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.SentEncryptedMessage? in
- let reader = BufferReader(buffer)
- var result: Api.messages.SentEncryptedMessage?
- if let signature = reader.readInt32() {
- result = Api.parse(reader, signature: signature) as? Api.messages.SentEncryptedMessage
- }
- return result
- })
- }
-
public static func sendEncryptedService(peer: Api.InputEncryptedChat, randomId: Int64, data: Buffer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) {
let buffer = Buffer()
buffer.appendInt32(852769188)
@@ -2243,26 +2210,6 @@ public extension Api {
})
}
- public static func getMessagesViews(peer: Api.InputPeer, id: [Int32], increment: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<[Int32]>) {
- let buffer = Buffer()
- buffer.appendInt32(-993483427)
- peer.serialize(buffer, true)
- buffer.appendInt32(481674261)
- buffer.appendInt32(Int32(id.count))
- for item in id {
- serializeInt32(item, buffer: buffer, boxed: false)
- }
- increment.serialize(buffer, true)
- return (FunctionDescription(name: "messages.getMessagesViews", parameters: [("peer", peer), ("id", id), ("increment", increment)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> [Int32]? in
- let reader = BufferReader(buffer)
- var result: [Int32]?
- if let _ = reader.readInt32() {
- result = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self)
- }
- return result
- })
- }
-
public static func editChatAdmin(chatId: Int32, userId: Api.InputUser, isAdmin: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) {
let buffer = Buffer()
buffer.appendInt32(-1444503762)
@@ -2429,23 +2376,6 @@ public extension Api {
})
}
- public static func getBotCallbackAnswer(flags: Int32, peer: Api.InputPeer, msgId: Int32, data: Buffer?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) {
- let buffer = Buffer()
- buffer.appendInt32(-2130010132)
- serializeInt32(flags, buffer: buffer, boxed: false)
- peer.serialize(buffer, true)
- serializeInt32(msgId, buffer: buffer, boxed: false)
- if Int(flags) & Int(1 << 0) != 0 {serializeBytes(data!, buffer: buffer, boxed: false)}
- return (FunctionDescription(name: "messages.getBotCallbackAnswer", parameters: [("flags", flags), ("peer", peer), ("msgId", msgId), ("data", data)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.BotCallbackAnswer? in
- let reader = BufferReader(buffer)
- var result: Api.messages.BotCallbackAnswer?
- if let signature = reader.readInt32() {
- result = Api.parse(reader, signature: signature) as? Api.messages.BotCallbackAnswer
- }
- return result
- })
- }
-
public static func setBotCallbackAnswer(flags: Int32, queryId: Int64, message: String?, url: String?, cacheTime: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) {
let buffer = Buffer()
buffer.appendInt32(-712043766)
@@ -3385,26 +3315,6 @@ public extension Api {
})
}
- public static func searchGlobal(flags: Int32, folderId: Int32?, q: String, offsetRate: Int32, offsetPeer: Api.InputPeer, offsetId: Int32, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) {
- let buffer = Buffer()
- buffer.appendInt32(-1083038300)
- serializeInt32(flags, buffer: buffer, boxed: false)
- if Int(flags) & Int(1 << 0) != 0 {serializeInt32(folderId!, buffer: buffer, boxed: false)}
- serializeString(q, buffer: buffer, boxed: false)
- serializeInt32(offsetRate, buffer: buffer, boxed: false)
- offsetPeer.serialize(buffer, true)
- serializeInt32(offsetId, buffer: buffer, boxed: false)
- serializeInt32(limit, buffer: buffer, boxed: false)
- return (FunctionDescription(name: "messages.searchGlobal", parameters: [("flags", flags), ("folderId", folderId), ("q", q), ("offsetRate", offsetRate), ("offsetPeer", offsetPeer), ("offsetId", offsetId), ("limit", limit)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.Messages? in
- let reader = BufferReader(buffer)
- var result: Api.messages.Messages?
- if let signature = reader.readInt32() {
- result = Api.parse(reader, signature: signature) as? Api.messages.Messages
- }
- return result
- })
- }
-
public static func sendMessage(flags: Int32, peer: Api.InputPeer, replyToMsgId: Int32?, message: String, randomId: Int64, replyMarkup: Api.ReplyMarkup?, entities: [Api.MessageEntity]?, scheduleDate: Int32?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) {
let buffer = Buffer()
buffer.appendInt32(1376532592)
@@ -3718,6 +3628,100 @@ public extension Api {
return result
})
}
+
+ public static func getBotCallbackAnswer(flags: Int32, peer: Api.InputPeer, msgId: Int32, data: Buffer?, password: Api.InputCheckPasswordSRP?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) {
+ let buffer = Buffer()
+ buffer.appendInt32(-1824339449)
+ serializeInt32(flags, buffer: buffer, boxed: false)
+ peer.serialize(buffer, true)
+ serializeInt32(msgId, buffer: buffer, boxed: false)
+ if Int(flags) & Int(1 << 0) != 0 {serializeBytes(data!, buffer: buffer, boxed: false)}
+ if Int(flags) & Int(1 << 2) != 0 {password!.serialize(buffer, true)}
+ return (FunctionDescription(name: "messages.getBotCallbackAnswer", parameters: [("flags", flags), ("peer", peer), ("msgId", msgId), ("data", data), ("password", password)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.BotCallbackAnswer? in
+ let reader = BufferReader(buffer)
+ var result: Api.messages.BotCallbackAnswer?
+ if let signature = reader.readInt32() {
+ result = Api.parse(reader, signature: signature) as? Api.messages.BotCallbackAnswer
+ }
+ return result
+ })
+ }
+
+ public static func getMessagesViews(peer: Api.InputPeer, id: [Int32], increment: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<[Api.MessageViews]>) {
+ let buffer = Buffer()
+ buffer.appendInt32(-39035462)
+ peer.serialize(buffer, true)
+ buffer.appendInt32(481674261)
+ buffer.appendInt32(Int32(id.count))
+ for item in id {
+ serializeInt32(item, buffer: buffer, boxed: false)
+ }
+ increment.serialize(buffer, true)
+ return (FunctionDescription(name: "messages.getMessagesViews", parameters: [("peer", peer), ("id", id), ("increment", increment)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> [Api.MessageViews]? in
+ let reader = BufferReader(buffer)
+ var result: [Api.MessageViews]?
+ if let _ = reader.readInt32() {
+ result = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageViews.self)
+ }
+ return result
+ })
+ }
+
+ public static func searchGlobal(flags: Int32, folderId: Int32?, q: String, filter: Api.MessagesFilter, offsetRate: Int32, offsetPeer: Api.InputPeer, offsetId: Int32, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) {
+ let buffer = Buffer()
+ buffer.appendInt32(1934479725)
+ serializeInt32(flags, buffer: buffer, boxed: false)
+ if Int(flags) & Int(1 << 0) != 0 {serializeInt32(folderId!, buffer: buffer, boxed: false)}
+ serializeString(q, buffer: buffer, boxed: false)
+ filter.serialize(buffer, true)
+ serializeInt32(offsetRate, buffer: buffer, boxed: false)
+ offsetPeer.serialize(buffer, true)
+ serializeInt32(offsetId, buffer: buffer, boxed: false)
+ serializeInt32(limit, buffer: buffer, boxed: false)
+ return (FunctionDescription(name: "messages.searchGlobal", parameters: [("flags", flags), ("folderId", folderId), ("q", q), ("filter", filter), ("offsetRate", offsetRate), ("offsetPeer", offsetPeer), ("offsetId", offsetId), ("limit", limit)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.Messages? in
+ let reader = BufferReader(buffer)
+ var result: Api.messages.Messages?
+ if let signature = reader.readInt32() {
+ result = Api.parse(reader, signature: signature) as? Api.messages.Messages
+ }
+ return result
+ })
+ }
+
+ public static func sendEncrypted(flags: Int32, peer: Api.InputEncryptedChat, randomId: Int64, data: Buffer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) {
+ let buffer = Buffer()
+ buffer.appendInt32(1157265941)
+ serializeInt32(flags, buffer: buffer, boxed: false)
+ peer.serialize(buffer, true)
+ serializeInt64(randomId, buffer: buffer, boxed: false)
+ serializeBytes(data, buffer: buffer, boxed: false)
+ return (FunctionDescription(name: "messages.sendEncrypted", parameters: [("flags", flags), ("peer", peer), ("randomId", randomId), ("data", data)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.SentEncryptedMessage? in
+ let reader = BufferReader(buffer)
+ var result: Api.messages.SentEncryptedMessage?
+ if let signature = reader.readInt32() {
+ result = Api.parse(reader, signature: signature) as? Api.messages.SentEncryptedMessage
+ }
+ return result
+ })
+ }
+
+ public static func sendEncryptedFile(flags: Int32, peer: Api.InputEncryptedChat, randomId: Int64, data: Buffer, file: Api.InputEncryptedFile) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) {
+ let buffer = Buffer()
+ buffer.appendInt32(1431914525)
+ serializeInt32(flags, buffer: buffer, boxed: false)
+ peer.serialize(buffer, true)
+ serializeInt64(randomId, buffer: buffer, boxed: false)
+ serializeBytes(data, buffer: buffer, boxed: false)
+ file.serialize(buffer, true)
+ return (FunctionDescription(name: "messages.sendEncryptedFile", parameters: [("flags", flags), ("peer", peer), ("randomId", randomId), ("data", data), ("file", file)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.SentEncryptedMessage? in
+ let reader = BufferReader(buffer)
+ var result: Api.messages.SentEncryptedMessage?
+ if let signature = reader.readInt32() {
+ result = Api.parse(reader, signature: signature) as? Api.messages.SentEncryptedMessage
+ }
+ return result
+ })
+ }
}
public struct channels {
public static func readHistory(channel: Api.InputChannel, maxId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) {
@@ -4450,6 +4454,41 @@ public extension Api {
return result
})
}
+
+ public static func getMessagePublicForwards(channel: Api.InputChannel, msgId: Int32, offsetRate: Int32, offsetPeer: Api.InputPeer, offsetId: Int32, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) {
+ let buffer = Buffer()
+ buffer.appendInt32(1445996571)
+ channel.serialize(buffer, true)
+ serializeInt32(msgId, buffer: buffer, boxed: false)
+ serializeInt32(offsetRate, buffer: buffer, boxed: false)
+ offsetPeer.serialize(buffer, true)
+ serializeInt32(offsetId, buffer: buffer, boxed: false)
+ serializeInt32(limit, buffer: buffer, boxed: false)
+ return (FunctionDescription(name: "stats.getMessagePublicForwards", parameters: [("channel", channel), ("msgId", msgId), ("offsetRate", offsetRate), ("offsetPeer", offsetPeer), ("offsetId", offsetId), ("limit", limit)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.Messages? in
+ let reader = BufferReader(buffer)
+ var result: Api.messages.Messages?
+ if let signature = reader.readInt32() {
+ result = Api.parse(reader, signature: signature) as? Api.messages.Messages
+ }
+ return result
+ })
+ }
+
+ public static func getMessageStats(flags: Int32, channel: Api.InputChannel, msgId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) {
+ let buffer = Buffer()
+ buffer.appendInt32(-1226791947)
+ serializeInt32(flags, buffer: buffer, boxed: false)
+ channel.serialize(buffer, true)
+ serializeInt32(msgId, buffer: buffer, boxed: false)
+ return (FunctionDescription(name: "stats.getMessageStats", parameters: [("flags", flags), ("channel", channel), ("msgId", msgId)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.stats.MessageStats? in
+ let reader = BufferReader(buffer)
+ var result: Api.stats.MessageStats?
+ if let signature = reader.readInt32() {
+ result = Api.parse(reader, signature: signature) as? Api.stats.MessageStats
+ }
+ return result
+ })
+ }
}
public struct auth {
public static func checkPhone(phoneNumber: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) {
@@ -5369,34 +5408,6 @@ public extension Api {
})
}
- public static func getPromoData() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) {
- let buffer = Buffer()
- buffer.appendInt32(-1063816159)
-
- return (FunctionDescription(name: "help.getPromoData", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.help.PromoData? in
- let reader = BufferReader(buffer)
- var result: Api.help.PromoData?
- if let signature = reader.readInt32() {
- result = Api.parse(reader, signature: signature) as? Api.help.PromoData
- }
- return result
- })
- }
-
- public static func hidePromoData(peer: Api.InputPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) {
- let buffer = Buffer()
- buffer.appendInt32(505748629)
- peer.serialize(buffer, true)
- return (FunctionDescription(name: "help.hidePromoData", parameters: [("peer", peer)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in
- let reader = BufferReader(buffer)
- var result: Api.Bool?
- if let signature = reader.readInt32() {
- result = Api.parse(reader, signature: signature) as? Api.Bool
- }
- return result
- })
- }
-
public static func editUserInfo(userId: Api.InputUser, message: String, entities: [Api.MessageEntity]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) {
let buffer = Buffer()
buffer.appendInt32(1723407216)
@@ -5431,6 +5442,34 @@ public extension Api {
})
}
+ public static func getPromoData() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) {
+ let buffer = Buffer()
+ buffer.appendInt32(-1063816159)
+
+ return (FunctionDescription(name: "help.getPromoData", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.help.PromoData? in
+ let reader = BufferReader(buffer)
+ var result: Api.help.PromoData?
+ if let signature = reader.readInt32() {
+ result = Api.parse(reader, signature: signature) as? Api.help.PromoData
+ }
+ return result
+ })
+ }
+
+ public static func hidePromoData(peer: Api.InputPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) {
+ let buffer = Buffer()
+ buffer.appendInt32(505748629)
+ peer.serialize(buffer, true)
+ return (FunctionDescription(name: "help.hidePromoData", parameters: [("peer", peer)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in
+ let reader = BufferReader(buffer)
+ var result: Api.Bool?
+ if let signature = reader.readInt32() {
+ result = Api.parse(reader, signature: signature) as? Api.Bool
+ }
+ return result
+ })
+ }
+
public static func dismissSuggestion(suggestion: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) {
let buffer = Buffer()
buffer.appendInt32(125807007)
@@ -5444,6 +5483,21 @@ public extension Api {
return result
})
}
+
+ public static func getCountriesList(langCode: String, hash: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) {
+ let buffer = Buffer()
+ buffer.appendInt32(1935116200)
+ serializeString(langCode, buffer: buffer, boxed: false)
+ serializeInt32(hash, buffer: buffer, boxed: false)
+ return (FunctionDescription(name: "help.getCountriesList", parameters: [("langCode", langCode), ("hash", hash)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.help.CountriesList? in
+ let reader = BufferReader(buffer)
+ var result: Api.help.CountriesList?
+ if let signature = reader.readInt32() {
+ result = Api.parse(reader, signature: signature) as? Api.help.CountriesList
+ }
+ return result
+ })
+ }
}
public struct updates {
public static func getState() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) {
@@ -6802,11 +6856,14 @@ public extension Api {
})
}
- public static func updateProfilePhoto(id: Api.InputPhoto) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) {
+ public static func uploadProfilePhoto(flags: Int32, file: Api.InputFile?, video: Api.InputFile?, videoStartTs: Double?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) {
let buffer = Buffer()
- buffer.appendInt32(1926525996)
- id.serialize(buffer, true)
- return (FunctionDescription(name: "photos.updateProfilePhoto", parameters: [("id", id)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.photos.Photo? in
+ buffer.appendInt32(-1980559511)
+ serializeInt32(flags, buffer: buffer, boxed: false)
+ if Int(flags) & Int(1 << 0) != 0 {file!.serialize(buffer, true)}
+ if Int(flags) & Int(1 << 1) != 0 {video!.serialize(buffer, true)}
+ if Int(flags) & Int(1 << 2) != 0 {serializeDouble(videoStartTs!, buffer: buffer, boxed: false)}
+ return (FunctionDescription(name: "photos.uploadProfilePhoto", parameters: [("flags", flags), ("file", file), ("video", video), ("videoStartTs", videoStartTs)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.photos.Photo? in
let reader = BufferReader(buffer)
var result: Api.photos.Photo?
if let signature = reader.readInt32() {
@@ -6816,14 +6873,11 @@ public extension Api {
})
}
- public static func uploadProfilePhoto(flags: Int32, file: Api.InputFile?, video: Api.InputFile?, videoStartTs: Double?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) {
+ public static func updateProfilePhoto(id: Api.InputPhoto) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) {
let buffer = Buffer()
- buffer.appendInt32(-1980559511)
- serializeInt32(flags, buffer: buffer, boxed: false)
- if Int(flags) & Int(1 << 0) != 0 {file!.serialize(buffer, true)}
- if Int(flags) & Int(1 << 1) != 0 {video!.serialize(buffer, true)}
- if Int(flags) & Int(1 << 2) != 0 {serializeDouble(videoStartTs!, buffer: buffer, boxed: false)}
- return (FunctionDescription(name: "photos.uploadProfilePhoto", parameters: [("flags", flags), ("file", file), ("video", video), ("videoStartTs", videoStartTs)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.photos.Photo? in
+ buffer.appendInt32(1926525996)
+ id.serialize(buffer, true)
+ return (FunctionDescription(name: "photos.updateProfilePhoto", parameters: [("id", id)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.photos.Photo? in
let reader = BufferReader(buffer)
var result: Api.photos.Photo?
if let signature = reader.readInt32() {
diff --git a/submodules/TelegramCallsUI/Sources/CallControllerNode.swift b/submodules/TelegramCallsUI/Sources/CallControllerNode.swift
index 1463789e60..32cccf208a 100644
--- a/submodules/TelegramCallsUI/Sources/CallControllerNode.swift
+++ b/submodules/TelegramCallsUI/Sources/CallControllerNode.swift
@@ -1024,8 +1024,10 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
self.updateButtonsMode()
self.updateDimVisibility()
- if self.incomingVideoViewRequested && self.outgoingVideoViewRequested {
- self.displayedCameraTooltip = true
+ if self.incomingVideoViewRequested || self.outgoingVideoViewRequested {
+ if self.incomingVideoViewRequested && self.outgoingVideoViewRequested {
+ self.displayedCameraTooltip = true
+ }
self.displayedCameraConfirmation = true
}
if self.incomingVideoViewRequested && !self.outgoingVideoViewRequested && !self.displayedCameraTooltip && (self.toastContent?.isEmpty ?? true) {
@@ -1331,6 +1333,10 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
isCompactLayout = false
}
+ if !self.hasVideoNodes {
+ self.isUIHidden = false
+ }
+
var isUIHidden = self.isUIHidden
switch self.callState?.state {
case .terminated, .terminating:
@@ -1639,9 +1645,9 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
} else if let _ = self.keyPreviewNode {
self.backPressed()
} else {
- if let expandedVideoNode = self.expandedVideoNode, let minimizedVideoNode = self.minimizedVideoNode {
+ if self.hasVideoNodes {
let point = recognizer.location(in: recognizer.view)
- if minimizedVideoNode.frame.contains(point) {
+ if let expandedVideoNode = self.expandedVideoNode, let minimizedVideoNode = self.minimizedVideoNode, minimizedVideoNode.frame.contains(point) {
if !self.areUserActionsDisabledNow() {
let copyView = minimizedVideoNode.view.snapshotView(afterScreenUpdates: false)
copyView?.frame = minimizedVideoNode.frame
diff --git a/submodules/TelegramCallsUI/Sources/LegacyCallControllerButton.swift b/submodules/TelegramCallsUI/Sources/LegacyCallControllerButton.swift
index 79c82f11bb..055bc7d333 100644
--- a/submodules/TelegramCallsUI/Sources/LegacyCallControllerButton.swift
+++ b/submodules/TelegramCallsUI/Sources/LegacyCallControllerButton.swift
@@ -45,7 +45,7 @@ private func generateEmptyButtonImage(icon: UIImage?, strokeColor: UIColor?, fil
context.fill(imageRect)
} else {
context.setBlendMode(.normal)
- context.draw(icon.cgImage!, in: imageRect)
+ context.draw(generateTintedImage(image: icon, color: .white)!.cgImage!, in: imageRect)
}
}
})
diff --git a/submodules/TelegramCore/Sources/AccountManager.swift b/submodules/TelegramCore/Sources/AccountManager.swift
index fb9b223ac9..9877a921ae 100644
--- a/submodules/TelegramCore/Sources/AccountManager.swift
+++ b/submodules/TelegramCore/Sources/AccountManager.swift
@@ -163,6 +163,8 @@ private var declaredEncodables: Void = {
declareEncodable(CachedChatContextResult.self, f: { CachedChatContextResult(decoder: $0) })
declareEncodable(PeerAccessRestrictionInfo.self, f: { PeerAccessRestrictionInfo(decoder: $0) })
declareEncodable(TelegramMediaImage.VideoRepresentation.self, f: { TelegramMediaImage.VideoRepresentation(decoder: $0) })
+ declareEncodable(Country.self, f: { Country(decoder: $0) })
+ declareEncodable(Country.CountryCode.self, f: { Country.CountryCode(decoder: $0) })
return
}()
diff --git a/submodules/TelegramCore/Sources/AccountViewTracker.swift b/submodules/TelegramCore/Sources/AccountViewTracker.swift
index c439133ebb..9f166ff156 100644
--- a/submodules/TelegramCore/Sources/AccountViewTracker.swift
+++ b/submodules/TelegramCore/Sources/AccountViewTracker.swift
@@ -591,7 +591,7 @@ public final class AccountViewTracker {
if let peer = transaction.getPeer(peerId), let inputPeer = apiInputPeer(peer) {
return account.network.request(Api.functions.messages.getMessagesViews(peer: inputPeer, id: messageIds.map { $0.id }, increment: .boolTrue))
|> map(Optional.init)
- |> `catch` { _ -> Signal<[Int32]?, NoError> in
+ |> `catch` { _ -> Signal<[Api.MessageViews]?, NoError> in
return .single(nil)
}
|> mapToSignal { viewCounts -> Signal in
@@ -599,20 +599,7 @@ public final class AccountViewTracker {
return account.postbox.transaction { transaction -> Void in
for i in 0 ..< messageIds.count {
if i < viewCounts.count {
- let views = viewCounts[i]
- do {
- transaction.updateMessage(messageIds[i], update: { currentMessage in
- let storeForwardInfo = currentMessage.forwardInfo.flatMap(StoreMessageForwardInfo.init)
- var attributes = currentMessage.attributes
- loop: for j in 0 ..< attributes.count {
- if let attribute = attributes[j] as? ViewCountMessageAttribute {
- attributes[j] = ViewCountMessageAttribute(count: max(attribute.count, Int(views)))
- }
- }
- return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media))
- })
- }
- /*if case let .messageViews(views, forwards) = viewCounts[i] {
+ if case let .messageViews(views, forwards) = viewCounts[i] {
transaction.updateMessage(messageIds[i], update: { currentMessage in
let storeForwardInfo = currentMessage.forwardInfo.flatMap(StoreMessageForwardInfo.init)
var attributes = currentMessage.attributes
@@ -626,7 +613,7 @@ public final class AccountViewTracker {
}
return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media))
})
- }*/
+ }
}
}
}
diff --git a/submodules/TelegramCore/Sources/ChannelOwnershipTransfer.swift b/submodules/TelegramCore/Sources/ChannelOwnershipTransfer.swift
index b66c178ffb..023130be15 100644
--- a/submodules/TelegramCore/Sources/ChannelOwnershipTransfer.swift
+++ b/submodules/TelegramCore/Sources/ChannelOwnershipTransfer.swift
@@ -67,7 +67,7 @@ public func checkOwnershipTranfserAvailability(postbox: Postbox, network: Networ
}
|> mapToSignal { updates -> Signal in
accountStateManager.addUpdates(updates)
- return.complete()
+ return .complete()
}
}
}
diff --git a/submodules/TelegramCore/Sources/Countries.swift b/submodules/TelegramCore/Sources/Countries.swift
new file mode 100644
index 0000000000..15ebe870cb
--- /dev/null
+++ b/submodules/TelegramCore/Sources/Countries.swift
@@ -0,0 +1,164 @@
+import Foundation
+import Postbox
+import SwiftSignalKit
+import TelegramApi
+
+import SyncCore
+
+public struct Country: PostboxCoding, Equatable {
+ public static func == (lhs: Country, rhs: Country) -> Bool {
+ return lhs.code == rhs.code && lhs.name == rhs.name && lhs.localizedName == rhs.localizedName && lhs.countryCodes == rhs.countryCodes && lhs.hidden == rhs.hidden
+ }
+
+ public struct CountryCode: PostboxCoding, Equatable {
+ public let code: String
+ public let prefixes: [String]
+ public let patterns: [String]
+
+ public init(code: String, prefixes: [String], patterns: [String]) {
+ self.code = code
+ self.prefixes = prefixes
+ self.patterns = patterns
+ }
+
+ public init(decoder: PostboxDecoder) {
+ self.code = decoder.decodeStringForKey("c", orElse: "")
+ self.prefixes = decoder.decodeStringArrayForKey("pfx")
+ self.patterns = decoder.decodeStringArrayForKey("ptrn")
+ }
+
+ public func encode(_ encoder: PostboxEncoder) {
+ encoder.encodeString(self.code, forKey: "c")
+ encoder.encodeStringArray(self.prefixes, forKey: "pfx")
+ encoder.encodeStringArray(self.patterns, forKey: "ptrn")
+ }
+ }
+
+ public let code: String
+ public let name: String
+ public let localizedName: String?
+ public let countryCodes: [CountryCode]
+ public let hidden: Bool
+
+ public init(code: String, name: String, localizedName: String?, countryCodes: [CountryCode], hidden: Bool) {
+ self.code = code
+ self.name = name
+ self.localizedName = localizedName
+ self.countryCodes = countryCodes
+ self.hidden = hidden
+ }
+
+ public init(decoder: PostboxDecoder) {
+ self.code = decoder.decodeStringForKey("c", orElse: "")
+ self.name = decoder.decodeStringForKey("n", orElse: "")
+ self.localizedName = decoder.decodeOptionalStringForKey("ln")
+ self.countryCodes = decoder.decodeObjectArrayForKey("cc").map { $0 as! CountryCode }
+ self.hidden = decoder.decodeBoolForKey("h", orElse: false)
+ }
+
+ public func encode(_ encoder: PostboxEncoder) {
+ encoder.encodeString(self.code, forKey: "c")
+ encoder.encodeString(self.name, forKey: "n")
+ if let localizedName = self.localizedName {
+ encoder.encodeString(localizedName, forKey: "ln")
+ } else {
+ encoder.encodeNil(forKey: "ln")
+ }
+ encoder.encodeObjectArray(self.countryCodes, forKey: "cc")
+ encoder.encodeBool(self.hidden, forKey: "h")
+ }
+}
+
+public final class CountriesList: PreferencesEntry, Equatable {
+ public let countries: [Country]
+ public let hash: Int32
+
+ public init(countries: [Country], hash: Int32) {
+ self.countries = countries
+ self.hash = hash
+ }
+
+ public init(decoder: PostboxDecoder) {
+ self.countries = decoder.decodeObjectArrayForKey("c").map { $0 as! Country }
+ self.hash = decoder.decodeInt32ForKey("h", orElse: 0)
+ }
+
+ public func encode(_ encoder: PostboxEncoder) {
+ encoder.encodeObjectArray(self.countries, forKey: "c")
+ encoder.encodeInt32(self.hash, forKey: "h")
+ }
+
+ public func isEqual(to: PreferencesEntry) -> Bool {
+ if let to = to as? CountriesList {
+ return self == to
+ } else {
+ return false
+ }
+ }
+
+ public static func ==(lhs: CountriesList, rhs: CountriesList) -> Bool {
+ return lhs.countries == rhs.countries && lhs.hash == rhs.hash
+ }
+}
+
+
+public func getCountriesList(accountManager: AccountManager, network: Network, langCode: String?, forceUpdate: Bool = false) -> Signal<[Country], NoError> {
+ let fetch: ([Country]?, Int32?) -> Signal<[Country], NoError> = { current, hash in
+ return network.request(Api.functions.help.getCountriesList(langCode: langCode ?? "", hash: hash ?? 0))
+ |> retryRequest
+ |> mapToSignal { result -> Signal<[Country], NoError> in
+ switch result {
+ case let .countriesList(apiCountries, hash):
+ let result = apiCountries.compactMap { Country(apiCountry: $0) }
+ if result == current {
+ return .complete()
+ } else {
+ let _ = accountManager.transaction { transaction in
+ transaction.updateSharedData(SharedDataKeys.countriesList, { _ in
+ return CountriesList(countries: result, hash: hash)
+ })
+ }.start()
+ return .single(result)
+ }
+ case .countriesListNotModified:
+ return .complete()
+ }
+ }
+ }
+
+ if forceUpdate {
+ return fetch(nil, nil)
+ } else {
+ return accountManager.sharedData(keys: [SharedDataKeys.countriesList])
+ |> map { sharedData -> ([Country], Int32) in
+ if let countriesList = sharedData.entries[SharedDataKeys.countriesList] as? CountriesList {
+ return (countriesList.countries, countriesList.hash)
+ } else {
+ return ([], 0)
+ }
+ } |> mapToSignal { current, hash -> Signal<[Country], NoError> in
+ return .single(current)
+ |> then(fetch(current, hash))
+ }
+ }
+}
+
+extension Country.CountryCode {
+ init(apiCountryCode: Api.help.CountryCode) {
+ switch apiCountryCode {
+ case let .countryCode(_, countryCode, apiPrefixes, apiPatterns):
+ let prefixes: [String] = apiPrefixes.flatMap { $0 } ?? []
+ let patterns: [String] = apiPatterns.flatMap { $0 } ?? []
+ self.init(code: countryCode, prefixes: prefixes, patterns: patterns)
+ }
+ }
+}
+
+extension Country {
+ init(apiCountry: Api.help.Country) {
+ switch apiCountry {
+ case let .country(flags, iso2, defaultName, name, countryCodes):
+ self.init(code: iso2, name: defaultName, localizedName: name, countryCodes: countryCodes.map { Country.CountryCode(apiCountryCode: $0) }, hidden: (flags & 1 << 0) != 0)
+ }
+ }
+}
diff --git a/submodules/TelegramCore/Sources/Holes.swift b/submodules/TelegramCore/Sources/Holes.swift
index c4aebc3daf..53c07bf180 100644
--- a/submodules/TelegramCore/Sources/Holes.swift
+++ b/submodules/TelegramCore/Sources/Holes.swift
@@ -9,6 +9,10 @@ import SyncCore
func messageFilterForTagMask(_ tagMask: MessageTags) -> Api.MessagesFilter? {
if tagMask == .photoOrVideo {
return Api.MessagesFilter.inputMessagesFilterPhotoVideo
+ } else if tagMask == .photo {
+ return Api.MessagesFilter.inputMessagesFilterPhotos
+ } else if tagMask == .video {
+ return Api.MessagesFilter.inputMessagesFilterVideo
} else if tagMask == .file {
return Api.MessagesFilter.inputMessagesFilterDocument
} else if tagMask == .music {
diff --git a/submodules/TelegramCore/Sources/ManagedSecretChatOutgoingOperations.swift b/submodules/TelegramCore/Sources/ManagedSecretChatOutgoingOperations.swift
index f65204e0d9..636f3f52ba 100644
--- a/submodules/TelegramCore/Sources/ManagedSecretChatOutgoingOperations.swift
+++ b/submodules/TelegramCore/Sources/ManagedSecretChatOutgoingOperations.swift
@@ -757,6 +757,7 @@ private func boxedDecryptedMessage(transaction: Transaction, message: Message, g
var viaBotName: String?
var entities: [MessageTextEntity]?
+ var muted: Bool = false
for attribute in message.attributes {
if let attribute = attribute as? AutoremoveTimeoutMessageAttribute {
@@ -769,6 +770,10 @@ private func boxedDecryptedMessage(transaction: Transaction, message: Message, g
}
} else if let attribute = attribute as? TextEntitiesMessageAttribute {
entities = attribute.entities
+ } else if let attribute = attribute as? NotificationInfoMessageAttribute {
+ if attribute.flags.contains(.muted) {
+ muted = true
+ }
}
}
@@ -797,6 +802,9 @@ private func boxedDecryptedMessage(transaction: Transaction, message: Message, g
return .layer8(.decryptedMessage(randomId: globallyUniqueId, randomBytes: randomBytes, message: message.text, media: decryptedMedia))
case .layer46:
+ if muted {
+ flags |= (1 << 5)
+ }
if let _ = viaBotName {
flags |= (1 << 11)
}
@@ -804,6 +812,9 @@ private func boxedDecryptedMessage(transaction: Transaction, message: Message, g
flags |= (1 << 9)
return .layer46(.decryptedMessage(flags: flags, randomId: globallyUniqueId, ttl: messageAutoremoveTimeout, message: message.text, media: decryptedMedia, entities: nil, viaBotName: viaBotName, replyToRandomId: replyGlobalId))
case .layer73:
+ if muted {
+ flags |= (1 << 5)
+ }
if let _ = viaBotName {
flags |= (1 << 11)
}
@@ -818,6 +829,9 @@ private func boxedDecryptedMessage(transaction: Transaction, message: Message, g
}
return .layer73(.decryptedMessage(flags: flags, randomId: globallyUniqueId, ttl: messageAutoremoveTimeout, message: message.text, media: decryptedMedia, entities: decryptedEntites, viaBotName: viaBotName, replyToRandomId: replyGlobalId, groupedId: message.groupingKey))
case .layer101:
+ if muted {
+ flags |= (1 << 5)
+ }
if let _ = viaBotName {
flags |= (1 << 11)
}
@@ -889,6 +903,9 @@ private func boxedDecryptedMessage(transaction: Transaction, message: Message, g
}
if let decryptedMedia = decryptedMedia {
+ if muted {
+ flags |= (1 << 5)
+ }
if let _ = viaBotName {
flags |= (1 << 11)
}
@@ -913,6 +930,9 @@ private func boxedDecryptedMessage(transaction: Transaction, message: Message, g
}
if let decryptedMedia = decryptedMedia {
+ if muted {
+ flags |= (1 << 5)
+ }
if let _ = viaBotName {
flags |= (1 << 11)
}
@@ -944,6 +964,9 @@ private func boxedDecryptedMessage(transaction: Transaction, message: Message, g
}
if let decryptedMedia = decryptedMedia {
+ if muted {
+ flags |= (1 << 5)
+ }
if let _ = viaBotName {
flags |= (1 << 11)
}
@@ -969,6 +992,9 @@ private func boxedDecryptedMessage(transaction: Transaction, message: Message, g
case .layer8:
break
case .layer46:
+ if muted {
+ flags |= (1 << 5)
+ }
if let _ = viaBotName {
flags |= (1 << 11)
}
@@ -976,6 +1002,9 @@ private func boxedDecryptedMessage(transaction: Transaction, message: Message, g
flags |= (1 << 9)
return .layer46(.decryptedMessage(flags: flags, randomId: globallyUniqueId, ttl: messageAutoremoveTimeout, message: message.text, media: decryptedMedia, entities: nil, viaBotName: viaBotName, replyToRandomId: replyGlobalId))
case .layer73:
+ if muted {
+ flags |= (1 << 5)
+ }
if let _ = viaBotName {
flags |= (1 << 11)
}
@@ -987,6 +1016,9 @@ private func boxedDecryptedMessage(transaction: Transaction, message: Message, g
flags |= (1 << 9)
return .layer73(.decryptedMessage(flags: flags, randomId: globallyUniqueId, ttl: messageAutoremoveTimeout, message: message.text, media: decryptedMedia, entities: decryptedEntites, viaBotName: viaBotName, replyToRandomId: replyGlobalId, groupedId: message.groupingKey))
case .layer101:
+ if muted {
+ flags |= (1 << 5)
+ }
if let _ = viaBotName {
flags |= (1 << 11)
}
@@ -1004,6 +1036,9 @@ private func boxedDecryptedMessage(transaction: Transaction, message: Message, g
case .layer8:
break
case .layer46:
+ if muted {
+ flags |= (1 << 5)
+ }
if let _ = viaBotName {
flags |= (1 << 11)
}
@@ -1016,6 +1051,9 @@ private func boxedDecryptedMessage(transaction: Transaction, message: Message, g
}
return .layer46(.decryptedMessage(flags: flags, randomId: globallyUniqueId, ttl: messageAutoremoveTimeout, message: message.text, media: decryptedMedia, entities: nil, viaBotName: viaBotName, replyToRandomId: replyGlobalId))
case .layer73:
+ if muted {
+ flags |= (1 << 5)
+ }
if let _ = viaBotName {
flags |= (1 << 11)
}
@@ -1033,6 +1071,9 @@ private func boxedDecryptedMessage(transaction: Transaction, message: Message, g
}
return .layer73(.decryptedMessage(flags: flags, randomId: globallyUniqueId, ttl: messageAutoremoveTimeout, message: message.text, media: decryptedMedia, entities: decryptedEntites, viaBotName: viaBotName, replyToRandomId: replyGlobalId, groupedId: message.groupingKey))
case .layer101:
+ if muted {
+ flags |= (1 << 5)
+ }
if let _ = viaBotName {
flags |= (1 << 11)
}
@@ -1055,6 +1096,9 @@ private func boxedDecryptedMessage(transaction: Transaction, message: Message, g
case .layer8:
break
case .layer46:
+ if muted {
+ flags |= (1 << 5)
+ }
if let _ = viaBotName {
flags |= (1 << 11)
}
@@ -1062,6 +1106,9 @@ private func boxedDecryptedMessage(transaction: Transaction, message: Message, g
flags |= (1 << 9)
return .layer46(.decryptedMessage(flags: flags, randomId: globallyUniqueId, ttl: messageAutoremoveTimeout, message: message.text, media: decryptedMedia, entities: nil, viaBotName: viaBotName, replyToRandomId: replyGlobalId))
case .layer73:
+ if muted {
+ flags |= (1 << 5)
+ }
if let _ = viaBotName {
flags |= (1 << 11)
}
@@ -1074,6 +1121,9 @@ private func boxedDecryptedMessage(transaction: Transaction, message: Message, g
flags |= (1 << 9)
return .layer73(.decryptedMessage(flags: flags, randomId: globallyUniqueId, ttl: messageAutoremoveTimeout, message: message.text, media: decryptedMedia, entities: decryptedEntites, viaBotName: viaBotName, replyToRandomId: replyGlobalId, groupedId: message.groupingKey))
case .layer101:
+ if muted {
+ flags |= (1 << 5)
+ }
if let _ = viaBotName {
flags |= (1 << 11)
}
@@ -1097,11 +1147,17 @@ private func boxedDecryptedMessage(transaction: Transaction, message: Message, g
return .layer8(.decryptedMessage(randomId: globallyUniqueId, randomBytes: randomBytes, message: message.text, media: .decryptedMessageMediaEmpty))
case .layer46:
+ if muted {
+ flags |= (1 << 5)
+ }
if let _ = viaBotName {
flags |= (1 << 11)
}
return .layer46(.decryptedMessage(flags: flags, randomId: globallyUniqueId, ttl: messageAutoremoveTimeout, message: message.text, media: .decryptedMessageMediaEmpty, entities: nil, viaBotName: viaBotName, replyToRandomId: replyGlobalId))
case .layer73:
+ if muted {
+ flags |= (1 << 5)
+ }
if let _ = viaBotName {
flags |= (1 << 11)
}
@@ -1111,6 +1167,9 @@ private func boxedDecryptedMessage(transaction: Transaction, message: Message, g
}
return .layer73(.decryptedMessage(flags: flags, randomId: globallyUniqueId, ttl: messageAutoremoveTimeout, message: message.text, media: .decryptedMessageMediaEmpty, entities: decryptedEntites, viaBotName: viaBotName, replyToRandomId: replyGlobalId, groupedId: message.groupingKey))
case .layer101:
+ if muted {
+ flags |= (1 << 5)
+ }
if let _ = viaBotName {
flags |= (1 << 11)
}
@@ -1375,7 +1434,7 @@ private func sendMessage(auxiliaryMethods: AccountAuxiliaryMethods, postbox: Pos
if let state = transaction.getPeerChatState(messageId.peerId) as? SecretChatState, let peer = transaction.getPeer(messageId.peerId) as? TelegramSecretChat {
if let message = transaction.getMessage(messageId), let globallyUniqueId = message.globallyUniqueId {
let decryptedMessage = boxedDecryptedMessage(transaction: transaction, message: message, globallyUniqueId: globallyUniqueId, uploadedFile: file, thumbnailData: thumbnailData, layer: layer)
- return sendBoxedDecryptedMessage(postbox: postbox, network: network, peer: peer, state: state, operationIndex: tagLocalIndex, decryptedMessage: decryptedMessage, globallyUniqueId: globallyUniqueId, file: file, asService: wasDelivered, wasDelivered: wasDelivered)
+ return sendBoxedDecryptedMessage(postbox: postbox, network: network, peer: peer, state: state, operationIndex: tagLocalIndex, decryptedMessage: decryptedMessage, globallyUniqueId: globallyUniqueId, file: file, silent: message.muted, asService: wasDelivered, wasDelivered: wasDelivered)
|> mapToSignal { result in
return postbox.transaction { transaction -> Void in
let forceRemove: Bool
@@ -1469,7 +1528,7 @@ private func sendServiceActionMessage(postbox: Postbox, network: Network, peerId
return postbox.transaction { transaction -> Signal in
if let state = transaction.getPeerChatState(peerId) as? SecretChatState, let peer = transaction.getPeer(peerId) as? TelegramSecretChat {
let decryptedMessage = boxedDecryptedSecretMessageAction(action: action)
- return sendBoxedDecryptedMessage(postbox: postbox, network: network, peer: peer, state: state, operationIndex: tagLocalIndex, decryptedMessage: decryptedMessage, globallyUniqueId: action.globallyUniqueId, file: nil, asService: true, wasDelivered: wasDelivered)
+ return sendBoxedDecryptedMessage(postbox: postbox, network: network, peer: peer, state: state, operationIndex: tagLocalIndex, decryptedMessage: decryptedMessage, globallyUniqueId: action.globallyUniqueId, file: nil, silent: false, asService: true, wasDelivered: wasDelivered)
|> mapToSignal { result in
return postbox.transaction { transaction -> Void in
let forceRemove: Bool
@@ -1528,7 +1587,7 @@ private enum SendBoxedDecryptedMessageResult {
case error(SendBoxedDecryptedMessageError)
}
-private func sendBoxedDecryptedMessage(postbox: Postbox, network: Network, peer: TelegramSecretChat, state: SecretChatState, operationIndex: Int32, decryptedMessage: BoxedDecryptedMessage, globallyUniqueId: Int64, file: SecretChatOutgoingFile?, asService: Bool, wasDelivered: Bool) -> Signal {
+private func sendBoxedDecryptedMessage(postbox: Postbox, network: Network, peer: TelegramSecretChat, state: SecretChatState, operationIndex: Int32, decryptedMessage: BoxedDecryptedMessage, globallyUniqueId: Int64, file: SecretChatOutgoingFile?, silent: Bool, asService: Bool, wasDelivered: Bool) -> Signal {
let payload = Buffer()
var sequenceInfo: SecretChatOperationSequenceInfo?
var maybeParameters: SecretChatEncryptionParameters?
@@ -1573,6 +1632,11 @@ private func sendBoxedDecryptedMessage(postbox: Postbox, network: Network, peer:
let sendMessage: Signal
let inputPeer = Api.InputEncryptedChat.inputEncryptedChat(chatId: peer.id.id, accessHash: peer.accessHash)
+ var flags: Int32 = 0
+ if silent {
+ flags |= (1 << 0)
+ }
+
if asService {
let actionRandomId: Int64
if wasDelivered {
@@ -1583,9 +1647,9 @@ private func sendBoxedDecryptedMessage(postbox: Postbox, network: Network, peer:
sendMessage = network.request(Api.functions.messages.sendEncryptedService(peer: inputPeer, randomId: actionRandomId, data: Buffer(data: encryptedPayload)))
} else {
if let file = file {
- sendMessage = network.request(Api.functions.messages.sendEncryptedFile(peer: inputPeer, randomId: globallyUniqueId, data: Buffer(data: encryptedPayload), file: file.reference.apiInputFile))
+ sendMessage = network.request(Api.functions.messages.sendEncryptedFile(flags: flags, peer: inputPeer, randomId: globallyUniqueId, data: Buffer(data: encryptedPayload), file: file.reference.apiInputFile))
} else {
- sendMessage = network.request(Api.functions.messages.sendEncrypted(peer: inputPeer, randomId: globallyUniqueId, data: Buffer(data: encryptedPayload)))
+ sendMessage = network.request(Api.functions.messages.sendEncrypted(flags: flags, peer: inputPeer, randomId: globallyUniqueId, data: Buffer(data: encryptedPayload)))
}
}
return sendMessage
diff --git a/submodules/TelegramCore/Sources/ProcessSecretChatIncomingDecryptedOperations.swift b/submodules/TelegramCore/Sources/ProcessSecretChatIncomingDecryptedOperations.swift
index 8dd5af8087..b86d428398 100644
--- a/submodules/TelegramCore/Sources/ProcessSecretChatIncomingDecryptedOperations.swift
+++ b/submodules/TelegramCore/Sources/ProcessSecretChatIncomingDecryptedOperations.swift
@@ -686,7 +686,7 @@ private func maximumMediaAutoremoveTimeout(_ media: [Media]) -> Int32 {
private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32, timestamp: Int32, apiMessage: SecretApi46.DecryptedMessage, file: SecretChatFileReference?, messageIdForGloballyUniqueMessageId: (Int64) -> MessageId?) -> (StoreMessage, [(MediaResource, Data)])? {
switch apiMessage {
- case let .decryptedMessage(_, randomId, ttl, message, media, entities, viaBotName, replyToRandomId):
+ case let .decryptedMessage(flags, randomId, ttl, message, media, entities, viaBotName, replyToRandomId):
var text = message
var parsedMedia: [Media] = []
var attributes: [MessageAttribute] = []
@@ -698,6 +698,10 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32
attributes.append(InlineBotMessageAttribute(peerId: nil, title: viaBotName))
}
+ if (flags & 1 << 5) != 0 {
+ attributes.append(NotificationInfoMessageAttribute(flags: .muted))
+ }
+
if let media = media {
switch media {
case let .decryptedMessageMediaPhoto(thumb, thumbW, thumbH, w, h, size, key, iv, caption):
@@ -882,7 +886,7 @@ private func parseEntities(_ entities: [SecretApi73.MessageEntity]) -> TextEntit
private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32, timestamp: Int32, apiMessage: SecretApi73.DecryptedMessage, file: SecretChatFileReference?, messageIdForGloballyUniqueMessageId: (Int64) -> MessageId?) -> (StoreMessage, [(MediaResource, Data)])? {
switch apiMessage {
- case let .decryptedMessage(_, randomId, ttl, message, media, entities, viaBotName, replyToRandomId, groupedId):
+ case let .decryptedMessage(flags, randomId, ttl, message, media, entities, viaBotName, replyToRandomId, groupedId):
var text = message
var parsedMedia: [Media] = []
var attributes: [MessageAttribute] = []
@@ -896,6 +900,10 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32
attributes.append(InlineBotMessageAttribute(peerId: nil, title: viaBotName))
}
+ if (flags & 1 << 5) != 0 {
+ attributes.append(NotificationInfoMessageAttribute(flags: .muted))
+ }
+
if let media = media {
switch media {
case let .decryptedMessageMediaPhoto(thumb, thumbW, thumbH, w, h, size, key, iv, caption):
@@ -1116,7 +1124,7 @@ private func parseEntities(_ entities: [SecretApi101.MessageEntity]) -> TextEnti
private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32, timestamp: Int32, apiMessage: SecretApi101.DecryptedMessage, file: SecretChatFileReference?, messageIdForGloballyUniqueMessageId: (Int64) -> MessageId?) -> (StoreMessage, [(MediaResource, Data)])? {
switch apiMessage {
- case let .decryptedMessage(_, randomId, ttl, message, media, entities, viaBotName, replyToRandomId, groupedId):
+ case let .decryptedMessage(flags, randomId, ttl, message, media, entities, viaBotName, replyToRandomId, groupedId):
var text = message
var parsedMedia: [Media] = []
var attributes: [MessageAttribute] = []
@@ -1130,6 +1138,10 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32
attributes.append(InlineBotMessageAttribute(peerId: nil, title: viaBotName))
}
+ if (flags & 1 << 5) != 0 {
+ attributes.append(NotificationInfoMessageAttribute(flags: .muted))
+ }
+
if let media = media {
switch media {
case let .decryptedMessageMediaPhoto(thumb, thumbW, thumbH, w, h, size, key, iv, caption):
diff --git a/submodules/TelegramCore/Sources/ReplyMarkupMessageAttribute.swift b/submodules/TelegramCore/Sources/ReplyMarkupMessageAttribute.swift
index 587ee1be42..3f42956000 100644
--- a/submodules/TelegramCore/Sources/ReplyMarkupMessageAttribute.swift
+++ b/submodules/TelegramCore/Sources/ReplyMarkupMessageAttribute.swift
@@ -9,11 +9,11 @@ extension ReplyMarkupButton {
switch apiButton {
case let .keyboardButton(text):
self.init(title: text, titleWhenForwarded: nil, action: .text)
- case let .keyboardButtonCallback(text, data):
+ case let .keyboardButtonCallback(flags, text, data):
let memory = malloc(data.size)!
memcpy(memory, data.data, data.size)
let dataBuffer = MemoryBuffer(memory: memory, capacity: data.size, length: data.size, freeWhenDone: true)
- self.init(title: text, titleWhenForwarded: nil, action: .callback(dataBuffer))
+ self.init(title: text, titleWhenForwarded: nil, action: .callback(requiresPassword: (flags & (1 << 0)) != 0, data: dataBuffer))
case let .keyboardButtonRequestGeoLocation(text):
self.init(title: text, titleWhenForwarded: nil, action: .requestMap)
case let .keyboardButtonRequestPhone(text):
diff --git a/submodules/TelegramCore/Sources/RequestMessageActionCallback.swift b/submodules/TelegramCore/Sources/RequestMessageActionCallback.swift
index 7631d4bee6..5aa33db84d 100644
--- a/submodules/TelegramCore/Sources/RequestMessageActionCallback.swift
+++ b/submodules/TelegramCore/Sources/RequestMessageActionCallback.swift
@@ -3,8 +3,6 @@ import Postbox
import SwiftSignalKit
import TelegramApi
import MtProtoKit
-
-
import SyncCore
public enum MessageActionCallbackResult {
@@ -14,8 +12,69 @@ public enum MessageActionCallbackResult {
case url(String)
}
-public func requestMessageActionCallback(account: Account, messageId: MessageId, isGame:Bool, data: MemoryBuffer?) -> Signal {
+public enum MessageActionCallbackError {
+ case generic
+ case twoStepAuthMissing
+ case twoStepAuthTooFresh(Int32)
+ case authSessionTooFresh(Int32)
+ case limitExceeded
+ case requestPassword
+ case invalidPassword
+ case restricted
+ case userBlocked
+}
+
+public func requestMessageActionCallbackPasswordCheck(account: Account, messageId: MessageId, isGame: Bool, data: MemoryBuffer?) -> Signal {
return account.postbox.loadedPeerWithId(messageId.peerId)
+ |> castError(MessageActionCallbackError.self)
+ |> take(1)
+ |> mapToSignal { peer in
+ if let inputPeer = apiInputPeer(peer) {
+ var flags: Int32 = 0
+ var dataBuffer: Buffer?
+ if let data = data {
+ flags |= Int32(1 << 0)
+ dataBuffer = Buffer(data: data.makeData())
+ }
+ if isGame {
+ flags |= Int32(1 << 1)
+ }
+
+ return account.network.request(Api.functions.messages.getBotCallbackAnswer(flags: flags, peer: inputPeer, msgId: messageId.id, data: dataBuffer, password: .inputCheckPasswordEmpty))
+ |> mapError { error -> MessageActionCallbackError in
+ if error.errorDescription == "PASSWORD_HASH_INVALID" {
+ return .requestPassword
+ } else if error.errorDescription == "PASSWORD_MISSING" {
+ return .twoStepAuthMissing
+ } else if error.errorDescription.hasPrefix("PASSWORD_TOO_FRESH_") {
+ let timeout = String(error.errorDescription[error.errorDescription.index(error.errorDescription.startIndex, offsetBy: "PASSWORD_TOO_FRESH_".count)...])
+ if let value = Int32(timeout) {
+ return .twoStepAuthTooFresh(value)
+ }
+ } else if error.errorDescription.hasPrefix("SESSION_TOO_FRESH_") {
+ let timeout = String(error.errorDescription[error.errorDescription.index(error.errorDescription.startIndex, offsetBy: "SESSION_TOO_FRESH_".count)...])
+ if let value = Int32(timeout) {
+ return .authSessionTooFresh(value)
+ }
+ } else if error.errorDescription == "USER_PRIVACY_RESTRICTED" {
+ return .restricted
+ } else if error.errorDescription == "USER_BLOCKED" {
+ return .userBlocked
+ }
+ return .generic
+ }
+ |> mapToSignal { _ -> Signal in
+ return .complete()
+ }
+ } else {
+ return .fail(.generic)
+ }
+ }
+}
+
+public func requestMessageActionCallback(account: Account, messageId: MessageId, isGame :Bool, password: String?, data: MemoryBuffer?) -> Signal {
+ return account.postbox.loadedPeerWithId(messageId.peerId)
+ |> castError(MessageActionCallbackError.self)
|> take(1)
|> mapToSignal { peer in
if let inputPeer = apiInputPeer(peer) {
@@ -28,28 +87,79 @@ public func requestMessageActionCallback(account: Account, messageId: MessageId,
if isGame {
flags |= Int32(1 << 1)
}
- return account.network.request(Api.functions.messages.getBotCallbackAnswer(flags: flags, peer: inputPeer, msgId: messageId.id, data: dataBuffer))
- |> map(Optional.init)
- |> `catch` { _ -> Signal in
- return .single(nil)
- }
- |> map { result -> MessageActionCallbackResult in
- guard let result = result else {
- return .none
+
+ let checkPassword: Signal
+ if let password = password, !password.isEmpty {
+ flags |= Int32(1 << 2)
+
+ checkPassword = twoStepAuthData(account.network)
+ |> mapError { error -> MessageActionCallbackError in
+ if error.errorDescription.hasPrefix("FLOOD_WAIT") {
+ return .limitExceeded
+ } else {
+ return .generic
+ }
}
- switch result {
- case let .botCallbackAnswer(flags, message, url, cacheTime):
- if let message = message {
- if (flags & (1 << 1)) != 0 {
- return .alert(message)
- } else {
- return .toast(message)
- }
- } else if let url = url {
- return .url(url)
- } else {
- return .none
+ |> mapToSignal { authData -> Signal in
+ if let currentPasswordDerivation = authData.currentPasswordDerivation, let srpSessionData = authData.srpSessionData {
+ guard let kdfResult = passwordKDF(encryptionProvider: account.network.encryptionProvider, password: password, derivation: currentPasswordDerivation, srpSessionData: srpSessionData) else {
+ return .fail(.generic)
}
+ return .single(.inputCheckPasswordSRP(srpId: kdfResult.id, A: Buffer(data: kdfResult.A), M1: Buffer(data: kdfResult.M1)))
+ } else {
+ return .fail(.twoStepAuthMissing)
+ }
+ }
+ } else {
+ checkPassword = .single(nil)
+ }
+
+ return checkPassword
+ |> mapToSignal { password -> Signal in
+ return account.network.request(Api.functions.messages.getBotCallbackAnswer(flags: flags, peer: inputPeer, msgId: messageId.id, data: dataBuffer, password: password))
+ |> map(Optional.init)
+ |> mapError { error -> MessageActionCallbackError in
+ if error.errorDescription.hasPrefix("FLOOD_WAIT") {
+ return .limitExceeded
+ } else if error.errorDescription == "PASSWORD_HASH_INVALID" {
+ return .invalidPassword
+ } else if error.errorDescription == "PASSWORD_MISSING" {
+ return .twoStepAuthMissing
+ } else if error.errorDescription.hasPrefix("PASSWORD_TOO_FRESH_") {
+ let timeout = String(error.errorDescription[error.errorDescription.index(error.errorDescription.startIndex, offsetBy: "PASSWORD_TOO_FRESH_".count)...])
+ if let value = Int32(timeout) {
+ return .twoStepAuthTooFresh(value)
+ }
+ } else if error.errorDescription.hasPrefix("SESSION_TOO_FRESH_") {
+ let timeout = String(error.errorDescription[error.errorDescription.index(error.errorDescription.startIndex, offsetBy: "SESSION_TOO_FRESH_".count)...])
+ if let value = Int32(timeout) {
+ return .authSessionTooFresh(value)
+ }
+ } else if error.errorDescription == "USER_PRIVACY_RESTRICTED" {
+ return .restricted
+ } else if error.errorDescription == "USER_BLOCKED" {
+ return .userBlocked
+ }
+ return .generic
+ }
+ |> map { result -> MessageActionCallbackResult in
+ guard let result = result else {
+ return .none
+ }
+ switch result {
+ case let .botCallbackAnswer(flags, message, url, cacheTime):
+ if let message = message {
+ if (flags & (1 << 1)) != 0 {
+ return .alert(message)
+ } else {
+ return .toast(message)
+ }
+ } else if let url = url {
+ return .url(url)
+ } else {
+ return .none
+ }
+ }
}
}
} else {
diff --git a/submodules/TelegramCore/Sources/SearchMessages.swift b/submodules/TelegramCore/Sources/SearchMessages.swift
index c7710ab77e..e0e3f0a79c 100644
--- a/submodules/TelegramCore/Sources/SearchMessages.swift
+++ b/submodules/TelegramCore/Sources/SearchMessages.swift
@@ -7,7 +7,7 @@ import MtProtoKit
import SyncCore
public enum SearchMessagesLocation: Equatable {
- case general
+ case general(tags: MessageTags?)
case group(PeerGroupId)
case peer(peerId: PeerId, fromId: PeerId?, tags: MessageTags?)
case publicForwards(messageId: MessageId, datacenterId: Int?)
@@ -192,21 +192,7 @@ public func searchMessages(account: Account, location: SearchMessagesLocation, q
}
}
- let filter: Api.MessagesFilter
-
- if let tags = tags {
- if tags.contains(.file) {
- filter = .inputMessagesFilterDocument
- } else if tags.contains(.music) {
- filter = .inputMessagesFilterMusic
- } else if tags.contains(.webPage) {
- filter = .inputMessagesFilterUrl
- } else {
- filter = .inputMessagesFilterEmpty
- }
- } else {
- filter = .inputMessagesFilterEmpty
- }
+ let filter: Api.MessagesFilter = tags.flatMap { messageFilterForTagMask($0) } ?? .inputMessagesFilterEmpty
remoteSearchResult = account.postbox.transaction { transaction -> (peer: Peer, additionalPeer: Peer?, from: Peer?)? in
guard let peer = transaction.getPeer(peerId) else {
return nil
@@ -270,7 +256,8 @@ public func searchMessages(account: Account, location: SearchMessagesLocation, q
}
case .group:
remoteSearchResult = .single((nil, nil))
- case .general:
+ case let .general(tags):
+ let filter: Api.MessagesFilter = tags.flatMap { messageFilterForTagMask($0) } ?? .inputMessagesFilterEmpty
remoteSearchResult = account.postbox.transaction { transaction -> (Int32, MessageIndex?, Api.InputPeer) in
var lowerBound: MessageIndex?
if let state = state, let message = state.main.messages.last {
@@ -283,7 +270,7 @@ public func searchMessages(account: Account, location: SearchMessagesLocation, q
}
}
|> mapToSignal { (nextRate, lowerBound, inputPeer) in
- return account.network.request(Api.functions.messages.searchGlobal(flags: 0, folderId: nil, q: query, offsetRate: nextRate, offsetPeer: inputPeer, offsetId: lowerBound?.id.id ?? 0, limit: limit), automaticFloodWait: false)
+ return account.network.request(Api.functions.messages.searchGlobal(flags: 0, folderId: nil, q: query, filter: filter, offsetRate: nextRate, offsetPeer: inputPeer, offsetId: lowerBound?.id.id ?? 0, limit: limit), automaticFloodWait: false)
|> map { result -> (Api.messages.Messages?, Api.messages.Messages?) in
return (result, nil)
}
@@ -292,8 +279,7 @@ public func searchMessages(account: Account, location: SearchMessagesLocation, q
}
}
case let .publicForwards(messageId, datacenterId):
- remoteSearchResult = .single((nil, nil))
- /*remoteSearchResult = account.postbox.transaction { transaction -> (Api.InputChannel?, Int32, MessageIndex?, Api.InputPeer) in
+ remoteSearchResult = account.postbox.transaction { transaction -> (Api.InputChannel?, Int32, MessageIndex?, Api.InputPeer) in
let sourcePeer = transaction.getPeer(messageId.peerId)
let inputChannel = sourcePeer.flatMap { apiInputChannel($0) }
@@ -330,7 +316,7 @@ public func searchMessages(account: Account, location: SearchMessagesLocation, q
|> `catch` { _ -> Signal<(Api.messages.Messages?, Api.messages.Messages?), NoError> in
return .single((nil, nil))
}
- }*/
+ }
}
return remoteSearchResult
diff --git a/submodules/TelegramCore/Sources/Serialization.swift b/submodules/TelegramCore/Sources/Serialization.swift
index eee8616a22..9d7dec93c3 100644
--- a/submodules/TelegramCore/Sources/Serialization.swift
+++ b/submodules/TelegramCore/Sources/Serialization.swift
@@ -210,7 +210,7 @@ public class BoxedMessage: NSObject {
public class Serialization: NSObject, MTSerialization {
public func currentLayer() -> UInt {
- return 117
+ return 118
}
public func parseMessage(_ data: Data!) -> Any! {
diff --git a/submodules/TelegramCore/Sources/StoreMessage_Telegram.swift b/submodules/TelegramCore/Sources/StoreMessage_Telegram.swift
index aa1ec9bd2e..02fd29efd6 100644
--- a/submodules/TelegramCore/Sources/StoreMessage_Telegram.swift
+++ b/submodules/TelegramCore/Sources/StoreMessage_Telegram.swift
@@ -30,6 +30,7 @@ public func tagsForStoreMessage(incoming: Bool, attributes: [MessageAttribute],
if let _ = attachment as? TelegramMediaImage {
if !isSecret {
tags.insert(.photoOrVideo)
+ tags.insert(.photo)
}
} else if let file = attachment as? TelegramMediaFile {
var refinedTag: MessageTags? = .file
@@ -41,7 +42,7 @@ public func tagsForStoreMessage(incoming: Bool, attributes: [MessageAttribute],
refinedTag = .voiceOrInstantVideo
} else {
if !isSecret {
- refinedTag = .photoOrVideo
+ refinedTag = [.photoOrVideo, .video]
} else {
refinedTag = nil
}
@@ -136,7 +137,7 @@ func apiMessagePeerId(_ messsage: Api.Message) -> PeerId? {
func apiMessagePeerIds(_ message: Api.Message) -> [PeerId] {
switch message {
- case let .message(flags, _, fromId, toId, fwdHeader, viaBotId, _, _, _, media, _, entities, _, _, _, _, _):
+ case let .message(flags, _, fromId, toId, fwdHeader, viaBotId, _, _, _, media, _, entities, _, _, _, _, _, _):
let peerId: PeerId
switch toId {
case let .peerUser(userId):
@@ -240,7 +241,7 @@ func apiMessagePeerIds(_ message: Api.Message) -> [PeerId] {
func apiMessageAssociatedMessageIds(_ message: Api.Message) -> [MessageId]? {
switch message {
- case let .message(flags, _, fromId, toId, _, _, replyToMsgId, _, _, _, _, _, _, _, _, _, _):
+ case let .message(flags, _, fromId, toId, _, _, replyToMsgId, _, _, _, _, _, _, _, _, _, _, _):
if let replyToMsgId = replyToMsgId {
let peerId: PeerId
switch toId {
@@ -398,7 +399,7 @@ func messageTextEntitiesFromApiEntities(_ entities: [Api.MessageEntity]) -> [Mes
extension StoreMessage {
convenience init?(apiMessage: Api.Message, namespace: MessageId.Namespace = Namespaces.Message.Cloud) {
switch apiMessage {
- case let .message(flags, id, fromId, toId, fwdFrom, viaBotId, replyToMsgId, date, message, media, replyMarkup, entities, views, editDate, postAuthor, groupingId, restrictionReason):
+ case let .message(flags, id, fromId, toId, fwdFrom, viaBotId, replyToMsgId, date, message, media, replyMarkup, entities, views, forwards, editDate, postAuthor, groupingId, restrictionReason):
let peerId: PeerId
var authorId: PeerId?
switch toId {
@@ -517,14 +518,16 @@ extension StoreMessage {
attributes.append(ReplyMessageAttribute(messageId: MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: replyToMsgId)))
}
- if let views = views, namespace != Namespaces.Message.ScheduledCloud {
- attributes.append(ViewCountMessageAttribute(count: Int(views)))
+ if namespace != Namespaces.Message.ScheduledCloud {
+ if let views = views {
+ attributes.append(ViewCountMessageAttribute(count: Int(views)))
+ }
+
+ if let forwards = forwards {
+ attributes.append(ForwardCountMessageAttribute(count: Int(forwards)))
+ }
}
- /*if let forwards = forwards, namespace != Namespaces.Message.ScheduledCloud {
- attributes.append(ForwardCountMessageAttribute(count: Int(forwards)))
- }*/
-
if let editDate = editDate {
attributes.append(EditedMessageAttribute(date: editDate, isHidden: (flags & (1 << 21)) != 0))
}
diff --git a/submodules/TelegramCore/Sources/UpdateMessageService.swift b/submodules/TelegramCore/Sources/UpdateMessageService.swift
index 34cd793682..7ca077332f 100644
--- a/submodules/TelegramCore/Sources/UpdateMessageService.swift
+++ b/submodules/TelegramCore/Sources/UpdateMessageService.swift
@@ -58,7 +58,7 @@ class UpdateMessageService: NSObject, MTMessageService {
self.putNext(groups)
}
case let .updateShortChatMessage(flags, id, fromId, chatId, message, pts, ptsCount, date, fwdFrom, viaBotId, replyToMsgId, entities):
- let generatedMessage = Api.Message.message(flags: flags, id: id, fromId: fromId, toId: Api.Peer.peerChat(chatId: chatId), fwdFrom: fwdFrom, viaBotId: viaBotId, replyToMsgId: replyToMsgId, date: date, message: message, media: Api.MessageMedia.messageMediaEmpty, replyMarkup: nil, entities: entities, views: nil, editDate: nil, postAuthor: nil, groupedId: nil, restrictionReason: nil)
+ let generatedMessage = Api.Message.message(flags: flags, id: id, fromId: fromId, toId: Api.Peer.peerChat(chatId: chatId), fwdFrom: fwdFrom, viaBotId: viaBotId, replyToMsgId: replyToMsgId, date: date, message: message, media: Api.MessageMedia.messageMediaEmpty, replyMarkup: nil, entities: entities, views: nil, forwards: nil, editDate: nil, postAuthor: nil, groupedId: nil, restrictionReason: nil)
let update = Api.Update.updateNewMessage(message: generatedMessage, pts: pts, ptsCount: ptsCount)
let groups = groupUpdates([update], users: [], chats: [], date: date, seqRange: nil)
if groups.count != 0 {
@@ -75,7 +75,7 @@ class UpdateMessageService: NSObject, MTMessageService {
generatedToId = Api.Peer.peerUser(userId: self.peerId.id)
}
- let generatedMessage = Api.Message.message(flags: flags, id: id, fromId: generatedFromId, toId: generatedToId, fwdFrom: fwdFrom, viaBotId: viaBotId, replyToMsgId: replyToMsgId, date: date, message: message, media: Api.MessageMedia.messageMediaEmpty, replyMarkup: nil, entities: entities, views: nil, editDate: nil, postAuthor: nil, groupedId: nil, restrictionReason: nil)
+ let generatedMessage = Api.Message.message(flags: flags, id: id, fromId: generatedFromId, toId: generatedToId, fwdFrom: fwdFrom, viaBotId: viaBotId, replyToMsgId: replyToMsgId, date: date, message: message, media: Api.MessageMedia.messageMediaEmpty, replyMarkup: nil, entities: entities, views: nil, forwards: nil, editDate: nil, postAuthor: nil, groupedId: nil, restrictionReason: nil)
let update = Api.Update.updateNewMessage(message: generatedMessage, pts: pts, ptsCount: ptsCount)
let groups = groupUpdates([update], users: [], chats: [], date: date, seqRange: nil)
if groups.count != 0 {
diff --git a/submodules/TelegramPresentationData/Sources/PresentationThemeEssentialGraphics.swift b/submodules/TelegramPresentationData/Sources/PresentationThemeEssentialGraphics.swift
index 2aa29887cd..065fbfd7b5 100644
--- a/submodules/TelegramPresentationData/Sources/PresentationThemeEssentialGraphics.swift
+++ b/submodules/TelegramPresentationData/Sources/PresentationThemeEssentialGraphics.swift
@@ -308,10 +308,13 @@ public final class PrincipalThemeEssentialGraphics {
self.clockFreeMinImage = emptyImage
self.dateAndStatusMediaBackground = emptyImage
self.dateAndStatusFreeBackground = emptyImage
- self.incomingDateAndStatusImpressionIcon = emptyImage
- self.outgoingDateAndStatusImpressionIcon = emptyImage
- self.mediaImpressionIcon = emptyImage
- self.freeImpressionIcon = emptyImage
+
+ let impressionCountImage = UIImage(bundleImageName: "Chat/Message/ImpressionCount")!
+ self.incomingDateAndStatusImpressionIcon = generateTintedImage(image: impressionCountImage, color: theme.message.incoming.secondaryTextColor)!
+ self.outgoingDateAndStatusImpressionIcon = generateTintedImage(image: impressionCountImage, color: theme.message.outgoing.secondaryTextColor)!
+ self.mediaImpressionIcon = generateTintedImage(image: impressionCountImage, color: .white)!
+ self.freeImpressionIcon = generateTintedImage(image: impressionCountImage, color: serviceColor.primaryText)!
+
self.radialIndicatorFileIconIncoming = emptyImage
self.radialIndicatorFileIconOutgoing = emptyImage
} else {
diff --git a/submodules/TelegramUI/Sources/AuthorizationSequencePhoneEntryController.swift b/submodules/TelegramUI/Sources/AuthorizationSequencePhoneEntryController.swift
index db77030f94..1ff91532a6 100644
--- a/submodules/TelegramUI/Sources/AuthorizationSequencePhoneEntryController.swift
+++ b/submodules/TelegramUI/Sources/AuthorizationSequencePhoneEntryController.swift
@@ -58,6 +58,8 @@ final class AuthorizationSequencePhoneEntryController: ViewController {
self.openUrl = openUrl
self.back = back
+ loadServerCountryCodes(accountManager: sharedContext.accountManager, network: account.network)
+
super.init(navigationBarPresentationData: NavigationBarPresentationData(theme: AuthorizationSequenceController.navigationBarTheme(presentationData.theme), strings: NavigationBarStrings(presentationStrings: presentationData.strings)))
self.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait)
diff --git a/submodules/TelegramUI/Sources/AuthorizationSequencePhoneEntryControllerNode.swift b/submodules/TelegramUI/Sources/AuthorizationSequencePhoneEntryControllerNode.swift
index 8444c74660..0f514b7657 100644
--- a/submodules/TelegramUI/Sources/AuthorizationSequencePhoneEntryControllerNode.swift
+++ b/submodules/TelegramUI/Sources/AuthorizationSequencePhoneEntryControllerNode.swift
@@ -121,26 +121,32 @@ private final class PhoneAndCountryNode: ASDisplayNode {
self.phoneInputNode.countryCodeField.textField.disableAutomaticKeyboardHandling = [.forward]
self.phoneInputNode.numberField.textField.disableAutomaticKeyboardHandling = [.forward]
-
self.countryButton.contentEdgeInsets = UIEdgeInsets(top: 0.0, left: 15.0, bottom: 10.0, right: 0.0)
self.countryButton.contentHorizontalAlignment = .left
- self.phoneInputNode.numberField.textField.attributedPlaceholder = NSAttributedString(string: strings.Login_PhonePlaceholder, font: Font.regular(20.0), textColor: theme.list.itemPlaceholderTextColor)
+// self.phoneInputNode.numberField.textField.attributedPlaceholder = NSAttributedString(string: strings.Login_PhonePlaceholder, font: Font.regular(20.0), textColor: theme.list.itemPlaceholderTextColor)
self.countryButton.addTarget(self, action: #selector(self.countryPressed), forControlEvents: .touchUpInside)
self.phoneInputNode.countryCodeUpdated = { [weak self] code, name in
+ let font = Font.with(size: 20.0, design: .monospace, traits: [])
if let strongSelf = self {
if let code = Int(code), let name = name, let countryName = countryCodeAndIdToName[CountryCodeAndId(code: code, id: name)] {
let flagString = emojiFlagForISOCountryCode(name as NSString)
let localizedName: String = AuthorizationSequenceCountrySelectionController.lookupCountryNameById(name, strings: strongSelf.strings) ?? countryName
strongSelf.countryButton.setTitle("\(flagString) \(localizedName)", with: Font.regular(20.0), with: theme.list.itemPrimaryTextColor, for: [])
+
+ strongSelf.phoneInputNode.mask = AuthorizationSequenceCountrySelectionController.lookupPatternByCode(code).flatMap { NSAttributedString(string: $0, font: font, textColor: theme.list.itemPlaceholderTextColor) }
} else if let code = Int(code), let (countryId, countryName) = countryCodeToIdAndName[code] {
let flagString = emojiFlagForISOCountryCode(countryId as NSString)
let localizedName: String = AuthorizationSequenceCountrySelectionController.lookupCountryNameById(countryId, strings: strongSelf.strings) ?? countryName
strongSelf.countryButton.setTitle("\(flagString) \(localizedName)", with: Font.regular(20.0), with: theme.list.itemPrimaryTextColor, for: [])
+
+ strongSelf.phoneInputNode.mask = AuthorizationSequenceCountrySelectionController.lookupPatternByCode(code).flatMap { NSAttributedString(string: $0, font: font, textColor: theme.list.itemPlaceholderTextColor) }
} else {
strongSelf.countryButton.setTitle(strings.Login_SelectCountry_Title, with: Font.regular(20.0), with: theme.list.itemPlaceholderTextColor, for: [])
+
+ strongSelf.phoneInputNode.mask = nil
}
}
}
@@ -165,12 +171,14 @@ private final class PhoneAndCountryNode: ASDisplayNode {
let countryCodeFrame = CGRect(origin: CGPoint(x: 18.0, y: size.height - 57.0), size: CGSize(width: 60.0, height: 57.0))
let numberFrame = CGRect(origin: CGPoint(x: 96.0, y: size.height - 57.0), size: CGSize(width: size.width - 96.0 - 8.0, height: 57.0))
+ let placeholderFrame = numberFrame.offsetBy(dx: -1.0, dy: 16.0)
let phoneInputFrame = countryCodeFrame.union(numberFrame)
self.phoneInputNode.frame = phoneInputFrame
self.phoneInputNode.countryCodeField.frame = countryCodeFrame.offsetBy(dx: -phoneInputFrame.minX, dy: -phoneInputFrame.minY)
self.phoneInputNode.numberField.frame = numberFrame.offsetBy(dx: -phoneInputFrame.minX, dy: -phoneInputFrame.minY)
+ self.phoneInputNode.placeholderNode.frame = placeholderFrame.offsetBy(dx: -phoneInputFrame.minX, dy: -phoneInputFrame.minY)
}
}
@@ -204,6 +212,8 @@ private final class ContactSyncNode: ASDisplayNode {
}
}
+
+
final class AuthorizationSequencePhoneEntryControllerNode: ASDisplayNode {
private let sharedContext: SharedAccountContext
private var account: UnauthorizedAccount
diff --git a/submodules/TelegramUI/Sources/ChatButtonKeyboardInputNode.swift b/submodules/TelegramUI/Sources/ChatButtonKeyboardInputNode.swift
index dfd63fe8b1..0d1ec9db8c 100644
--- a/submodules/TelegramUI/Sources/ChatButtonKeyboardInputNode.swift
+++ b/submodules/TelegramUI/Sources/ChatButtonKeyboardInputNode.swift
@@ -176,11 +176,11 @@ final class ChatButtonKeyboardInputNode: ChatInputNode {
self.controllerInteraction.shareAccountContact()
case .openWebApp:
if let message = self.message {
- self.controllerInteraction.requestMessageActionCallback(message.id, nil, true)
+ self.controllerInteraction.requestMessageActionCallback(message.id, nil, true, false)
}
- case let .callback(data):
+ case let .callback(requiresPassword, data):
if let message = self.message {
- self.controllerInteraction.requestMessageActionCallback(message.id, data, false)
+ self.controllerInteraction.requestMessageActionCallback(message.id, data, false, requiresPassword)
}
case let .switchInline(samePeer, query):
if let message = message {
diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift
index 99003cbfe6..ca5c2dd380 100644
--- a/submodules/TelegramUI/Sources/ChatController.swift
+++ b/submodules/TelegramUI/Sources/ChatController.swift
@@ -793,7 +793,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
strongSelf.enqueueChatContextResult(collection, result, hideVia: true, closeMediaInput: true)
return true
- }, requestMessageActionCallback: { [weak self] messageId, data, isGame in
+ }, requestMessageActionCallback: { [weak self] messageId, data, isGame, requiresPassword in
if let strongSelf = self {
guard !strongSelf.presentationInterfaceState.isScheduledMessages else {
strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: strongSelf.presentationData.strings.ScheduledMessages_BotActionUnavailable, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
@@ -819,52 +819,73 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
})
- strongSelf.messageActionCallbackDisposable.set(((requestMessageActionCallback(account: strongSelf.context.account, messageId: messageId, isGame: isGame, data: data)
- |> afterDisposed {
- Queue.mainQueue().async {
- if let strongSelf = self {
- strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, {
- return $0.updatedTitlePanelContext {
- if let index = $0.firstIndex(where: {
- switch $0 {
- case .requestInProgress:
- return true
- default:
- return false
- }
- }) {
- var updatedContexts = $0
- updatedContexts.remove(at: index)
- return updatedContexts
- }
- return $0
- }
+ let proceedWithResult: (MessageActionCallbackResult) -> Void = { [weak self] result in
+ guard let strongSelf = self else {
+ return
+ }
+
+ switch result {
+ case .none:
+ break
+ case let .alert(text):
+ strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
+ case let .toast(text):
+ let message: Signal = .single(text)
+ let noMessage: Signal = .single(nil)
+ let delayedNoMessage: Signal = noMessage |> delay(1.0, queue: Queue.mainQueue())
+ strongSelf.botCallbackAlertMessage.set(message |> then(delayedNoMessage))
+ case let .url(url):
+ if isGame {
+ strongSelf.chatDisplayNode.dismissInput()
+ strongSelf.effectiveNavigationController?.pushViewController(GameController(context: strongSelf.context, url: url, message: message))
+ } else {
+ strongSelf.openUrl(url, concealed: false)
+ }
+ }
+ }
+
+ let account = strongSelf.context.account
+ if requiresPassword {
+ strongSelf.messageActionCallbackDisposable.set((requestMessageActionCallbackPasswordCheck(account: account, messageId: messageId, isGame: isGame, data: data)
+ |> deliverOnMainQueue).start(error: { error in
+ let controller = ownershipTransferController(context: context, initialError: error, present: { c, a in
+ strongSelf.present(c, in: .window(.root), with: a)
+ }, commit: { password in
+ return requestMessageActionCallback(account: account, messageId: messageId, isGame: isGame, password: password, data: data)
+ }, completion: { result in
+ proceedWithResult(result)
})
+ strongSelf.present(controller, in: .window(.root))
+ }))
+ } else {
+ strongSelf.messageActionCallbackDisposable.set(((requestMessageActionCallback(account: account, messageId: messageId, isGame: isGame, password: nil, data: data)
+ |> afterDisposed {
+ Queue.mainQueue().async {
+ if let strongSelf = self {
+ strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, {
+ return $0.updatedTitlePanelContext {
+ if let index = $0.firstIndex(where: {
+ switch $0 {
+ case .requestInProgress:
+ return true
+ default:
+ return false
+ }
+ }) {
+ var updatedContexts = $0
+ updatedContexts.remove(at: index)
+ return updatedContexts
+ }
+ return $0
+ }
+ })
+ }
}
- }
- })
- |> deliverOnMainQueue).start(next: { result in
- if let strongSelf = self {
- switch result {
- case .none:
- break
- case let .alert(text):
- strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
- case let .toast(text):
- let message: Signal = .single(text)
- let noMessage: Signal = .single(nil)
- let delayedNoMessage: Signal = noMessage |> delay(1.0, queue: Queue.mainQueue())
- strongSelf.botCallbackAlertMessage.set(message |> then(delayedNoMessage))
- case let .url(url):
- if isGame {
- strongSelf.chatDisplayNode.dismissInput()
- strongSelf.effectiveNavigationController?.pushViewController(GameController(context: strongSelf.context, url: url, message: message))
- } else {
- strongSelf.openUrl(url, concealed: false)
- }
- }
- }
- }))
+ })
+ |> deliverOnMainQueue).start(next: { result in
+ proceedWithResult(result)
+ }))
+ }
}
}
}, requestMessageActionUrlAuth: { [weak self] defaultUrl, messageId, buttonId in
@@ -9215,6 +9236,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
public override var keyShortcuts: [KeyShortcut] {
+ if !self.traceVisibility() || !isTopmostChatController(self) {
+ return []
+ }
+
let strings = self.presentationData.strings
var inputShortcuts: [KeyShortcut]
@@ -9325,16 +9350,16 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
let otherShortcuts: [KeyShortcut] = [
- KeyShortcut(title: strings.KeyCommand_ScrollUp, input: UIKeyCommand.inputUpArrow, modifiers: [.shift], action: { [weak self] in
- if let strongSelf = self {
- _ = strongSelf.chatDisplayNode.historyNode.scrollWithDirection(.down, distance: 75.0)
- }
- }),
- KeyShortcut(title: strings.KeyCommand_ScrollDown, input: UIKeyCommand.inputDownArrow, modifiers: [.shift], action: { [weak self] in
- if let strongSelf = self {
- _ = strongSelf.chatDisplayNode.historyNode.scrollWithDirection(.up, distance: 75.0)
- }
- }),
+// KeyShortcut(title: strings.KeyCommand_ScrollUp, input: UIKeyCommand.inputUpArrow, modifiers: [.shift], action: { [weak self] in
+// if let strongSelf = self {
+// _ = strongSelf.chatDisplayNode.historyNode.scrollWithDirection(.down, distance: 75.0)
+// }
+// }),
+// KeyShortcut(title: strings.KeyCommand_ScrollDown, input: UIKeyCommand.inputDownArrow, modifiers: [.shift], action: { [weak self] in
+// if let strongSelf = self {
+// _ = strongSelf.chatDisplayNode.historyNode.scrollWithDirection(.up, distance: 75.0)
+// }
+// }),
KeyShortcut(title: strings.KeyCommand_ChatInfo, input: "I", modifiers: [.command, .control], action: { [weak self] in
if let strongSelf = self {
strongSelf.interfaceInteraction?.openPeerInfo()
diff --git a/submodules/TelegramUI/Sources/ChatControllerInteraction.swift b/submodules/TelegramUI/Sources/ChatControllerInteraction.swift
index 5e401f8a91..844ffdef81 100644
--- a/submodules/TelegramUI/Sources/ChatControllerInteraction.swift
+++ b/submodules/TelegramUI/Sources/ChatControllerInteraction.swift
@@ -71,7 +71,7 @@ public final class ChatControllerInteraction {
let sendSticker: (FileMediaReference, Bool, ASDisplayNode, CGRect) -> Bool
let sendGif: (FileMediaReference, ASDisplayNode, CGRect) -> Bool
let sendBotContextResultAsGif: (ChatContextResultCollection, ChatContextResult, ASDisplayNode, CGRect) -> Bool
- let requestMessageActionCallback: (MessageId, MemoryBuffer?, Bool) -> Void
+ let requestMessageActionCallback: (MessageId, MemoryBuffer?, Bool, Bool) -> Void
let requestMessageActionUrlAuth: (String, MessageId, Int32) -> Void
let activateSwitchInline: (PeerId?, String) -> Void
let openUrl: (String, Bool, Bool?, Message?) -> Void
@@ -138,7 +138,7 @@ public final class ChatControllerInteraction {
var searchTextHighightState: (String, [MessageIndex])?
var seenOneTimeAnimatedMedia = Set()
- init(openMessage: @escaping (Message, ChatControllerInteractionOpenMessageMode) -> Bool, openPeer: @escaping (PeerId?, ChatControllerInteractionNavigateToPeer, Message?) -> Void, openPeerMention: @escaping (String) -> Void, openMessageContextMenu: @escaping (Message, Bool, ASDisplayNode, CGRect, UIGestureRecognizer?) -> Void, openMessageContextActions: @escaping (Message, ASDisplayNode, CGRect, ContextGesture?) -> Void, navigateToMessage: @escaping (MessageId, MessageId) -> Void, tapMessage: ((Message) -> Void)?, clickThroughMessage: @escaping () -> Void, toggleMessagesSelection: @escaping ([MessageId], Bool) -> Void, sendCurrentMessage: @escaping (Bool) -> Void, sendMessage: @escaping (String) -> Void, sendSticker: @escaping (FileMediaReference, Bool, ASDisplayNode, CGRect) -> Bool, sendGif: @escaping (FileMediaReference, ASDisplayNode, CGRect) -> Bool, sendBotContextResultAsGif: @escaping (ChatContextResultCollection, ChatContextResult, ASDisplayNode, CGRect) -> Bool, requestMessageActionCallback: @escaping (MessageId, MemoryBuffer?, Bool) -> Void, requestMessageActionUrlAuth: @escaping (String, MessageId, Int32) -> Void, activateSwitchInline: @escaping (PeerId?, String) -> Void, openUrl: @escaping (String, Bool, Bool?, Message?) -> Void, shareCurrentLocation: @escaping () -> Void, shareAccountContact: @escaping () -> Void, sendBotCommand: @escaping (MessageId?, String) -> Void, openInstantPage: @escaping (Message, ChatMessageItemAssociatedData?) -> Void, openWallpaper: @escaping (Message) -> Void, openTheme: @escaping (Message) -> Void, openHashtag: @escaping (String?, String) -> Void, updateInputState: @escaping ((ChatTextInputState) -> ChatTextInputState) -> Void, updateInputMode: @escaping ((ChatInputMode) -> ChatInputMode) -> Void, openMessageShareMenu: @escaping (MessageId) -> Void, presentController: @escaping (ViewController, Any?) -> Void, navigationController: @escaping () -> NavigationController?, chatControllerNode: @escaping () -> ASDisplayNode?, reactionContainerNode: @escaping () -> ReactionSelectionParentNode?, presentGlobalOverlayController: @escaping (ViewController, Any?) -> Void, callPeer: @escaping (PeerId, Bool) -> Void, longTap: @escaping (ChatControllerInteractionLongTapAction, Message?) -> Void, openCheckoutOrReceipt: @escaping (MessageId) -> Void, openSearch: @escaping () -> Void, setupReply: @escaping (MessageId) -> Void, canSetupReply: @escaping (Message) -> ChatControllerInteractionSwipeAction, navigateToFirstDateMessage: @escaping(Int32) ->Void, requestRedeliveryOfFailedMessages: @escaping (MessageId) -> Void, addContact: @escaping (String) -> Void, rateCall: @escaping (Message, CallId, Bool) -> Void, requestSelectMessagePollOptions: @escaping (MessageId, [Data]) -> Void, requestOpenMessagePollResults: @escaping (MessageId, MediaId) -> Void, openAppStorePage: @escaping () -> Void, displayMessageTooltip: @escaping (MessageId, String, ASDisplayNode?, CGRect?) -> Void, seekToTimecode: @escaping (Message, Double, Bool) -> Void, scheduleCurrentMessage: @escaping () -> Void, sendScheduledMessagesNow: @escaping ([MessageId]) -> Void, editScheduledMessagesTime: @escaping ([MessageId]) -> Void, performTextSelectionAction: @escaping (UInt32, NSAttributedString, TextSelectionAction) -> Void, updateMessageLike: @escaping (MessageId, Bool) -> Void, openMessageReactions: @escaping (MessageId) -> Void, displaySwipeToReplyHint: @escaping () -> Void, dismissReplyMarkupMessage: @escaping (Message) -> Void, openMessagePollResults: @escaping (MessageId, Data) -> Void, openPollCreation: @escaping (Bool?) -> Void, displayPollSolution: @escaping (TelegramMediaPollResults.Solution, ASDisplayNode) -> Void, displayPsa: @escaping (String, ASDisplayNode) -> Void, displayDiceTooltip: @escaping (TelegramMediaDice) -> Void, animateDiceSuccess: @escaping () -> Void, greetingStickerNode: @escaping () -> (ASDisplayNode, ASDisplayNode, ASDisplayNode, () -> Void)?, openPeerContextMenu: @escaping (Peer, ASDisplayNode, CGRect, ContextGesture?) -> Void, requestMessageUpdate: @escaping (MessageId) -> Void, cancelInteractiveKeyboardGestures: @escaping () -> Void, automaticMediaDownloadSettings: MediaAutoDownloadSettings, pollActionState: ChatInterfacePollActionState, stickerSettings: ChatInterfaceStickerSettings) {
+ init(openMessage: @escaping (Message, ChatControllerInteractionOpenMessageMode) -> Bool, openPeer: @escaping (PeerId?, ChatControllerInteractionNavigateToPeer, Message?) -> Void, openPeerMention: @escaping (String) -> Void, openMessageContextMenu: @escaping (Message, Bool, ASDisplayNode, CGRect, UIGestureRecognizer?) -> Void, openMessageContextActions: @escaping (Message, ASDisplayNode, CGRect, ContextGesture?) -> Void, navigateToMessage: @escaping (MessageId, MessageId) -> Void, tapMessage: ((Message) -> Void)?, clickThroughMessage: @escaping () -> Void, toggleMessagesSelection: @escaping ([MessageId], Bool) -> Void, sendCurrentMessage: @escaping (Bool) -> Void, sendMessage: @escaping (String) -> Void, sendSticker: @escaping (FileMediaReference, Bool, ASDisplayNode, CGRect) -> Bool, sendGif: @escaping (FileMediaReference, ASDisplayNode, CGRect) -> Bool, sendBotContextResultAsGif: @escaping (ChatContextResultCollection, ChatContextResult, ASDisplayNode, CGRect) -> Bool, requestMessageActionCallback: @escaping (MessageId, MemoryBuffer?, Bool, Bool) -> Void, requestMessageActionUrlAuth: @escaping (String, MessageId, Int32) -> Void, activateSwitchInline: @escaping (PeerId?, String) -> Void, openUrl: @escaping (String, Bool, Bool?, Message?) -> Void, shareCurrentLocation: @escaping () -> Void, shareAccountContact: @escaping () -> Void, sendBotCommand: @escaping (MessageId?, String) -> Void, openInstantPage: @escaping (Message, ChatMessageItemAssociatedData?) -> Void, openWallpaper: @escaping (Message) -> Void, openTheme: @escaping (Message) -> Void, openHashtag: @escaping (String?, String) -> Void, updateInputState: @escaping ((ChatTextInputState) -> ChatTextInputState) -> Void, updateInputMode: @escaping ((ChatInputMode) -> ChatInputMode) -> Void, openMessageShareMenu: @escaping (MessageId) -> Void, presentController: @escaping (ViewController, Any?) -> Void, navigationController: @escaping () -> NavigationController?, chatControllerNode: @escaping () -> ASDisplayNode?, reactionContainerNode: @escaping () -> ReactionSelectionParentNode?, presentGlobalOverlayController: @escaping (ViewController, Any?) -> Void, callPeer: @escaping (PeerId, Bool) -> Void, longTap: @escaping (ChatControllerInteractionLongTapAction, Message?) -> Void, openCheckoutOrReceipt: @escaping (MessageId) -> Void, openSearch: @escaping () -> Void, setupReply: @escaping (MessageId) -> Void, canSetupReply: @escaping (Message) -> ChatControllerInteractionSwipeAction, navigateToFirstDateMessage: @escaping(Int32) ->Void, requestRedeliveryOfFailedMessages: @escaping (MessageId) -> Void, addContact: @escaping (String) -> Void, rateCall: @escaping (Message, CallId) -> Void, requestSelectMessagePollOptions: @escaping (MessageId, [Data]) -> Void, requestOpenMessagePollResults: @escaping (MessageId, MediaId) -> Void, openAppStorePage: @escaping () -> Void, displayMessageTooltip: @escaping (MessageId, String, ASDisplayNode?, CGRect?) -> Void, seekToTimecode: @escaping (Message, Double, Bool) -> Void, scheduleCurrentMessage: @escaping () -> Void, sendScheduledMessagesNow: @escaping ([MessageId]) -> Void, editScheduledMessagesTime: @escaping ([MessageId]) -> Void, performTextSelectionAction: @escaping (UInt32, NSAttributedString, TextSelectionAction) -> Void, updateMessageLike: @escaping (MessageId, Bool) -> Void, openMessageReactions: @escaping (MessageId) -> Void, displaySwipeToReplyHint: @escaping () -> Void, dismissReplyMarkupMessage: @escaping (Message) -> Void, openMessagePollResults: @escaping (MessageId, Data) -> Void, openPollCreation: @escaping (Bool?) -> Void, displayPollSolution: @escaping (TelegramMediaPollResults.Solution, ASDisplayNode) -> Void, displayPsa: @escaping (String, ASDisplayNode) -> Void, displayDiceTooltip: @escaping (TelegramMediaDice) -> Void, animateDiceSuccess: @escaping () -> Void, greetingStickerNode: @escaping () -> (ASDisplayNode, ASDisplayNode, ASDisplayNode, () -> Void)?, openPeerContextMenu: @escaping (Peer, ASDisplayNode, CGRect, ContextGesture?) -> Void, requestMessageUpdate: @escaping (MessageId) -> Void, cancelInteractiveKeyboardGestures: @escaping () -> Void, automaticMediaDownloadSettings: MediaAutoDownloadSettings, pollActionState: ChatInterfacePollActionState, stickerSettings: ChatInterfaceStickerSettings) {
self.openMessage = openMessage
self.openPeer = openPeer
self.openPeerMention = openPeerMention
@@ -215,7 +215,7 @@ public final class ChatControllerInteraction {
static var `default`: ChatControllerInteraction {
return ChatControllerInteraction(openMessage: { _, _ in
- return false }, openPeer: { _, _, _ in }, openPeerMention: { _ in }, openMessageContextMenu: { _, _, _, _, _ in }, openMessageContextActions: { _, _, _, _ in }, navigateToMessage: { _, _ in }, tapMessage: nil, clickThroughMessage: { }, toggleMessagesSelection: { _, _ in }, sendCurrentMessage: { _ in }, sendMessage: { _ in }, sendSticker: { _, _, _, _ in return false }, sendGif: { _, _, _ in return false }, sendBotContextResultAsGif: { _, _, _, _ in return false }, requestMessageActionCallback: { _, _, _ in }, requestMessageActionUrlAuth: { _, _, _ in }, activateSwitchInline: { _, _ in }, openUrl: { _, _, _, _ in }, shareCurrentLocation: {}, shareAccountContact: {}, sendBotCommand: { _, _ in }, openInstantPage: { _, _ in }, openWallpaper: { _ in }, openTheme: { _ in }, openHashtag: { _, _ in }, updateInputState: { _ in }, updateInputMode: { _ in }, openMessageShareMenu: { _ in
+ return false }, openPeer: { _, _, _ in }, openPeerMention: { _ in }, openMessageContextMenu: { _, _, _, _, _ in }, openMessageContextActions: { _, _, _, _ in }, navigateToMessage: { _, _ in }, tapMessage: nil, clickThroughMessage: { }, toggleMessagesSelection: { _, _ in }, sendCurrentMessage: { _ in }, sendMessage: { _ in }, sendSticker: { _, _, _, _ in return false }, sendGif: { _, _, _ in return false }, sendBotContextResultAsGif: { _, _, _, _ in return false }, requestMessageActionCallback: { _, _, _, _ in }, requestMessageActionUrlAuth: { _, _, _ in }, activateSwitchInline: { _, _ in }, openUrl: { _, _, _, _ in }, shareCurrentLocation: {}, shareAccountContact: {}, sendBotCommand: { _, _ in }, openInstantPage: { _, _ in }, openWallpaper: { _ in }, openTheme: { _ in }, openHashtag: { _, _ in }, updateInputState: { _ in }, updateInputMode: { _ in }, openMessageShareMenu: { _ in
}, presentController: { _, _ in }, navigationController: {
return nil
}, chatControllerNode: {
diff --git a/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift
index a475c1be81..b27c890dba 100644
--- a/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift
+++ b/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift
@@ -68,7 +68,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
private var highlightedState: Bool = false
- private var heartbeatHaptic: HeartbeatHaptic?
+ private var haptic: EmojiHaptic?
private var currentSwipeToReplyTranslation: CGFloat = 0.0
@@ -217,7 +217,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
didSet {
if self.visibilityStatus != oldValue {
self.updateVisibility()
- self.heartbeatHaptic?.enabled = self.visibilityStatus
+ self.haptic?.enabled = self.visibilityStatus
}
}
}
@@ -228,15 +228,20 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
}
if let telegramDice = self.telegramDice {
- let animationNode = ManagedDiceAnimationNode(context: item.context, emoji: telegramDice.emoji.strippedEmoji)
- if !item.message.effectivelyIncoming(item.context.account.peerId) {
- animationNode.success = { [weak self] in
- if let strongSelf = self, let item = strongSelf.item {
- item.controllerInteraction.animateDiceSuccess()
+// if telegramDice.emoji == "🎲" {
+// let animationNode = SlotMachineAnimationNode(context: item.context)
+// self.animationNode = animationNode
+// } else {
+ let animationNode = ManagedDiceAnimationNode(context: item.context, emoji: telegramDice.emoji.strippedEmoji)
+ if !item.message.effectivelyIncoming(item.context.account.peerId) {
+ animationNode.success = { [weak self] in
+ if let strongSelf = self, let item = strongSelf.item {
+ item.controllerInteraction.animateDiceSuccess()
+ }
}
}
- }
- self.animationNode = animationNode
+ self.animationNode = animationNode
+// }
} else {
let animationNode: AnimatedStickerNode
if let (node, parentNode, listNode, greetingCompletion) = item.controllerInteraction.greetingStickerNode(), let greetingStickerNode = node as? AnimatedStickerNode {
@@ -290,7 +295,13 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
self.setupNode(item: item)
- if let telegramDice = self.telegramDice, let diceNode = self.animationNode as? ManagedDiceAnimationNode {
+ if let telegramDice = self.telegramDice, let diceNode = self.animationNode as? SlotMachineAnimationNode {
+ if let value = telegramDice.value {
+ diceNode.setState(value == 0 ? .rolling : .value(value, true))
+ } else {
+ diceNode.setState(.rolling)
+ }
+ } else if let telegramDice = self.telegramDice, let diceNode = self.animationNode as? ManagedDiceAnimationNode {
if let value = telegramDice.value {
diceNode.setState(value == 0 ? .rolling : .value(value, true))
} else {
@@ -1098,25 +1109,30 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|> deliverOnMainQueue
}
- let beatingHearts: [UInt32] = [0x2764, 0x1F90E, 0x1F9E1, 0x1F49A, 0x1F49C, 0x1F49B, 0x1F5A4, 0x1F90D]
-
- if let text = self.item?.message.text, let firstScalar = text.unicodeScalars.first, beatingHearts.contains(firstScalar.value) {
+ let beatingHearts: [UInt32] = [0x2764, 0x1F90E, 0x1F9E1, 0x1F499, 0x1F49A, 0x1F49C, 0x1F49B, 0x1F5A4, 0x1F90D]
+ let peach = 0x1F351
+
+ if let text = self.item?.message.text, let firstScalar = text.unicodeScalars.first, beatingHearts.contains(firstScalar.value) || firstScalar.value == peach {
return .optionalAction({
let _ = startTime.start(next: { [weak self] time in
guard let strongSelf = self else {
return
}
- let heartbeatHaptic: HeartbeatHaptic
- if let current = strongSelf.heartbeatHaptic {
- heartbeatHaptic = current
+ var haptic: EmojiHaptic
+ if let current = strongSelf.haptic {
+ haptic = current
} else {
- heartbeatHaptic = HeartbeatHaptic()
- heartbeatHaptic.enabled = true
- strongSelf.heartbeatHaptic = heartbeatHaptic
+ if beatingHearts.contains(firstScalar.value) {
+ haptic = HeartbeatHaptic()
+ } else {
+ haptic = PeachHaptic()
+ }
+ haptic.enabled = true
+ strongSelf.haptic = haptic
}
- if !heartbeatHaptic.active {
- heartbeatHaptic.start(time: time)
+ if !haptic.active {
+ haptic.start(time: time)
}
})
})
diff --git a/submodules/TelegramUI/Sources/ChatMessageAttachedContentNode.swift b/submodules/TelegramUI/Sources/ChatMessageAttachedContentNode.swift
index 75ca4111d1..2b7e30a494 100644
--- a/submodules/TelegramUI/Sources/ChatMessageAttachedContentNode.swift
+++ b/submodules/TelegramUI/Sources/ChatMessageAttachedContentNode.swift
@@ -282,6 +282,7 @@ final class ChatMessageAttachedContentNode: ASDisplayNode {
let currentAdditionalImageBadgeNode = self.additionalImageBadgeNode
return { presentationData, automaticDownloadSettings, associatedData, attributes, context, controllerInteraction, message, messageRead, title, subtitle, text, entities, mediaAndFlags, mediaBadge, actionIcon, actionTitle, displayLine, layoutConstants, constrainedSize in
+ let isPreview = presentationData.isPreview
let fontSize: CGFloat = floor(presentationData.fontSize.baseDisplaySize * 15.0 / 17.0)
let titleFont = Font.semibold(fontSize)
@@ -526,7 +527,6 @@ final class ChatMessageAttachedContentNode: ASDisplayNode {
let imageMode = !((refineContentImageLayout == nil && refineContentFileLayout == nil && contentInstantVideoSizeAndApply == nil) || preferMediaBeforeText)
statusInText = !imageMode
-
if let count = webpageGalleryMediaCount {
additionalImageBadgeContent = .text(inset: 0.0, backgroundColor: presentationData.theme.theme.chat.message.mediaDateAndStatusFillColor, foregroundColor: presentationData.theme.theme.chat.message.mediaDateAndStatusTextColor, text: NSAttributedString(string: presentationData.strings.Items_NOfM("1", "\(count)").0))
skipStandardStatus = imageMode
@@ -658,7 +658,7 @@ final class ChatMessageAttachedContentNode: ASDisplayNode {
}
var continueActionButtonLayout: ((CGFloat) -> (CGSize, () -> ChatMessageAttachedContentButtonNode))?
- if let actionTitle = actionTitle {
+ if let actionTitle = actionTitle, !isPreview {
let buttonImage: UIImage
let buttonHighlightedImage: UIImage
var buttonIconImage: UIImage?
@@ -781,6 +781,8 @@ final class ChatMessageAttachedContentNode: ASDisplayNode {
strongSelf.lineNode.frame = CGRect(origin: CGPoint(x: 13.0, y: insets.top), size: CGSize(width: 2.0, height: adjustedLineHeight - insets.top - insets.bottom - 2.0))
strongSelf.lineNode.isHidden = !displayLine
+ strongSelf.textNode.displaysAsynchronously = !isPreview
+
let _ = textApply()
if let imageFrame = imageFrame {
diff --git a/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift
index 6b153f1771..0a03a0f7de 100644
--- a/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift
+++ b/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift
@@ -759,6 +759,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
currentForwardInfo: (Peer?, String?)?,
isSelected: Bool?
) -> (ListViewItemNodeLayout, (ListViewItemUpdateAnimation, Bool) -> Void) {
+ let isPreview = item.presentationData.isPreview
let accessibilityData = ChatMessageAccessibilityData(item: item, isSelected: isSelected)
let fontSize = floor(item.presentationData.fontSize.baseDisplaySize * 14.0 / 17.0)
@@ -848,11 +849,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
}
}
- if hasAvatar {
- avatarInset = layoutConstants.avatarDiameter
- } else {
- avatarInset = 0.0
- }
+ avatarInset = hasAvatar ? layoutConstants.avatarDiameter : 0.0
let isFailed = item.content.firstMessage.effectivelyFailed(timestamp: item.context.account.network.getApproximateRemoteTimestamp())
@@ -907,6 +904,10 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
}
}
+ if isPreview {
+ needShareButton = false
+ }
+
var tmpWidth: CGFloat
if allowFullWidth {
tmpWidth = baseWidth
@@ -988,7 +989,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
}
} else if let attribute = attribute as? ReplyMessageAttribute {
replyMessage = firstMessage.associatedMessages[attribute.messageId]
- } else if let attribute = attribute as? ReplyMarkupMessageAttribute, attribute.flags.contains(.inline), !attribute.rows.isEmpty {
+ } else if let attribute = attribute as? ReplyMarkupMessageAttribute, attribute.flags.contains(.inline), !attribute.rows.isEmpty && !isPreview {
replyMarkup = attribute
}
}
@@ -1843,6 +1844,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
strongSelf.contextSourceNode.contentNode.addSubnode(nameNode)
}
nameNode.frame = CGRect(origin: CGPoint(x: contentOrigin.x + layoutConstants.text.bubbleInsets.left, y: layoutConstants.bubble.contentInsets.top + nameNodeOriginY), size: nameNodeSizeApply.0)
+ nameNode.displaysAsynchronously = !item.presentationData.isPreview
if let credibilityIconImage = currentCredibilityIconImage {
let credibilityIconNode: ASImageNode
diff --git a/submodules/TelegramUI/Sources/ChatMessageDateAndStatusNode.swift b/submodules/TelegramUI/Sources/ChatMessageDateAndStatusNode.swift
index 3ed9230c12..0d6f734525 100644
--- a/submodules/TelegramUI/Sources/ChatMessageDateAndStatusNode.swift
+++ b/submodules/TelegramUI/Sources/ChatMessageDateAndStatusNode.swift
@@ -496,6 +496,7 @@ class ChatMessageDateAndStatusNode: ASDisplayNode {
let _ = dateApply()
if let currentImpressionIcon = currentImpressionIcon {
+ currentImpressionIcon.displaysAsynchronously = !presentationData.isPreview
if currentImpressionIcon.image !== impressionImage {
currentImpressionIcon.image = impressionImage
}
@@ -509,6 +510,7 @@ class ChatMessageDateAndStatusNode: ASDisplayNode {
strongSelf.impressionIcon = nil
}
+
strongSelf.dateNode.frame = CGRect(origin: CGPoint(x: leftInset + backgroundInsets.left + impressionWidth, y: backgroundInsets.top + 1.0 + offset), size: date.size)
if let clockFrameNode = clockFrameNode {
diff --git a/submodules/TelegramUI/Sources/ChatMessageGameBubbleContentNode.swift b/submodules/TelegramUI/Sources/ChatMessageGameBubbleContentNode.swift
index de894e9555..43c68319f1 100644
--- a/submodules/TelegramUI/Sources/ChatMessageGameBubbleContentNode.swift
+++ b/submodules/TelegramUI/Sources/ChatMessageGameBubbleContentNode.swift
@@ -26,7 +26,7 @@ final class ChatMessageGameBubbleContentNode: ChatMessageBubbleContentNode {
self.addSubnode(self.contentNode)
self.contentNode.openMedia = { [weak self] _ in
if let strongSelf = self, let item = strongSelf.item {
- item.controllerInteraction.requestMessageActionCallback(item.message.id, nil, true)
+ item.controllerInteraction.requestMessageActionCallback(item.message.id, nil, true, false)
}
}
}
diff --git a/submodules/TelegramUI/Sources/ChatMessageItemView.swift b/submodules/TelegramUI/Sources/ChatMessageItemView.swift
index e4bab4067d..f114d2af30 100644
--- a/submodules/TelegramUI/Sources/ChatMessageItemView.swift
+++ b/submodules/TelegramUI/Sources/ChatMessageItemView.swift
@@ -782,9 +782,9 @@ public class ChatMessageItemView: ListViewItemNode {
case .requestPhone:
item.controllerInteraction.shareAccountContact()
case .openWebApp:
- item.controllerInteraction.requestMessageActionCallback(item.message.id, nil, true)
- case let .callback(data):
- item.controllerInteraction.requestMessageActionCallback(item.message.id, data, false)
+ item.controllerInteraction.requestMessageActionCallback(item.message.id, nil, true, false)
+ case let .callback(requiresPassword, data):
+ item.controllerInteraction.requestMessageActionCallback(item.message.id, data, false, requiresPassword)
case let .switchInline(samePeer, query):
var botPeer: Peer?
diff --git a/submodules/TelegramUI/Sources/ChatRecentActionsControllerNode.swift b/submodules/TelegramUI/Sources/ChatRecentActionsControllerNode.swift
index 5d52d2f087..501b521a41 100644
--- a/submodules/TelegramUI/Sources/ChatRecentActionsControllerNode.swift
+++ b/submodules/TelegramUI/Sources/ChatRecentActionsControllerNode.swift
@@ -216,7 +216,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
}, openMessageContextMenu: { [weak self] message, selectAll, node, frame, _ in
self?.openMessageContextMenu(message: message, selectAll: selectAll, node: node, frame: frame)
}, openMessageContextActions: { _, _, _, _ in
- }, navigateToMessage: { _, _ in }, tapMessage: nil, clickThroughMessage: { }, toggleMessagesSelection: { _, _ in }, sendCurrentMessage: { _ in }, sendMessage: { _ in }, sendSticker: { _, _, _, _ in return false }, sendGif: { _, _, _ in return false }, sendBotContextResultAsGif: { _, _, _, _ in return false }, requestMessageActionCallback: { _, _, _ in }, requestMessageActionUrlAuth: { _, _, _ in }, activateSwitchInline: { _, _ in }, openUrl: { [weak self] url, _, _, _ in
+ }, navigateToMessage: { _, _ in }, tapMessage: nil, clickThroughMessage: { }, toggleMessagesSelection: { _, _ in }, sendCurrentMessage: { _ in }, sendMessage: { _ in }, sendSticker: { _, _, _, _ in return false }, sendGif: { _, _, _ in return false }, sendBotContextResultAsGif: { _, _, _, _ 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() {
diff --git a/submodules/TelegramUI/Sources/ChatRecordingPreviewInputPanelNode.swift b/submodules/TelegramUI/Sources/ChatRecordingPreviewInputPanelNode.swift
index e3c59fed58..c5f753bf36 100644
--- a/submodules/TelegramUI/Sources/ChatRecordingPreviewInputPanelNode.swift
+++ b/submodules/TelegramUI/Sources/ChatRecordingPreviewInputPanelNode.swift
@@ -101,7 +101,7 @@ final class ChatRecordingPreviewInputPanelNode: ChatInputPanelNode {
super.init()
self.addSubnode(self.deleteButton)
- self.deleteButton.addSubnode(binNode)
+ self.deleteButton.addSubnode(self.binNode)
self.addSubnode(self.waveformBackgroundNode)
self.addSubnode(self.sendButton)
self.addSubnode(self.waveformScubberNode)
diff --git a/submodules/TelegramUI/Sources/ChatSendMessageActionSheetController.swift b/submodules/TelegramUI/Sources/ChatSendMessageActionSheetController.swift
index b3dc91fb9b..4b420cf10f 100644
--- a/submodules/TelegramUI/Sources/ChatSendMessageActionSheetController.swift
+++ b/submodules/TelegramUI/Sources/ChatSendMessageActionSheetController.swift
@@ -3,6 +3,7 @@ import UIKit
import Display
import AsyncDisplayKit
import SwiftSignalKit
+import SyncCore
import TelegramPresentationData
import AccountContext
import ContextUI
@@ -67,8 +68,10 @@ final class ChatSendMessageActionSheetController: ViewController {
}
var reminders = false
- if case let .peer(peerId) = self.interfaceState.chatLocation, peerId == context.account.peerId {
- reminders = true
+ var isSecret = false
+ if case let .peer(peerId) = self.interfaceState.chatLocation {
+ reminders = peerId == context.account.peerId
+ isSecret = peerId.namespace == Namespaces.Peer.SecretChat
}
self.displayNode = ChatSendMessageActionSheetControllerNode(context: self.context, reminders: reminders, gesture: gesture, sendButtonFrame: self.sendButtonFrame, textInputNode: self.textInputNode, forwardedCount: forwardedCount, send: { [weak self] in
@@ -77,7 +80,7 @@ final class ChatSendMessageActionSheetController: ViewController {
}, sendSilently: { [weak self] in
self?.controllerInteraction?.sendCurrentMessage(true)
self?.dismiss(cancel: false)
- }, schedule: { [weak self] in
+ }, schedule: isSecret ? nil : { [weak self] in
self?.controllerInteraction?.scheduleCurrentMessage()
self?.dismiss(cancel: false)
}, cancel: { [weak self] in
diff --git a/submodules/TelegramUI/Sources/ChatSendMessageActionSheetControllerNode.swift b/submodules/TelegramUI/Sources/ChatSendMessageActionSheetControllerNode.swift
index 9c845b4699..29415a9bb9 100644
--- a/submodules/TelegramUI/Sources/ChatSendMessageActionSheetControllerNode.swift
+++ b/submodules/TelegramUI/Sources/ChatSendMessageActionSheetControllerNode.swift
@@ -241,9 +241,11 @@ final class ChatSendMessageActionSheetControllerNode: ViewControllerTracingNode,
sendSilently?()
}))
}
- contentNodes.append(ActionSheetItemNode(theme: self.presentationData.theme, title: reminders ? self.presentationData.strings.Conversation_SendMessage_SetReminder: self.presentationData.strings.Conversation_SendMessage_ScheduleMessage, icon: .schedule, hasSeparator: false, action: {
- schedule?()
- }))
+ if let _ = schedule {
+ contentNodes.append(ActionSheetItemNode(theme: self.presentationData.theme, title: reminders ? self.presentationData.strings.Conversation_SendMessage_SetReminder: self.presentationData.strings.Conversation_SendMessage_ScheduleMessage, icon: .schedule, hasSeparator: false, action: {
+ schedule?()
+ }))
+ }
self.contentNodes = contentNodes
super.init()
diff --git a/submodules/TelegramUI/Sources/ChatTextInputActionButtonsNode.swift b/submodules/TelegramUI/Sources/ChatTextInputActionButtonsNode.swift
index 843549408b..7f46c65097 100644
--- a/submodules/TelegramUI/Sources/ChatTextInputActionButtonsNode.swift
+++ b/submodules/TelegramUI/Sources/ChatTextInputActionButtonsNode.swift
@@ -84,12 +84,6 @@ final class ChatTextInputActionButtonsNode: ASDisplayNode {
self.micButtonPointerInteraction = PointerInteraction(view: self.micButton, style: .circle)
}
- @objc func handleLongPress(_ gestureRecognizer: UILongPressGestureRecognizer) {
- if !self.sendButtonHasApplyIcon && gestureRecognizer.state == .began {
- //self.sendButtonLongPressed?()
- }
- }
-
func updateTheme(theme: PresentationTheme) {
self.micButton.updateTheme(theme: theme)
self.expandMediaInputButton.setImage(PresentationResourcesChat.chatInputPanelExpandButtonImage(theme), for: [])
diff --git a/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift b/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift
index c6321b20ee..5a21dcb5f2 100644
--- a/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift
+++ b/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift
@@ -809,7 +809,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
self.textPlaceholderNode.frame = CGRect(origin: self.textPlaceholderNode.frame.origin, size: placeholderSize)
}
- self.actionButtons.sendButtonLongPressEnabled = peer.id.namespace != Namespaces.Peer.SecretChat && !interfaceState.isScheduledMessages
+ self.actionButtons.sendButtonLongPressEnabled = !interfaceState.isScheduledMessages
}
let sendButtonHasApplyIcon = interfaceState.interfaceState.editMessage != nil
diff --git a/submodules/TelegramUI/Sources/DrawingStickersScreen.swift b/submodules/TelegramUI/Sources/DrawingStickersScreen.swift
index 0b1578f203..31a2758560 100644
--- a/submodules/TelegramUI/Sources/DrawingStickersScreen.swift
+++ b/submodules/TelegramUI/Sources/DrawingStickersScreen.swift
@@ -108,7 +108,7 @@ private final class DrawingStickersScreenNode: ViewControllerTracingNode {
var selectStickerImpl: ((FileMediaReference, ASDisplayNode, CGRect) -> Bool)?
self.controllerInteraction = ChatControllerInteraction(openMessage: { _, _ in
- return false }, openPeer: { _, _, _ in }, openPeerMention: { _ in }, openMessageContextMenu: { _, _, _, _, _ in }, openMessageContextActions: { _, _, _, _ in }, navigateToMessage: { _, _ in }, tapMessage: nil, clickThroughMessage: { }, toggleMessagesSelection: { _, _ in }, sendCurrentMessage: { _ in }, sendMessage: { _ in }, sendSticker: { fileReference, _, node, rect in return selectStickerImpl?(fileReference, node, rect) ?? false }, sendGif: { _, _, _ in return false }, sendBotContextResultAsGif: { _, _, _, _ in return false }, requestMessageActionCallback: { _, _, _ in }, requestMessageActionUrlAuth: { _, _, _ in }, activateSwitchInline: { _, _ in }, openUrl: { _, _, _, _ in }, shareCurrentLocation: {}, shareAccountContact: {}, sendBotCommand: { _, _ in }, openInstantPage: { _, _ in }, openWallpaper: { _ in }, openTheme: { _ in }, openHashtag: { _, _ in }, updateInputState: { _ in }, updateInputMode: { _ in }, openMessageShareMenu: { _ in
+ return false }, openPeer: { _, _, _ in }, openPeerMention: { _ in }, openMessageContextMenu: { _, _, _, _, _ in }, openMessageContextActions: { _, _, _, _ in }, navigateToMessage: { _, _ in }, tapMessage: nil, clickThroughMessage: { }, toggleMessagesSelection: { _, _ in }, sendCurrentMessage: { _ in }, sendMessage: { _ in }, sendSticker: { fileReference, _, node, rect in return selectStickerImpl?(fileReference, node, rect) ?? false }, sendGif: { _, _, _ in return false }, sendBotContextResultAsGif: { _, _, _, _ in return false }, requestMessageActionCallback: { _, _, _, _ in }, requestMessageActionUrlAuth: { _, _, _ in }, activateSwitchInline: { _, _ in }, openUrl: { _, _, _, _ in }, shareCurrentLocation: {}, shareAccountContact: {}, sendBotCommand: { _, _ in }, openInstantPage: { _, _ in }, openWallpaper: { _ in }, openTheme: { _ in }, openHashtag: { _, _ in }, updateInputState: { _ in }, updateInputMode: { _ in }, openMessageShareMenu: { _ in
}, presentController: { _, _ in }, navigationController: {
return nil
}, chatControllerNode: {
diff --git a/submodules/TelegramUI/Sources/HeartbeatHaptic.swift b/submodules/TelegramUI/Sources/HeartbeatHaptic.swift
index 5c636cbea0..e70cc92788 100644
--- a/submodules/TelegramUI/Sources/HeartbeatHaptic.swift
+++ b/submodules/TelegramUI/Sources/HeartbeatHaptic.swift
@@ -2,11 +2,18 @@ import Foundation
import Display
import SwiftSignalKit
-final class HeartbeatHaptic {
+protocol EmojiHaptic {
+ var enabled: Bool { get set }
+ var active: Bool { get }
+
+ func start(time: Double)
+}
+
+final class HeartbeatHaptic: EmojiHaptic {
private var hapticFeedback = HapticFeedback()
private var timer: SwiftSignalKit.Timer?
private var time: Double = 0.0
- var enabled = false {
+ var enabled: Bool = false {
didSet {
if !self.enabled {
self.reset()
diff --git a/submodules/TelegramUI/Sources/OverlayPlayerControllerNode.swift b/submodules/TelegramUI/Sources/OverlayPlayerControllerNode.swift
index 77b5ae3a47..dbdaa4786e 100644
--- a/submodules/TelegramUI/Sources/OverlayPlayerControllerNode.swift
+++ b/submodules/TelegramUI/Sources/OverlayPlayerControllerNode.swift
@@ -76,7 +76,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, UIGestu
return false
}, sendBotContextResultAsGif: { _, _, _, _ in
return false
- }, requestMessageActionCallback: { _, _, _ in
+ }, requestMessageActionCallback: { _, _, _, _ in
}, requestMessageActionUrlAuth: { _, _, _ in
}, activateSwitchInline: { _, _ in
}, openUrl: { _, _, _, _ in
diff --git a/submodules/TelegramUI/Sources/OwnershipTransferController.swift b/submodules/TelegramUI/Sources/OwnershipTransferController.swift
new file mode 100644
index 0000000000..b2028a051a
--- /dev/null
+++ b/submodules/TelegramUI/Sources/OwnershipTransferController.swift
@@ -0,0 +1,128 @@
+import Foundation
+import UIKit
+import AsyncDisplayKit
+import Display
+import SwiftSignalKit
+import Postbox
+import TelegramCore
+import SyncCore
+import TelegramPresentationData
+import ActivityIndicator
+import TextFormat
+import AccountContext
+import AlertUI
+import PresentationDataUtils
+import PasswordSetupUI
+import Markdown
+import PeerInfoUI
+
+private func commitOwnershipTransferController(context: AccountContext, present: @escaping (ViewController, Any?) -> Void, commit: @escaping (String) -> Signal, completion: @escaping (MessageActionCallbackResult) -> Void) -> ViewController {
+ let presentationData = context.sharedContext.currentPresentationData.with { $0 }
+
+ var dismissImpl: (() -> Void)?
+ var proceedImpl: (() -> Void)?
+
+ var pushControllerImpl: ((ViewController) -> Void)?
+
+ let disposable = MetaDisposable()
+
+ let contentNode = ChannelOwnershipTransferAlertContentNode(theme: AlertControllerTheme(presentationData: presentationData), ptheme: presentationData.theme, strings: presentationData.strings, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: {
+ dismissImpl?()
+ }), TextAlertAction(type: .defaultAction, title: presentationData.strings.OwnershipTransfer_Transfer, action: {
+ proceedImpl?()
+ })])
+
+ contentNode.complete = {
+ proceedImpl?()
+ }
+
+ let controller = AlertController(theme: AlertControllerTheme(presentationData: presentationData), contentNode: contentNode)
+ let presentationDataDisposable = context.sharedContext.presentationData.start(next: { [weak controller, weak contentNode] presentationData in
+ controller?.theme = AlertControllerTheme(presentationData: presentationData)
+ contentNode?.theme = presentationData.theme
+ })
+ controller.dismissed = {
+ presentationDataDisposable.dispose()
+ disposable.dispose()
+ }
+ dismissImpl = { [weak controller, weak contentNode] in
+ contentNode?.dismissInput()
+ controller?.dismissAnimated()
+ }
+ proceedImpl = { [weak contentNode] in
+ guard let contentNode = contentNode else {
+ return
+ }
+ contentNode.updateIsChecking(true)
+
+ disposable.set((commit(contentNode.password) |> deliverOnMainQueue).start(next: { result in
+ dismissImpl?()
+ }, error: { [weak contentNode] error in
+ var errorTextAndActions: (String, [TextAlertAction])?
+ switch error {
+ case .invalidPassword:
+ contentNode?.animateError()
+ case .limitExceeded:
+ errorTextAndActions = (presentationData.strings.TwoStepAuth_FloodError, [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})])
+ case .userBlocked, .restricted:
+ errorTextAndActions = (presentationData.strings.Group_OwnershipTransfer_ErrorPrivacyRestricted, [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})])
+ default:
+ errorTextAndActions = (presentationData.strings.Login_UnknownError, [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})])
+ }
+ contentNode?.updateIsChecking(false)
+
+ if let (text, actions) = errorTextAndActions {
+ dismissImpl?()
+ present(textAlertController(context: context, title: nil, text: text, actions: actions), nil)
+ }
+ }))
+ }
+
+ pushControllerImpl = { [weak controller] c in
+ controller?.push(c)
+ }
+
+ return controller
+}
+
+
+func ownershipTransferController(context: AccountContext, initialError: MessageActionCallbackError, present: @escaping (ViewController, Any?) -> Void, commit: @escaping (String) -> Signal, completion: @escaping (MessageActionCallbackResult) -> Void) -> ViewController {
+ let presentationData = context.sharedContext.currentPresentationData.with { $0 }
+ let theme = AlertControllerTheme(presentationData: presentationData)
+
+ var title: NSAttributedString? = NSAttributedString(string: presentationData.strings.OwnershipTransfer_SecurityCheck, font: Font.medium(presentationData.listsFontSize.itemListBaseFontSize), textColor: theme.primaryColor, paragraphAlignment: .center)
+
+ var text = presentationData.strings.OwnershipTransfer_SecurityRequirements
+ var actions: [TextAlertAction] = []
+
+ switch initialError {
+ case .requestPassword:
+ return commitOwnershipTransferController(context: context, present: present, commit: commit, completion: completion)
+ case .twoStepAuthTooFresh, .authSessionTooFresh:
+ text = text + presentationData.strings.OwnershipTransfer_ComeBackLater
+ actions = [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]
+ case .twoStepAuthMissing:
+ actions = [TextAlertAction(type: .genericAction, title: presentationData.strings.OwnershipTransfer_SetupTwoStepAuth, action: {
+ let controller = SetupTwoStepVerificationController(context: context, initialState: .automatic, stateUpdated: { update, shouldDismiss, controller in
+ if shouldDismiss {
+ controller.dismiss()
+ }
+ })
+ present(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
+ }), TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {})]
+ case .userBlocked, .restricted:
+ title = nil
+ text = presentationData.strings.Group_OwnershipTransfer_ErrorPrivacyRestricted
+ actions = [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]
+ default:
+ title = nil
+ text = presentationData.strings.Login_UnknownError
+ actions = [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]
+ }
+
+ let body = MarkdownAttributeSet(font: Font.regular(13.0), textColor: theme.primaryColor)
+ let bold = MarkdownAttributeSet(font: Font.semibold(13.0), textColor: theme.primaryColor)
+ let attributedText = parseMarkdownIntoAttributedString(text, attributes: MarkdownAttributes(body: body, bold: bold, link: body, linkAttribute: { _ in return nil }), textAlignment: .center)
+
+ return richTextAlertController(context: context, title: title, text: attributedText, actions: actions)
+}
diff --git a/submodules/TelegramUI/Sources/PeachHaptic.swift b/submodules/TelegramUI/Sources/PeachHaptic.swift
new file mode 100644
index 0000000000..e92ffeb0af
--- /dev/null
+++ b/submodules/TelegramUI/Sources/PeachHaptic.swift
@@ -0,0 +1,73 @@
+import Foundation
+import Display
+import SwiftSignalKit
+
+private let impactTime: Double = 0.4
+
+final class PeachHaptic: EmojiHaptic {
+ private var hapticFeedback = HapticFeedback()
+ private var timer: SwiftSignalKit.Timer?
+ private var time: Double = 0.0
+ var enabled: Bool = false {
+ didSet {
+ if !self.enabled {
+ self.reset()
+ }
+ }
+ }
+
+ var active: Bool {
+ return self.timer != nil
+ }
+
+ private func reset() {
+ if let timer = self.timer {
+ self.time = 0.0
+ timer.invalidate()
+ self.timer = nil
+ }
+ }
+
+ private func beat(time: Double) {
+ let epsilon = 0.1
+ if fabs(impactTime - time) < epsilon {
+ self.hapticFeedback.impact(.heavy)
+ }
+ }
+
+ func start(time: Double) {
+ self.hapticFeedback.prepareImpact()
+
+ if time > impactTime {
+ return
+ }
+
+ let startTime: Double = 0.0
+
+ let block = { [weak self] in
+ guard let strongSelf = self, strongSelf.enabled else {
+ return
+ }
+
+ strongSelf.time = startTime
+ strongSelf.beat(time: startTime)
+ strongSelf.timer = SwiftSignalKit.Timer(timeout: 0.2, repeat: true, completion: { [weak self] in
+ guard let strongSelf = self, strongSelf.enabled else {
+ return
+ }
+ strongSelf.time += 0.2
+ strongSelf.beat(time: strongSelf.time)
+
+ if strongSelf.time > impactTime {
+ strongSelf.reset()
+ strongSelf.time = 0.0
+ strongSelf.timer?.invalidate()
+ strongSelf.timer = nil
+ }
+ }, queue: Queue.mainQueue())
+ strongSelf.timer?.start()
+ }
+
+ block()
+ }
+}
diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift
index eddeae664c..b8f58c38a2 100644
--- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift
+++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift
@@ -1825,7 +1825,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
return false
}, sendBotContextResultAsGif: { _, _, _, _ in
return false
- }, requestMessageActionCallback: { _, _, _ in
+ }, requestMessageActionCallback: { _, _, _, _ in
}, requestMessageActionUrlAuth: { _, _, _ in
}, activateSwitchInline: { _, _ in
}, openUrl: { [weak self] url, concealed, external, _ in
diff --git a/submodules/TelegramUI/Sources/PeerMediaCollectionController.swift b/submodules/TelegramUI/Sources/PeerMediaCollectionController.swift
index 2d777858eb..4fdb8667ec 100644
--- a/submodules/TelegramUI/Sources/PeerMediaCollectionController.swift
+++ b/submodules/TelegramUI/Sources/PeerMediaCollectionController.swift
@@ -327,7 +327,7 @@ public class PeerMediaCollectionController: TelegramBaseController {
return false
}, sendBotContextResultAsGif: { _, _, _, _ in
return false
- }, requestMessageActionCallback: { _, _, _ in
+ }, requestMessageActionCallback: { _, _, _, _ in
}, requestMessageActionUrlAuth: { _, _, _ in
}, activateSwitchInline: { _, _ in
}, openUrl: { [weak self] url, _, external, _ in
diff --git a/submodules/TelegramUI/Sources/PrefetchManager.swift b/submodules/TelegramUI/Sources/PrefetchManager.swift
index f95df18e5b..314a814da1 100644
--- a/submodules/TelegramUI/Sources/PrefetchManager.swift
+++ b/submodules/TelegramUI/Sources/PrefetchManager.swift
@@ -38,7 +38,7 @@ private final class PrefetchManagerImpl {
|> map { networkType -> MediaAutoDownloadNetworkType in
switch networkType {
case .none, .cellular:
- return.cellular
+ return .cellular
case .wifi:
return .wifi
}
diff --git a/submodules/TelegramUI/Sources/SharedAccountContext.swift b/submodules/TelegramUI/Sources/SharedAccountContext.swift
index 4d2afd5b89..5e6fdc18cf 100644
--- a/submodules/TelegramUI/Sources/SharedAccountContext.swift
+++ b/submodules/TelegramUI/Sources/SharedAccountContext.swift
@@ -1148,7 +1148,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
return PeerSelectionControllerImpl(params)
}
- public func makeChatMessagePreviewItem(context: AccountContext, message: Message, theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, chatBubbleCorners: PresentationChatBubbleCorners, dateTimeFormat: PresentationDateTimeFormat, nameOrder: PresentationPersonNameOrder, forcedResourceStatus: FileMediaResourceStatus?, tapMessage: ((Message) -> Void)? = nil, clickThroughMessage: (() -> Void)? = nil) -> ListViewItem {
+ public func makeChatMessagePreviewItem(context: AccountContext, messages: [Message], theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, chatBubbleCorners: PresentationChatBubbleCorners, dateTimeFormat: PresentationDateTimeFormat, nameOrder: PresentationPersonNameOrder, forcedResourceStatus: FileMediaResourceStatus?, tapMessage: ((Message) -> Void)? = nil, clickThroughMessage: (() -> Void)? = nil) -> ListViewItem {
let controllerInteraction: ChatControllerInteraction
if tapMessage != nil || clickThroughMessage != nil {
controllerInteraction = ChatControllerInteraction(openMessage: { _, _ in
@@ -1158,7 +1158,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
clickThroughMessage?()
}, toggleMessagesSelection: { _, _ in }, sendCurrentMessage: { _ in }, sendMessage: { _ in }, sendSticker: { _, _, _, _ in return false }, sendGif: { _, _, _ in return false }, sendBotContextResultAsGif: { _, _, _, _ in
return false
- }, requestMessageActionCallback: { _, _, _ in }, requestMessageActionUrlAuth: { _, _, _ in }, activateSwitchInline: { _, _ in }, openUrl: { _, _, _, _ in }, shareCurrentLocation: {}, shareAccountContact: {}, sendBotCommand: { _, _ in }, openInstantPage: { _, _ in }, openWallpaper: { _ in }, openTheme: { _ in }, openHashtag: { _, _ in }, updateInputState: { _ in }, updateInputMode: { _ in }, openMessageShareMenu: { _ in
+ }, requestMessageActionCallback: { _, _, _, _ in }, requestMessageActionUrlAuth: { _, _, _ in }, activateSwitchInline: { _, _ in }, openUrl: { _, _, _, _ in }, shareCurrentLocation: {}, shareAccountContact: {}, sendBotCommand: { _, _ in }, openInstantPage: { _, _ in }, openWallpaper: { _ in }, openTheme: { _ in }, openHashtag: { _, _ in }, updateInputState: { _ in }, updateInputMode: { _ in }, openMessageShareMenu: { _ in
}, presentController: { _, _ in }, navigationController: {
return nil
}, chatControllerNode: {
@@ -1202,7 +1202,17 @@ public final class SharedAccountContextImpl: SharedAccountContext {
controllerInteraction = defaultChatControllerInteraction
}
- return ChatMessageItem(presentationData: ChatPresentationData(theme: ChatPresentationThemeData(theme: theme, wallpaper: wallpaper), fontSize: fontSize, strings: strings, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameOrder, disableAnimations: false, largeEmoji: false, chatBubbleCorners: chatBubbleCorners, animatedEmojiScale: 1.0, isPreview: true), context: context, chatLocation: .peer(message.id.peerId), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .contact, automaticDownloadNetworkType: .cellular, isRecentActions: false, isScheduledMessages: false, contactsPeerIds: Set(), animatedEmojiStickers: [:], forcedResourceStatus: forcedResourceStatus), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes()), disableDate: true, additionalContent: nil)
+ let content: ChatMessageItemContent
+ let chatLocation: ChatLocation
+ if messages.count > 1 {
+ content = .group(messages: messages.map { ($0, true, .none, ChatMessageEntryAttributes()) })
+ chatLocation = .peer(messages.first!.id.peerId)
+ } else {
+ content = .message(message: messages.first!, read: true, selection: .none, attributes: ChatMessageEntryAttributes())
+ chatLocation = .peer(messages.first!.id.peerId)
+ }
+
+ return ChatMessageItem(presentationData: ChatPresentationData(theme: ChatPresentationThemeData(theme: theme, wallpaper: wallpaper), fontSize: fontSize, strings: strings, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameOrder, disableAnimations: false, largeEmoji: false, chatBubbleCorners: chatBubbleCorners, animatedEmojiScale: 1.0, isPreview: true), context: context, chatLocation: chatLocation, associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .contact, automaticDownloadNetworkType: .cellular, isRecentActions: false, isScheduledMessages: false, contactsPeerIds: Set(), animatedEmojiStickers: [:], forcedResourceStatus: forcedResourceStatus), controllerInteraction: controllerInteraction, content: content, disableDate: true, additionalContent: nil)
}
public func makeChatMessageDateHeaderItem(context: AccountContext, timestamp: Int32, theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, chatBubbleCorners: PresentationChatBubbleCorners, dateTimeFormat: PresentationDateTimeFormat, nameOrder: PresentationPersonNameOrder) -> ListViewItemHeader {
diff --git a/submodules/TelegramUI/Sources/SlotMachineAnimationNode.swift b/submodules/TelegramUI/Sources/SlotMachineAnimationNode.swift
new file mode 100644
index 0000000000..179c62f895
--- /dev/null
+++ b/submodules/TelegramUI/Sources/SlotMachineAnimationNode.swift
@@ -0,0 +1,241 @@
+import Foundation
+import Display
+import AsyncDisplayKit
+import Postbox
+import SyncCore
+import TelegramCore
+import SwiftSignalKit
+import AccountContext
+import StickerResources
+import ManagedAnimationNode
+
+enum ReelValue {
+ case rolling
+ case bar
+ case berries
+ case lemon
+ case seven
+ case sevenWin
+}
+
+private func leftReelAnimationItem(value: ReelValue, immediate: Bool = false) -> ManagedAnimationItem {
+ let frames: ManagedAnimationFrameRange? = immediate ? .still(.end) : nil
+ switch value {
+ case .rolling:
+ return ManagedAnimationItem(source: .local("Slot_L_Spinning"), loop: true)
+ case .bar:
+ return ManagedAnimationItem(source: .local("Slot_L_Bar"), frames: frames, loop: false)
+ case .berries:
+ return ManagedAnimationItem(source: .local("Slot_L_Berries"), frames: frames, loop: false)
+ case .lemon:
+ return ManagedAnimationItem(source: .local("Slot_L_Lemon"), frames: frames, loop: false)
+ case .seven:
+ return ManagedAnimationItem(source: .local("Slot_L_7"), frames: frames, loop: false)
+ case .sevenWin:
+ return ManagedAnimationItem(source: .local("Slot_L_7_Win"), frames: frames, loop: false)
+ }
+}
+
+private func centerReelAnimationItem(value: ReelValue, immediate: Bool = false) -> ManagedAnimationItem {
+ let frames: ManagedAnimationFrameRange? = immediate ? .still(.end) : nil
+ switch value {
+ case .rolling:
+ return ManagedAnimationItem(source: .local("Slot_M_Spinning"), frames: frames, loop: true)
+ case .bar:
+ return ManagedAnimationItem(source: .local("Slot_M_Bar"), frames: frames, loop: false)
+ case .berries:
+ return ManagedAnimationItem(source: .local("Slot_M_Berries"), frames: frames, loop: false)
+ case .lemon:
+ return ManagedAnimationItem(source: .local("Slot_M_Lemon"), frames: frames, loop: false)
+ case .seven:
+ return ManagedAnimationItem(source: .local("Slot_M_7"), frames: frames, loop: false)
+ case .sevenWin:
+ return ManagedAnimationItem(source: .local("Slot_M_7_Win"), frames: frames, loop: false)
+ }
+}
+
+private func rightReelAnimationItem(value: ReelValue, immediate: Bool = false) -> ManagedAnimationItem {
+ let frames: ManagedAnimationFrameRange? = immediate ? .still(.end) : nil
+ switch value {
+ case .rolling:
+ return ManagedAnimationItem(source: .local("Slot_R_Spinning"), frames: frames, loop: true)
+ case .bar:
+ return ManagedAnimationItem(source: .local("Slot_R_Bar"), frames: frames, loop: false)
+ case .berries:
+ return ManagedAnimationItem(source: .local("Slot_R_Berries"), frames: frames, loop: false)
+ case .lemon:
+ return ManagedAnimationItem(source: .local("Slot_R_Lemon"), frames: frames, loop: false)
+ case .seven:
+ return ManagedAnimationItem(source: .local("Slot_R_7"), frames: frames, loop: false)
+ case .sevenWin:
+ return ManagedAnimationItem(source: .local("Slot_R_7_Win"), frames: frames, loop: false)
+ }
+}
+
+final class SlotMachineAnimationNode: ASDisplayNode, GenericAnimatedStickerNode {
+ private let context: AccountContext
+
+ private let backNode: ManagedAnimationNode
+ private let leftReelNode: ManagedAnimationNode
+ private let centerReelNode: ManagedAnimationNode
+ private let rightReelNode: ManagedAnimationNode
+ private let frontNode: ManagedAnimationNode
+
+ private var diceState: ManagedDiceAnimationState? = nil
+ private let disposables = DisposableSet()
+
+ init(context: AccountContext) {
+ self.context = context
+
+ let size = CGSize(width: 184.0, height: 184.0)
+ self.backNode = ManagedAnimationNode(size: size)
+ self.leftReelNode = ManagedAnimationNode(size: size)
+ self.centerReelNode = ManagedAnimationNode(size: size)
+ self.rightReelNode = ManagedAnimationNode(size: size)
+ self.frontNode = ManagedAnimationNode(size: size)
+
+ super.init()
+
+ self.addSubnode(self.backNode)
+ self.addSubnode(self.leftReelNode)
+ self.addSubnode(self.centerReelNode)
+ self.addSubnode(self.rightReelNode)
+ self.addSubnode(self.frontNode)
+ }
+
+ deinit {
+ self.disposables.dispose()
+ }
+
+ override func layout() {
+ super.layout()
+
+ self.backNode.frame = self.bounds
+ self.leftReelNode.frame = self.bounds
+ self.centerReelNode.frame = self.bounds
+ self.rightReelNode.frame = self.bounds
+ self.frontNode.frame = self.bounds
+ }
+
+ func setState(_ diceState: ManagedDiceAnimationState) {
+ let previousState = self.diceState
+ self.diceState = diceState
+
+ if let previousState = previousState {
+ switch previousState {
+ case .rolling:
+ switch diceState {
+ case let .value(value, _):
+ let l: ReelValue
+ let c: ReelValue
+ let r: ReelValue
+ switch value {
+ case 1:
+ l = .seven
+ c = .berries
+ r = .bar
+ case 2:
+ l = .berries
+ c = .berries
+ r = .bar
+ case 3:
+ l = .seven
+ c = .berries
+ r = .seven
+ case 4:
+ l = .bar
+ c = .lemon
+ r = .seven
+ case 5:
+ l = .berries
+ c = .berries
+ r = .berries
+ case 6:
+ l = .sevenWin
+ c = .sevenWin
+ r = .sevenWin
+ default:
+ l = .sevenWin
+ c = .sevenWin
+ r = .sevenWin
+ }
+ if value == 6 {
+ Queue.mainQueue().after(1.5) {
+ self.backNode.trackTo(item: ManagedAnimationItem(source: .local("Slot_Back_Win"), loop: false))
+ }
+ } else {
+ self.backNode.trackTo(item: ManagedAnimationItem(source: .local("Slot_Back_Idle"), loop: false))
+ }
+ self.leftReelNode.trackTo(item: leftReelAnimationItem(value: l))
+ self.centerReelNode.trackTo(item: centerReelAnimationItem(value: c))
+ self.rightReelNode.trackTo(item: rightReelAnimationItem(value: r))
+ self.frontNode.trackTo(item: ManagedAnimationItem(source: .local("Slot_Front_Pull"), frames: .still(.end), loop: false))
+ case .rolling:
+ break
+ }
+ case .value:
+ switch diceState {
+ case .rolling:
+ self.backNode.trackTo(item: ManagedAnimationItem(source: .local("Slot_Back_Idle"), loop: false))
+ self.leftReelNode.trackTo(item: leftReelAnimationItem(value: .rolling))
+ self.centerReelNode.trackTo(item: centerReelAnimationItem(value: .rolling))
+ self.rightReelNode.trackTo(item: rightReelAnimationItem(value: .rolling))
+ self.frontNode.trackTo(item: ManagedAnimationItem(source: .local("Slot_Front_Pull"), loop: false))
+ case .value:
+ break
+ }
+ }
+ } else {
+ switch diceState {
+ case let .value(value, immediate):
+ self.backNode.trackTo(item: ManagedAnimationItem(source: .local("Slot_Back_Idle"), loop: false))
+
+ let l: ReelValue
+ let c: ReelValue
+ let r: ReelValue
+ switch value {
+ case 1:
+ l = .seven
+ c = .berries
+ r = .bar
+ case 2:
+ l = .berries
+ c = .berries
+ r = .bar
+ case 3:
+ l = .seven
+ c = .berries
+ r = .seven
+ case 4:
+ l = .bar
+ c = .lemon
+ r = .seven
+ case 5:
+ l = .berries
+ c = .berries
+ r = .berries
+ case 6:
+ l = .sevenWin
+ c = .sevenWin
+ r = .sevenWin
+ default:
+ l = .sevenWin
+ c = .sevenWin
+ r = .sevenWin
+ }
+ self.leftReelNode.trackTo(item: leftReelAnimationItem(value: l, immediate: immediate))
+ self.centerReelNode.trackTo(item: centerReelAnimationItem(value: c, immediate: immediate))
+ self.rightReelNode.trackTo(item: rightReelAnimationItem(value: r, immediate: immediate))
+
+ let frames: ManagedAnimationFrameRange? = immediate ? .still(.end) : nil
+ self.frontNode.trackTo(item: ManagedAnimationItem(source: .local("Slot_Front_Pull"), frames: frames, loop: false))
+ case .rolling:
+ self.backNode.trackTo(item: ManagedAnimationItem(source: .local("Slot_Back_Idle"), loop: false))
+ self.leftReelNode.trackTo(item: leftReelAnimationItem(value: .rolling))
+ self.centerReelNode.trackTo(item: centerReelAnimationItem(value: .rolling))
+ self.rightReelNode.trackTo(item: rightReelAnimationItem(value: .rolling))
+ self.frontNode.trackTo(item: ManagedAnimationItem(source: .local("Slot_Front_Pull"), loop: false))
+ }
+ }
+ }
+}
diff --git a/voicebin.json b/voicebin.json
deleted file mode 100644
index baf0e38c66..0000000000
--- a/voicebin.json
+++ /dev/null
@@ -1 +0,0 @@
-{"v":"5.5.9","fr":60,"ip":0,"op":78,"w":240,"h":240,"nm":"RedBin 2","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Cap1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0],"y":[0]},"o":{"x":[0.333],"y":[0]},"t":42,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[1],"y":[1]},"t":52,"s":[10]},{"i":{"x":[0],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":62,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[1],"y":[0]},"t":68,"s":[-5]},{"t":77,"s":[0]}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":22,"s":[120,18,0],"to":[0,11.667,0],"ti":[0,-6.667,0]},{"i":{"x":0,"y":1},"o":{"x":0.333,"y":0},"t":42,"s":[120,88,0],"to":[0,6.667,0],"ti":[0,0.833,0]},{"i":{"x":0.667,"y":1},"o":{"x":1,"y":0},"t":52,"s":[120,58,0],"to":[0,-0.833,0],"ti":[0,-1.667,0]},{"i":{"x":0,"y":1},"o":{"x":0.333,"y":0},"t":62,"s":[120,83,0],"to":[0,1.667,0],"ti":[0,0.833,0]},{"i":{"x":0.667,"y":1},"o":{"x":1,"y":0},"t":68,"s":[120,68,0],"to":[0,-0.833,0],"ti":[0,-1.667,0]},{"t":77,"s":[120,78,0]}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-8.5,0],[8.5,0]],"c":false},"ix":2},"nm":"Контур 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.231372563979,0.188235309077,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1.33,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Обводка 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Преобразовать"}],"nm":"Cap1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":27,"s":[50]},{"t":32,"s":[0]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":27,"s":[50]},{"t":32,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Обрезать контуры 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":0,"op":78,"st":-188,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Cap2","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,-12,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[-0.83,0],[0,0],[0,-0.83],[0,0],[0,0]],"o":[[0,0],[0,-0.83],[0,0],[0.83,0],[0,0],[0,0],[0,0]],"v":[[-3.5,1.5],[-3.5,0],[-2,-1.5],[2,-1.5],[3.5,0],[3.5,1.5],[3.5,1.5]],"c":false},"ix":2},"nm":"Контур 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.231372563979,0.188235309077,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1.33,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Обводка 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Преобразовать"}],"nm":"Cap2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":27,"s":[50]},{"t":32,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":27,"s":[50]},{"t":32,"s":[0]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Обрезать контуры 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":0,"op":78,"st":-188,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Line3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[139.5,129,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":32,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0.25,-5.5],[-0.25,5.5]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":42,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0.667,-3.833],[0.167,5.5]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":52,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0.25,-5.5],[-0.25,5.5]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.333,"y":0},"t":59,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0.25,-5.5],[-0.25,5.5]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0},"t":62,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0.458,-5.083],[-0.042,5.5]],"c":false}]},{"t":68,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0.25,-5.5],[-0.25,5.5]],"c":false}]}],"ix":2},"nm":"Контур 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.231372563979,0.188235309077,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1.2,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Обводка 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Преобразовать"}],"nm":"Line3","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":26,"s":[0]},{"t":29,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Обрезать контуры 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.231372563979,0.188235309077,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1.2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Обводка 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Заливка 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":78,"st":-188,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Line2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[120,129,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":32,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0,-5.5],[0,5.5]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":42,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0,-3.833],[0,5.5]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":52,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0,-5.5],[0,5.5]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":59,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0,-5.5],[0,5.5]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":62,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0,-5.083],[0,5.5]],"c":false}]},{"t":68,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0,-5.5],[0,5.5]],"c":false}]}],"ix":2},"nm":"Контур 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.231372563979,0.188235309077,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1.2,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Обводка 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Преобразовать"}],"nm":"Line2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Обрезать контуры 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":26,"s":[0]},{"t":29,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":3,"nm":"Обрезать контуры 2","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":0,"op":78,"st":-188,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Line1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[100.5,129,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":32,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.25,-5.5],[0.25,5.5]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":42,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.667,-3.833],[-0.167,5.5]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":52,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.25,-5.5],[0.25,5.5]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":59,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.25,-5.5],[0.25,5.5]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":62,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.458,-5.083],[0.042,5.5]],"c":false}]},{"t":68,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.25,-5.5],[0.25,5.5]],"c":false}]}],"ix":2},"nm":"Контур 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.231372563979,0.188235309077,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1.2,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Обводка 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Преобразовать"}],"nm":"Line1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":26,"s":[0]},{"t":29,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Обрезать контуры 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":0,"op":78,"st":-188,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"Bin","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[120,120,0],"ix":2},"a":{"a":0,"k":[0,-9,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":32,"s":[{"i":[[0,0],[0,0],[-1.06,0],[0,0],[-0.06,1.05],[0,0]],"o":[[0,0],[0.06,1.05],[0,0],[1.06,0],[0,0],[0,0]],"v":[[-7,-8.5],[-6.11,6.62],[-4.11,8.5],[4.11,8.5],[6.11,6.62],[7,-8.5]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":42,"s":[{"i":[[0,0],[0,0],[-1.06,0],[0,0],[-0.06,1.05],[0,0]],"o":[[0,0],[0.06,1.05],[0,0],[1.06,0],[0,0],[0,0]],"v":[[-7.833,-6.833],[-6.943,6.62],[-4.943,8.5],[4.943,8.5],[6.943,6.62],[7.833,-6.833]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":52,"s":[{"i":[[0,0],[0,0],[-1.06,0],[0,0],[-0.06,1.05],[0,0]],"o":[[0,0],[0.06,1.05],[0,0],[1.06,0],[0,0],[0,0]],"v":[[-7,-8.5],[-6.11,6.62],[-4.11,8.5],[4.11,8.5],[6.11,6.62],[7,-8.5]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":59,"s":[{"i":[[0,0],[0,0],[-1.06,0],[0,0],[-0.06,1.05],[0,0]],"o":[[0,0],[0.06,1.05],[0,0],[1.06,0],[0,0],[0,0]],"v":[[-7,-8.5],[-6.11,6.62],[-4.11,8.5],[4.11,8.5],[6.11,6.62],[7,-8.5]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":62,"s":[{"i":[[0,0],[0,0],[-1.06,0],[0,0],[-0.06,1.05],[0,0]],"o":[[0,0],[0.06,1.05],[0,0],[1.06,0],[0,0],[0,0]],"v":[[-7.417,-7.667],[-6.527,6.62],[-4.527,8.5],[4.527,8.5],[6.527,6.62],[7.417,-7.667]],"c":true}]},{"t":68,"s":[{"i":[[0,0],[0,0],[-1.06,0],[0,0],[-0.06,1.05],[0,0]],"o":[[0,0],[0.06,1.05],[0,0],[1.06,0],[0,0],[0,0]],"v":[[-7,-8.5],[-6.11,6.62],[-4.11,8.5],[4.11,8.5],[6.11,6.62],[7,-8.5]],"c":true}]}],"ix":2},"nm":"Контур 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.231372563979,0.188235309077,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1.33,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Обводка 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Преобразовать"}],"nm":"Bin","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":22,"s":[50]},{"t":42,"s":[0]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":22,"s":[50]},{"t":42,"s":[100]}],"ix":2},"o":{"a":0,"k":137,"ix":3},"m":1,"ix":2,"nm":"Обрезать контуры 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":0,"op":78,"st":-188,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":"RecMask 2","td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[120,120,0],"to":[0,-15,0],"ti":[0,13.667,0]},{"i":{"x":0.667,"y":1},"o":{"x":1,"y":0},"t":15,"s":[120,30,0],"to":[0,-13.667,0],"ti":[0,-1.333,0]},{"t":24,"s":[120,38,0]}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,84.375,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[8.5,-8],[8.5,8],[-8.5,8],[-8.5,-8]],"c":true},"ix":2},"nm":"Контур 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.96862745098,0.96862745098,0.96862745098,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Заливка 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Преобразовать"}],"nm":"Mask","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":78,"st":-190,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":"Recording","tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[120,120,0],"to":[0,-15,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":1,"y":0},"t":15,"s":[120,30,0],"to":[0,0,0],"ti":[0,-15,0]},{"t":30,"s":[120,120,0]}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-2.76,0],[0,2.76],[2.76,0],[0,-2.76]],"o":[[2.76,0],[0,-2.76],[-2.76,0],[0,2.76]],"v":[[0,5],[5,0],[0,-5],[-5,0]],"c":true},"ix":2},"nm":"Контур 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.231372997165,0.188234999776,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Заливка 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Преобразовать"}],"nm":"Recording","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":40,"st":-180,"bm":0}],"markers":[]}
\ No newline at end of file