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