diff --git a/.gitattributes b/.gitattributes index d68bdaecf..9e26b3585 100644 --- a/.gitattributes +++ b/.gitattributes @@ -4,3 +4,6 @@ # Ensure diffs have LF endings *.diff text eol=lf *.bat text eol=crlf + +# Ensure lottie animations are treated as binary files +*.lottie binary diff --git a/.gitignore b/.gitignore index 35d74ff42..3897af2b3 100644 --- a/.gitignore +++ b/.gitignore @@ -49,3 +49,5 @@ stage /Linux/ /Telegram/Makefile *.*~ +.idea/ +cmake-build-debug/ diff --git a/.gitmodules b/.gitmodules index c583cb60b..3bbab8e6c 100644 --- a/.gitmodules +++ b/.gitmodules @@ -88,3 +88,6 @@ [submodule "Telegram/ThirdParty/tgcalls"] path = Telegram/ThirdParty/tgcalls url = https://github.com/TelegramMessenger/tgcalls.git +[submodule "Telegram/lib_webview"] + path = Telegram/lib_webview + url = https://github.com/desktop-app/lib_webview.git diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index 08ba7a13e..0e00fcee1 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -19,6 +19,7 @@ add_subdirectory(lib_storage) add_subdirectory(lib_lottie) add_subdirectory(lib_qr) add_subdirectory(lib_webrtc) +add_subdirectory(lib_webview) add_subdirectory(codegen) get_filename_component(src_loc SourceFiles REALPATH) @@ -26,6 +27,7 @@ get_filename_component(res_loc Resources REALPATH) include(cmake/telegram_options.cmake) include(cmake/lib_ffmpeg.cmake) +include(cmake/lib_stripe.cmake) include(cmake/lib_tgvoip.cmake) include(cmake/lib_tgcalls.cmake) include(cmake/td_export.cmake) @@ -35,6 +37,11 @@ include(cmake/td_scheme.cmake) include(cmake/td_ui.cmake) include(cmake/generate_appdata_changelog.cmake) +if (WIN32) + include(cmake/generate_midl.cmake) + generate_midl(Telegram ${src_loc}/platform/win/windows_quiethours.idl) +endif() + set_target_properties(Telegram PROPERTIES AUTOMOC ON AUTORCC ON) target_link_libraries(Telegram @@ -55,7 +62,9 @@ PRIVATE desktop-app::lib_storage desktop-app::lib_lottie desktop-app::lib_qr + desktop-app::lib_webview desktop-app::lib_ffmpeg + desktop-app::lib_stripe desktop-app::external_lz4 desktop-app::external_rlottie desktop-app::external_zlib @@ -70,7 +79,12 @@ PRIVATE desktop-app::external_xxhash ) -if (LINUX) +if (WIN32) + target_link_libraries(Telegram + PRIVATE + desktop-app::lib_webview_winrt + ) +elseif (LINUX) target_link_libraries(Telegram PRIVATE desktop-app::external_glibmm @@ -249,8 +263,6 @@ PRIVATE boxes/sessions_box.h boxes/share_box.cpp boxes/share_box.h - boxes/single_choice_box.cpp - boxes/single_choice_box.h boxes/sticker_set_box.cpp boxes/sticker_set_box.h boxes/stickers_box.cpp @@ -383,8 +395,6 @@ PRIVATE data/data_cloud_file.h data/data_cloud_themes.cpp data/data_cloud_themes.h - data/data_countries.cpp - data/data_countries.h data/data_document.cpp data/data_document.h data/data_document_media.cpp @@ -415,6 +425,8 @@ PRIVATE data/data_notify_settings.h data/data_peer.cpp data/data_peer.h + data/data_peer_id.cpp + data/data_peer_id.h data/data_peer_values.cpp data/data_peer_values.h data/data_photo.cpp @@ -811,8 +823,6 @@ PRIVATE passport/passport_panel.h passport/passport_panel_controller.cpp passport/passport_panel_controller.h - passport/passport_panel_details_row.cpp - passport/passport_panel_details_row.h passport/passport_panel_edit_contact.cpp passport/passport_panel_edit_contact.h passport/passport_panel_edit_document.cpp @@ -823,6 +833,10 @@ PRIVATE passport/passport_panel_form.h passport/passport_panel_password.cpp passport/passport_panel_password.h + payments/payments_checkout_process.cpp + payments/payments_checkout_process.h + payments/payments_form.cpp + payments/payments_form.h platform/linux/linux_desktop_environment.cpp platform/linux/linux_desktop_environment.h platform/linux/linux_gdk_helper.cpp @@ -1022,8 +1036,6 @@ PRIVATE ui/widgets/level_meter.h ui/widgets/multi_select.cpp ui/widgets/multi_select.h - ui/widgets/separate_panel.cpp - ui/widgets/separate_panel.h ui/countryinput.cpp ui/countryinput.h ui/empty_userpic.cpp @@ -1039,8 +1051,6 @@ PRIVATE ui/search_field_controller.h ui/special_buttons.cpp ui/special_buttons.h - ui/special_fields.cpp - ui/special_fields.h ui/unread_badge.cpp ui/unread_badge.h window/main_window.cpp diff --git a/Telegram/Resources/icons/calls/active_hand.json b/Telegram/Resources/icons/calls/active_hand.json deleted file mode 100644 index 50b29928a..000000000 --- a/Telegram/Resources/icons/calls/active_hand.json +++ /dev/null @@ -1 +0,0 @@ -{"v":"5.5.7","meta":{"g":"LottieFiles AE 0.1.20","a":"","k":"","d":"","tc":""},"fr":60,"ip":0,"op":23,"w":55,"h":55,"nm":"Scale Single 55","ddd":0,"assets":[{"id":"comp_0","layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Head X3","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":1,"s":[27.659,22.026,0],"to":[0.113,-0.861,0],"ti":[-0.113,0.861,0]},{"t":21,"s":[28.34,16.86,0]}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[33.333,33.333,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":1,"s":[{"i":[[0,0],[-1.85,0],[-0.27,10.18],[0,0],[10.73,0],[0.26,-10.19],[0,0],[-7.96,-2.33]],"o":[[1.69,0.47],[10.6,0],[0,0],[-0.07,-10.35],[-10.61,0],[0,0],[0.08,8.43],[0,0]],"v":[[-5.33,33.627],[0,34.347],[19.49,15.997],[19.5,-15.623],[0,-34.343],[-19.5,-15.973],[-19.5,15.667],[-5.66,33.537]],"c":false}]},{"t":21,"s":[{"i":[[0,0],[-1.79,0],[-0.27,10.18],[0,0],[10.37,0],[0.26,-10.19],[0,0],[-7.69,-2.33]],"o":[[1.64,0.47],[10.24,0],[0,0],[-0.06,-10.35],[-10.25,0],[0,0],[0.08,8.43],[0,0]],"v":[[-5.152,18.127],[-0.002,18.847],[18.838,0.497],[18.838,-0.123],[-0.002,-18.843],[-18.842,-0.473],[-18.842,0.167],[-5.472,18.037]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":9,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 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":[100,100],"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":"Transform"}],"nm":"Head X3","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":1,"s":[0]},{"t":21,"s":[0]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":1,"s":[100]},{"t":21,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":0,"op":24,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Bottom Part","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.649,"y":0.438},"o":{"x":0.299,"y":0},"t":1,"s":[27.567,42.931,0],"to":[-0.027,0,0],"ti":[-0.039,-0.19,0]},{"i":{"x":0.678,"y":0.596},"o":{"x":0.339,"y":0.19},"t":6,"s":[27.594,43.493,0],"to":[0.044,0.216,0],"ti":[-0.077,-0.275,0]},{"i":{"x":0.687,"y":0.473},"o":{"x":0.35,"y":0.279},"t":9,"s":[27.789,44.446,0],"to":[0.151,0.542,0],"ti":[0.046,-0.083,0]},{"i":{"x":0.938,"y":0},"o":{"x":0.062,"y":1},"t":11,"s":[28.055,45.424,0],"to":[-0.015,0.026,0],"ti":[0.089,-0.161,0]},{"t":21,"s":[28.355,45.824,0]}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[33.333,33.333,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":[[0,-6.085],[0,6.085]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":10,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 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":[100,100],"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":"Transform"}],"nm":"Bottom Part","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.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":1,"s":[100]},{"t":12,"s":[0]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":0,"op":24,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Body","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":1,"s":[27.596,34.573,0],"to":[0.145,0.438,0],"ti":[-0.145,-0.438,0]},{"t":21,"s":[28.465,37.202,0]}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[33.333,33.333,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":1,"s":[{"i":[[0,0],[-7.9,-7.71],[-9.5,0],[-7.28,8.5],[-0.412,0.572],[0,0]],"o":[[0,0],[7.9,7.7],[9.51,0],[0.453,-0.529],[6.202,-8.614],[0,0]],"v":[[-39.084,-17.666],[-29.594,5.754],[-0.314,17.664],[29.986,5.724],[31.283,4.069],[39.086,-17.436]],"c":false}]},{"t":21,"s":[{"i":[[0,0],[-2.59,-5.68],[-9.66,0],[-2.51,5.82],[-0.15,0.843],[0,0]],"o":[[0,0],[2.58,5.68],[9.67,0],[0.21,-0.487],[1.647,-9.244],[0,0]],"v":[[-20.693,-23],[-19.093,16.72],[0.237,23],[19.577,16.72],[20.115,14.707],[20.057,-23]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":9,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 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":[100,100],"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":"Transform"}],"nm":"Path","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":1,"s":[0]},{"t":21,"s":[0]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":1,"s":[100]},{"t":21,"s":[100]}],"ix":2},"o":{"a":0,"k":285,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":0,"op":25,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Hand","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":0.667},"o":{"x":0.333,"y":0.333},"t":1,"s":[26.78,27.702,0],"to":[0,0,0],"ti":[0,0,0]},{"t":21,"s":[26.78,27.702,0]}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[33.333,33.333,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":9,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 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":"Fill 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":[100,100],"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":"Transform"}],"nm":"Shape 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"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":1,"s":[{"i":[[0,0],[-9.5,-9.38],[-4.63,-4.42],[-0.29,-0.29],[0,0],[-0.15,-0.14],[-7.99,-8.02],[-1.34,-1.42]],"o":[[5.77,6.17],[9.19,9.09],[0.3,0.29],[0,0],[0.15,0.14],[2.5,2.4],[8.22,8.27],[0,0]],"v":[[-45.283,-45.358],[-22.383,-22.038],[-1.653,-1.778],[-0.763,-0.898],[14.767,14.762],[15.207,15.192],[30.947,30.832],[45.287,45.362]],"c":false}]},{"t":21,"s":[{"i":[[0,0],[-6.24,-8.14],[-0.45,-0.57],[-14.31,0],[0,0],[-4.86,-5.64],[-1.76,-4.22],[-0.4,-9.17]],"o":[[2.38,15.6],[0.45,0.59],[8.84,11.24],[0,0],[7.44,0],[3.8,4.41],[2.93,7.03],[0,0]],"v":[[-54.94,-50.651],[-42.01,-15.041],[-40.66,-13.301],[-4,4.499],[22.23,4.499],[41.61,13.389],[49.94,26.339],[54.94,50.649]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":9,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 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":[100,100],"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":"Transform"}],"nm":"Hand","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":1,"s":[0]},{"t":21,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":3,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":0,"op":24,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":0,"nm":"MIC FIX -single- 55","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[27.5,27.5,0],"ix":2},"a":{"a":0,"k":[27.5,27.5,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":1,"s":[100,100,100]},{"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":[92,92,100]},{"t":22,"s":[100,100,100]}],"ix":6}},"ao":0,"w":55,"h":55,"ip":0,"op":23,"st":0,"bm":0}],"markers":[]} \ No newline at end of file diff --git a/Telegram/Resources/icons/calls/group_calls_share.png b/Telegram/Resources/icons/calls/group_calls_share.png new file mode 100644 index 000000000..cc2dc5b2c Binary files /dev/null and b/Telegram/Resources/icons/calls/group_calls_share.png differ diff --git a/Telegram/Resources/icons/calls/group_calls_share@2x.png b/Telegram/Resources/icons/calls/group_calls_share@2x.png new file mode 100644 index 000000000..9ce9df041 Binary files /dev/null and b/Telegram/Resources/icons/calls/group_calls_share@2x.png differ diff --git a/Telegram/Resources/icons/calls/group_calls_share@3x.png b/Telegram/Resources/icons/calls/group_calls_share@3x.png new file mode 100644 index 000000000..76abb0e88 Binary files /dev/null and b/Telegram/Resources/icons/calls/group_calls_share@3x.png differ diff --git a/Telegram/Resources/icons/calls/hand_muted_active.json b/Telegram/Resources/icons/calls/hand_muted_active.json deleted file mode 100644 index c29ffcc70..000000000 --- a/Telegram/Resources/icons/calls/hand_muted_active.json +++ /dev/null @@ -1 +0,0 @@ -{"v":"5.5.7","meta":{"g":"LottieFiles AE 0.1.20","a":"","k":"","d":"","tc":""},"fr":60,"ip":0,"op":84,"w":55,"h":55,"nm":"Scale All 55","ddd":0,"assets":[{"id":"comp_0","layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Corner Straight","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":14,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":16,"s":[100]},{"t":20,"s":[100],"h":1},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":70,"s":[100]},{"t":72,"s":[0]}],"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":[23.166,30.285,0],"to":[0.183,0,0],"ti":[-0.183,0,0]},{"i":{"x":0.667,"y":0.667},"o":{"x":0.333,"y":0.333},"t":20,"s":[24.266,30.285,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":63,"s":[24.266,30.285,0],"to":[-0.183,0,0],"ti":[0.183,0,0]},{"t":83,"s":[23.166,30.285,0]}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[33.333,33.333,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 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":"Fill 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":[100,100],"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":"Transform"}],"nm":"Shape 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 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":"Fill 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":[100,100],"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":"Transform"}],"nm":"Shape 1","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"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":14,"s":[{"i":[[-0.56,0],[-0.53,-0.56],[-1.52,-1.67],[0,0],[-0.09,-0.42],[0,0],[0,0],[0.04,4.04]],"o":[[0.55,0],[1.75,1.8],[0,0],[1.06,0.89],[0.08,0.43],[0,0],[-4.06,-1.14],[-0.01,-0.97]],"v":[[-4.81,-5.516],[-2.92,-3.646],[0.58,-0.016],[3.54,2.594],[5.5,4.494],[4.24,5.484],[1.45,5.104],[-5.5,-3.566]],"c":true}]},{"t":20,"s":[{"i":[[-1.36,0],[-1.32,-1.42],[-3.72,-4.27],[0,0],[-1.286,-1.213],[0,0],[0,0],[0.1,10.31]],"o":[[1.37,0],[4.29,4.6],[0,0],[2.316,2.252],[1.256,1.184],[0,0],[-9.96,-2.92],[-0.02,-2.48]],"v":[[-11.83,-14.054],[-7.17,-9.274],[1.41,0.006],[8.825,7.156],[13.51,11.536],[10.43,14.056],[3.55,13.076],[-13.52,-9.064]],"c":true}],"h":1},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":24,"s":[{"i":[[-1.36,0],[-1.32,-1.42],[-3.72,-4.27],[0,0],[-1.286,-1.213],[0,0],[0,0],[0.1,10.31]],"o":[[1.37,0],[4.29,4.6],[0,0],[2.316,2.252],[1.256,1.184],[0,0],[-9.96,-2.92],[-0.02,-2.48]],"v":[[-11.83,-14.054],[-7.17,-9.274],[1.41,0.006],[8.825,7.156],[13.51,11.536],[10.43,14.056],[3.55,13.076],[-13.52,-9.064]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.333,"y":0},"t":31,"s":[{"i":[[-2.49,0.02],[-0.02,-2.49],[-6.04,-1.92],[0,0],[0,-2.48],[2.48,0],[0,0],[0.1,10.31]],"o":[[2.48,-0.02],[0.06,6.16],[0,0],[2.48,0],[0,2.49],[-2.23,0],[-9.96,-2.91],[-0.02,-2.48]],"v":[[-9.352,-13.829],[-4.812,-9.369],[5.02,4.114],[9.75,4.834],[14.25,9.334],[9.75,13.834],[2.82,12.844],[-13.812,-9.289]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":36,"s":[{"i":[[-2.49,0.02],[-0.02,-2.49],[-6.04,-1.92],[0,0],[0,-2.48],[2.48,0],[0,0],[0.1,10.31]],"o":[[2.48,-0.02],[0.06,6.16],[0,0],[2.48,0],[0,2.49],[-2.23,0],[-9.96,-2.91],[-0.02,-2.48]],"v":[[-9.401,-13.829],[-4.861,-9.369],[5.02,4.114],[9.735,5.022],[14.235,9.522],[9.735,14.022],[2.82,12.844],[-13.861,-9.289]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0.167},"t":51,"s":[{"i":[[-2.49,0.02],[-0.02,-2.49],[-6.04,-1.92],[0,0],[0,-2.48],[2.48,0],[0,0],[0.1,10.31]],"o":[[2.48,-0.02],[0.06,6.16],[0,0],[2.48,0],[0,2.49],[-2.23,0],[-9.96,-2.91],[-0.02,-2.48]],"v":[[-9.401,-13.829],[-4.861,-9.369],[5.02,4.114],[9.735,5.022],[14.235,9.522],[9.735,14.022],[2.82,12.844],[-13.861,-9.289]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":56,"s":[{"i":[[-1.36,0],[-1.32,-1.42],[-3.72,-4.27],[0,0],[-1.286,-1.213],[0,0],[0,0],[0.1,10.31]],"o":[[1.37,0],[4.29,4.6],[0,0],[2.316,2.252],[1.256,1.184],[0,0],[-9.96,-2.92],[-0.02,-2.48]],"v":[[-11.83,-14.054],[-7.17,-9.274],[1.41,0.006],[8.825,7.156],[13.51,11.536],[10.43,14.056],[3.55,13.076],[-13.52,-9.064]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0},"t":63,"s":[{"i":[[-1.36,0],[-1.32,-1.42],[-3.72,-4.27],[0,0],[-1.286,-1.213],[0,0],[0,0],[0.1,10.31]],"o":[[1.37,0],[4.29,4.6],[0,0],[2.316,2.252],[1.256,1.184],[0,0],[-9.96,-2.92],[-0.02,-2.48]],"v":[[-11.83,-14.054],[-7.17,-9.274],[1.41,0.006],[8.825,7.156],[13.51,11.536],[10.43,14.056],[3.55,13.076],[-13.52,-9.064]],"c":true}]},{"t":72,"s":[{"i":[[-0.56,0],[-0.53,-0.56],[-1.52,-1.67],[0,0],[-0.09,-0.42],[0,0],[0,0],[0.04,4.04]],"o":[[0.55,0],[1.75,1.8],[0,0],[1.06,0.89],[0.08,0.43],[0,0],[-4.06,-1.14],[-0.01,-0.97]],"v":[[-4.81,-5.516],[-2.92,-3.646],[0.58,-0.016],[3.54,2.594],[5.5,4.494],[4.24,5.484],[1.45,5.104],[-5.5,-3.566]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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":"Fill 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":[100,100],"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":"Transform"}],"nm":"Path","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":84,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Head X3","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":[28.34,16.86,0],"to":[-0.113,0.861,0],"ti":[0.113,-0.861,0]},{"t":20,"s":[27.659,22.026,0],"h":1},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":63,"s":[27.659,22.026,0],"to":[0.113,-0.861,0],"ti":[-0.113,0.861,0]},{"t":83,"s":[28.34,16.86,0]}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[33.333,33.333,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":0,"s":[{"i":[[0,0],[-1.79,0],[-0.27,10.18],[0,0],[10.37,0],[0.26,-10.19],[0,0],[-7.69,-2.33]],"o":[[1.64,0.47],[10.24,0],[0,0],[-0.06,-10.35],[-10.25,0],[0,0],[0.08,8.43],[0,0]],"v":[[-5.152,18.127],[-0.002,18.847],[18.838,0.497],[18.838,-0.123],[-0.002,-18.843],[-18.842,-0.473],[-18.842,0.167],[-5.472,18.037]],"c":false}]},{"t":20,"s":[{"i":[[0,0],[-1.85,0],[-0.27,10.18],[0,0],[10.73,0],[0.26,-10.19],[0,0],[-7.96,-2.33]],"o":[[1.69,0.47],[10.6,0],[0,0],[-0.07,-10.35],[-10.61,0],[0,0],[0.08,8.43],[0,0]],"v":[[-5.33,33.627],[0,34.347],[19.49,15.997],[19.5,-15.623],[0,-34.343],[-19.5,-15.973],[-19.5,15.667],[-5.66,33.537]],"c":false}],"h":1},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":63,"s":[{"i":[[0,0],[-1.85,0],[-0.27,10.18],[0,0],[10.73,0],[0.26,-10.19],[0,0],[-7.96,-2.33]],"o":[[1.69,0.47],[10.6,0],[0,0],[-0.07,-10.35],[-10.61,0],[0,0],[0.08,8.43],[0,0]],"v":[[-5.33,33.627],[0,34.347],[19.49,15.997],[19.5,-15.623],[0,-34.343],[-19.5,-15.973],[-19.5,15.667],[-5.66,33.537]],"c":false}]},{"t":83,"s":[{"i":[[0,0],[-1.79,0],[-0.27,10.18],[0,0],[10.37,0],[0.26,-10.19],[0,0],[-7.69,-2.33]],"o":[[1.64,0.47],[10.24,0],[0,0],[-0.06,-10.35],[-10.25,0],[0,0],[0.08,8.43],[0,0]],"v":[[-5.152,18.127],[-0.002,18.847],[18.838,0.497],[18.838,-0.123],[-0.002,-18.843],[-18.842,-0.473],[-18.842,0.167],[-5.472,18.037]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":9,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 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":[100,100],"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":"Transform"}],"nm":"Head X3","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":[0.89]},"o":{"x":[0.333],"y":[0]},"t":8,"s":[0]},{"i":{"x":[0.667],"y":[0.865]},"o":{"x":[0.333],"y":[0.134]},"t":10,"s":[4.592]},{"i":{"x":[0.667],"y":[0.694]},"o":{"x":[0.333],"y":[0.356]},"t":12,"s":[8.352]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0.591]},"t":14,"s":[9.779]},{"t":20,"s":[12],"h":1},{"i":{"x":[0.667],"y":[0.632]},"o":{"x":[0.333],"y":[0]},"t":29,"s":[12]},{"t":36,"s":[2.408],"h":1},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0.039]},"t":56,"s":[2.4]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":58,"s":[12]},{"i":{"x":[0.667],"y":[2.933]},"o":{"x":[0.333],"y":[0]},"t":63,"s":[12]},{"i":{"x":[0.667],"y":[0.811]},"o":{"x":[0.333],"y":[0.133]},"t":68,"s":[12.527]},{"i":{"x":[0.667],"y":[0.519]},"o":{"x":[0.333],"y":[0.643]},"t":69,"s":[10.997]},{"i":{"x":[0.667],"y":[2.442]},"o":{"x":[0.333],"y":[-1.095]},"t":70,"s":[10.548]},{"i":{"x":[0.667],"y":[1.106]},"o":{"x":[0.333],"y":[0.119]},"t":71,"s":[10.745]},{"i":{"x":[0.667],"y":[1.067]},"o":{"x":[0.333],"y":[-0.068]},"t":72,"s":[8.352]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.333],"y":[-0.055]},"t":73,"s":[4.592]},{"t":74,"s":[0]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.667],"y":[0.856]},"o":{"x":[0.333],"y":[0]},"t":8,"s":[100]},{"i":{"x":[0.667],"y":[0.809]},"o":{"x":[0.333],"y":[0.182]},"t":10,"s":[93]},{"i":{"x":[0.667],"y":[0.701]},"o":{"x":[0.333],"y":[0.216]},"t":12,"s":[87.448]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0.667]},"t":14,"s":[82.562]},{"t":20,"s":[76],"h":1},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":24,"s":[76]},{"t":30,"s":[89],"h":1},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0.087]},"t":48,"s":[89]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":51,"s":[76]},{"i":{"x":[0.667],"y":[-0.29]},"o":{"x":[0.333],"y":[0]},"t":63,"s":[76]},{"i":{"x":[0.667],"y":[0.66]},"o":{"x":[0.333],"y":[0.283]},"t":68,"s":[78.388]},{"i":{"x":[0.667],"y":[0.64]},"o":{"x":[0.333],"y":[0.403]},"t":69,"s":[80.568]},{"i":{"x":[0.667],"y":[0.744]},"o":{"x":[0.333],"y":[0.2]},"t":70,"s":[82.407]},{"i":{"x":[0.667],"y":[1.142]},"o":{"x":[0.333],"y":[0.227]},"t":71,"s":[85.714]},{"i":{"x":[0.667],"y":[1.142]},"o":{"x":[0.333],"y":[-0.149]},"t":72,"s":[89.448]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.333],"y":[-0.072]},"t":73,"s":[93]},{"t":74,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":0,"op":84,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Bottom Part","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[27.3,43.973,0],"to":[0,0,0],"ti":[0.633,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":14,"s":[28.4,43.973,0],"to":[-0.363,0,0],"ti":[0.152,0,0]},{"t":20,"s":[27.567,42.931,0],"h":1},{"i":{"x":0.79,"y":0},"o":{"x":0.299,"y":0},"t":63,"s":[27.567,42.931,0],"to":[-0.118,0,0],"ti":[0.089,-0.161,0]},{"i":{"x":0.954,"y":0},"o":{"x":0.046,"y":1},"t":71,"s":[27.255,44.324,0],"to":[-0.015,0.026,0],"ti":[0.089,-0.161,0]},{"t":83,"s":[27.655,44.324,0]}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[33.333,33.333,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":[[0,-6.085],[0,6.085]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":10,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 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":[100,100],"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":"Transform"}],"nm":"Bottom Part","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":83,"s":[0]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":14,"s":[0]},{"t":20,"s":[100],"h":1},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":63,"s":[100]},{"t":72,"s":[0]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":0,"op":84,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Body","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":[28.465,37.202,0],"to":[-0.145,-0.438,0],"ti":[0.145,0.438,0]},{"t":20,"s":[27.596,34.573,0],"h":1},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":63,"s":[27.596,34.573,0],"to":[0.145,0.438,0],"ti":[-0.145,-0.438,0]},{"t":83,"s":[28.465,37.202,0]}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[33.333,33.333,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":0,"s":[{"i":[[0,0],[-2.59,-5.68],[-9.66,0],[-2.51,5.82],[-0.15,0.843],[0,0]],"o":[[0,0],[2.58,5.68],[9.67,0],[0.21,-0.487],[1.647,-9.244],[0,0]],"v":[[-20.693,-23],[-19.093,16.72],[0.237,23],[19.577,16.72],[20.115,14.707],[20.057,-23]],"c":false}]},{"t":20,"s":[{"i":[[0,0],[-7.9,-7.71],[-9.5,0],[-7.28,8.5],[-0.412,0.572],[0,0]],"o":[[0,0],[7.9,7.7],[9.51,0],[0.453,-0.529],[6.202,-8.614],[0,0]],"v":[[-39.084,-17.666],[-29.594,5.754],[-0.314,17.664],[29.986,5.724],[31.283,4.069],[39.086,-17.436]],"c":false}],"h":1},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":63,"s":[{"i":[[0,0],[-7.9,-7.71],[-9.5,0],[-7.28,8.5],[-0.412,0.572],[0,0]],"o":[[0,0],[7.9,7.7],[9.51,0],[0.453,-0.529],[6.202,-8.614],[0,0]],"v":[[-39.084,-17.666],[-29.594,5.754],[-0.314,17.664],[29.986,5.724],[31.283,4.069],[39.086,-17.436]],"c":false}]},{"t":83,"s":[{"i":[[0,0],[-2.59,-5.68],[-9.66,0],[-2.51,5.82],[-0.15,0.843],[0,0]],"o":[[0,0],[2.58,5.68],[9.67,0],[0.21,-0.487],[1.647,-9.244],[0,0]],"v":[[-20.693,-23],[-19.093,16.72],[0.237,23],[19.577,16.72],[20.115,14.707],[20.057,-23]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":9,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 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":[100,100],"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":"Transform"}],"nm":"Path","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":12,"s":[0]},{"t":20,"s":[0],"h":1},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":33,"s":[0]},{"t":39,"s":[0],"h":1},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":58,"s":[0]},{"t":62,"s":[0],"h":1},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":63,"s":[0]},{"t":73,"s":[0]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":12,"s":[100]},{"t":20,"s":[85],"h":1},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0.084]},"t":33,"s":[85]},{"t":39,"s":[100],"h":1},{"i":{"x":[0.667],"y":[1.056]},"o":{"x":[0.333],"y":[0]},"t":58,"s":[100]},{"t":62,"s":[85],"h":1},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":63,"s":[85]},{"t":73,"s":[100]}],"ix":2},"o":{"a":1,"k":[{"t":20,"s":[285],"h":1},{"t":63,"s":[285],"h":1}],"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":0,"op":84,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Hand","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":[26.78,27.702,0],"to":[0.146,-0.067,0],"ti":[-0.146,0.067,0]},{"t":20,"s":[27.655,27.299,0],"h":1},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":63,"s":[27.655,27.299,0],"to":[-0.146,0.067,0],"ti":[0.146,-0.067,0]},{"t":83,"s":[26.78,27.702,0]}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[33.333,33.333,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":9,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 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":"Fill 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":[100,100],"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":"Transform"}],"nm":"Shape 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"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":0,"s":[{"i":[[0,0],[-6.24,-8.14],[-0.45,-0.57],[-14.31,0],[0,0],[-4.86,-5.64],[-1.76,-4.22],[-0.4,-9.17]],"o":[[2.38,15.6],[0.45,0.59],[8.84,11.24],[0,0],[7.44,0],[3.8,4.41],[2.93,7.03],[0,0]],"v":[[-54.94,-50.651],[-42.01,-15.041],[-40.66,-13.301],[-4,4.499],[22.23,4.499],[41.61,13.389],[49.94,26.339],[54.94,50.649]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":6,"s":[{"i":[[0,0],[-7.218,-8.512],[-1.704,-1.725],[-10.911,-2.285],[0,0],[-4.629,-3.585],[-3.629,-5.36],[-0.682,-6.845]],"o":[[3.397,12.771],[3.072,3.14],[9.018,6.939],[0,0],[5.569,1.517],[3.41,3.807],[4.517,7.402],[0,0]],"v":[[-52.043,-49.063],[-36.122,-17.14],[-28.958,-9.844],[-3.029,2.88],[19.991,7.578],[33.689,13.93],[44.242,27.687],[52.044,49.063]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":11,"s":[{"i":[[0,0],[-7.979,-8.801],[-2.679,-2.623],[-7.371,-1.62],[0,0],[-3.136,-2.437],[-5.083,-6.247],[-0.901,-5.037]],"o":[[4.188,10.571],[5.111,5.123],[6.112,4.723],[0,0],[3.763,1.058],[3.107,3.338],[5.751,7.691],[0,0]],"v":[[-49.79,-47.828],[-31.542,-18.773],[-19.856,-7.156],[-2.273,1.62],[18.25,9.972],[27.529,14.35],[39.811,28.735],[49.792,47.829]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":15,"s":[{"i":[[0,0],[-8.631,-9.049],[-3.515,-3.393],[-4.064,-1.947],[0,0],[-1.856,-1.452],[-6.329,-7.007],[-1.089,-3.487]],"o":[[4.866,8.685],[6.859,6.823],[3.621,2.823],[0,0],[5.053,3.319],[2.847,2.936],[6.809,7.939],[0,0]],"v":[[-47.858,-46.77],[-27.617,-20.172],[-12.055,-4.851],[-1.626,2.041],[14.257,10.525],[25.748,19.211],[36.012,29.634],[47.861,46.772]],"c":false}]},{"t":20,"s":[{"i":[[0,0],[-9.5,-9.38],[-4.63,-4.42],[-0.29,-0.29],[0,0],[-0.15,-0.14],[-7.99,-8.02],[-1.34,-1.42]],"o":[[5.77,6.17],[9.19,9.09],[0.3,0.29],[0,0],[0.15,0.14],[2.5,2.4],[8.22,8.27],[0,0]],"v":[[-45.283,-45.358],[-22.383,-22.038],[-1.653,-1.778],[-0.763,-0.898],[14.767,14.762],[15.207,15.192],[30.947,30.832],[45.287,45.362]],"c":false}],"h":1},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":63,"s":[{"i":[[0,0],[-9.5,-9.38],[-4.63,-4.42],[-0.29,-0.29],[0,0],[-0.15,-0.14],[-7.99,-8.02],[-1.34,-1.42]],"o":[[5.77,6.17],[9.19,9.09],[0.3,0.29],[0,0],[0.15,0.14],[2.5,2.4],[8.22,8.27],[0,0]],"v":[[-45.283,-45.358],[-22.383,-22.038],[-1.653,-1.778],[-0.763,-0.898],[14.767,14.762],[15.207,15.192],[30.947,30.832],[45.287,45.362]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":71,"s":[{"i":[[0,0],[-8.631,-9.049],[-3.515,-3.393],[-4.064,-1.947],[0,0],[-1.856,-1.452],[-6.329,-7.007],[-1.089,-3.487]],"o":[[4.866,8.685],[6.859,6.823],[3.621,2.823],[0,0],[5.053,3.319],[2.847,2.936],[6.809,7.939],[0,0]],"v":[[-47.858,-46.77],[-27.617,-20.172],[-12.055,-4.851],[-1.626,2.041],[14.257,10.525],[25.748,19.211],[36.012,29.634],[47.861,46.772]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":74,"s":[{"i":[[0,0],[-7.979,-8.801],[-2.679,-2.623],[-7.371,-1.62],[0,0],[-3.136,-2.437],[-5.083,-6.247],[-0.901,-5.037]],"o":[[4.188,10.571],[5.111,5.123],[6.112,4.723],[0,0],[3.763,1.058],[3.107,3.338],[5.751,7.691],[0,0]],"v":[[-49.79,-47.828],[-31.542,-18.773],[-19.856,-7.156],[-2.273,1.62],[18.25,9.972],[27.529,14.35],[39.811,28.735],[49.792,47.829]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":78,"s":[{"i":[[0,0],[-7.218,-8.512],[-1.704,-1.725],[-10.911,-2.285],[0,0],[-4.629,-3.585],[-3.629,-5.36],[-0.682,-6.845]],"o":[[3.397,12.771],[3.072,3.14],[9.018,6.939],[0,0],[5.569,1.517],[3.41,3.807],[4.517,7.402],[0,0]],"v":[[-52.043,-49.063],[-36.122,-17.14],[-28.958,-9.844],[-3.029,2.88],[19.991,7.578],[33.689,13.93],[44.242,27.687],[52.044,49.063]],"c":false}]},{"t":83,"s":[{"i":[[0,0],[-6.24,-8.14],[-0.45,-0.57],[-14.31,0],[0,0],[-4.86,-5.64],[-1.76,-4.22],[-0.4,-9.17]],"o":[[2.38,15.6],[0.45,0.59],[8.84,11.24],[0,0],[7.44,0],[3.8,4.41],[2.93,7.03],[0,0]],"v":[[-54.94,-50.651],[-42.01,-15.041],[-40.66,-13.301],[-4,4.499],[22.23,4.499],[41.61,13.389],[49.94,26.339],[54.94,50.649]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":9,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 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":[100,100],"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":"Transform"}],"nm":"Hand","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"t":20,"s":[0],"h":1},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0.045]},"t":22,"s":[0]},{"t":41,"s":[100],"h":1},{"t":44,"s":[0],"h":1}],"ix":1},"e":{"a":1,"k":[{"t":20,"s":[100],"h":1},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":22,"s":[100]},{"t":41,"s":[100],"h":1},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":44,"s":[0]},{"t":63,"s":[100],"h":1}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":3,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":0,"op":84,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":0,"nm":"MIC FIX -full- 55","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[27.5,27.5,0],"ix":2},"a":{"a":0,"k":[27.5,27.5,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":0,"s":[100,100,100]},{"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":13,"s":[92,92,100]},{"t":20,"s":[100,100,100],"h":1},{"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":21,"s":[100,100,100]},{"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":34,"s":[92,92,100]},{"t":41,"s":[100,100,100],"h":1},{"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":42,"s":[100,100,100]},{"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":55,"s":[92,92,100]},{"t":62,"s":[100,100,100],"h":1},{"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":63,"s":[100,100,100]},{"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":76,"s":[92,92,100]},{"t":83,"s":[100,100,100]}],"ix":6}},"ao":0,"w":55,"h":55,"ip":0,"op":84,"st":0,"bm":0}],"markers":[]} \ No newline at end of file diff --git a/Telegram/Resources/icons/calls/hands.lottie b/Telegram/Resources/icons/calls/hands.lottie new file mode 100644 index 000000000..04bf1afee --- /dev/null +++ b/Telegram/Resources/icons/calls/hands.lottie @@ -0,0 +1 @@ +{"v":"5.5.7","meta":{"g":"LottieFiles AE 0.1.20","a":"","k":"","d":"","tc":""},"fr":60,"ip":0,"op":540,"w":60,"h":60,"nm":"Raise Hand Test","ddd":0,"assets":[{"id":"comp_0","layers":[{"ddd":0,"ind":1,"ty":3,"nm":"NULL CONTROL","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[256.989,275.127,0],"ix":2},"a":{"a":0,"k":[115,115,0],"ix":1},"s":{"a":0,"k":[500,500,100],"ix":6}},"ao":0,"ip":0,"op":119,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"head","parent":6,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.6],"y":[0]},"t":1,"s":[0]},{"i":{"x":[0.3],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":11,"s":[0]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":23,"s":[-13]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0]},"t":66,"s":[-13]},{"i":{"x":[0.09],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":75,"s":[7]},{"t":92,"s":[0]}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.6,"y":0},"t":1,"s":[0,-37.327,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.6,"y":0},"t":11,"s":[0,-26.827,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.167,"y":0},"t":23,"s":[-4.4,-39.427,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.167,"y":0},"t":36,"s":[-2.401,-30.424,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.167,"y":0},"t":50,"s":[-4.4,-39.427,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.09,"y":1},"o":{"x":0.167,"y":0},"t":66,"s":[-2.401,-30.424,0],"to":[0,0,0],"ti":[0,0,0]},{"t":92,"s":[0,-37.327,0]}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.6,0.6,0.6],"y":[0,0,0]},"t":1,"s":[100,100,100]},{"i":{"x":[0.3,0.3,0.3],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":5,"s":[97,103,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.6,0.6,0.6],"y":[0,0,0]},"t":11,"s":[105,95,100]},{"i":{"x":[0.3,0.3,0.3],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":16,"s":[97,103,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.6,0.6,0.6],"y":[0,0,0]},"t":23,"s":[105,95,100]},{"i":{"x":[0.3,0.3,0.3],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":28,"s":[97,103,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":36,"s":[100,100,100]},{"i":{"x":[0.3,0.3,0.3],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":44,"s":[97,103,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.6,0.6,0.6],"y":[0,0,0]},"t":50,"s":[105,95,100]},{"i":{"x":[0.3,0.3,0.3],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":58,"s":[97,103,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":66,"s":[100,100,100]},{"i":{"x":[0.09,0.09,0.09],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":75,"s":[95,105,100]},{"t":92,"s":[100,100,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-6.341,0],[0,-6.341],[6.341,0],[0,6.341]],"o":[[6.341,0],[0,6.341],[-6.341,0],[0,-6.341]],"v":[[0,-11.482],[11.482,0],[0,11.482],[-11.482,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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":"Fill 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":[100,100],"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":"Transform"}],"nm":"Oval","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":3,"ty":3,"nm":"hands","parent":6,"sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.5],"y":[1]},"o":{"x":[0.5],"y":[0]},"t":0,"s":[0]},{"i":{"x":[0.5],"y":[1]},"o":{"x":[0.5],"y":[0]},"t":21,"s":[-10]},{"i":{"x":[0.17],"y":[1]},"o":{"x":[0.5],"y":[0]},"t":65,"s":[-10]},{"t":93,"s":[0]}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":0,"s":[-2.341,-17.777,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":10,"s":[-2.341,-9.777,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":21,"s":[-3.741,-17.977,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":35,"s":[-1.541,-11.777,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.4,"y":1},"o":{"x":0.5,"y":0},"t":49,"s":[-1.541,-17.777,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.17,"y":1},"o":{"x":0.6,"y":0},"t":65,"s":[-1.541,-11.777,0],"to":[0,0,0],"ti":[0,0,0]},{"t":93,"s":[-2.341,-17.777,0]}],"ix":2},"a":{"a":0,"k":[115,115,0],"ix":1},"s":{"a":0,"k":[20,20,100],"ix":6}},"ao":0,"ip":0,"op":120,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"hands 3","parent":3,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[115,115,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[500,500,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.6,"y":0},"t":0,"s":[{"i":[[0,0],[-4.026,-6.73],[-10.797,-1.273],[-2.119,-0.01]],"o":[[0.65,4.552],[3.882,6.488],[3.499,0.413],[1.186,0.005]],"v":[[-34.027,-30.664],[-27.902,-11.553],[-6.28,2.761],[4.392,3.022]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.6,"y":0},"t":10,"s":[{"i":[[0,0],[-0.71,-5.814],[-13.22,1.67],[-6.659,-0.445]],"o":[[-3.232,5.731],[0.566,4.637],[3.994,-0.505],[1.183,0.079]],"v":[[-18.219,-21.464],[-27.079,-2.224],[-7.644,3.484],[11.49,2.929]],"c":false}]},{"i":{"x":0.5,"y":1},"o":{"x":0.167,"y":0.167},"t":16,"s":[{"i":[[0,0],[-1.902,-5.873],[-9.833,-0.14],[-7.372,0.373]],"o":[[-4.328,1.758],[1.817,5.11],[4.766,0.217],[1.182,0.009]],"v":[[-17.041,-20.099],[-26.446,-5.406],[-7.078,3.541],[9.615,2.763]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.5,"y":0},"t":23,"s":[{"i":[[0,0],[-4.205,-5.986],[-9.957,-1.926],[-4.458,0.474]],"o":[[5.753,4.944],[4.232,6.025],[3.773,0.73],[1.179,-0.125]],"v":[[-37.604,-27.175],[-25.224,-11.553],[-5.844,2.761],[7.055,3.069]],"c":false}]},{"i":{"x":0.5,"y":1},"o":{"x":0.167,"y":0.167},"t":27,"s":[{"i":[[0,0],[-1.728,-7.979],[-9.811,-4.073],[-4.458,0.474]],"o":[[3.018,5.449],[1.024,4.728],[4.574,1.385],[1.179,-0.125]],"v":[[-30.389,-31.532],[-24.556,-12.392],[-6.174,2.474],[7.055,3.069]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.5,"y":0},"t":32,"s":[{"i":[[0,0],[-0.133,-5.267],[-9.077,-3.752],[-4.458,0.474]],"o":[[-4.804,4.722],[0.193,7.66],[5.825,2.408],[1.179,-0.125]],"v":[[-10.804,-33.837],[-18.834,-15.906],[-6.69,2.025],[7.055,3.069]],"c":false}]},{"i":{"x":0.5,"y":1},"o":{"x":0.167,"y":0.167},"t":37,"s":[{"i":[[0,0],[-0.271,-4.621],[-9.517,-2.839],[-4.458,0.474]],"o":[[-3.394,4.297],[0.242,4.13],[4.799,1.569],[1.179,-0.125]],"v":[[-15.907,-30.468],[-22.029,-13.729],[-6.267,2.393],[7.055,3.069]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.5,"y":0},"t":42,"s":[{"i":[[0,0],[-3.928,-6.171],[-9.883,-2.273],[-3.875,0.81]],"o":[[5.522,5.2],[3.954,6.211],[4.837,1.112],[1.161,-0.243]],"v":[[-36.207,-29.267],[-24.551,-13.099],[-5.745,2.464],[7.055,3.069]],"c":false}]},{"i":{"x":0.5,"y":1},"o":{"x":0.167,"y":0.167},"t":48,"s":[{"i":[[0,0],[-1.728,-7.979],[-9.811,-4.073],[-4.458,0.474]],"o":[[3.018,5.449],[1.024,4.728],[4.574,1.385],[1.179,-0.125]],"v":[[-30.389,-31.532],[-24.556,-12.392],[-6.174,2.474],[7.055,3.069]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.5,"y":0},"t":54,"s":[{"i":[[0,0],[-0.133,-5.267],[-9.319,-3.103],[-4.568,0.674]],"o":[[-4.804,4.722],[0.193,7.66],[5.735,1.909],[1.173,-0.173]],"v":[[-10.804,-33.837],[-18.834,-15.906],[-6.656,1.832],[7.072,2.972]],"c":false}]},{"i":{"x":0.5,"y":1},"o":{"x":0.167,"y":0.167},"t":61,"s":[{"i":[[0,0],[-0.271,-4.621],[-9.517,-2.839],[-4.458,0.474]],"o":[[-3.394,4.297],[0.242,4.13],[4.799,1.569],[1.179,-0.125]],"v":[[-15.907,-30.468],[-22.029,-13.729],[-6.267,2.393],[7.055,3.069]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.5,"y":0},"t":68,"s":[{"i":[[0,0],[-4.205,-5.986],[-9.957,-1.926],[-4.458,0.474]],"o":[[5.753,4.944],[4.232,6.025],[3.773,0.73],[1.179,-0.125]],"v":[[-37.604,-27.175],[-25.224,-11.553],[-5.844,2.761],[7.055,3.069]],"c":false}]},{"i":{"x":0.3,"y":1},"o":{"x":0.167,"y":0.167},"t":78,"s":[{"i":[[0,0],[-3.924,-6.477],[-10.111,-0.781],[-3.392,0.105]],"o":[[4.365,3.67],[3.808,6.286],[4.491,0.347],[1.183,-0.028]],"v":[[-37.672,-26.337],[-25.271,-10.97],[-5.844,2.761],[6.463,3.033]],"c":false}]},{"t":95,"s":[{"i":[[0,0],[-4.026,-6.73],[-10.797,-1.273],[-2.119,-0.01]],"o":[[0.65,4.552],[3.882,6.488],[3.499,0.413],[1.186,0.005]],"v":[[-34.027,-30.664],[-27.902,-11.553],[-6.28,2.761],[4.392,3.022]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4.8,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 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":[100,100],"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":"Transform"}],"nm":"Path-8","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":5,"ty":4,"nm":"hands 2","parent":3,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[115,115,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[500,500,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.6,"y":0},"t":0,"s":[{"i":[[-3.103,0.058],[-2.523,0.098],[-2.257,-5.922],[-0.05,-3.076]],"o":[[2.726,-0.051],[10.848,-0.42],[1.636,4.291],[0,0]],"v":[[3.047,3.014],[11.974,3.008],[30.43,17.71],[32.973,31.114]],"c":false}]},{"i":{"x":0.3,"y":1},"o":{"x":0.6,"y":0},"t":10,"s":[{"i":[[-2.831,0.099],[-2.303,-0.066],[-1.926,-3.085],[-0.239,-5.607]],"o":[[3.206,-0.113],[11.894,0.339],[2.109,3.377],[0,0]],"v":[[2.767,2.88],[11.324,2.869],[28.065,7.836],[30.818,22.809]],"c":false}]},{"i":{"x":0.3,"y":1},"o":{"x":0.6,"y":0},"t":22,"s":[{"i":[[-2.833,0.019],[-2.144,-0.845],[-1.574,-3.736],[-3.615,-5.451]],"o":[[4.963,-0.034],[5.323,2.097],[1.94,4.605],[0,0]],"v":[[2.632,3.384],[12.827,2.873],[22.174,19.291],[30.152,32.241]],"c":false}]},{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":36,"s":[{"i":[[-2.83,-0.135],[-3.536,-1.048],[-1.592,-3.476],[-1.09,-6.235]],"o":[[7.035,0.337],[5.485,1.626],[1.67,3.647],[0,0]],"v":[[1.498,3.488],[12.683,2.542],[23.022,15.874],[26.398,31.323]],"c":false}]},{"i":{"x":0.4,"y":1},"o":{"x":0.6,"y":0},"t":50,"s":[{"i":[[-2.829,0.15],[-2.144,-0.845],[-1.574,-3.736],[-3.615,-5.451]],"o":[[4.712,-0.25],[5.323,2.097],[1.94,4.605],[0,0]],"v":[[2.417,3.447],[12.827,2.873],[22.174,19.291],[30.152,32.241]],"c":false}]},{"i":{"x":0.3,"y":1},"o":{"x":0.4,"y":0},"t":66,"s":[{"i":[[-2.833,0.023],[-3.532,-0.206],[-2.149,-3.438],[-1.064,-6.535]],"o":[[4.317,-0.036],[4.623,0.27],[2.442,3.906],[0,0]],"v":[[1.983,3.325],[12.861,3.014],[29.432,13.325],[34.61,28.122]],"c":false}]},{"t":90,"s":[{"i":[[-3.103,0.058],[-2.523,0.098],[-2.257,-5.922],[-0.05,-3.076]],"o":[[2.726,-0.051],[10.848,-0.42],[1.636,4.291],[0,0]],"v":[[3.047,3.014],[11.974,3.008],[30.43,17.71],[32.973,31.114]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4.8,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 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":[100,100],"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":"Transform"}],"nm":"Path-8","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":6,"ty":4,"nm":"body","parent":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.5,"y":1},"o":{"x":0.5,"y":0},"t":0,"s":[116.171,146.724,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":10,"s":[116.171,151.524,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":21,"s":[116.171,140.724,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":35,"s":[116.171,146.724,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.4,"y":1},"o":{"x":0.5,"y":0},"t":49,"s":[116.171,143.924,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.17,"y":0.17},"o":{"x":0.6,"y":0.6},"t":65,"s":[116.171,146.724,0],"to":[0,0,0],"ti":[0,0,0]},{"t":93,"s":[116.171,146.724,0]}],"ix":2},"a":{"a":0,"k":[0,14.211,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.5,0.5,0.5],"y":[0,0,0]},"t":0,"s":[100,100,100]},{"i":{"x":[0.5,0.5,0.5],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":5,"s":[97,103,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.484,0.484,0.484],"y":[0,0,0]},"t":10,"s":[100,100,100]},{"i":{"x":[0.486,0.486,0.486],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":15,"s":[103,97,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.5,0.5,0.5],"y":[0,0,0]},"t":21,"s":[100,100,100]},{"i":{"x":[0.5,0.5,0.5],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":27,"s":[97,103,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.5,0.5,0.5],"y":[0,0,0]},"t":35,"s":[100,100,100]},{"i":{"x":[0.5,0.5,0.5],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":42,"s":[103,97,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.5,0.5,0.5],"y":[0,0,0]},"t":49,"s":[100,100,100]},{"i":{"x":[0.4,0.4,0.4],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":56,"s":[97,103,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.6,0.6,0.6],"y":[0,0,0]},"t":65,"s":[100,100,100]},{"i":{"x":[0.17,0.17,0.17],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":77,"s":[97,103,100]},{"t":93,"s":[100,100,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":0,"s":[{"i":[[0,0],[-0.741,-5.903],[-1.922,-0.751],[-2.946,0],[-2.468,1.063],[-0.228,3.004],[0,0],[0,0]],"o":[[0,0],[0.328,2.608],[2.556,0.999],[2.898,0],[1.959,-0.844],[0.638,-8.388],[0,0],[0,0]],"v":[[-12.432,-14.575],[-11.977,8.464],[-8.487,13.499],[0.4,14.598],[9.223,13.566],[12.656,8.149],[12.482,-13.225],[-1.232,-13.968]],"c":false}]},{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":10,"s":[{"i":[[0,0],[0,0],[-1.89,-0.83],[-2.946,0],[-2.468,1.063],[-0.086,2.131],[0,0],[7.2,-1.2]],"o":[[0,0],[0.14,2.059],[2.504,1.099],[2.898,0],[1.959,-0.844],[0,0],[0,0],[-7.2,1.2]],"v":[[-14.032,-4.05],[-13.127,7.863],[-9.825,12.562],[0.35,14.211],[10.398,12.616],[13.744,7.749],[14.232,-7.571],[-0.968,-7.539]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.5,"y":0},"t":21,"s":[{"i":[[-0.869,-2.458],[0,0],[-1.89,-0.83],[-2.946,0],[-2.468,1.063],[-0.086,2.131],[1.3,6.472],[11.956,-3.973]],"o":[[1.863,5.271],[0.14,2.059],[2.504,1.099],[2.898,0],[1.959,-0.844],[0,0],[-0.119,-0.593],[-5.848,1.943]],"v":[[-14.632,-13.411],[-12.127,7.863],[-8.825,12.562],[0.35,14.211],[9.398,12.616],[12.744,7.749],[11.232,-16.011],[-3.825,-14.466]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0},"t":35,"s":[{"i":[[-0.896,-2.536],[0,0],[-1.89,-0.83],[-2.946,0],[-2.468,1.063],[-0.086,2.131],[2.094,6.249],[8.562,0.638]],"o":[[1.922,5.439],[0.14,2.059],[2.504,1.099],[2.898,0],[1.959,-0.844],[0,0],[0,-0.172],[-6.543,-0.487]],"v":[[-15.346,-10.627],[-12.527,7.863],[-9.225,12.562],[0.35,14.211],[9.798,12.616],[13.144,7.749],[11.974,-10.116],[-6.43,-8.177]],"c":false}]},{"i":{"x":0.5,"y":1},"o":{"x":0.167,"y":0.167},"t":42,"s":[{"i":[[-0.878,-2.484],[0,0],[-1.89,-0.83],[-2.946,0],[-2.468,1.063],[-0.086,2.131],[1.559,6.399],[11.133,-0.108]],"o":[[1.882,5.326],[0.14,2.059],[2.504,1.099],[2.898,0],[1.959,-0.844],[0,0],[-0.105,-0.49],[-6.188,0.06]],"v":[[-14.481,-12.569],[-12.098,7.863],[-8.796,12.562],[0.35,14.211],[9.369,12.616],[12.715,7.749],[11.484,-13.946],[-3.724,-11.3]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.5,"y":0},"t":49,"s":[{"i":[[-0.869,-2.458],[0,0],[-1.89,-0.83],[-2.946,0],[-2.468,1.063],[-0.086,2.131],[1.3,6.472],[11.176,-3.111]],"o":[[1.863,5.271],[0.14,2.059],[2.504,1.099],[2.898,0],[1.959,-0.844],[0,0],[-0.119,-0.593],[-6.043,1.682]],"v":[[-14.732,-16.111],[-12.127,7.863],[-8.825,12.562],[0.35,14.211],[9.398,12.616],[12.744,7.749],[11.532,-15.511],[-3.745,-13.528]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":54,"s":[{"i":[[-0.873,-2.469],[0,0],[-1.89,-0.83],[-2.946,0],[-2.468,1.063],[-0.086,2.131],[1.413,6.44],[7.166,0.651]],"o":[[1.872,5.295],[0.14,2.059],[2.504,1.099],[2.898,0],[1.959,-0.844],[0,0],[-0.102,-0.533],[-6.261,-0.569]],"v":[[-14.525,-15.487],[-12.348,7.863],[-9.046,12.562],[0.35,14.211],[9.619,12.616],[12.964,7.749],[11.758,-14.741],[-3.318,-12.791]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0.167},"t":57,"s":[{"i":[[-0.878,-2.484],[0,0],[-1.89,-0.83],[-2.946,0],[-2.468,1.063],[-0.086,2.131],[1.559,6.399],[7.228,0.668]],"o":[[1.882,5.326],[0.14,2.059],[2.504,1.099],[2.898,0],[1.959,-0.844],[0,0],[0,0.146],[-6.315,-0.584]],"v":[[-14.816,-12.663],[-12.153,7.863],[-8.85,12.562],[0.35,14.211],[9.424,12.616],[12.769,7.749],[11.588,-12.58],[-3.901,-10.75]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0},"t":65,"s":[{"i":[[-0.896,-2.536],[0,0],[-1.89,-0.83],[-2.946,0],[-2.468,1.063],[-0.086,2.131],[2.094,6.249],[11.969,-4.333]],"o":[[1.922,5.439],[0.14,2.059],[2.504,1.099],[2.898,0],[1.959,-0.844],[0,0],[0,-0.172],[-6.23,2.255]],"v":[[-15.524,-9.227],[-12.727,7.863],[-9.425,12.562],[0.35,14.211],[9.998,12.616],[13.344,7.749],[12.174,-10.116],[-3.838,-7.806]],"c":false}]},{"i":{"x":0.17,"y":1},"o":{"x":0.167,"y":0.167},"t":73,"s":[{"i":[[-0.39,-1.104],[0,0],[-1.89,-0.83],[-2.946,0],[-2.468,1.063],[-0.086,2.131],[0.912,2.72],[13.551,-2.732]],"o":[[0.837,2.368],[0.14,2.059],[2.504,1.099],[2.898,0],[1.959,-0.844],[0,0],[0,-0.075],[-6.34,1.278]],"v":[[-14.493,-10.166],[-12.749,7.863],[-9.446,12.562],[0.35,14.211],[10.02,12.616],[13.365,7.749],[12.988,-9.89],[-1.256,-9.949]],"c":false}]},{"t":93,"s":[{"i":[[0,0],[-0.741,-5.903],[-1.922,-0.751],[-2.946,0],[-2.468,1.063],[-0.228,3.004],[0,0],[0,0]],"o":[[0,0],[0.328,2.608],[2.556,0.999],[2.898,0],[1.959,-0.844],[0.638,-8.388],[0,0],[0,0]],"v":[[-12.432,-14.575],[-11.977,8.464],[-8.487,13.499],[0.4,14.598],[9.223,13.566],[12.656,8.149],[12.482,-13.225],[-1.232,-13.968]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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":"Fill 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":[100,100],"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":"Transform"}],"nm":"Path","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":120,"st":0,"bm":0}]},{"id":"comp_1","layers":[{"ddd":0,"ind":1,"ty":3,"nm":"NULL CONTROL","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[256.989,275.127,0],"ix":2},"a":{"a":0,"k":[115,115,0],"ix":1},"s":{"a":0,"k":[500,500,100],"ix":6}},"ao":0,"ip":0,"op":183,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"head","parent":6,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.6,"y":0},"t":0,"s":[0,-37.327,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.6,"y":0},"t":11,"s":[0,-27.027,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.167,"y":0},"t":23,"s":[0.383,-39.627,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.167,"y":0},"t":35,"s":[0.383,-33.606,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.167,"y":0},"t":49,"s":[0.383,-39.627,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.167,"y":0},"t":63,"s":[0.383,-33.606,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.167,"y":0},"t":79,"s":[3.183,-39.627,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.15,"y":1},"o":{"x":0.167,"y":0},"t":93,"s":[3.583,-33.606,0],"to":[0,0,0],"ti":[0,0,0]},{"t":124,"s":[0,-37.327,0]}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.6,0.6,0.6],"y":[0,0,0]},"t":0,"s":[100,100,100]},{"i":{"x":[0.3,0.3,0.3],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":5,"s":[97,103,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.6,0.6,0.6],"y":[0,0,0]},"t":11,"s":[105,95,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":16,"s":[97,103,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.6,0.6,0.6],"y":[0,0,0]},"t":23,"s":[100,100,100]},{"i":{"x":[0.3,0.3,0.3],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":29,"s":[97,103,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.6,0.6,0.6],"y":[0,0,0]},"t":35,"s":[105,95,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":41,"s":[97,103,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.6,0.6,0.6],"y":[0,0,0]},"t":49,"s":[100,100,100]},{"i":{"x":[0.3,0.3,0.3],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":55,"s":[97,103,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.6,0.6,0.6],"y":[0,0,0]},"t":63,"s":[105,95,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":70,"s":[97,103,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.6,0.6,0.6],"y":[0,0,0]},"t":79,"s":[100,100,100]},{"i":{"x":[0.3,0.3,0.3],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":85,"s":[97,103,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.6,0.6,0.6],"y":[0,0,0]},"t":93,"s":[105,95,100]},{"i":{"x":[0.15,0.15,0.15],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":105,"s":[97,103,100]},{"t":124,"s":[100,100,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-6.341,0],[0,-6.341],[6.341,0],[0,6.341]],"o":[[6.341,0],[0,6.341],[-6.341,0],[0,-6.341]],"v":[[0,-11.482],[11.482,0],[0,11.482],[-11.482,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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":"Fill 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":[100,100],"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":"Transform"}],"nm":"Oval","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":184,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":3,"nm":"hands","parent":6,"sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.5],"y":[1]},"o":{"x":[0.5],"y":[0]},"t":0,"s":[0]},{"i":{"x":[0.16],"y":[1]},"o":{"x":[0.5],"y":[0]},"t":21,"s":[0]},{"t":118,"s":[0]}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":0,"s":[-2.341,-17.777,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":10,"s":[-2.341,-9.777,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":21,"s":[-1.541,-17.777,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":33,"s":[-1.541,-15.377,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":47,"s":[-1.541,-17.777,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":61,"s":[-1.541,-15.377,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":78,"s":[-1.541,-17.777,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.16,"y":1},"o":{"x":0.5,"y":0},"t":102,"s":[-1.541,-15.377,0],"to":[0,0,0],"ti":[0,0,0]},{"t":118,"s":[-2.341,-17.777,0]}],"ix":2},"a":{"a":0,"k":[115,115,0],"ix":1},"s":{"a":0,"k":[20,20,100],"ix":6}},"ao":0,"ip":0,"op":184,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"hands 3","parent":3,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[115,115,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[500,500,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.6,"y":0},"t":0,"s":[{"i":[[0,0],[-4.026,-6.73],[-10.797,-1.273],[-2.119,-0.01]],"o":[[0.65,4.552],[3.882,6.488],[3.499,0.413],[1.186,0.005]],"v":[[-34.027,-30.664],[-27.902,-11.553],[-6.28,2.761],[4.392,3.022]],"c":false}]},{"i":{"x":0.3,"y":1},"o":{"x":0.6,"y":0},"t":10,"s":[{"i":[[0,0],[-0.71,-5.814],[-13.22,1.67],[-6.667,-0.441]],"o":[[-3.232,5.731],[0.566,4.637],[3.994,-0.505],[1.183,0.078]],"v":[[-18.219,-21.464],[-27.079,-2.224],[-7.644,3.584],[11.49,2.929]],"c":false}]},{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":23,"s":[{"i":[[0,0],[-1.268,-5.758],[-4.491,-2.782],[-9.089,1.417]],"o":[[-0.238,6.778],[1.347,6.12],[4.565,2.828],[1.172,-0.183]],"v":[[-19.257,-37.475],[-19.531,-14.871],[-9.165,2.078],[11.49,3.527]],"c":false}]},{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":35,"s":[{"i":[[0,0],[-0.961,-3.853],[-6.666,-1.601],[-9.089,1.417]],"o":[[0.03,7.313],[1.517,6.08],[5.221,1.254],[1.172,-0.183]],"v":[[-23.457,-27.675],[-24.527,-6.059],[-7.961,2.838],[11.49,2.929]],"c":false}]},{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":49,"s":[{"i":[[0,0],[-1.268,-5.758],[-4.491,-2.782],[-9.089,1.417]],"o":[[-0.238,6.778],[1.347,6.12],[4.565,2.828],[1.172,-0.183]],"v":[[-19.257,-37.475],[-19.531,-14.871],[-9.165,2.078],[11.49,3.527]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.7,"y":0},"t":59,"s":[{"i":[[0,0],[-0.961,-3.853],[-6.666,-1.601],[-9.116,1.293]],"o":[[0.03,7.313],[1.517,6.08],[5.221,1.254],[1.174,-0.167]],"v":[[-23.457,-27.675],[-24.527,-6.059],[-7.961,2.838],[11.49,2.929]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0.167},"t":65,"s":[{"i":[[0,0],[-0.651,-3.886],[-6.545,-1.827],[-9.086,1.105]],"o":[[4.595,6.728],[1.028,6.132],[5.027,1.403],[1.177,-0.143]],"v":[[-25.036,-29.511],[-22.232,-9.273],[-7.789,2.781],[11.545,2.978]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0},"t":73,"s":[{"i":[[0,0],[0.125,-3.969],[-6.039,-3.086],[-8.715,1.918]],"o":[[-2.798,7.352],[-0.197,6.263],[4.981,2.545],[1.158,-0.255]],"v":[[-9.536,-38.346],[-16.487,-17.314],[-7.359,2.636],[11.683,3.1]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0.167},"t":79,"s":[{"i":[[0,0],[-0.447,-3.908],[-4.757,-3.608],[-9.817,2.3]],"o":[[-4.119,5.691],[0.705,6.167],[4.297,3.185],[1.155,-0.271]],"v":[[-10.167,-36.731],[-16.027,-16.023],[-7.359,2.636],[12.29,3.029]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0},"t":85,"s":[{"i":[[0,0],[-0.961,-3.853],[-4.491,-2.782],[-7.117,1.703]],"o":[[1.597,6.857],[1.517,6.08],[4.565,2.828],[1.153,-0.276]],"v":[[-20.247,-37.336],[-17.926,-14.859],[-7.359,2.636],[11.59,3.229]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0.167},"t":90,"s":[{"i":[[0,0],[-0.511,-3.901],[-4.724,-3.504],[-8.017,1.106]],"o":[[2.238,7.884],[0.807,6.156],[4.331,3.14],[1.175,-0.162]],"v":[[-18.139,-37.479],[-16.265,-15.877],[-7.667,2.047],[11.49,2.929]],"c":false}]},{"i":{"x":0.1,"y":1},"o":{"x":0.167,"y":0},"t":96,"s":[{"i":[[0,0],[0.125,-3.969],[-6.039,-3.086],[-9.123,0.867]],"o":[[-2.798,7.352],[-0.197,6.263],[4.981,2.545],[1.181,-0.112]],"v":[[-9.536,-38.346],[-16.487,-17.314],[-7.559,2.136],[11.683,3.1]],"c":false}]},{"t":137,"s":[{"i":[[0,0],[-4.026,-6.73],[-10.797,-1.273],[-2.119,-0.01]],"o":[[0.65,4.552],[3.882,6.488],[3.499,0.413],[1.186,0.005]],"v":[[-34.027,-30.664],[-27.902,-11.553],[-6.28,2.761],[4.392,3.022]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4.8,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 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":[100,100],"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":"Transform"}],"nm":"Path-8","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":184,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"hands 2","parent":3,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[115,115,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[500,500,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.6,"y":0},"t":0,"s":[{"i":[[-3.103,0.058],[-2.523,0.098],[-2.257,-5.922],[-0.05,-3.076]],"o":[[2.726,-0.051],[10.848,-0.42],[1.636,4.291],[0,0]],"v":[[3.047,3.014],[11.974,3.008],[30.43,17.71],[32.973,31.114]],"c":false}]},{"i":{"x":0.3,"y":1},"o":{"x":0.6,"y":0},"t":10,"s":[{"i":[[-2.832,-0.073],[-2.303,-0.066],[-1.926,-3.085],[-0.239,-5.607]],"o":[[2.794,0.072],[11.894,0.339],[2.109,3.377],[0,0]],"v":[[2.763,2.93],[11.216,2.961],[28.065,7.836],[30.818,22.809]],"c":false}]},{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":23,"s":[{"i":[[-2.832,0.111],[-2.264,0.431],[-1.371,7.044],[-0.143,3.51]],"o":[[2.794,-0.11],[8.983,-1.711],[1.168,-6],[0,0]],"v":[[2.891,4.486],[10.849,3.71],[25.852,-14.597],[24.804,-37.956]],"c":false}]},{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":35,"s":[{"i":[[-2.832,0.111],[-2.272,0.384],[-1.192,6.245],[-0.009,3.513]],"o":[[2.794,-0.11],[9.419,-1.594],[1.146,-6.005],[0,0]],"v":[[3.091,3.837],[10.849,3.012],[29.365,-6.408],[27.224,-29.702]],"c":false}]},{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":49,"s":[{"i":[[-2.832,0.111],[-2.264,0.431],[-1.371,7.044],[-0.143,3.51]],"o":[[2.794,-0.11],[8.983,-1.711],[1.168,-6],[0,0]],"v":[[2.891,4.486],[10.849,3.71],[25.852,-14.597],[24.804,-37.956]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.7,"y":0},"t":63,"s":[{"i":[[-2.832,0.111],[-2.272,0.384],[-1.192,6.245],[-0.009,3.513]],"o":[[2.794,-0.11],[9.419,-1.594],[1.146,-6.005],[0,0]],"v":[[3.115,3.787],[10.849,3.012],[29.365,-6.408],[27.224,-29.702]],"c":false}]},{"i":{"x":0.3,"y":1},"o":{"x":0.167,"y":0.167},"t":68,"s":[{"i":[[-2.832,0.111],[-2.329,0.428],[-1.207,6.31],[-0.019,3.512]],"o":[[2.794,-0.11],[9.405,-1.697],[1.148,-6.004],[0,0]],"v":[[3.081,4.077],[10.905,3.036],[29.081,-7.069],[27.028,-30.369]],"c":false}]},{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":81,"s":[{"i":[[-2.832,0.111],[-2.976,0.923],[-1.371,7.044],[-0.143,3.51]],"o":[[2.794,-0.11],[9.238,-2.866],[1.168,-6],[0,0]],"v":[[2.091,4.836],[11.549,3.31],[25.852,-14.597],[24.804,-37.956]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.7,"y":0},"t":95,"s":[{"i":[[-2.832,0.111],[-2.272,0.384],[-1.192,6.245],[-0.009,3.513]],"o":[[2.794,-0.11],[9.419,-1.594],[1.146,-6.005],[0,0]],"v":[[3.091,3.837],[10.849,3.012],[29.365,-6.408],[27.224,-29.702]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":104,"s":[{"i":[[-2.831,0.08],[-2.289,0.181],[-7.091,2.705],[-0.08,0.435]],"o":[[2.753,-0.078],[9.55,-0.749],[7.091,-2.705],[0,0]],"v":[[2.94,3.427],[10.784,3.015],[28.399,6.452],[28.599,-12.123]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":108,"s":[{"i":[[-2.826,0.181],[-2.296,0.098],[-2.884,-0.516],[0.064,0.955]],"o":[[2.87,-0.184],[9.603,-0.406],[2.056,-0.027],[0,0]],"v":[[2.852,3.381],[10.755,2.875],[28.007,11.666],[29.663,9.28]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":109,"s":[{"i":[[-2.829,0.086],[-2.298,0.077],[-0.947,-0.492],[-0.646,-0.097]],"o":[[2.923,-0.087],[9.617,-0.321],[0.947,0.492],[0,0]],"v":[[2.841,3.319],[10.792,2.896],[27.909,12.969],[29.9,13.784]],"c":false}]},{"i":{"x":0.1,"y":1},"o":{"x":0.167,"y":0.167},"t":110,"s":[{"i":[[-2.832,-0.009],[-2.3,0.057],[-0.78,-1.215],[-0.945,-1.801]],"o":[[2.976,0.009],[9.63,-0.235],[0.78,1.215],[0,0]],"v":[[2.83,3.257],[10.73,2.954],[27.812,14.272],[30.136,18.289]],"c":false}]},{"t":134,"s":[{"i":[[-3.103,0.058],[-2.523,0.098],[-2.257,-5.922],[-0.05,-3.076]],"o":[[2.726,-0.051],[10.848,-0.42],[1.636,4.291],[0,0]],"v":[[3.047,3.014],[11.974,3.008],[30.43,17.71],[32.973,31.114]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4.8,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 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":[100,100],"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":"Transform"}],"nm":"Path-8","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":184,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"body","parent":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.5,"y":1},"o":{"x":0.5,"y":0},"t":0,"s":[116.171,146.724,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.31,"y":1},"o":{"x":0.5,"y":0},"t":10,"s":[116.171,151.524,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.31,"y":1},"o":{"x":0.69,"y":0},"t":21,"s":[116.171,140.724,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.31,"y":1},"o":{"x":0.69,"y":0},"t":33,"s":[116.171,150.324,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.31,"y":1},"o":{"x":0.69,"y":0},"t":47,"s":[116.171,146.724,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.31,"y":1},"o":{"x":0.69,"y":0},"t":61,"s":[116.171,150.324,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.31,"y":1},"o":{"x":0.69,"y":0},"t":78,"s":[116.171,146.724,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.1,"y":1},"o":{"x":0.69,"y":0},"t":102,"s":[116.171,150.324,0],"to":[0,0,0],"ti":[0,0,0]},{"t":133,"s":[116.171,146.724,0]}],"ix":2},"a":{"a":0,"k":[0,14.211,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.5,0.5,0.5],"y":[0,0,0]},"t":0,"s":[100,100,100]},{"i":{"x":[0.5,0.5,0.5],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":5,"s":[97,103,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.484,0.484,0.484],"y":[0,0,0]},"t":10,"s":[100,100,100]},{"i":{"x":[0.31,0.31,0.31],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":15,"s":[103,97,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.563,0.563,0.563],"y":[0,0,0]},"t":21,"s":[100,100,100]},{"i":{"x":[0.219,0.219,0.219],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":27,"s":[97,103,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.406,0.406,0.406],"y":[0,0,0]},"t":33,"s":[100,100,100]},{"i":{"x":[0.233,0.233,0.233],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":40,"s":[103,97,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.69,0.69,0.69],"y":[0,0,0]},"t":47,"s":[100,100,100]},{"i":{"x":[0.31,0.31,0.31],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":54,"s":[97,103,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.495,0.495,0.495],"y":[0,0,0]},"t":61,"s":[100,100,100]},{"i":{"x":[0.203,0.203,0.203],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":67,"s":[103,97,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.69,0.69,0.69],"y":[0,0,0]},"t":78,"s":[100,100,100]},{"i":{"x":[0.31,0.31,0.31],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":89,"s":[97,103,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.495,0.495,0.495],"y":[0,0,0]},"t":102,"s":[100,100,100]},{"i":{"x":[0.1,0.1,0.1],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":117,"s":[103,97,100]},{"t":133,"s":[100,100,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":0,"s":[{"i":[[0,0],[-0.741,-5.903],[-1.922,-0.751],[-2.946,0],[-2.468,1.063],[-0.228,3.004],[0,0],[0,0]],"o":[[0,0],[0.328,2.608],[2.556,0.999],[2.898,0],[1.959,-0.844],[0.638,-8.388],[0,0],[0,0]],"v":[[-12.432,-14.575],[-11.977,8.464],[-8.487,13.499],[0.4,14.598],[9.223,13.566],[12.656,8.149],[12.482,-13.225],[0.089,-13.897]],"c":false}]},{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":10,"s":[{"i":[[0,0],[0,0],[-1.89,-0.83],[-2.946,0],[-2.468,1.063],[-0.086,2.131],[0,0],[0,0]],"o":[[0,0],[0.14,2.059],[2.504,1.099],[2.898,0],[1.959,-0.844],[0,0],[0,0],[0,0]],"v":[[-13.432,-6.25],[-13.127,7.863],[-9.825,12.562],[0.35,14.211],[10.398,12.616],[13.744,7.749],[13.432,-6.171],[0.069,-6.21]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.5,"y":0},"t":21,"s":[{"i":[[0,0],[0,0],[-1.772,-0.83],[-2.763,0],[-2.314,1.063],[-0.081,2.131],[0,0],[4.6,0.1]],"o":[[0,0],[0.132,2.059],[2.348,1.099],[2.717,0],[1.837,-0.844],[0,0],[0,0],[-4.6,-0.1]],"v":[[-12.732,-16.711],[-11.415,7.863],[-8.318,12.562],[0.348,14.211],[8.894,12.616],[12.032,7.749],[12.732,-14.111],[0.032,-12.939]],"c":false}]},{"i":{"x":0.5,"y":1},"o":{"x":0.167,"y":0},"t":33,"s":[{"i":[[0,0],[0,0],[-1.89,-0.83],[-2.946,0],[-2.468,1.063],[-0.086,2.131],[0,0],[0,0]],"o":[[0,0],[0.14,2.059],[2.504,1.099],[2.898,0],[1.959,-0.844],[0,0],[0,0],[0,0]],"v":[[-13.832,-12.011],[-12.127,7.863],[-8.825,12.562],[0.35,14.211],[9.398,12.616],[12.744,7.749],[13.832,-12.011],[0.071,-12.011]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.5,"y":0},"t":47,"s":[{"i":[[0,0],[0,0],[-1.772,-0.83],[-2.763,0],[-2.314,1.063],[-0.081,2.131],[0,0],[0,0]],"o":[[0,0],[0.132,2.059],[2.348,1.099],[2.717,0],[1.837,-0.844],[0,0],[0,0],[0,0]],"v":[[-12.732,-16.711],[-11.415,7.863],[-8.318,12.562],[0.348,14.211],[8.894,12.616],[12.032,7.749],[12.732,-14.111],[0.065,-13.504]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0},"t":61,"s":[{"i":[[0,0],[0,0],[-1.89,-0.83],[-2.946,0],[-2.468,1.063],[-0.086,2.131],[0,0],[0,0]],"o":[[0,0],[0.14,2.059],[2.504,1.099],[2.898,0],[1.959,-0.844],[0,0],[0,0],[0,0]],"v":[[-13.832,-12.011],[-12.127,7.863],[-8.825,12.562],[0.35,14.211],[9.398,12.616],[12.744,7.749],[13.832,-12.011],[0.071,-12.011]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0.167},"t":68,"s":[{"i":[[0.583,-2.853],[0,0],[-1.89,-0.83],[-2.946,0],[-2.468,1.063],[-0.086,2.131],[-1.681,3.721],[9.547,0.72]],"o":[[-1.162,5.686],[0.14,2.059],[2.504,1.099],[2.898,0],[1.959,-0.844],[0,0],[0.172,-0.387],[-6.293,-0.475]],"v":[[-11.685,-13.446],[-12.103,7.863],[-8.8,12.562],[0.35,14.211],[9.374,12.616],[12.719,7.749],[13.832,-14.267],[-0.931,-12.07]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0},"t":78,"s":[{"i":[[0.583,-2.853],[0,0],[-1.89,-0.83],[-2.946,0],[-2.468,1.063],[-0.086,2.131],[-1.202,3.902],[10.545,0.088]],"o":[[-1.162,5.686],[0.14,2.059],[2.504,1.099],[2.898,0],[1.959,-0.844],[0,0],[0.108,-0.363],[-6.244,-0.052]],"v":[[-11.402,-16.846],[-12.127,7.863],[-8.825,12.562],[0.35,14.211],[9.398,12.616],[12.744,7.749],[13.815,-16.611],[-0.813,-13.327]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0.167},"t":90,"s":[{"i":[[0.279,-1.426],[0,0],[-1.89,-0.83],[-2.946,0],[-2.468,1.063],[-0.086,2.131],[-0.575,1.951],[6.396,0.322]],"o":[[-0.556,2.843],[0.14,2.059],[2.504,1.099],[2.898,0],[1.959,-0.844],[0,0],[0.052,-0.181],[-8.294,-0.418]],"v":[[-11.944,-17.745],[-12.154,7.863],[-8.852,12.562],[0.35,14.211],[9.425,12.616],[12.771,7.749],[13.386,-13.461],[-1.432,-12.505]],"c":false}]},{"i":{"x":0.1,"y":1},"o":{"x":0.167,"y":0},"t":102,"s":[{"i":[[0,0],[0,0],[-1.89,-0.83],[-2.946,0],[-2.468,1.063],[-0.086,2.131],[0,0],[0,0]],"o":[[0,0],[0.14,2.059],[2.504,1.099],[2.898,0],[1.959,-0.844],[0,0],[0,0],[0,0]],"v":[[-12.832,-15.411],[-12.127,7.863],[-8.825,12.562],[0.35,14.211],[9.398,12.616],[12.744,7.749],[12.932,-11.911],[-1.184,-11.652]],"c":false}]},{"t":133,"s":[{"i":[[0,0],[-0.741,-5.903],[-1.922,-0.751],[-2.946,0],[-2.468,1.063],[-0.228,3.004],[0,0],[0,0]],"o":[[0,0],[0.328,2.608],[2.556,0.999],[2.898,0],[1.959,-0.844],[0.638,-8.388],[0,0],[0,0]],"v":[[-12.432,-14.575],[-11.977,8.464],[-8.487,13.499],[0.4,14.598],[9.223,13.566],[12.656,8.149],[12.482,-13.225],[0.089,-13.897]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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":"Fill 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":[100,100],"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":"Transform"}],"nm":"Path","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":184,"st":0,"bm":0}]},{"id":"comp_2","layers":[{"ddd":0,"ind":1,"ty":3,"nm":"NULL CONTROL","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[256.989,275.127,0],"ix":2},"a":{"a":0,"k":[115,115,0],"ix":1},"s":{"a":0,"k":[500,500,100],"ix":6}},"ao":0,"ip":0,"op":119,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"head","parent":6,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.6],"y":[0]},"t":1,"s":[0]},{"i":{"x":[0.3],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":11,"s":[0]},{"i":{"x":[0.3],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":23,"s":[21]},{"i":{"x":[0.3],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":36,"s":[11]},{"i":{"x":[0.4],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":50,"s":[21]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.243],"y":[0]},"t":68,"s":[21]},{"i":{"x":[0.566],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":78,"s":[-15.198]},{"t":88,"s":[0]}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.6,"y":0},"t":1,"s":[0,-37.327,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.6,"y":0},"t":11,"s":[0,-26.927,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.167,"y":0},"t":23,"s":[5.6,-39.527,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.167,"y":0},"t":36,"s":[4,-29.127,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.4,"y":1},"o":{"x":0.167,"y":0},"t":50,"s":[5.6,-39.527,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":1},"o":{"x":0.5,"y":0},"t":66,"s":[4,-30.327,0],"to":[0,0,0],"ti":[0,0,0]},{"t":88,"s":[0,-37.327,0]}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.6,0.6,0.6],"y":[0,0,0]},"t":1,"s":[100,100,100]},{"i":{"x":[0.3,0.3,0.3],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":5,"s":[97,103,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.6,0.6,0.6],"y":[0,0,0]},"t":11,"s":[105,95,100]},{"i":{"x":[0.3,0.3,0.3],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":16,"s":[97,103,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.6,0.6,0.6],"y":[0,0,0]},"t":23,"s":[105,95,100]},{"i":{"x":[0.3,0.3,0.3],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":29,"s":[97,103,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.6,0.6,0.6],"y":[0,0,0]},"t":36,"s":[105,95,100]},{"i":{"x":[0.3,0.3,0.3],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":43,"s":[97,103,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.6,0.6,0.6],"y":[0,0,0]},"t":50,"s":[105,95,100]},{"i":{"x":[0.4,0.4,0.4],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":57,"s":[95,105,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":66,"s":[102,98,100]},{"i":{"x":[0.2,0.2,0.2],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":78,"s":[95,105,100]},{"t":88,"s":[100,100,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-6.341,0],[0,-6.341],[6.341,0],[0,6.341]],"o":[[6.341,0],[0,6.341],[-6.341,0],[0,-6.341]],"v":[[0,-11.482],[11.482,0],[0,11.482],[-11.482,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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":"Fill 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":[100,100],"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":"Transform"}],"nm":"Oval","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":3,"ty":3,"nm":"hands","parent":6,"sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.5],"y":[1]},"o":{"x":[0.5],"y":[0]},"t":0,"s":[0]},{"i":{"x":[0.5],"y":[1]},"o":{"x":[0.5],"y":[0]},"t":21,"s":[8]},{"i":{"x":[0.4],"y":[1]},"o":{"x":[0.5],"y":[0]},"t":49,"s":[8]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.6],"y":[0]},"t":65,"s":[8]},{"t":89,"s":[0]}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":0,"s":[-2.341,-17.777,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":10,"s":[-2.341,-9.777,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":21,"s":[-1.541,-17.777,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":35,"s":[-1.541,-11.777,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.4,"y":1},"o":{"x":0.5,"y":0},"t":49,"s":[-1.541,-17.777,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":1},"o":{"x":0.6,"y":0},"t":65,"s":[-1.541,-11.777,0],"to":[0,0,0],"ti":[0,0,0]},{"t":89,"s":[-2.341,-17.777,0]}],"ix":2},"a":{"a":0,"k":[115,115,0],"ix":1},"s":{"a":0,"k":[20,20,100],"ix":6}},"ao":0,"ip":0,"op":120,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"hands 3","parent":3,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[115,115,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[500,500,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.6,"y":0},"t":0,"s":[{"i":[[0,0],[-4.026,-6.73],[-10.797,-1.273],[-2.119,-0.01]],"o":[[0.65,4.552],[3.882,6.488],[3.499,0.413],[1.186,0.005]],"v":[[-34.027,-30.664],[-27.902,-11.553],[-6.28,2.761],[4.392,3.022]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.6,"y":0},"t":10,"s":[{"i":[[0,0],[-0.71,-5.814],[-13.22,1.67],[-6.659,-0.445]],"o":[[-3.232,5.731],[0.566,4.637],[3.994,-0.505],[1.183,0.079]],"v":[[-18.219,-21.464],[-27.079,-2.224],[-7.644,3.484],[10.985,2.86]],"c":false}]},{"i":{"x":0.3,"y":1},"o":{"x":0.167,"y":0},"t":23,"s":[{"i":[[0,0],[-0.961,-3.853],[-4.984,-1.753],[-8.926,0.862]],"o":[[1.597,6.857],[1.517,6.08],[5.484,1.929],[1.18,-0.114]],"v":[[-20.247,-37.336],[-17.926,-14.859],[-7.906,2.713],[8.267,2.627]],"c":false}]},{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":37,"s":[{"i":[[0,0],[-1.803,-4.807],[-6.68,-0.3],[-7.645,-0.484]],"o":[[-1.052,7.028],[2.083,5.552],[5.685,0.256],[1.184,0.075]],"v":[[-20.218,-23.988],[-22.536,-2.921],[-7.359,2.636],[10.433,2.572]],"c":false}]},{"i":{"x":0.4,"y":1},"o":{"x":0.7,"y":0},"t":51,"s":[{"i":[[0,0],[-0.961,-3.853],[-4.984,-1.753],[-8.926,0.862]],"o":[[1.597,6.857],[1.517,6.08],[5.484,1.929],[1.18,-0.114]],"v":[[-20.247,-37.336],[-17.926,-14.859],[-7.906,2.713],[8.267,2.627]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.4,"y":0},"t":67,"s":[{"i":[[0,0],[-1.803,-4.807],[-6.68,-0.3],[-7.645,-0.484]],"o":[[-1.052,7.028],[2.083,5.552],[5.685,0.256],[1.184,0.075]],"v":[[-20.218,-23.988],[-22.536,-2.921],[-7.359,2.636],[10.433,2.572]],"c":false}]},{"i":{"x":0.1,"y":1},"o":{"x":0.167,"y":0.167},"t":78,"s":[{"i":[[0,0],[-1.746,-7.006],[-9.167,-1.369],[-3.018,-0.266]],"o":[[-3.628,5.112],[1.694,6.8],[4.106,0.209],[1.182,0.076]],"v":[[-20.614,-25.461],[-25.11,-7.259],[-6.208,2.731],[7.297,3.206]],"c":false}]},{"t":105,"s":[{"i":[[0,0],[-4.026,-6.73],[-10.797,-1.273],[-2.119,-0.01]],"o":[[0.65,4.552],[3.882,6.488],[3.499,0.413],[1.186,0.005]],"v":[[-34.027,-30.664],[-27.902,-11.553],[-6.28,2.761],[4.392,3.022]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4.8,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 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":[100,100],"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":"Transform"}],"nm":"Path-8","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":5,"ty":4,"nm":"hands 2","parent":3,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[115,115,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[500,500,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.6,"y":0},"t":0,"s":[{"i":[[-3.103,0.058],[-2.523,0.098],[-2.257,-5.922],[-0.05,-3.076]],"o":[[2.726,-0.051],[10.848,-0.42],[1.636,4.291],[0,0]],"v":[[3.047,3.014],[11.974,3.008],[30.43,17.71],[32.973,31.114]],"c":false}]},{"i":{"x":0.3,"y":1},"o":{"x":0.6,"y":0},"t":10,"s":[{"i":[[-2.832,-0.073],[-2.303,-0.066],[-1.926,-3.085],[-0.239,-5.607]],"o":[[2.794,0.072],[11.894,0.339],[2.109,3.377],[0,0]],"v":[[2.747,2.681],[11.207,2.811],[28.065,7.836],[30.818,22.809]],"c":false}]},{"i":{"x":0.3,"y":1},"o":{"x":0.6,"y":0},"t":22,"s":[{"i":[[-2.805,0.395],[-2.144,-0.845],[-1.574,-3.736],[-3.615,-5.451]],"o":[[4.23,-0.595],[5.323,2.097],[1.94,4.605],[0,0]],"v":[[2.696,3.339],[12.911,2.962],[22.174,19.291],[30.152,32.241]],"c":false}]},{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":36,"s":[{"i":[[-2.83,0.127],[-2.144,-0.845],[-1.985,-3.535],[-1.3,-6.195]],"o":[[5.352,-0.241],[5.323,2.097],[1.964,3.497],[0,0]],"v":[[1.908,2.538],[13.562,3.072],[22.983,13.903],[27.16,28.662]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.6,"y":0},"t":50,"s":[{"i":[[-2.805,0.395],[-2.144,-0.845],[-1.574,-3.736],[-3.615,-5.451]],"o":[[4.23,-0.595],[5.323,2.097],[1.94,4.605],[0,0]],"v":[[2.696,3.339],[12.911,2.962],[22.174,19.291],[30.152,32.241]],"c":false}]},{"i":{"x":0.4,"y":1},"o":{"x":0.167,"y":0.167},"t":58,"s":[{"i":[[-2.818,0.261],[-2.144,-0.845],[-1.78,-3.636],[-2.457,-5.823]],"o":[[4.791,-0.418],[5.323,2.097],[1.952,4.051],[0,0]],"v":[[2.321,3.134],[13.236,3.017],[22.578,16.597],[28.656,30.451]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.4,"y":0},"t":66,"s":[{"i":[[-2.83,0.127],[-2.144,-0.845],[-1.985,-3.535],[-1.3,-6.195]],"o":[[5.352,-0.241],[5.323,2.097],[1.964,3.497],[0,0]],"v":[[1.908,2.538],[13.562,3.072],[22.983,13.903],[27.16,28.662]],"c":false}]},{"i":{"x":0.1,"y":1},"o":{"x":0.167,"y":0.167},"t":76,"s":[{"i":[[-2.803,-0.138],[-2.518,-0.399],[-2.207,-4.957],[2.376,-4.348]],"o":[[3.802,0.187],[4.102,0.65],[1.627,3.656],[0,0]],"v":[[1.941,2.94],[11.88,3.616],[26.524,13.453],[23.943,27.653]],"c":false}]},{"t":97,"s":[{"i":[[-3.103,0.058],[-2.523,0.098],[-2.257,-5.922],[-0.05,-3.076]],"o":[[2.726,-0.051],[10.848,-0.42],[1.636,4.291],[0,0]],"v":[[3.047,3.014],[11.974,3.008],[30.43,17.71],[32.973,31.114]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4.8,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 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":[100,100],"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":"Transform"}],"nm":"Path-8","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":6,"ty":4,"nm":"body","parent":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.5,"y":1},"o":{"x":0.5,"y":0},"t":0,"s":[116.171,146.724,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":10,"s":[116.171,151.524,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":21,"s":[116.171,140.724,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":35,"s":[116.171,146.724,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.4,"y":1},"o":{"x":0.5,"y":0},"t":49,"s":[116.171,143.924,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":0.2},"o":{"x":0.6,"y":0.6},"t":65,"s":[116.171,146.724,0],"to":[0,0,0],"ti":[0,0,0]},{"t":89,"s":[116.171,146.724,0]}],"ix":2},"a":{"a":0,"k":[0,14.211,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.5,0.5,0.5],"y":[0,0,0]},"t":0,"s":[100,100,100]},{"i":{"x":[0.5,0.5,0.5],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":5,"s":[97,103,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.484,0.484,0.484],"y":[0,0,0]},"t":10,"s":[100,100,100]},{"i":{"x":[0.486,0.486,0.486],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":15,"s":[103,97,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.5,0.5,0.5],"y":[0,0,0]},"t":21,"s":[100,100,100]},{"i":{"x":[0.5,0.5,0.5],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":27,"s":[97,103,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.5,0.5,0.5],"y":[0,0,0]},"t":35,"s":[100,100,100]},{"i":{"x":[0.5,0.5,0.5],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":42,"s":[103,97,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.5,0.5,0.5],"y":[0,0,0]},"t":49,"s":[100,100,100]},{"i":{"x":[0.4,0.4,0.4],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":56,"s":[97,103,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.6,0.6,0.6],"y":[0,0,0]},"t":65,"s":[100,100,100]},{"i":{"x":[0.2,0.2,0.2],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":76,"s":[97,103,100]},{"t":89,"s":[100,100,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":0,"s":[{"i":[[0,0],[-0.741,-5.903],[-1.922,-0.751],[-2.946,0],[-2.468,1.063],[-0.228,3.004],[0,0],[0,0]],"o":[[0,0],[0.328,2.608],[2.556,0.999],[2.898,0],[1.959,-0.844],[0.638,-8.388],[0,0],[0,0]],"v":[[-12.432,-14.575],[-11.977,8.464],[-8.487,13.499],[0.4,14.598],[9.223,13.566],[12.656,8.149],[12.482,-13.225],[-2.127,-14.017]],"c":false}]},{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":10,"s":[{"i":[[0,0],[0,0],[-2.036,-0.83],[-3.175,0],[-2.659,1.063],[-0.093,2.131],[0,0],[0,0]],"o":[[0,0],[0.446,2.497],[2.698,1.099],[3.123,0],[2.111,-0.844],[0,0],[0,0],[0,0]],"v":[[-14.932,-5.55],[-13.915,7.864],[-10.056,12.562],[0.354,14.211],[10.626,12.616],[14.232,7.749],[14.732,-4.571],[-1.668,-6.839]],"c":false}]},{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":21,"s":[{"i":[[0.416,-4.877],[0,0],[-1.748,-0.83],[-2.726,0],[-2.283,1.063],[-0.08,2.131],[-1.162,6.43],[7.773,-0.606]],"o":[[-0.818,9.587],[0.13,2.059],[2.317,1.099],[2.681,0],[1.812,-0.844],[0,0],[0.023,-0.095],[-6.044,0.472]],"v":[[-11.695,-16.726],[-11.827,7.863],[-8.772,12.562],[0.392,14.211],[9.438,12.616],[12.534,7.749],[14.054,-9.469],[3.058,-14.133]],"c":false}]},{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":35,"s":[{"i":[[0.168,-4.901],[0,0],[-1.759,-0.83],[-2.743,0],[-2.297,1.063],[-0.08,2.131],[-1.325,4.892],[7.682,-1.08]],"o":[[-0.283,8.264],[0.131,2.059],[2.331,1.099],[2.698,0],[1.823,-0.844],[0,0],[0.15,-0.385],[-6.216,0.874]],"v":[[-12.874,-10.003],[-11.908,7.863],[-8.834,12.562],[0.377,14.211],[9.469,12.616],[12.584,7.749],[14.152,-3.848],[-1.25,-8.759]],"c":false}]},{"i":{"x":0.4,"y":1},"o":{"x":0.5,"y":0},"t":49,"s":[{"i":[[0.416,-4.877],[0,0],[-1.748,-0.83],[-2.726,0],[-2.283,1.063],[-0.08,2.131],[-1.162,6.43],[9.673,-2.072]],"o":[[-0.818,9.587],[0.13,2.059],[2.317,1.099],[2.681,0],[1.812,-0.844],[0,0],[0.023,-0.095],[-5.963,1.277]],"v":[[-11.695,-17.126],[-11.827,7.863],[-8.772,12.562],[0.392,14.211],[9.438,12.616],[12.534,7.749],[14.054,-9.469],[-0.642,-13.867]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.6,"y":0},"t":65,"s":[{"i":[[0.168,-4.901],[0,0],[-1.759,-0.83],[-2.743,0],[-2.297,1.063],[-0.08,2.131],[-1.325,4.892],[9.682,-1.898]],"o":[[-0.283,8.264],[0.131,2.059],[2.331,1.099],[2.698,0],[1.823,-0.844],[0,0],[0.15,-0.385],[-6.146,1.205]],"v":[[-12.874,-10.003],[-11.908,7.863],[-8.834,12.562],[0.377,14.211],[9.469,12.616],[12.584,7.749],[14.152,-4.048],[-1.55,-8.541]],"c":false}]},{"t":89,"s":[{"i":[[0,0],[-0.741,-5.903],[-1.922,-0.751],[-2.946,0],[-2.468,1.063],[-0.228,3.004],[0,0],[0,0]],"o":[[0,0],[0.328,2.608],[2.556,0.999],[2.898,0],[1.959,-0.844],[0.638,-8.388],[0,0],[0,0]],"v":[[-12.432,-14.575],[-11.977,8.464],[-8.487,13.499],[0.4,14.598],[9.223,13.566],[12.656,8.149],[12.482,-13.225],[-2.127,-14.017]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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":"Fill 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":[100,100],"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":"Transform"}],"nm":"Path","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":120,"st":0,"bm":0}]},{"id":"comp_3","layers":[{"ddd":0,"ind":1,"ty":3,"nm":"NULL CONTROL","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[256.989,275.127,0],"ix":2},"a":{"a":0,"k":[115,115,0],"ix":1},"s":{"a":0,"k":[500,500,100],"ix":6}},"ao":0,"ip":0,"op":119,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"head","parent":3,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.6],"y":[0]},"t":1,"s":[0]},{"i":{"x":[0.3],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":11,"s":[0]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":23,"s":[21]},{"t":107,"s":[0]}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.6,"y":0},"t":1,"s":[122.706,17.15,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.6,"y":0},"t":11,"s":[122.706,68.999,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.589,"y":1},"o":{"x":0.275,"y":0},"t":23,"s":[150.706,5.999,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.206,"y":0},"t":32,"s":[150.706,32.05,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.589,"y":1},"o":{"x":0.275,"y":0},"t":40,"s":[150.706,27.67,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.206,"y":0},"t":47,"s":[150.706,32.05,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.589,"y":1},"o":{"x":0.275,"y":0},"t":54,"s":[150.706,27.67,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":1},"o":{"x":0.206,"y":0},"t":61,"s":[150.706,32.05,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.1,"y":1},"o":{"x":0.6,"y":0},"t":70,"s":[150.706,27.67,0],"to":[0,0,0],"ti":[0,0,0]},{"t":107,"s":[122.706,17.15,0]}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.6,0.6,0.6],"y":[0,0,0]},"t":1,"s":[500,500,100]},{"i":{"x":[0.3,0.3,0.3],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":5,"s":[485,515,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.6,0.6,0.6],"y":[0,0,0]},"t":11,"s":[525,475,100]},{"i":{"x":[0.3,0.3,0.3],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":16,"s":[485,515,100]},{"i":{"x":[0.3,0.3,0.3],"y":[1,1,1]},"o":{"x":[0.6,0.6,0.6],"y":[0,0,0]},"t":23,"s":[525,475,100]},{"i":{"x":[0.3,0.3,0.3],"y":[1,1,1]},"o":{"x":[0.6,0.6,0.6],"y":[0,0,0]},"t":32,"s":[500,500,100]},{"i":{"x":[0.3,0.3,0.3],"y":[1,1,1]},"o":{"x":[0.6,0.6,0.6],"y":[0,0,0]},"t":40,"s":[500,500,100]},{"i":{"x":[0.3,0.3,0.3],"y":[1,1,1]},"o":{"x":[0.6,0.6,0.6],"y":[0,0,0]},"t":47,"s":[500,500,100]},{"i":{"x":[0.3,0.3,0.3],"y":[1,1,1]},"o":{"x":[0.6,0.6,0.6],"y":[0,0,0]},"t":54,"s":[500,500,100]},{"i":{"x":[0.2,0.2,0.2],"y":[1,1,1]},"o":{"x":[0.6,0.6,0.6],"y":[0,0,0]},"t":61,"s":[500,500,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.6,0.6,0.6],"y":[0,0,0]},"t":70,"s":[500,500,100]},{"i":{"x":[0.1,0.1,0.1],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":77,"s":[480,520,100]},{"t":107,"s":[500,500,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-6.341,0],[0,-6.341],[6.341,0],[0,6.341]],"o":[[6.341,0],[0,6.341],[-6.341,0],[0,-6.341]],"v":[[0,-11.482],[11.482,0],[0,11.482],[-11.482,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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":"Fill 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":[100,100],"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":"Transform"}],"nm":"Oval","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":3,"ty":3,"nm":"NULL CONTROL","parent":7,"sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.5,"y":0},"t":21,"s":[-1.541,-17.777,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.13,"y":1},"o":{"x":0.6,"y":0},"t":66,"s":[-1.541,-20.177,0],"to":[0,0,0],"ti":[0,0,0]},{"t":94,"s":[-1.541,-17.777,0]}],"ix":2},"a":{"a":0,"k":[115,115,0],"ix":1},"s":{"a":0,"k":[20,20,100],"ix":6}},"ao":0,"ip":0,"op":120,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":3,"nm":"hands","parent":3,"sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.5],"y":[1]},"o":{"x":[0.5],"y":[0]},"t":0,"s":[0]},{"i":{"x":[0.5],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":23,"s":[8]},{"i":{"x":[0.13],"y":[1]},"o":{"x":[0.6],"y":[0]},"t":65,"s":[8]},{"t":94,"s":[0]}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":0,"s":[111,115,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":10,"s":[111,155,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.167,"y":0},"t":23,"s":[115,115,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.167,"y":0},"t":30,"s":[115,121,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.167,"y":0},"t":38,"s":[115,115,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.167,"y":0},"t":45,"s":[115,121,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.167,"y":0},"t":53,"s":[115,115,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":1},"o":{"x":0.167,"y":0},"t":57,"s":[115,121,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.13,"y":1},"o":{"x":0.6,"y":0},"t":65,"s":[115,115,0],"to":[0,0,0],"ti":[0,0,0]},{"t":94,"s":[111,115,0]}],"ix":2},"a":{"a":0,"k":[115,115,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"ip":0,"op":120,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"hands 3","parent":4,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[115,115,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[500,500,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.6,"y":0},"t":0,"s":[{"i":[[0,0],[-4.026,-6.73],[-10.797,-1.273],[-2.119,-0.01]],"o":[[0.65,4.552],[3.882,6.488],[3.499,0.413],[1.186,0.005]],"v":[[-34.027,-30.664],[-27.902,-11.553],[-6.28,2.761],[4.392,3.022]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.6,"y":0},"t":10,"s":[{"i":[[0,0],[-0.71,-5.814],[-13.22,1.67],[-6.666,-0.138]],"o":[[-3.232,5.731],[0.566,4.637],[3.994,-0.505],[1.186,0.024]],"v":[[-18.219,-21.464],[-27.079,-2.224],[-7.644,3.484],[11.485,2.829]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0},"t":23,"s":[{"i":[[0,0],[-0.961,-3.853],[-4.491,-2.782],[-8.28,0.661]],"o":[[1.597,6.857],[1.517,6.08],[4.565,2.828],[1.182,-0.094]],"v":[[-20.247,-37.336],[-17.926,-14.859],[-7.359,2.636],[10.724,2.534]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0.167},"t":26,"s":[{"i":[[0,0],[-0.511,-3.901],[-4.724,-3.504],[-8.291,0.526]],"o":[[2.238,7.884],[0.807,6.156],[4.331,3.14],[1.184,-0.075]],"v":[[-18.139,-37.479],[-16.265,-15.877],[-7.359,2.636],[10.941,2.657]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0},"t":30,"s":[{"i":[[0,0],[0.125,-3.969],[-5.052,-4.524],[-8.435,1.023]],"o":[[-2.798,7.352],[-0.197,6.263],[4,3.582],[1.177,-0.143]],"v":[[-6.969,-38.346],[-13.919,-17.314],[-7.359,2.636],[9.48,3.009]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0.167},"t":34,"s":[{"i":[[0,0],[-0.447,-3.908],[-4.757,-3.608],[-8.213,0.625]],"o":[[-4.119,5.691],[0.705,6.167],[4.297,3.185],[1.182,-0.09]],"v":[[-10.167,-36.731],[-16.027,-16.023],[-7.359,2.636],[11.49,2.929]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0},"t":37.666,"s":[{"i":[[0,0],[-0.961,-3.853],[-4.491,-2.782],[-8.28,0.661]],"o":[[1.597,6.857],[1.517,6.08],[4.565,2.828],[1.182,-0.094]],"v":[[-20.247,-37.336],[-17.926,-14.859],[-7.359,2.636],[10.724,2.534]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0.167},"t":41,"s":[{"i":[[0,0],[-0.511,-3.901],[-4.724,-3.504],[-8.291,0.526]],"o":[[2.238,7.884],[0.807,6.156],[4.331,3.14],[1.184,-0.075]],"v":[[-18.139,-37.479],[-16.265,-15.877],[-7.359,2.636],[10.941,2.657]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0},"t":45,"s":[{"i":[[0,0],[0.125,-3.969],[-5.052,-4.524],[-8.435,1.023]],"o":[[-2.798,7.352],[-0.197,6.263],[4,3.582],[1.177,-0.143]],"v":[[-6.969,-38.346],[-13.919,-17.314],[-7.359,2.636],[9.48,3.009]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0.167},"t":48,"s":[{"i":[[0,0],[-0.447,-3.908],[-4.757,-3.608],[-8.213,0.625]],"o":[[-4.119,5.691],[0.705,6.167],[4.297,3.185],[1.182,-0.09]],"v":[[-10.167,-36.731],[-16.027,-16.023],[-7.359,2.636],[11.49,2.929]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0},"t":52.334,"s":[{"i":[[0,0],[-0.961,-3.853],[-4.491,-2.782],[-8.28,0.661]],"o":[[1.597,6.857],[1.517,6.08],[4.565,2.828],[1.182,-0.094]],"v":[[-20.247,-37.336],[-17.926,-14.859],[-7.359,2.636],[10.724,2.534]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0.167},"t":56,"s":[{"i":[[0,0],[-0.511,-3.901],[-4.724,-3.504],[-8.291,0.526]],"o":[[2.238,7.884],[0.807,6.156],[4.331,3.14],[1.184,-0.075]],"v":[[-18.139,-37.479],[-16.265,-15.877],[-7.359,2.636],[10.941,2.657]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0},"t":60,"s":[{"i":[[0,0],[0.125,-3.969],[-5.052,-4.524],[-8.435,1.023]],"o":[[-2.798,7.352],[-0.197,6.263],[4,3.582],[1.177,-0.143]],"v":[[-6.969,-38.346],[-13.919,-17.314],[-7.359,2.636],[9.48,3.009]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0.167},"t":64,"s":[{"i":[[0,0],[-0.447,-3.908],[-4.757,-3.608],[-8.213,0.625]],"o":[[-4.119,5.691],[0.705,6.167],[4.297,3.185],[1.182,-0.09]],"v":[[-10.167,-36.731],[-16.027,-16.023],[-7.359,2.636],[11.49,2.929]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0},"t":68,"s":[{"i":[[0,0],[-0.961,-3.853],[-4.491,-2.782],[-8.28,0.661]],"o":[[1.597,6.857],[1.517,6.08],[4.565,2.828],[1.182,-0.094]],"v":[[-20.247,-37.336],[-17.926,-14.859],[-7.359,2.636],[10.724,2.534]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0.167},"t":71.666,"s":[{"i":[[0,0],[-0.511,-3.901],[-4.724,-3.504],[-8.522,-0.294]],"o":[[2.238,7.884],[0.807,6.156],[4.331,3.14],[1.185,0.041]],"v":[[-18.139,-37.479],[-16.265,-15.877],[-7.359,2.636],[11.49,2.929]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0},"t":76,"s":[{"i":[[0,0],[0.125,-3.969],[-5.052,-4.524],[-9.764,-0.338]],"o":[[-2.798,7.352],[-0.197,6.263],[4,3.582],[1.185,0.041]],"v":[[-6.969,-38.346],[-13.919,-17.314],[-7.359,2.636],[11.487,2.829]],"c":false}]},{"i":{"x":0.09,"y":1},"o":{"x":0.167,"y":0.167},"t":84,"s":[{"i":[[0,0],[-1.151,-9.822],[-6.924,-2.401],[-5.542,0.051]],"o":[[-3.654,5.5],[0.733,6.255],[4.389,1.538],[1.186,-0.011]],"v":[[-12.896,-35.078],[-19.976,-13.433],[-7.442,2.05],[9.027,3.064]],"c":false}]},{"t":104,"s":[{"i":[[0,0],[-4.026,-6.73],[-10.797,-1.273],[-2.119,-0.01]],"o":[[0.65,4.552],[3.882,6.488],[3.499,0.413],[1.186,0.005]],"v":[[-34.027,-30.664],[-27.902,-11.553],[-6.28,2.761],[4.392,3.022]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4.8,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 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":[100,100],"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":"Transform"}],"nm":"Path-8","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":6,"ty":4,"nm":"hands 2","parent":4,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[115,115,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[500,500,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.6,"y":0},"t":0,"s":[{"i":[[-3.103,0.058],[-2.523,0.098],[-2.257,-5.922],[-0.05,-3.076]],"o":[[2.726,-0.051],[10.848,-0.42],[1.636,4.291],[0,0]],"v":[[3.047,3.014],[11.974,3.008],[30.43,17.71],[32.973,31.114]],"c":false}]},{"i":{"x":0.5,"y":1},"o":{"x":0.6,"y":0},"t":10,"s":[{"i":[[-2.832,-0.073],[-2.303,-0.066],[-1.926,-3.085],[-0.239,-5.607]],"o":[[2.794,0.072],[11.894,0.339],[2.109,3.377],[0,0]],"v":[[2.758,2.83],[11.206,2.761],[28.065,7.836],[30.818,22.809]],"c":false}]},{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":23,"s":[{"i":[[-2.816,0.314],[-2.144,-0.845],[-1.574,-3.736],[-3.615,-5.451]],"o":[[4.14,-0.462],[5.323,2.097],[1.94,4.605],[0,0]],"v":[[2.456,3.576],[12.827,2.873],[22.174,19.291],[30.152,32.241]],"c":false}]},{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":31,"s":[{"i":[[-2.805,0.396],[-2.168,-0.779],[-1.2,-3.73],[-2.259,-5.223]],"o":[[3.592,-0.507],[6.085,2.162],[1.511,4.741],[0,0]],"v":[[2.725,4.04],[12.869,3.17],[22.316,18.867],[27.587,32.67]],"c":false}]},{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":38,"s":[{"i":[[-2.816,0.314],[-2.144,-0.845],[-1.574,-3.736],[-3.615,-5.451]],"o":[[4.14,-0.462],[5.323,2.097],[1.94,4.605],[0,0]],"v":[[2.456,3.576],[12.827,2.873],[22.174,19.291],[30.152,32.241]],"c":false}]},{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":46,"s":[{"i":[[-2.805,0.396],[-2.168,-0.779],[-1.2,-3.73],[-2.259,-5.223]],"o":[[3.592,-0.507],[6.085,2.162],[1.511,4.741],[0,0]],"v":[[2.725,4.04],[12.869,3.17],[22.316,18.867],[27.587,32.67]],"c":false}]},{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":53,"s":[{"i":[[-2.816,0.314],[-2.144,-0.845],[-1.574,-3.736],[-3.615,-5.451]],"o":[[4.14,-0.462],[5.323,2.097],[1.94,4.605],[0,0]],"v":[[2.456,3.576],[12.827,2.873],[22.174,19.291],[30.152,32.241]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.5,"y":0},"t":61,"s":[{"i":[[-2.805,0.396],[-2.168,-0.779],[-1.2,-3.73],[-2.259,-5.223]],"o":[[3.592,-0.507],[6.085,2.162],[1.511,4.741],[0,0]],"v":[[2.725,4.04],[12.869,3.17],[22.316,18.867],[27.587,32.67]],"c":false}]},{"i":{"x":0.13,"y":1},"o":{"x":0.6,"y":0},"t":69,"s":[{"i":[[-2.816,0.314],[-2.144,-0.845],[-1.574,-3.736],[-3.615,-5.451]],"o":[[4.14,-0.462],[5.323,2.097],[1.94,4.605],[0,0]],"v":[[2.456,3.576],[12.827,2.873],[22.174,19.291],[30.152,32.241]],"c":false}]},{"t":100,"s":[{"i":[[-3.103,0.058],[-2.523,0.098],[-2.257,-5.922],[-0.05,-3.076]],"o":[[2.726,-0.051],[10.848,-0.42],[1.636,4.291],[0,0]],"v":[[3.047,3.014],[11.974,3.008],[30.43,17.71],[32.973,31.114]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4.8,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 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":[100,100],"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":"Transform"}],"nm":"Path-8","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":7,"ty":4,"nm":"body","parent":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.5,"y":1},"o":{"x":0.5,"y":0},"t":0,"s":[116.171,146.724,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":10,"s":[116.171,151.524,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.723,"y":1},"o":{"x":0.395,"y":0},"t":21,"s":[116.171,146.724,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.361,"y":0},"t":30,"s":[116.171,147.924,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.723,"y":1},"o":{"x":0.395,"y":0},"t":38,"s":[116.171,146.724,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.361,"y":0},"t":45,"s":[116.171,147.924,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.723,"y":1},"o":{"x":0.395,"y":0},"t":52,"s":[116.171,146.724,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.361,"y":0},"t":59,"s":[116.171,147.924,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.104,"y":0.104},"o":{"x":0.395,"y":0.395},"t":66,"s":[116.171,146.724,0],"to":[0,0,0],"ti":[0,0,0]},{"t":94,"s":[116.171,146.724,0]}],"ix":2},"a":{"a":0,"k":[0,14.211,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.5,0.5,0.5],"y":[0,0,0]},"t":0,"s":[100,100,100]},{"i":{"x":[0.5,0.5,0.5],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":5,"s":[97,103,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.484,0.484,0.484],"y":[0,0,0]},"t":10,"s":[100,100,100]},{"i":{"x":[0.486,0.486,0.486],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":15,"s":[103,97,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.5,0.5,0.5],"y":[0,0,0]},"t":21,"s":[100,100,100]},{"i":{"x":[0.486,0.486,0.486],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":25,"s":[98,102,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.5,0.5,0.5],"y":[0,0,0]},"t":30,"s":[100,100,100]},{"i":{"x":[0.486,0.486,0.486],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":34,"s":[103,97,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.48,0.48,0.48],"y":[0,0,0]},"t":38,"s":[100,100,100]},{"i":{"x":[0.471,0.471,0.471],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":41,"s":[98,102,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.537,0.537,0.537],"y":[0,0,0]},"t":45,"s":[100,100,100]},{"i":{"x":[0.582,0.582,0.582],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":49,"s":[103,97,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.48,0.48,0.48],"y":[0,0,0]},"t":52,"s":[100,100,100]},{"i":{"x":[0.471,0.471,0.471],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":55,"s":[98,102,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.468,0.468,0.468],"y":[0,0,0]},"t":59,"s":[100,100,100]},{"i":{"x":[0.2,0.2,0.2],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":62,"s":[103,97,100]},{"i":{"x":[0.13,0.13,0.13],"y":[1,1,1]},"o":{"x":[0.6,0.6,0.6],"y":[0,0,0]},"t":66,"s":[100,100,100]},{"t":94,"s":[100,100,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":0,"s":[{"i":[[0,0],[-0.741,-5.903],[-1.922,-0.751],[-2.946,0],[-2.468,1.063],[-0.228,3.004],[0,0],[0,0],[0,0]],"o":[[0,0],[0.328,2.608],[2.556,0.999],[2.898,0],[1.959,-0.844],[0.638,-8.388],[0,0],[0,0],[0,0]],"v":[[-12.432,-14.575],[-11.977,8.464],[-8.487,13.499],[0.4,14.598],[9.223,13.566],[12.656,8.149],[12.482,-13.225],[8.179,-13.458],[-3.728,-14.103]],"c":false}]},{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":10,"s":[{"i":[[0,0],[0,0],[-1.89,-0.83],[-2.946,0],[-2.468,1.063],[-0.086,2.131],[0,0],[0,0],[0,0]],"o":[[0,0],[0.14,2.059],[2.504,1.099],[2.898,0],[1.959,-0.844],[0,0],[0,0],[0,0],[0,0]],"v":[[-14.032,-5.05],[-13.127,7.863],[-9.825,12.562],[0.35,14.211],[10.398,12.616],[13.744,7.749],[14.232,-5.571],[9.351,-5.481],[-4.158,-6.032]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.5,"y":0},"t":23,"s":[{"i":[[0.662,-3.59],[0,0],[-1.89,-0.83],[-2.946,0],[-2.468,1.063],[-0.086,2.131],[-2.532,4.395],[2.35,1.639],[4.409,1.158]],"o":[[-1.585,8.587],[0.14,2.059],[2.504,1.099],[2.898,0],[1.959,-0.844],[0,0],[0.006,-0.084],[-3.61,-2.518],[-3.578,-0.94]],"v":[[-11.286,-17.808],[-12.133,7.863],[-8.83,12.562],[0.35,14.211],[9.404,12.616],[12.749,7.749],[13.232,-10.927],[9.318,-13.932],[-4.455,-14.008]],"c":false}]},{"i":{"x":0.5,"y":1},"o":{"x":0.167,"y":0.167},"t":30,"s":[{"i":[[1.056,-3.495],[0,0],[-1.89,-0.83],[-2.946,0],[-2.468,1.063],[-0.086,2.131],[-3.19,3.944],[2.35,1.814],[4.6,1.543]],"o":[[-2.702,8.946],[0.14,2.059],[2.504,1.099],[2.898,0],[1.959,-0.844],[0,0],[0.021,-0.037],[-3.61,-2.786],[-3.733,-1.252]],"v":[[-11.366,-17.585],[-11.927,7.863],[-8.625,12.562],[0.35,14.211],[9.398,12.616],[12.744,7.749],[14.152,-8.699],[10.273,-11.929],[-3.437,-12.668]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.5,"y":0},"t":38,"s":[{"i":[[0.662,-3.59],[0,0],[-1.89,-0.83],[-2.946,0],[-2.468,1.063],[-0.086,2.131],[-2.532,4.395],[2.35,1.804],[4.409,1.445]],"o":[[-1.585,8.587],[0.14,2.059],[2.504,1.099],[2.898,0],[1.959,-0.844],[0,0],[0.006,-0.084],[-3.61,-2.771],[-3.578,-1.173]],"v":[[-11.286,-19.408],[-12.133,7.863],[-8.83,12.562],[0.35,14.211],[9.404,12.616],[12.749,7.749],[13.232,-10.927],[9.318,-14.209],[-5.653,-15.762]],"c":false}]},{"i":{"x":0.5,"y":1},"o":{"x":0.167,"y":0.167},"t":45,"s":[{"i":[[3.203,-10.603],[0,0],[-1.89,-0.83],[-2.946,0],[-2.468,1.063],[-0.086,2.131],[-3.19,3.944],[3,-0.7],[3.7,2]],"o":[[-2.702,8.946],[0.14,2.059],[2.504,1.099],[2.898,0],[1.959,-0.844],[0,0],[0.08,-0.14],[-3,0.7],[-3.7,-2]],"v":[[-11.166,-19.585],[-11.927,7.863],[-8.625,12.562],[0.35,14.211],[9.398,12.616],[12.744,7.749],[14.152,-8.699],[8.332,-14.539],[-4.768,-13.439]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.5,"y":0},"t":52,"s":[{"i":[[0.662,-3.59],[0,0],[-1.89,-0.83],[-2.946,0],[-2.468,1.063],[-0.086,2.131],[-2.532,4.395],[2.352,1.824],[4.412,1.481]],"o":[[-1.585,8.587],[0.14,2.059],[2.504,1.099],[2.898,0],[1.959,-0.844],[0,0],[0.006,-0.084],[-3.613,-2.802],[-3.581,-1.202]],"v":[[-11.303,-19.608],[-12.133,7.863],[-8.83,12.562],[0.35,14.211],[9.404,12.616],[12.749,7.749],[13.232,-10.927],[9.315,-14.243],[-5.266,-16.412]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.167,"y":0.167},"t":59,"s":[{"i":[[1.056,-3.495],[0,0],[-1.89,-0.83],[-2.946,0],[-2.468,1.063],[-0.086,2.131],[-3.19,3.944],[2.33,2.05],[4.563,1.956]],"o":[[-2.702,8.946],[0.14,2.059],[2.504,1.099],[2.898,0],[1.959,-0.844],[0,0],[0.021,-0.037],[-3.578,-3.149],[-3.704,-1.587]],"v":[[-11.164,-19.885],[-11.927,7.863],[-8.625,12.562],[0.35,14.211],[9.398,12.616],[12.744,7.749],[14.152,-8.699],[10.308,-12.326],[-4.786,-14.983]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.6,"y":0},"t":66,"s":[{"i":[[0.662,-3.59],[0,0],[-1.89,-0.83],[-2.946,0],[-2.468,1.063],[-0.086,2.131],[-2.532,4.395],[2.352,1.886],[4.412,1.589]],"o":[[-1.585,8.587],[0.14,2.059],[2.504,1.099],[2.898,0],[1.959,-0.844],[0,0],[0.006,-0.084],[-3.613,-2.897],[-3.581,-1.29]],"v":[[-11.303,-20.208],[-12.133,7.863],[-8.83,12.562],[0.35,14.211],[9.404,12.616],[12.749,7.749],[13.232,-10.927],[9.315,-14.347],[-4.166,-15.759]],"c":false}]},{"i":{"x":0.13,"y":1},"o":{"x":0.167,"y":0.167},"t":79,"s":[{"i":[[0.229,-1.092],[0,0],[-1.89,-0.83],[-2.946,0],[-2.468,1.063],[-0.086,2.131],[-0.774,1.343],[2.54,0.846],[4.54,0.959]],"o":[[-0.964,4.6],[0.14,2.059],[2.504,1.099],[2.898,0],[1.959,-0.844],[0,0],[0.002,-0.026],[-3.902,-1.3],[-3.684,-0.778]],"v":[[-12.005,-18.139],[-12.127,7.863],[-8.825,12.562],[0.35,14.211],[9.398,12.616],[12.744,7.749],[13.275,-12.666],[9.02,-14.164],[-5.367,-14.041]],"c":false}]},{"t":94,"s":[{"i":[[0,0],[-0.741,-5.903],[-1.922,-0.751],[-2.946,0],[-2.468,1.063],[-0.228,3.004],[0,0],[0,0],[0,0]],"o":[[0,0],[0.328,2.608],[2.556,0.999],[2.898,0],[1.959,-0.844],[0.638,-8.388],[0,0],[0,0],[0,0]],"v":[[-12.432,-14.575],[-11.977,8.464],[-8.487,13.499],[0.4,14.598],[9.223,13.566],[12.656,8.149],[12.482,-13.225],[8.179,-13.458],[-3.728,-14.103]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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":"Fill 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":[100,100],"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":"Transform"}],"nm":"Path","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":120,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":0,"nm":"hand_4","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[29.062,27.773,0],"ix":2},"a":{"a":0,"k":[256,256,0],"ix":1},"s":{"a":0,"k":[11.426,11.426,100],"ix":6}},"ao":0,"w":512,"h":512,"ip":420,"op":540,"st":420,"bm":0},{"ddd":0,"ind":2,"ty":0,"nm":"hand_3","refId":"comp_1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[29.062,27.773,0],"ix":2},"a":{"a":0,"k":[256,256,0],"ix":1},"s":{"a":0,"k":[11.426,11.426,100],"ix":6}},"ao":0,"w":512,"h":512,"ip":240,"op":420,"st":240,"bm":0},{"ddd":0,"ind":3,"ty":0,"nm":"hand_2","refId":"comp_2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[29.062,27.773,0],"ix":2},"a":{"a":0,"k":[256,256,0],"ix":1},"s":{"a":0,"k":[11.426,11.426,100],"ix":6}},"ao":0,"w":512,"h":512,"ip":120,"op":240,"st":120,"bm":0},{"ddd":0,"ind":4,"ty":0,"nm":"hand_1","refId":"comp_3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[29.062,27.773,0],"ix":2},"a":{"a":0,"k":[256,256,0],"ix":1},"s":{"a":0,"k":[11.426,11.426,100],"ix":6}},"ao":0,"w":512,"h":512,"ip":0,"op":120,"st":0,"bm":0}],"markers":[]} \ No newline at end of file diff --git a/Telegram/Resources/icons/calls/raised_hand.json b/Telegram/Resources/icons/calls/raised_hand.json deleted file mode 100644 index 536085469..000000000 --- a/Telegram/Resources/icons/calls/raised_hand.json +++ /dev/null @@ -1 +0,0 @@ -{"v":"5.5.7","meta":{"g":"LottieFiles AE 0.1.20","a":"","k":"","d":"","tc":""},"fr":60,"ip":0,"op":720,"w":55,"h":55,"nm":"Scale All Hands FIX 55","ddd":0,"assets":[{"id":"comp_0","layers":[{"ddd":0,"ind":1,"ty":4,"nm":"handle","parent":6,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.6],"y":[0]},"t":10,"s":[174]},{"i":{"x":[0.5],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":16,"s":[147]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.213],"y":[0]},"t":23,"s":[-17]},{"i":{"x":[0.61],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":27,"s":[-35.589]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.5],"y":[0]},"t":32,"s":[27.015]},{"i":{"x":[0.5],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":37,"s":[47.22]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0]},"t":42,"s":[-18]},{"i":{"x":[0.61],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":48,"s":[-35.589]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.5],"y":[0]},"t":54,"s":[27.015]},{"i":{"x":[0.5],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":61,"s":[47.22]},{"i":{"x":[0.785],"y":[-2.394]},"o":{"x":[0.518],"y":[0]},"t":73,"s":[-18]},{"i":{"x":[0.312],"y":[1]},"o":{"x":[0.204],"y":[0.31]},"t":81,"s":[-9.175]},{"t":97,"s":[174]}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.6,"y":0},"t":10,"s":[-9.96,1.125,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.167,"y":0.167},"t":16,"s":[-16.661,-20.671,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.5,"y":0},"t":23,"s":[-36.588,-27.368,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.167,"y":0.167},"t":27,"s":[-30.271,-30.836,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.5,"y":0},"t":32,"s":[-11.215,-32.394,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.167,"y":0.167},"t":37,"s":[-16.116,-31.018,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.5,"y":0},"t":42,"s":[-36.588,-27.368,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.167,"y":0.167},"t":48,"s":[-30.271,-30.836,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.5,"y":0},"t":54,"s":[-11.215,-32.394,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.167,"y":0.167},"t":61,"s":[-16.116,-31.018,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.5,"y":0},"t":73,"s":[-36.588,-27.368,0],"to":[0,0,0],"ti":[0,0,0]},{"t":97,"s":[-9.802,-0.869,0]}],"ix":2},"a":{"a":0,"k":[-215.5,-116,0],"ix":1},"s":{"a":0,"k":[20.019,19.981,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":10,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-215.5,-116],[-215.146,-127.132]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":14,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-215.5,-116],[-210.023,-182.103]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":16,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-215.5,-116],[-207.5,-198.5]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":86,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-215.5,-116],[-207.5,-198.5]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":87,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-215.5,-116],[-211.874,-179.057]],"c":false}]},{"t":89,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-215.5,-116],[-215.991,-154.088]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":12,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 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":"Fill 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":[100,100],"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":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":10,"op":92,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"flag","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":92.982,"ix":10},"p":{"a":0,"k":[-204.72,-162.79,0],"ix":2},"a":{"a":0,"k":[-122.092,14.474,0],"ix":1},"s":{"a":0,"k":[99.986,100.014,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.6,"y":0},"t":12,"s":[{"i":[[0.317,-0.377],[9.871,0.008],[-5.426,0.66]],"o":[[-6.226,-0.015],[-1.013,-0.074],[4.743,0.541]],"v":[[-5.96,154.637],[-50.486,154.578],[-27.743,151.11]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":14,"s":[{"i":[[0.317,-1.093],[9.871,0.023],[-3.056,2.72]],"o":[[-6.226,-0.044],[-1.013,-0.215],[7.435,1.476]],"v":[[-5.96,154.637],[-28.783,155.672],[-28.323,148.174]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":16,"s":[{"i":[[0.317,-4.427],[9.871,0.094],[-7.661,10.615]],"o":[[-6.226,-0.178],[-1.013,-0.869],[16.085,12.234]],"v":[[-5.96,154.637],[-39.634,154.55],[-66.371,118.451]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":18,"s":[{"i":[[0.317,-7.761],[9.871,0.165],[-21.058,28.062]],"o":[[-6.226,-0.312],[-1.013,-1.524],[1.096,39.632]],"v":[[-5.96,154.637],[-50.486,153.428],[-40.692,84.982]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":20,"s":[{"i":[[0.317,-7.761],[9.871,0.165],[-6.509,17.627]],"o":[[-6.226,-0.312],[-1.013,-1.524],[5.307,24.241]],"v":[[-5.96,154.637],[-50.486,153.428],[-25.785,87.237]],"c":true}]},{"i":{"x":0.5,"y":1},"o":{"x":0.167,"y":0.167},"t":22,"s":[{"i":[[0.317,-7.761],[9.871,0.165],[-32.021,13.36]],"o":[[-6.226,-0.312],[-1.013,-1.524],[-16.69,26.199]],"v":[[-5.96,154.637],[-50.486,153.428],[15.356,105.805]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0},"t":25,"s":[{"i":[[0.317,-7.761],[9.871,0.165],[-5.426,13.583]],"o":[[-6.226,-0.312],[-1.013,-1.524],[4.743,11.147]],"v":[[-5.96,154.637],[-50.486,153.428],[-27.743,82.045]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":27,"s":[{"i":[[0.317,-7.761],[9.871,0.165],[12.378,29.044]],"o":[[-6.226,-0.312],[-1.013,-1.524],[29.83,8.678]],"v":[[-5.96,154.637],[-50.486,153.428],[-51.505,85]],"c":true}]},{"i":{"x":0.5,"y":1},"o":{"x":0.167,"y":0.167},"t":32,"s":[{"i":[[0.317,7.203],[9.871,-0.153],[-31.325,-39.218]],"o":[[-6.226,0.289],[-1.013,1.414],[-3.169,-41.969]],"v":[[-5.83,154.152],[-51.499,151.85],[-15.606,221.238]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.5,"y":0},"t":34,"s":[{"i":[[0.317,7.203],[9.871,-0.153],[-5.426,-12.606]],"o":[[-6.226,0.289],[-1.013,1.414],[4.743,-10.345]],"v":[[-5.83,154.152],[-51.499,151.85],[-27.874,222.489]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":37,"s":[{"i":[[0.317,7.203],[9.871,-0.153],[11.573,-55.334]],"o":[[-6.226,0.289],[-1.013,1.414],[39.449,-25.407]],"v":[[-5.83,154.152],[-51.499,151.85],[-52.752,224.049]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":42,"s":[{"i":[[0.317,-6.39],[9.871,0.136],[-11.942,3.44]],"o":[[-6.226,-0.257],[-1.013,-1.254],[-1.26,15.905]],"v":[[-5.948,154.592],[-50.579,153.284],[-20.684,99.413]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":45,"s":[{"i":[[0.317,-7.761],[9.871,0.165],[12.378,29.044]],"o":[[-6.226,-0.312],[-1.013,-1.524],[29.83,8.678]],"v":[[-5.96,154.637],[-50.486,153.428],[-51.505,85]],"c":true}]},{"i":{"x":0.5,"y":1},"o":{"x":0.167,"y":0.167},"t":53,"s":[{"i":[[0.317,7.203],[9.871,-0.153],[-31.325,-39.218]],"o":[[-6.226,0.289],[-1.013,1.414],[-3.169,-41.969]],"v":[[-5.83,154.152],[-51.499,151.85],[-15.606,221.238]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.5,"y":0},"t":55.842,"s":[{"i":[[0.317,7.203],[9.871,-0.153],[-5.426,-12.606]],"o":[[-6.226,0.289],[-1.013,1.414],[4.743,-10.345]],"v":[[-5.83,154.152],[-51.499,151.85],[-27.874,222.489]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":63,"s":[{"i":[[0.317,7.203],[9.871,-0.153],[11.573,-55.334]],"o":[[-6.226,0.289],[-1.013,1.414],[39.449,-25.407]],"v":[[-5.83,154.152],[-51.499,151.85],[-52.752,224.049]],"c":true}]},{"i":{"x":0.5,"y":1},"o":{"x":0.167,"y":0.167},"t":71,"s":[{"i":[[0.317,-5.893],[9.871,0.126],[-21.71,21.684]],"o":[[-6.226,-0.237],[-1.013,-1.157],[-2.05,24.036]],"v":[[-5.944,154.576],[-50.612,153.231],[-14.362,101.878]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0},"t":75,"s":[{"i":[[0.317,-7.761],[9.871,0.165],[-5.426,13.583]],"o":[[-6.226,-0.312],[-1.013,-1.524],[4.743,11.147]],"v":[[-5.96,154.637],[-50.486,153.428],[-27.743,82.045]],"c":true}]},{"i":{"x":0.5,"y":1},"o":{"x":0.167,"y":0.167},"t":81,"s":[{"i":[[0.317,5.961],[9.871,-0.127],[30.989,-21.541]],"o":[[-6.226,0.24],[-1.013,1.17],[42.685,-3.8]],"v":[[-5.841,154.192],[-51.415,151.981],[-117.05,184.128]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0},"t":83,"s":[{"i":[[0.317,7.203],[9.871,-0.153],[-22.34,-40.1]],"o":[[-6.226,0.289],[-1.013,1.414],[1.595,-36.326]],"v":[[-5.83,154.152],[-51.499,151.85],[-28.747,222.978]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0.167},"t":86,"s":[{"i":[[0.139,7.208],[9.871,0.091],[-5.112,-12.737]],"o":[[-6.231,0.135],[-1.048,1.388],[4.997,-10.225]],"v":[[-5.313,153.232],[-48.29,151.152],[-29.044,221.002]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":87,"s":[{"i":[[0.317,7.203],[9.871,-0.153],[-5.426,-12.606]],"o":[[-13.386,-2.417],[-1.013,1.414],[4.743,-10.345]],"v":[[-4.828,156.44],[-48.115,167.327],[-27.874,222.489]],"c":true}]},{"t":89,"s":[{"i":[[0.317,7.203],[9.871,-0.153],[-0.737,-10.604]],"o":[[-6.226,0.289],[-1.013,1.414],[4.743,-10.345]],"v":[[-5.83,154.152],[-15.405,184.967],[-16.93,216.13]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":12,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 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":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-113.297,-133.109],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"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":"Transform"}],"nm":"Polystar 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":14,"op":90,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":3,"nm":"NULL CONTROL","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[256.989,275.127,0],"ix":2},"a":{"a":0,"k":[115,115,0],"ix":1},"s":{"a":0,"k":[500,500,100],"ix":6}},"ao":0,"ip":0,"op":182,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"head","parent":8,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.6],"y":[0]},"t":1,"s":[0]},{"i":{"x":[0.3],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":11,"s":[0]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":23,"s":[-13]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.16],"y":[0]},"t":71,"s":[-13]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0]},"t":98.215,"s":[0]},{"i":{"x":[0.09],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":106.715,"s":[7]},{"t":127.357421875,"s":[0]}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.6,"y":0},"t":1,"s":[0,-37.327,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.6,"y":0},"t":11,"s":[0,-26.827,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.167,"y":0},"t":23,"s":[-4.4,-39.427,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.167,"y":0},"t":36,"s":[-2.401,-30.424,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.167,"y":0},"t":50,"s":[-4.4,-39.427,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.315,"y":0},"t":71,"s":[-2.401,-30.424,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.496,"y":1},"o":{"x":0.6,"y":0},"t":98,"s":[5.201,-26.827,0],"to":[0,0,0],"ti":[0,0,0]},{"t":127.357421875,"s":[0,-37.327,0]}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.6,0.6,0.6],"y":[0,0,0]},"t":1,"s":[100,100,100]},{"i":{"x":[0.3,0.3,0.3],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":5,"s":[97,103,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.6,0.6,0.6],"y":[0,0,0]},"t":11,"s":[105,95,100]},{"i":{"x":[0.3,0.3,0.3],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":16,"s":[97,103,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.6,0.6,0.6],"y":[0,0,0]},"t":23,"s":[105,95,100]},{"i":{"x":[0.3,0.3,0.3],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":28,"s":[97,103,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":36,"s":[100,100,100]},{"i":{"x":[0.3,0.3,0.3],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":44,"s":[97,103,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.6,0.6,0.6],"y":[0,0,0]},"t":50,"s":[105,95,100]},{"i":{"x":[0.3,0.3,0.3],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":58,"s":[97,103,100]},{"i":{"x":[0.3,0.3,0.3],"y":[1,1,1]},"o":{"x":[0.16,0.16,0.16],"y":[0,0,0]},"t":71,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.6,0.6,0.6],"y":[0,0,0]},"t":98.215,"s":[105,95,100]},{"i":{"x":[0.09,0.09,0.09],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":106.715,"s":[95,105,100]},{"t":127.357421875,"s":[100,100,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-6.341,0],[0,-6.341],[6.341,0],[0,6.341]],"o":[[6.341,0],[0,6.341],[-6.341,0],[0,-6.341]],"v":[[0,-11.482],[11.482,0],[0,11.482],[-11.482,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"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":"Transform"}],"nm":"Oval","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":5.5,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false}],"ip":0,"op":183,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":3,"nm":"hands","parent":8,"sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.5],"y":[1]},"o":{"x":[0.5],"y":[0]},"t":0,"s":[0]},{"i":{"x":[0.5],"y":[1]},"o":{"x":[0.5],"y":[0]},"t":21,"s":[-10]},{"i":{"x":[0.17],"y":[1]},"o":{"x":[0.5],"y":[0]},"t":70,"s":[-10]},{"t":128.572265625,"s":[0]}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":0,"s":[-2.341,-17.777,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":10,"s":[-2.341,-9.777,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":21,"s":[-3.741,-17.977,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":35,"s":[-1.541,-11.777,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.4,"y":1},"o":{"x":0.5,"y":0},"t":49,"s":[-1.541,-17.777,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.544,"y":0},"t":70,"s":[-1.541,-11.777,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.134,"y":1},"o":{"x":0.5,"y":0},"t":97,"s":[-2.341,-9.777,0],"to":[0,0,0],"ti":[0,0,0]},{"t":128.572265625,"s":[-2.341,-17.777,0]}],"ix":2},"a":{"a":0,"k":[115,115,0],"ix":1},"s":{"a":0,"k":[20,20,100],"ix":6}},"ao":0,"ip":0,"op":183,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"hands 3","parent":5,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[115,115,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[500,500,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.6,"y":0},"t":0,"s":[{"i":[[0,0],[-4.026,-6.73],[-10.797,-1.273],[-2.119,-0.01]],"o":[[0.65,4.552],[3.882,6.488],[3.499,0.413],[1.186,0.005]],"v":[[-34.027,-30.664],[-27.902,-11.553],[-6.28,2.761],[4.392,3.022]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.6,"y":0},"t":10,"s":[{"i":[[0,0],[-0.71,-5.814],[-13.22,1.67],[-6.659,-0.445]],"o":[[-4.86,-9.215],[0.566,4.637],[3.994,-0.505],[1.183,0.079]],"v":[[-9.125,0.522],[-27.079,-2.224],[-7.644,3.484],[11.49,2.929]],"c":false}]},{"i":{"x":0.5,"y":1},"o":{"x":0.167,"y":0.167},"t":16,"s":[{"i":[[0,0],[-1.902,-5.873],[-9.833,-0.14],[-7.372,0.373]],"o":[[-4.328,1.758],[1.817,5.11],[4.766,0.217],[1.182,0.009]],"v":[[-17.041,-20.099],[-26.446,-5.406],[-7.078,3.541],[9.615,2.763]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.5,"y":0},"t":23,"s":[{"i":[[0,0],[-4.205,-5.986],[-9.957,-1.926],[-4.458,0.474]],"o":[[5.753,4.944],[4.232,6.025],[3.773,0.73],[1.179,-0.125]],"v":[[-37.604,-27.175],[-25.224,-11.553],[-5.844,2.761],[7.055,3.069]],"c":false}]},{"i":{"x":0.5,"y":1},"o":{"x":0.167,"y":0.167},"t":27,"s":[{"i":[[0,0],[-1.728,-7.979],[-9.811,-4.073],[-4.458,0.474]],"o":[[3.018,5.449],[1.024,4.728],[4.574,1.385],[1.179,-0.125]],"v":[[-30.389,-31.532],[-24.556,-12.392],[-6.174,2.474],[7.055,3.069]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.5,"y":0},"t":32,"s":[{"i":[[0,0],[-0.133,-5.267],[-9.077,-3.752],[-4.458,0.474]],"o":[[-4.804,4.722],[0.193,7.66],[5.825,2.408],[1.179,-0.125]],"v":[[-10.804,-33.837],[-18.834,-15.906],[-6.69,2.025],[7.055,3.069]],"c":false}]},{"i":{"x":0.5,"y":1},"o":{"x":0.167,"y":0.167},"t":37,"s":[{"i":[[0,0],[-0.271,-4.621],[-9.517,-2.839],[-4.458,0.474]],"o":[[-3.394,4.297],[0.242,4.13],[4.799,1.569],[1.179,-0.125]],"v":[[-15.907,-30.468],[-22.029,-13.729],[-6.267,2.393],[7.055,3.069]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.5,"y":0},"t":42,"s":[{"i":[[0,0],[-3.928,-6.171],[-9.957,-1.926],[-4.563,0.793]],"o":[[5.522,5.2],[3.954,6.211],[3.773,0.73],[1.168,-0.203]],"v":[[-36.207,-29.267],[-24.551,-13.099],[-5.703,2.786],[7.055,3.069]],"c":false}]},{"i":{"x":0.5,"y":1},"o":{"x":0.167,"y":0.167},"t":48,"s":[{"i":[[0,0],[-1.728,-7.979],[-9.811,-4.073],[-4.458,0.474]],"o":[[3.018,5.449],[1.024,4.728],[4.574,1.385],[1.179,-0.125]],"v":[[-30.389,-31.532],[-24.556,-12.392],[-6.174,2.474],[7.055,3.069]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.5,"y":0},"t":54,"s":[{"i":[[0,0],[-0.133,-5.267],[-9.319,-3.103],[-4.568,0.674]],"o":[[-4.804,4.722],[0.193,7.66],[5.735,1.909],[1.173,-0.173]],"v":[[-10.804,-33.837],[-18.834,-15.906],[-6.656,1.832],[7.072,2.972]],"c":false}]},{"i":{"x":0.5,"y":1},"o":{"x":0.167,"y":0.167},"t":61,"s":[{"i":[[0,0],[-0.271,-4.621],[-9.517,-2.839],[-4.458,0.474]],"o":[[-3.394,4.297],[0.242,4.13],[4.799,1.569],[1.179,-0.125]],"v":[[-15.907,-30.468],[-22.029,-13.729],[-6.267,2.393],[7.055,3.069]],"c":false}]},{"i":{"x":0.3,"y":1},"o":{"x":0.5,"y":0},"t":73,"s":[{"i":[[0,0],[-4.205,-5.986],[-9.957,-1.926],[-4.458,0.474]],"o":[[5.753,4.944],[4.232,6.025],[3.773,0.73],[1.179,-0.125]],"v":[[-37.604,-27.175],[-25.224,-11.553],[-5.844,2.761],[7.055,3.069]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.6,"y":0},"t":97,"s":[{"i":[[0,0],[-0.71,-5.814],[-13.22,1.67],[-6.659,-0.445]],"o":[[-4.86,-9.215],[0.566,4.637],[3.994,-0.505],[1.183,0.079]],"v":[[-9.125,0.522],[-27.079,-2.224],[-7.644,3.484],[11.49,2.929]],"c":false}]},{"i":{"x":0.3,"y":1},"o":{"x":0.167,"y":0.167},"t":110,"s":[{"i":[[0,0],[-3.924,-6.477],[-10.094,-0.98],[-3.546,0.075]],"o":[[4.365,3.67],[3.808,6.286],[3.961,0.384],[1.183,-0.028]],"v":[[-37.672,-26.337],[-25.271,-10.97],[-5.942,2.371],[7.258,2.943]],"c":false}]},{"t":131,"s":[{"i":[[0,0],[-4.026,-6.73],[-10.797,-1.273],[-2.119,-0.01]],"o":[[0.65,4.552],[3.882,6.488],[3.499,0.413],[1.186,0.005]],"v":[[-34.027,-30.664],[-27.902,-11.553],[-6.28,2.761],[4.392,3.022]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":5.5,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 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":[100,100],"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":"Transform"}],"nm":"Path-8","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":183,"st":0,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":"hands 2","parent":5,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[115,115,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[500,500,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.6,"y":0},"t":0,"s":[{"i":[[-3.103,0.058],[-2.523,0.098],[-2.257,-5.922],[-0.05,-3.076]],"o":[[2.726,-0.051],[10.848,-0.42],[1.636,4.291],[0,0]],"v":[[3.047,3.014],[11.974,3.008],[30.43,17.71],[32.973,31.114]],"c":false}]},{"i":{"x":0.3,"y":1},"o":{"x":0.6,"y":0},"t":10,"s":[{"i":[[-2.832,-0.073],[-2.303,-0.066],[-1.926,-3.085],[-0.239,-5.607]],"o":[[2.794,0.072],[11.894,0.339],[2.109,3.377],[0,0]],"v":[[2.763,2.93],[11.324,2.869],[28.065,7.836],[30.818,22.809]],"c":false}]},{"i":{"x":0.3,"y":1},"o":{"x":0.6,"y":0},"t":22,"s":[{"i":[[-2.833,0.019],[-2.144,-0.845],[-1.574,-3.736],[-3.615,-5.451]],"o":[[4.963,-0.034],[5.323,2.097],[1.94,4.605],[0,0]],"v":[[2.632,3.384],[12.827,2.873],[22.174,19.291],[30.152,32.241]],"c":false}]},{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":36,"s":[{"i":[[-2.83,-0.135],[-3.536,-1.048],[-1.592,-3.476],[-1.09,-6.235]],"o":[[7.035,0.337],[5.485,1.626],[1.67,3.647],[0,0]],"v":[[1.498,3.488],[12.683,2.542],[23.022,15.874],[26.398,31.323]],"c":false}]},{"i":{"x":0.4,"y":1},"o":{"x":0.6,"y":0},"t":50,"s":[{"i":[[-2.829,0.15],[-2.144,-0.845],[-1.574,-3.736],[-3.615,-5.451]],"o":[[4.712,-0.25],[5.323,2.097],[1.94,4.605],[0,0]],"v":[[2.417,3.447],[12.827,2.873],[22.174,19.291],[30.152,32.241]],"c":false}]},{"i":{"x":0.3,"y":1},"o":{"x":0.4,"y":0},"t":71,"s":[{"i":[[-2.833,0.023],[-3.532,-0.206],[-1.416,-3.799],[-0.329,-6.321]],"o":[[4.317,-0.036],[4.623,0.27],[1.401,3.758],[0,0]],"v":[[1.983,3.325],[12.861,3.014],[25.932,11.288],[27.784,26.515]],"c":false}]},{"i":{"x":0.3,"y":1},"o":{"x":0.6,"y":0},"t":97,"s":[{"i":[[-2.832,-0.073],[-2.303,-0.066],[-1.926,-3.085],[-0.239,-5.607]],"o":[[2.794,0.072],[11.894,0.339],[2.109,3.377],[0,0]],"v":[[2.763,2.93],[11.324,2.869],[28.065,7.836],[30.818,22.809]],"c":false}]},{"t":124.927734375,"s":[{"i":[[-3.103,0.058],[-2.523,0.098],[-2.257,-5.922],[-0.05,-3.076]],"o":[[2.726,-0.051],[10.848,-0.42],[1.636,4.291],[0,0]],"v":[[3.047,3.014],[11.974,3.008],[30.43,17.71],[32.973,31.114]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":5.5,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 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":[100,100],"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":"Transform"}],"nm":"Path-8","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":183,"st":0,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":"body","parent":3,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":0,"s":[116.171,146.724,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":10,"s":[116.171,151.524,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":21,"s":[116.171,140.724,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":35,"s":[116.171,146.724,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.4,"y":1},"o":{"x":0.5,"y":0},"t":49,"s":[116.171,143.924,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.544,"y":0},"t":70,"s":[116.171,146.724,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.134,"y":1},"o":{"x":0.5,"y":0},"t":97,"s":[116.171,151.524,0],"to":[0,0,0],"ti":[0,0,0]},{"t":128.572265625,"s":[116.171,146.724,0]}],"ix":2},"a":{"a":0,"k":[0,14.211,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.5,0.5,0.5],"y":[0,0,0]},"t":0,"s":[100,100,100]},{"i":{"x":[0.5,0.5,0.5],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":5,"s":[97,103,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.484,0.484,0.484],"y":[0,0,0]},"t":10,"s":[100,100,100]},{"i":{"x":[0.486,0.486,0.486],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":15,"s":[103,97,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.5,0.5,0.5],"y":[0,0,0]},"t":21,"s":[100,100,100]},{"i":{"x":[0.5,0.5,0.5],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":27,"s":[97,103,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.5,0.5,0.5],"y":[0,0,0]},"t":35,"s":[100,100,100]},{"i":{"x":[0.5,0.5,0.5],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":42,"s":[103,97,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.5,0.5,0.5],"y":[0,0,0]},"t":49,"s":[100,100,100]},{"i":{"x":[0.4,0.4,0.4],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":56,"s":[97,103,100]},{"i":{"x":[0.5,0.5,0.5],"y":[1,1,1]},"o":{"x":[0.422,0.422,0.422],"y":[0,0,0]},"t":70,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.484,0.484,0.484],"y":[0,0,0]},"t":97,"s":[100,100,100]},{"i":{"x":[0.17,0.17,0.17],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":109.143,"s":[97,103,100]},{"t":128.572265625,"s":[100,100,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":0,"s":[{"i":[[0,0],[-0.741,-5.903],[-1.922,-0.751],[-2.946,0],[-2.468,1.063],[-0.228,3.004],[0,0]],"o":[[0,0],[0.328,2.608],[2.556,0.999],[2.898,0],[1.959,-0.844],[0.638,-8.388],[0,0]],"v":[[-12.432,-14.575],[-11.977,7.814],[-8.487,12.849],[0.4,13.948],[9.223,12.916],[12.656,7.499],[12.482,-13.225]],"c":false}]},{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":10,"s":[{"i":[[0,0],[0,0],[-1.89,-0.83],[-2.946,0],[-2.468,1.063],[-0.086,2.131],[0,0]],"o":[[0,0],[0.14,2.059],[2.504,1.099],[2.898,0],[1.959,-0.844],[0,0],[0,0]],"v":[[-14.032,-4.05],[-13.127,7.863],[-9.825,12.562],[0.35,14.211],[10.398,12.616],[13.744,7.749],[14.232,-7.571]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.5,"y":0},"t":21,"s":[{"i":[[-1.863,-5.271],[0,0],[-1.89,-0.83],[-2.946,0],[-2.468,1.063],[-0.086,2.131],[1.3,6.472]],"o":[[1.863,5.271],[0.14,2.059],[2.504,1.099],[2.898,0],[1.959,-0.844],[0,0],[-0.223,-1.111]],"v":[[-14.632,-13.411],[-12.127,7.863],[-8.825,12.562],[0.35,14.211],[9.398,12.616],[12.744,7.749],[11.232,-16.011]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0},"t":35,"s":[{"i":[[-1.922,-5.439],[0,0],[-1.89,-0.83],[-2.946,0],[-2.468,1.063],[-0.086,2.131],[2.094,6.249]],"o":[[1.922,5.439],[0.14,2.059],[2.504,1.099],[2.898,0],[1.959,-0.844],[0,0],[0,-0.323]],"v":[[-15.346,-10.627],[-12.527,7.863],[-9.225,12.562],[0.35,14.211],[9.798,12.616],[13.144,7.749],[11.974,-10.116]],"c":false}]},{"i":{"x":0.5,"y":1},"o":{"x":0.167,"y":0.167},"t":42,"s":[{"i":[[-1.882,-5.326],[0,0],[-1.89,-0.83],[-2.946,0],[-2.468,1.063],[-0.086,2.131],[1.559,6.399]],"o":[[1.882,5.326],[0.14,2.059],[2.504,1.099],[2.898,0],[1.959,-0.844],[0,0],[-0.197,-0.919]],"v":[[-14.481,-12.569],[-12.098,7.863],[-8.796,12.562],[0.35,14.211],[9.369,12.616],[12.715,7.749],[11.484,-13.946]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.5,"y":0},"t":49,"s":[{"i":[[-1.863,-5.271],[0,0],[-1.89,-0.83],[-2.946,0],[-2.468,1.063],[-0.086,2.131],[1.3,6.472]],"o":[[1.863,5.271],[0.14,2.059],[2.504,1.099],[2.898,0],[1.959,-0.844],[0,0],[-0.223,-1.111]],"v":[[-14.732,-16.111],[-12.127,7.863],[-8.825,12.562],[0.35,14.211],[9.398,12.616],[12.744,7.749],[11.532,-15.511]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":54,"s":[{"i":[[-1.872,-5.295],[0,0],[-1.89,-0.83],[-2.946,0],[-2.468,1.063],[-0.086,2.131],[1.413,6.44]],"o":[[1.872,5.295],[0.14,2.059],[2.504,1.099],[2.898,0],[1.959,-0.844],[0,0],[-0.191,-0.998]],"v":[[-14.729,-16.667],[-12.144,7.863],[-8.842,12.562],[0.35,14.211],[9.415,12.616],[12.761,7.749],[11.555,-14.741]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0.167},"t":57,"s":[{"i":[[-1.882,-5.326],[0,0],[-1.89,-0.83],[-2.946,0],[-2.468,1.063],[-0.086,2.131],[1.559,6.399]],"o":[[1.882,5.326],[0.14,2.059],[2.504,1.099],[2.898,0],[1.959,-0.844],[0,0],[0,0.274]],"v":[[-14.818,-14.61],[-12.155,7.863],[-8.853,12.562],[0.35,14.211],[9.426,12.616],[12.771,7.749],[11.18,-14.235]],"c":false}]},{"i":{"x":0.5,"y":1},"o":{"x":0.167,"y":0},"t":70,"s":[{"i":[[-1.922,-5.439],[0,0],[-1.89,-0.83],[-2.946,0],[-2.468,1.063],[-0.086,2.131],[2.094,6.249]],"o":[[1.922,5.439],[0.14,2.059],[2.504,1.099],[2.898,0],[1.959,-0.844],[0,0],[0,-0.323]],"v":[[-15.324,-9.227],[-12.527,7.863],[-9.225,12.562],[0.35,14.211],[9.798,12.616],[13.144,7.749],[11.974,-10.116]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.5,"y":0},"t":97,"s":[{"i":[[0,0],[0,0],[-1.89,-0.83],[-2.946,0],[-2.468,1.063],[-0.086,2.131],[0,0]],"o":[[0,0],[0.14,2.059],[2.504,1.099],[2.898,0],[1.959,-0.844],[0,0],[0,0]],"v":[[-14.032,-4.05],[-13.127,7.863],[-9.825,12.562],[0.35,14.211],[10.398,12.616],[13.744,7.749],[14.232,-7.571]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":104,"s":[{"i":[[-0.837,-2.368],[0,0],[-1.89,-0.83],[-2.946,0],[-2.468,1.063],[-0.086,2.131],[0.912,2.72]],"o":[[0.837,2.368],[0.14,2.059],[2.504,1.099],[2.898,0],[1.959,-0.844],[0,0],[0,-0.141]],"v":[[-13.849,-7.596],[-12.139,7.863],[-8.836,12.562],[0.35,14.211],[9.41,12.616],[12.755,7.749],[12.383,-8.209]],"c":false}]},{"i":{"x":0.17,"y":1},"o":{"x":0.167,"y":0.167},"t":112,"s":[{"i":[[0,-1.426],[0,0],[-1.89,-0.83],[-2.946,0],[-2.468,1.063],[-0.086,2.131],[0.292,0.87]],"o":[[0,0.803],[0.14,2.059],[2.504,1.099],[2.898,0],[1.959,-0.844],[0,0],[0,-0.045]],"v":[[-13.276,-13.757],[-12.148,7.863],[-8.845,12.562],[0.35,14.211],[9.419,12.616],[12.764,7.749],[12.791,-11.807]],"c":false}]},{"t":128.572265625,"s":[{"i":[[0,0],[-0.741,-5.903],[-1.922,-0.751],[-2.946,0],[-2.468,1.063],[-0.228,3.004],[0,0]],"o":[[0,0],[0.328,2.608],[2.556,0.999],[2.898,0],[1.959,-0.844],[0.638,-8.388],[0,0]],"v":[[-12.432,-14.575],[-11.977,7.814],[-8.487,12.849],[0.4,13.948],[9.223,12.916],[12.656,7.499],[12.482,-13.225]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"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":"Transform"}],"nm":"Path","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":5.5,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false}],"ip":0,"op":183,"st":0,"bm":0}]},{"id":"comp_1","layers":[{"ddd":0,"ind":1,"ty":3,"nm":"NULL CONTROL","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[256.989,275.127,0],"ix":2},"a":{"a":0,"k":[115,115,0],"ix":1},"s":{"a":0,"k":[500,500,100],"ix":6}},"ao":0,"ip":0,"op":119,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"head","parent":6,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.6],"y":[0]},"t":1,"s":[0]},{"i":{"x":[0.3],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":11,"s":[0]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":23,"s":[-13]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0]},"t":66,"s":[-13]},{"i":{"x":[0.09],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":75,"s":[7]},{"t":92,"s":[0]}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.6,"y":0},"t":1,"s":[0,-37.327,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.6,"y":0},"t":11,"s":[0,-26.827,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.167,"y":0},"t":23,"s":[-4.4,-39.427,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.167,"y":0},"t":36,"s":[-2.401,-30.424,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.167,"y":0},"t":50,"s":[-4.4,-39.427,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.09,"y":1},"o":{"x":0.167,"y":0},"t":66,"s":[-2.401,-30.424,0],"to":[0,0,0],"ti":[0,0,0]},{"t":92,"s":[0,-37.327,0]}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.6,0.6,0.6],"y":[0,0,0]},"t":1,"s":[100,100,100]},{"i":{"x":[0.3,0.3,0.3],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":5,"s":[97,103,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.6,0.6,0.6],"y":[0,0,0]},"t":11,"s":[105,95,100]},{"i":{"x":[0.3,0.3,0.3],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":16,"s":[97,103,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.6,0.6,0.6],"y":[0,0,0]},"t":23,"s":[105,95,100]},{"i":{"x":[0.3,0.3,0.3],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":28,"s":[97,103,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":36,"s":[100,100,100]},{"i":{"x":[0.3,0.3,0.3],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":44,"s":[97,103,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.6,0.6,0.6],"y":[0,0,0]},"t":50,"s":[105,95,100]},{"i":{"x":[0.3,0.3,0.3],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":58,"s":[97,103,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":66,"s":[100,100,100]},{"i":{"x":[0.09,0.09,0.09],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":75,"s":[95,105,100]},{"t":92,"s":[100,100,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-6.341,0],[0,-6.341],[6.341,0],[0,6.341]],"o":[[6.341,0],[0,6.341],[-6.341,0],[0,-6.341]],"v":[[0,-11.482],[11.482,0],[0,11.482],[-11.482,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"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":"Transform"}],"nm":"Oval","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":5.5,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false}],"ip":0,"op":120,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":3,"nm":"hands","parent":6,"sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.5],"y":[1]},"o":{"x":[0.5],"y":[0]},"t":0,"s":[0]},{"i":{"x":[0.5],"y":[1]},"o":{"x":[0.5],"y":[0]},"t":21,"s":[-10]},{"i":{"x":[0.17],"y":[1]},"o":{"x":[0.5],"y":[0]},"t":65,"s":[-10]},{"t":93,"s":[0]}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":0,"s":[-2.341,-17.777,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":10,"s":[-2.341,-9.777,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":21,"s":[-3.741,-17.977,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":35,"s":[-1.541,-11.777,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.4,"y":1},"o":{"x":0.5,"y":0},"t":49,"s":[-1.541,-17.777,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.17,"y":1},"o":{"x":0.6,"y":0},"t":65,"s":[-1.541,-11.777,0],"to":[0,0,0],"ti":[0,0,0]},{"t":93,"s":[-2.341,-17.777,0]}],"ix":2},"a":{"a":0,"k":[115,115,0],"ix":1},"s":{"a":0,"k":[20,20,100],"ix":6}},"ao":0,"ip":0,"op":120,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"hands 3","parent":3,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[115,115,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[500,500,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.6,"y":0},"t":0,"s":[{"i":[[0,0],[-4.026,-6.73],[-10.797,-1.273],[-2.119,-0.01]],"o":[[0.65,4.552],[3.882,6.488],[3.499,0.413],[1.186,0.005]],"v":[[-34.027,-30.664],[-27.902,-11.553],[-6.28,2.761],[4.392,3.022]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.6,"y":0},"t":10,"s":[{"i":[[0,0],[-0.71,-5.814],[-13.22,1.67],[-6.659,-0.445]],"o":[[-3.232,5.731],[0.566,4.637],[3.994,-0.505],[1.183,0.079]],"v":[[-18.219,-21.464],[-27.079,-2.224],[-7.644,3.484],[11.49,2.929]],"c":false}]},{"i":{"x":0.5,"y":1},"o":{"x":0.167,"y":0.167},"t":16,"s":[{"i":[[0,0],[-1.902,-5.873],[-9.833,-0.14],[-7.372,0.373]],"o":[[-4.328,1.758],[1.817,5.11],[4.766,0.217],[1.182,0.009]],"v":[[-17.041,-20.099],[-26.446,-5.406],[-7.078,3.541],[9.615,2.763]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.5,"y":0},"t":23,"s":[{"i":[[0,0],[-4.205,-5.986],[-9.957,-1.926],[-4.458,0.474]],"o":[[5.753,4.944],[4.232,6.025],[3.773,0.73],[1.179,-0.125]],"v":[[-37.604,-27.175],[-25.224,-11.553],[-5.844,2.761],[7.055,3.069]],"c":false}]},{"i":{"x":0.5,"y":1},"o":{"x":0.167,"y":0.167},"t":27,"s":[{"i":[[0,0],[-1.728,-7.979],[-9.811,-4.073],[-4.458,0.474]],"o":[[3.018,5.449],[1.024,4.728],[4.574,1.385],[1.179,-0.125]],"v":[[-30.389,-31.532],[-24.556,-12.392],[-6.174,2.474],[7.055,3.069]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.5,"y":0},"t":32,"s":[{"i":[[0,0],[-0.133,-5.267],[-9.077,-3.752],[-4.458,0.474]],"o":[[-4.804,4.722],[0.193,7.66],[5.825,2.408],[1.179,-0.125]],"v":[[-10.804,-33.837],[-18.834,-15.906],[-6.69,2.025],[7.055,3.069]],"c":false}]},{"i":{"x":0.5,"y":1},"o":{"x":0.167,"y":0.167},"t":37,"s":[{"i":[[0,0],[-0.271,-4.621],[-9.517,-2.839],[-4.458,0.474]],"o":[[-3.394,4.297],[0.242,4.13],[4.799,1.569],[1.179,-0.125]],"v":[[-15.907,-30.468],[-22.029,-13.729],[-6.267,2.393],[7.055,3.069]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.5,"y":0},"t":42,"s":[{"i":[[0,0],[-3.928,-6.171],[-9.883,-2.273],[-3.875,0.81]],"o":[[5.522,5.2],[3.954,6.211],[4.837,1.112],[1.161,-0.243]],"v":[[-36.207,-29.267],[-24.551,-13.099],[-5.745,2.464],[7.055,3.069]],"c":false}]},{"i":{"x":0.5,"y":1},"o":{"x":0.167,"y":0.167},"t":48,"s":[{"i":[[0,0],[-1.728,-7.979],[-9.811,-4.073],[-4.458,0.474]],"o":[[3.018,5.449],[1.024,4.728],[4.574,1.385],[1.179,-0.125]],"v":[[-30.389,-31.532],[-24.556,-12.392],[-6.174,2.474],[7.055,3.069]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.5,"y":0},"t":54,"s":[{"i":[[0,0],[-0.133,-5.267],[-9.319,-3.103],[-4.568,0.674]],"o":[[-4.804,4.722],[0.193,7.66],[5.735,1.909],[1.173,-0.173]],"v":[[-10.804,-33.837],[-18.834,-15.906],[-6.656,1.832],[7.072,2.972]],"c":false}]},{"i":{"x":0.5,"y":1},"o":{"x":0.167,"y":0.167},"t":61,"s":[{"i":[[0,0],[-0.271,-4.621],[-9.517,-2.839],[-4.458,0.474]],"o":[[-3.394,4.297],[0.242,4.13],[4.799,1.569],[1.179,-0.125]],"v":[[-15.907,-30.468],[-22.029,-13.729],[-6.267,2.393],[7.055,3.069]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.5,"y":0},"t":68,"s":[{"i":[[0,0],[-4.205,-5.986],[-9.957,-1.926],[-4.458,0.474]],"o":[[5.753,4.944],[4.232,6.025],[3.773,0.73],[1.179,-0.125]],"v":[[-37.604,-27.175],[-25.224,-11.553],[-5.844,2.761],[7.055,3.069]],"c":false}]},{"i":{"x":0.3,"y":1},"o":{"x":0.167,"y":0.167},"t":78,"s":[{"i":[[0,0],[-3.924,-6.477],[-10.111,-0.781],[-3.392,0.105]],"o":[[4.365,3.67],[3.808,6.286],[4.491,0.347],[1.183,-0.028]],"v":[[-37.672,-26.337],[-25.271,-10.97],[-5.844,2.761],[6.463,3.033]],"c":false}]},{"t":95,"s":[{"i":[[0,0],[-4.026,-6.73],[-10.797,-1.273],[-2.119,-0.01]],"o":[[0.65,4.552],[3.882,6.488],[3.499,0.413],[1.186,0.005]],"v":[[-34.027,-30.664],[-27.902,-11.553],[-6.28,2.761],[4.392,3.022]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":5.5,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 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":[100,100],"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":"Transform"}],"nm":"Path-8","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":5,"ty":4,"nm":"hands 2","parent":3,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[115,115,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[500,500,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.6,"y":0},"t":0,"s":[{"i":[[-3.103,0.058],[-2.523,0.098],[-2.257,-5.922],[-0.05,-3.076]],"o":[[2.726,-0.051],[10.848,-0.42],[1.636,4.291],[0,0]],"v":[[3.047,3.014],[11.974,3.008],[30.43,17.71],[32.973,31.114]],"c":false}]},{"i":{"x":0.3,"y":1},"o":{"x":0.6,"y":0},"t":10,"s":[{"i":[[-2.831,0.099],[-2.303,-0.066],[-1.926,-3.085],[-0.239,-5.607]],"o":[[3.206,-0.113],[11.894,0.339],[2.109,3.377],[0,0]],"v":[[2.767,2.88],[11.324,2.869],[28.065,7.836],[30.818,22.809]],"c":false}]},{"i":{"x":0.3,"y":1},"o":{"x":0.6,"y":0},"t":22,"s":[{"i":[[-2.833,0.019],[-2.144,-0.845],[-1.574,-3.736],[-3.615,-5.451]],"o":[[4.963,-0.034],[5.323,2.097],[1.94,4.605],[0,0]],"v":[[2.632,3.384],[12.827,2.873],[22.174,19.291],[30.152,32.241]],"c":false}]},{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":36,"s":[{"i":[[-2.83,-0.135],[-3.536,-1.048],[-1.592,-3.476],[-1.09,-6.235]],"o":[[7.035,0.337],[5.485,1.626],[1.67,3.647],[0,0]],"v":[[1.498,3.488],[12.683,2.542],[23.022,15.874],[26.398,31.323]],"c":false}]},{"i":{"x":0.4,"y":1},"o":{"x":0.6,"y":0},"t":50,"s":[{"i":[[-2.829,0.15],[-2.144,-0.845],[-1.574,-3.736],[-3.615,-5.451]],"o":[[4.712,-0.25],[5.323,2.097],[1.94,4.605],[0,0]],"v":[[2.417,3.447],[12.827,2.873],[22.174,19.291],[30.152,32.241]],"c":false}]},{"i":{"x":0.3,"y":1},"o":{"x":0.4,"y":0},"t":66,"s":[{"i":[[-2.833,0.023],[-3.532,-0.206],[-2.149,-3.438],[-1.064,-6.535]],"o":[[4.317,-0.036],[4.623,0.27],[2.442,3.906],[0,0]],"v":[[1.983,3.325],[12.861,3.014],[29.432,13.325],[34.61,28.122]],"c":false}]},{"t":90,"s":[{"i":[[-3.103,0.058],[-2.523,0.098],[-2.257,-5.922],[-0.05,-3.076]],"o":[[2.726,-0.051],[10.848,-0.42],[1.636,4.291],[0,0]],"v":[[3.047,3.014],[11.974,3.008],[30.43,17.71],[32.973,31.114]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":5.5,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 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":[100,100],"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":"Transform"}],"nm":"Path-8","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":6,"ty":4,"nm":"body","parent":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.5,"y":1},"o":{"x":0.5,"y":0},"t":0,"s":[116.171,146.724,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":10,"s":[116.171,151.524,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":21,"s":[116.171,140.724,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":35,"s":[116.171,146.724,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.4,"y":1},"o":{"x":0.5,"y":0},"t":49,"s":[116.171,143.924,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.17,"y":0.17},"o":{"x":0.6,"y":0.6},"t":65,"s":[116.171,146.724,0],"to":[0,0,0],"ti":[0,0,0]},{"t":93,"s":[116.171,146.724,0]}],"ix":2},"a":{"a":0,"k":[0,14.211,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.5,0.5,0.5],"y":[0,0,0]},"t":0,"s":[100,100,100]},{"i":{"x":[0.5,0.5,0.5],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":5,"s":[97,103,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.484,0.484,0.484],"y":[0,0,0]},"t":10,"s":[100,100,100]},{"i":{"x":[0.486,0.486,0.486],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":15,"s":[103,97,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.5,0.5,0.5],"y":[0,0,0]},"t":21,"s":[100,100,100]},{"i":{"x":[0.5,0.5,0.5],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":27,"s":[97,103,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.5,0.5,0.5],"y":[0,0,0]},"t":35,"s":[100,100,100]},{"i":{"x":[0.5,0.5,0.5],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":42,"s":[103,97,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.5,0.5,0.5],"y":[0,0,0]},"t":49,"s":[100,100,100]},{"i":{"x":[0.4,0.4,0.4],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":56,"s":[97,103,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.6,0.6,0.6],"y":[0,0,0]},"t":65,"s":[100,100,100]},{"i":{"x":[0.17,0.17,0.17],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":77,"s":[97,103,100]},{"t":93,"s":[100,100,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":0,"s":[{"i":[[0,0],[-0.741,-5.903],[-1.922,-0.751],[-2.946,0],[-2.468,1.063],[-0.228,3.004],[0,0]],"o":[[0,0],[0.328,2.608],[2.556,0.999],[2.898,0],[1.959,-0.844],[0.638,-8.388],[0,0]],"v":[[-12.432,-14.575],[-11.977,7.814],[-8.487,12.849],[0.4,13.948],[9.223,12.916],[12.656,7.499],[12.482,-13.225]],"c":false}]},{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":10,"s":[{"i":[[0,0],[0,0],[-1.89,-0.83],[-2.946,0],[-2.468,1.063],[-0.086,2.131],[0,0]],"o":[[0,0],[0.14,2.059],[2.504,1.099],[2.898,0],[1.959,-0.844],[0,0],[0,0]],"v":[[-14.032,-4.05],[-13.127,7.863],[-9.825,12.562],[0.35,14.211],[10.398,12.616],[13.744,7.749],[14.232,-7.571]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.5,"y":0},"t":21,"s":[{"i":[[-1.863,-5.271],[0,0],[-1.89,-0.83],[-2.946,0],[-2.468,1.063],[-0.086,2.131],[1.3,6.472]],"o":[[1.863,5.271],[0.14,2.059],[2.504,1.099],[2.898,0],[1.959,-0.844],[0,0],[-0.223,-1.111]],"v":[[-14.632,-13.411],[-12.127,7.863],[-8.825,12.562],[0.35,14.211],[9.398,12.616],[12.744,7.749],[11.232,-16.011]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0},"t":35,"s":[{"i":[[-1.922,-5.439],[0,0],[-1.89,-0.83],[-2.946,0],[-2.468,1.063],[-0.086,2.131],[2.094,6.249]],"o":[[1.922,5.439],[0.14,2.059],[2.504,1.099],[2.898,0],[1.959,-0.844],[0,0],[0,-0.323]],"v":[[-15.346,-10.627],[-12.527,7.863],[-9.225,12.562],[0.35,14.211],[9.798,12.616],[13.144,7.749],[11.974,-10.116]],"c":false}]},{"i":{"x":0.5,"y":1},"o":{"x":0.167,"y":0.167},"t":42,"s":[{"i":[[-1.882,-5.326],[0,0],[-1.89,-0.83],[-2.946,0],[-2.468,1.063],[-0.086,2.131],[1.559,6.399]],"o":[[1.882,5.326],[0.14,2.059],[2.504,1.099],[2.898,0],[1.959,-0.844],[0,0],[-0.197,-0.919]],"v":[[-14.481,-12.569],[-12.098,7.863],[-8.796,12.562],[0.35,14.211],[9.369,12.616],[12.715,7.749],[11.484,-13.946]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.5,"y":0},"t":49,"s":[{"i":[[-1.863,-5.271],[0,0],[-1.89,-0.83],[-2.946,0],[-2.468,1.063],[-0.086,2.131],[1.3,6.472]],"o":[[1.863,5.271],[0.14,2.059],[2.504,1.099],[2.898,0],[1.959,-0.844],[0,0],[-0.223,-1.111]],"v":[[-14.732,-16.111],[-12.127,7.863],[-8.825,12.562],[0.35,14.211],[9.398,12.616],[12.744,7.749],[11.532,-15.511]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":54,"s":[{"i":[[-1.872,-5.295],[0,0],[-1.89,-0.83],[-2.946,0],[-2.468,1.063],[-0.086,2.131],[1.413,6.44]],"o":[[1.872,5.295],[0.14,2.059],[2.504,1.099],[2.898,0],[1.959,-0.844],[0,0],[-0.191,-0.998]],"v":[[-14.525,-15.487],[-12.348,7.863],[-9.046,12.562],[0.35,14.211],[9.619,12.616],[12.964,7.749],[11.758,-14.741]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0.167},"t":57,"s":[{"i":[[-1.882,-5.326],[0,0],[-1.89,-0.83],[-2.946,0],[-2.468,1.063],[-0.086,2.131],[1.559,6.399]],"o":[[1.882,5.326],[0.14,2.059],[2.504,1.099],[2.898,0],[1.959,-0.844],[0,0],[0,0.274]],"v":[[-14.816,-12.663],[-12.153,7.863],[-8.85,12.562],[0.35,14.211],[9.424,12.616],[12.769,7.749],[11.588,-12.58]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0},"t":65,"s":[{"i":[[-1.922,-5.439],[0,0],[-1.89,-0.83],[-2.946,0],[-2.468,1.063],[-0.086,2.131],[2.094,6.249]],"o":[[1.922,5.439],[0.14,2.059],[2.504,1.099],[2.898,0],[1.959,-0.844],[0,0],[0,-0.323]],"v":[[-15.524,-9.227],[-12.727,7.863],[-9.425,12.562],[0.35,14.211],[9.998,12.616],[13.344,7.749],[12.174,-10.116]],"c":false}]},{"i":{"x":0.17,"y":1},"o":{"x":0.167,"y":0.167},"t":73,"s":[{"i":[[-0.837,-2.368],[0,0],[-1.89,-0.83],[-2.946,0],[-2.468,1.063],[-0.086,2.131],[0.912,2.72]],"o":[[0.837,2.368],[0.14,2.059],[2.504,1.099],[2.898,0],[1.959,-0.844],[0,0],[0,-0.141]],"v":[[-14.493,-10.166],[-12.749,7.863],[-9.446,12.562],[0.35,14.211],[10.02,12.616],[13.365,7.749],[12.988,-9.89]],"c":false}]},{"t":93,"s":[{"i":[[0,0],[-0.741,-5.903],[-1.922,-0.751],[-2.946,0],[-2.468,1.063],[-0.228,3.004],[0,0]],"o":[[0,0],[0.328,2.608],[2.556,0.999],[2.898,0],[1.959,-0.844],[0.638,-8.388],[0,0]],"v":[[-12.432,-14.575],[-11.977,7.814],[-8.487,12.849],[0.4,13.948],[9.223,12.916],[12.656,7.499],[12.482,-13.225]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"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":"Transform"}],"nm":"Path","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":5.5,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false}],"ip":0,"op":120,"st":0,"bm":0}]},{"id":"comp_2","layers":[{"ddd":0,"ind":1,"ty":3,"nm":"NULL CONTROL","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[256.989,275.127,0],"ix":2},"a":{"a":0,"k":[115,115,0],"ix":1},"s":{"a":0,"k":[500,500,100],"ix":6}},"ao":0,"ip":0,"op":183,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"head","parent":6,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.6,"y":0},"t":0,"s":[0,-37.327,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.6,"y":0},"t":11,"s":[0,-27.027,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.167,"y":0},"t":23,"s":[0.383,-39.627,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.167,"y":0},"t":35,"s":[0.383,-33.606,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.167,"y":0},"t":49,"s":[0.383,-39.627,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.167,"y":0},"t":63,"s":[0.383,-33.606,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.167,"y":0},"t":79,"s":[3.183,-39.627,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.15,"y":1},"o":{"x":0.167,"y":0},"t":93,"s":[3.583,-33.606,0],"to":[0,0,0],"ti":[0,0,0]},{"t":124,"s":[0,-37.327,0]}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.6,0.6,0.6],"y":[0,0,0]},"t":0,"s":[100,100,100]},{"i":{"x":[0.3,0.3,0.3],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":5,"s":[97,103,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.6,0.6,0.6],"y":[0,0,0]},"t":11,"s":[105,95,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":16,"s":[97,103,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.6,0.6,0.6],"y":[0,0,0]},"t":23,"s":[100,100,100]},{"i":{"x":[0.3,0.3,0.3],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":29,"s":[97,103,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.6,0.6,0.6],"y":[0,0,0]},"t":35,"s":[105,95,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":41,"s":[97,103,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.6,0.6,0.6],"y":[0,0,0]},"t":49,"s":[100,100,100]},{"i":{"x":[0.3,0.3,0.3],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":55,"s":[97,103,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.6,0.6,0.6],"y":[0,0,0]},"t":63,"s":[105,95,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":70,"s":[97,103,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.6,0.6,0.6],"y":[0,0,0]},"t":79,"s":[100,100,100]},{"i":{"x":[0.3,0.3,0.3],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":85,"s":[97,103,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.6,0.6,0.6],"y":[0,0,0]},"t":93,"s":[105,95,100]},{"i":{"x":[0.15,0.15,0.15],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":105,"s":[97,103,100]},{"t":124,"s":[100,100,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-6.341,0],[0,-6.341],[6.341,0],[0,6.341]],"o":[[6.341,0],[0,6.341],[-6.341,0],[0,-6.341]],"v":[[0,-11.482],[11.482,0],[0,11.482],[-11.482,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"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":"Transform"}],"nm":"Oval","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":5.5,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false}],"ip":0,"op":184,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":3,"nm":"hands","parent":6,"sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.5],"y":[1]},"o":{"x":[0.5],"y":[0]},"t":0,"s":[0]},{"i":{"x":[0.16],"y":[1]},"o":{"x":[0.5],"y":[0]},"t":21,"s":[0]},{"t":118,"s":[0]}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":0,"s":[-2.341,-17.777,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":10,"s":[-2.341,-9.777,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":21,"s":[-1.541,-17.777,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":33,"s":[-1.541,-15.377,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":47,"s":[-1.541,-17.777,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":61,"s":[-1.541,-15.377,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":78,"s":[-1.541,-17.777,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.16,"y":1},"o":{"x":0.5,"y":0},"t":102,"s":[-1.541,-15.377,0],"to":[0,0,0],"ti":[0,0,0]},{"t":118,"s":[-2.341,-17.777,0]}],"ix":2},"a":{"a":0,"k":[115,115,0],"ix":1},"s":{"a":0,"k":[20,20,100],"ix":6}},"ao":0,"ip":0,"op":184,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"hands 3","parent":3,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[115,115,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[500,500,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.6,"y":0},"t":0,"s":[{"i":[[0,0],[-4.026,-6.73],[-10.797,-1.273],[-2.119,-0.01]],"o":[[0.65,4.552],[3.882,6.488],[3.499,0.413],[1.186,0.005]],"v":[[-34.027,-30.664],[-27.902,-11.553],[-6.28,2.761],[4.392,3.022]],"c":false}]},{"i":{"x":0.3,"y":1},"o":{"x":0.6,"y":0},"t":10,"s":[{"i":[[0,0],[-0.71,-5.814],[-13.22,1.67],[-6.667,-0.441]],"o":[[-3.232,5.731],[0.566,4.637],[3.994,-0.505],[1.183,0.078]],"v":[[-18.219,-21.464],[-27.079,-2.224],[-7.644,3.584],[11.49,2.929]],"c":false}]},{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":23,"s":[{"i":[[0,0],[-1.268,-5.758],[-4.491,-2.782],[-9.089,1.417]],"o":[[-0.238,6.778],[1.347,6.12],[4.565,2.828],[1.172,-0.183]],"v":[[-19.257,-37.475],[-19.531,-14.871],[-9.165,2.078],[11.49,3.527]],"c":false}]},{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":35,"s":[{"i":[[0,0],[-0.961,-3.853],[-6.666,-1.601],[-9.089,1.417]],"o":[[0.03,7.313],[1.517,6.08],[5.221,1.254],[1.172,-0.183]],"v":[[-23.457,-27.675],[-24.527,-6.059],[-7.961,2.838],[11.49,2.929]],"c":false}]},{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":49,"s":[{"i":[[0,0],[-1.268,-5.758],[-4.491,-2.782],[-9.089,1.417]],"o":[[-0.238,6.778],[1.347,6.12],[4.565,2.828],[1.172,-0.183]],"v":[[-19.257,-37.475],[-19.531,-14.871],[-9.165,2.078],[11.49,3.527]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.7,"y":0},"t":59,"s":[{"i":[[0,0],[-0.961,-3.853],[-6.666,-1.601],[-9.116,1.293]],"o":[[0.03,7.313],[1.517,6.08],[5.221,1.254],[1.174,-0.167]],"v":[[-23.457,-27.675],[-24.527,-6.059],[-7.961,2.838],[11.49,2.929]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0.167},"t":65,"s":[{"i":[[0,0],[-0.651,-3.886],[-6.545,-1.827],[-9.086,1.105]],"o":[[4.595,6.728],[1.028,6.132],[5.027,1.403],[1.177,-0.143]],"v":[[-25.036,-29.511],[-22.232,-9.273],[-7.789,2.781],[11.545,2.978]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0},"t":73,"s":[{"i":[[0,0],[0.125,-3.969],[-6.039,-3.086],[-8.715,1.918]],"o":[[-2.798,7.352],[-0.197,6.263],[4.981,2.545],[1.158,-0.255]],"v":[[-9.536,-38.346],[-16.487,-17.314],[-7.359,2.636],[11.683,3.1]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0.167},"t":79,"s":[{"i":[[0,0],[-0.447,-3.908],[-4.757,-3.608],[-9.817,2.3]],"o":[[-4.119,5.691],[0.705,6.167],[4.297,3.185],[1.155,-0.271]],"v":[[-10.167,-36.731],[-16.027,-16.023],[-7.359,2.636],[12.29,3.029]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0},"t":85,"s":[{"i":[[0,0],[-0.961,-3.853],[-4.491,-2.782],[-7.117,1.703]],"o":[[1.597,6.857],[1.517,6.08],[4.565,2.828],[1.153,-0.276]],"v":[[-20.247,-37.336],[-17.926,-14.859],[-7.359,2.636],[11.59,3.229]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0.167},"t":90,"s":[{"i":[[0,0],[-0.511,-3.901],[-4.724,-3.504],[-8.017,1.106]],"o":[[2.238,7.884],[0.807,6.156],[4.331,3.14],[1.175,-0.162]],"v":[[-18.139,-37.479],[-16.265,-15.877],[-7.667,2.047],[11.49,2.929]],"c":false}]},{"i":{"x":0.1,"y":1},"o":{"x":0.167,"y":0},"t":96,"s":[{"i":[[0,0],[0.125,-3.969],[-6.039,-3.086],[-9.123,0.867]],"o":[[-2.798,7.352],[-0.197,6.263],[4.981,2.545],[1.181,-0.112]],"v":[[-9.536,-38.346],[-16.487,-17.314],[-7.559,2.136],[11.683,3.1]],"c":false}]},{"t":137,"s":[{"i":[[0,0],[-4.026,-6.73],[-10.797,-1.273],[-2.119,-0.01]],"o":[[0.65,4.552],[3.882,6.488],[3.499,0.413],[1.186,0.005]],"v":[[-34.027,-30.664],[-27.902,-11.553],[-6.28,2.761],[4.392,3.022]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":5.5,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 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":[100,100],"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":"Transform"}],"nm":"Path-8","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":184,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"hands 2","parent":3,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[115,115,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[500,500,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.6,"y":0},"t":0,"s":[{"i":[[-3.103,0.058],[-2.523,0.098],[-2.257,-5.922],[-0.05,-3.076]],"o":[[2.726,-0.051],[10.848,-0.42],[1.636,4.291],[0,0]],"v":[[3.047,3.014],[11.974,3.008],[30.43,17.71],[32.973,31.114]],"c":false}]},{"i":{"x":0.3,"y":1},"o":{"x":0.6,"y":0},"t":10,"s":[{"i":[[-2.832,-0.073],[-2.303,-0.066],[-1.926,-3.085],[-0.239,-5.607]],"o":[[2.794,0.072],[11.894,0.339],[2.109,3.377],[0,0]],"v":[[2.763,2.93],[11.216,2.961],[28.065,7.836],[30.818,22.809]],"c":false}]},{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":23,"s":[{"i":[[-2.832,0.111],[-2.264,0.431],[-1.371,7.044],[-0.143,3.51]],"o":[[2.794,-0.11],[8.983,-1.711],[1.168,-6],[0,0]],"v":[[2.891,4.486],[10.849,3.71],[25.852,-14.597],[24.804,-37.956]],"c":false}]},{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":35,"s":[{"i":[[-2.832,0.111],[-2.272,0.384],[-1.192,6.245],[-0.009,3.513]],"o":[[2.794,-0.11],[9.419,-1.594],[1.146,-6.005],[0,0]],"v":[[3.091,3.837],[10.849,3.012],[29.365,-6.408],[27.224,-29.702]],"c":false}]},{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":49,"s":[{"i":[[-2.832,0.111],[-2.264,0.431],[-1.371,7.044],[-0.143,3.51]],"o":[[2.794,-0.11],[8.983,-1.711],[1.168,-6],[0,0]],"v":[[2.891,4.486],[10.849,3.71],[25.852,-14.597],[24.804,-37.956]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.7,"y":0},"t":63,"s":[{"i":[[-2.832,0.111],[-2.272,0.384],[-1.192,6.245],[-0.009,3.513]],"o":[[2.794,-0.11],[9.419,-1.594],[1.146,-6.005],[0,0]],"v":[[3.115,3.787],[10.849,3.012],[29.365,-6.408],[27.224,-29.702]],"c":false}]},{"i":{"x":0.3,"y":1},"o":{"x":0.167,"y":0.167},"t":68,"s":[{"i":[[-2.832,0.111],[-2.329,0.428],[-1.207,6.31],[-0.019,3.512]],"o":[[2.794,-0.11],[9.405,-1.697],[1.148,-6.004],[0,0]],"v":[[3.081,4.077],[10.905,3.036],[29.081,-7.069],[27.028,-30.369]],"c":false}]},{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":81,"s":[{"i":[[-2.832,0.111],[-2.976,0.923],[-1.371,7.044],[-0.143,3.51]],"o":[[2.794,-0.11],[9.238,-2.866],[1.168,-6],[0,0]],"v":[[2.091,4.836],[11.549,3.31],[25.852,-14.597],[24.804,-37.956]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.7,"y":0},"t":95,"s":[{"i":[[-2.832,0.111],[-2.272,0.384],[-1.192,6.245],[-0.009,3.513]],"o":[[2.794,-0.11],[9.419,-1.594],[1.146,-6.005],[0,0]],"v":[[3.091,3.837],[10.849,3.012],[29.365,-6.408],[27.224,-29.702]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":104,"s":[{"i":[[-2.831,0.08],[-2.289,0.181],[-7.091,2.705],[-0.08,0.435]],"o":[[2.753,-0.078],[9.55,-0.749],[7.091,-2.705],[0,0]],"v":[[2.94,3.427],[10.784,3.015],[28.399,6.452],[28.599,-12.123]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":108,"s":[{"i":[[-2.826,0.181],[-2.296,0.098],[-2.884,-0.516],[0.064,0.955]],"o":[[2.87,-0.184],[9.603,-0.406],[2.056,-0.027],[0,0]],"v":[[2.852,3.381],[10.755,2.875],[28.007,11.666],[29.663,9.28]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":109,"s":[{"i":[[-2.829,0.086],[-2.298,0.077],[-0.947,-0.492],[-0.646,-0.097]],"o":[[2.923,-0.087],[9.617,-0.321],[0.947,0.492],[0,0]],"v":[[2.841,3.319],[10.792,2.896],[27.909,12.969],[29.9,13.784]],"c":false}]},{"i":{"x":0.1,"y":1},"o":{"x":0.167,"y":0.167},"t":110,"s":[{"i":[[-2.832,-0.009],[-2.3,0.057],[-0.78,-1.215],[-0.945,-1.801]],"o":[[2.976,0.009],[9.63,-0.235],[0.78,1.215],[0,0]],"v":[[2.83,3.257],[10.73,2.954],[27.812,14.272],[30.136,18.289]],"c":false}]},{"t":134,"s":[{"i":[[-3.103,0.058],[-2.523,0.098],[-2.257,-5.922],[-0.05,-3.076]],"o":[[2.726,-0.051],[10.848,-0.42],[1.636,4.291],[0,0]],"v":[[3.047,3.014],[11.974,3.008],[30.43,17.71],[32.973,31.114]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":5.5,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 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":[100,100],"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":"Transform"}],"nm":"Path-8","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":184,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"body","parent":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.5,"y":1},"o":{"x":0.5,"y":0},"t":0,"s":[116.171,146.724,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.31,"y":1},"o":{"x":0.5,"y":0},"t":10,"s":[116.171,151.524,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.31,"y":1},"o":{"x":0.69,"y":0},"t":21,"s":[116.171,140.724,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.31,"y":1},"o":{"x":0.69,"y":0},"t":33,"s":[116.171,150.324,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.31,"y":1},"o":{"x":0.69,"y":0},"t":47,"s":[116.171,146.724,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.31,"y":1},"o":{"x":0.69,"y":0},"t":61,"s":[116.171,150.324,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.31,"y":1},"o":{"x":0.69,"y":0},"t":78,"s":[116.171,146.724,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.1,"y":1},"o":{"x":0.69,"y":0},"t":102,"s":[116.171,150.324,0],"to":[0,0,0],"ti":[0,0,0]},{"t":133,"s":[116.171,146.724,0]}],"ix":2},"a":{"a":0,"k":[0,14.211,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.5,0.5,0.5],"y":[0,0,0]},"t":0,"s":[100,100,100]},{"i":{"x":[0.5,0.5,0.5],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":5,"s":[97,103,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.484,0.484,0.484],"y":[0,0,0]},"t":10,"s":[100,100,100]},{"i":{"x":[0.31,0.31,0.31],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":15,"s":[103,97,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.563,0.563,0.563],"y":[0,0,0]},"t":21,"s":[100,100,100]},{"i":{"x":[0.219,0.219,0.219],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":27,"s":[97,103,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.406,0.406,0.406],"y":[0,0,0]},"t":33,"s":[100,100,100]},{"i":{"x":[0.233,0.233,0.233],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":40,"s":[103,97,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.69,0.69,0.69],"y":[0,0,0]},"t":47,"s":[100,100,100]},{"i":{"x":[0.31,0.31,0.31],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":54,"s":[97,103,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.495,0.495,0.495],"y":[0,0,0]},"t":61,"s":[100,100,100]},{"i":{"x":[0.203,0.203,0.203],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":67,"s":[103,97,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.69,0.69,0.69],"y":[0,0,0]},"t":78,"s":[100,100,100]},{"i":{"x":[0.31,0.31,0.31],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":89,"s":[97,103,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.495,0.495,0.495],"y":[0,0,0]},"t":102,"s":[100,100,100]},{"i":{"x":[0.1,0.1,0.1],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":117,"s":[103,97,100]},{"t":133,"s":[100,100,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":0,"s":[{"i":[[0,0],[-0.741,-5.903],[-1.922,-0.751],[-2.946,0],[-2.468,1.063],[-0.228,3.004],[0,0]],"o":[[0,0],[0.328,2.608],[2.556,0.999],[2.898,0],[1.959,-0.844],[0.638,-8.388],[0,0]],"v":[[-12.432,-14.575],[-11.977,7.814],[-8.487,12.849],[0.4,13.948],[9.223,12.916],[12.656,7.499],[12.482,-13.225]],"c":false}]},{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":10,"s":[{"i":[[0,0],[0,0],[-1.89,-0.83],[-2.946,0],[-2.468,1.063],[-0.086,2.131],[0,0]],"o":[[0,0],[0.14,2.059],[2.504,1.099],[2.898,0],[1.959,-0.844],[0,0],[0,0]],"v":[[-13.432,-6.25],[-13.127,7.863],[-9.825,12.562],[0.35,14.211],[10.398,12.616],[13.744,7.749],[13.432,-6.171]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.5,"y":0},"t":21,"s":[{"i":[[0,0],[0,0],[-1.772,-0.83],[-2.763,0],[-2.314,1.063],[-0.081,2.131],[0,0]],"o":[[0,0],[0.132,2.059],[2.348,1.099],[2.717,0],[1.837,-0.844],[0,0],[0,0]],"v":[[-12.732,-16.711],[-11.415,7.863],[-8.318,12.562],[0.348,14.211],[8.894,12.616],[12.032,7.749],[12.732,-14.111]],"c":false}]},{"i":{"x":0.5,"y":1},"o":{"x":0.167,"y":0},"t":33,"s":[{"i":[[0,0],[0,0],[-1.89,-0.83],[-2.946,0],[-2.468,1.063],[-0.086,2.131],[0,0]],"o":[[0,0],[0.14,2.059],[2.504,1.099],[2.898,0],[1.959,-0.844],[0,0],[0,0]],"v":[[-13.832,-12.011],[-12.127,7.863],[-8.825,12.562],[0.35,14.211],[9.398,12.616],[12.744,7.749],[13.832,-12.011]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.5,"y":0},"t":47,"s":[{"i":[[0,0],[0,0],[-1.772,-0.83],[-2.763,0],[-2.314,1.063],[-0.081,2.131],[0,0]],"o":[[0,0],[0.132,2.059],[2.348,1.099],[2.717,0],[1.837,-0.844],[0,0],[0,0]],"v":[[-12.732,-16.711],[-11.415,7.863],[-8.318,12.562],[0.348,14.211],[8.894,12.616],[12.032,7.749],[12.732,-14.111]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0},"t":61,"s":[{"i":[[0,0],[0,0],[-1.89,-0.83],[-2.946,0],[-2.468,1.063],[-0.086,2.131],[0,0]],"o":[[0,0],[0.14,2.059],[2.504,1.099],[2.898,0],[1.959,-0.844],[0,0],[0,0]],"v":[[-13.832,-12.011],[-12.127,7.863],[-8.825,12.562],[0.35,14.211],[9.398,12.616],[12.744,7.749],[13.832,-12.011]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0.167},"t":68,"s":[{"i":[[1.162,-5.686],[0,0],[-1.89,-0.83],[-2.946,0],[-2.468,1.063],[-0.086,2.131],[-1.681,3.721]],"o":[[-1.162,5.686],[0.14,2.059],[2.504,1.099],[2.898,0],[1.959,-0.844],[0,0],[0.345,-0.776]],"v":[[-11.685,-13.446],[-12.103,7.863],[-8.8,12.562],[0.35,14.211],[9.374,12.616],[12.719,7.749],[13.832,-14.267]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0},"t":78,"s":[{"i":[[1.162,-5.686],[0,0],[-1.89,-0.83],[-2.946,0],[-2.468,1.063],[-0.086,2.131],[-1.202,3.902]],"o":[[-1.162,5.686],[0.14,2.059],[2.504,1.099],[2.898,0],[1.959,-0.844],[0,0],[0.217,-0.728]],"v":[[-11.402,-16.846],[-12.127,7.863],[-8.825,12.562],[0.35,14.211],[9.398,12.616],[12.744,7.749],[13.815,-16.611]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0.167},"t":90,"s":[{"i":[[0.556,-2.843],[0,0],[-1.89,-0.83],[-2.946,0],[-2.468,1.063],[-0.086,2.131],[-0.575,1.951]],"o":[[-0.556,2.843],[0.14,2.059],[2.504,1.099],[2.898,0],[1.959,-0.844],[0,0],[0.104,-0.364]],"v":[[-11.944,-17.745],[-12.154,7.863],[-8.852,12.562],[0.35,14.211],[9.425,12.616],[12.771,7.749],[13.386,-13.461]],"c":false}]},{"i":{"x":0.1,"y":1},"o":{"x":0.167,"y":0},"t":102,"s":[{"i":[[0,0],[0,0],[-1.89,-0.83],[-2.946,0],[-2.468,1.063],[-0.086,2.131],[0,0]],"o":[[0,0],[0.14,2.059],[2.504,1.099],[2.898,0],[1.959,-0.844],[0,0],[0,0]],"v":[[-12.832,-15.411],[-12.127,7.863],[-8.825,12.562],[0.35,14.211],[9.398,12.616],[12.744,7.749],[12.932,-11.911]],"c":false}]},{"t":133,"s":[{"i":[[0,0],[-0.741,-5.903],[-1.922,-0.751],[-2.946,0],[-2.468,1.063],[-0.228,3.004],[0,0]],"o":[[0,0],[0.328,2.608],[2.556,0.999],[2.898,0],[1.959,-0.844],[0.638,-8.388],[0,0]],"v":[[-12.432,-14.575],[-11.977,7.814],[-8.487,12.849],[0.4,13.948],[9.223,12.916],[12.656,7.499],[12.482,-13.225]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"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":"Transform"}],"nm":"Path","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":5.5,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false}],"ip":0,"op":184,"st":0,"bm":0}]},{"id":"comp_3","layers":[{"ddd":0,"ind":1,"ty":3,"nm":"NULL CONTROL","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[256.989,275.127,0],"ix":2},"a":{"a":0,"k":[115,115,0],"ix":1},"s":{"a":0,"k":[500,500,100],"ix":6}},"ao":0,"ip":0,"op":119,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"head","parent":6,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.6],"y":[0]},"t":1,"s":[0]},{"i":{"x":[0.3],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":11,"s":[0]},{"i":{"x":[0.3],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":23,"s":[21]},{"i":{"x":[0.3],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":36,"s":[11]},{"i":{"x":[0.4],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":50,"s":[21]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.243],"y":[0]},"t":68,"s":[21]},{"i":{"x":[0.566],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":78,"s":[-15.198]},{"t":88,"s":[0]}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.6,"y":0},"t":1,"s":[0,-37.327,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.6,"y":0},"t":11,"s":[0,-26.927,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.167,"y":0},"t":23,"s":[5.6,-39.527,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.167,"y":0},"t":36,"s":[4,-29.127,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.4,"y":1},"o":{"x":0.167,"y":0},"t":50,"s":[5.6,-39.527,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":1},"o":{"x":0.5,"y":0},"t":66,"s":[4,-30.327,0],"to":[0,0,0],"ti":[0,0,0]},{"t":88,"s":[0,-37.327,0]}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.6,0.6,0.6],"y":[0,0,0]},"t":1,"s":[100,100,100]},{"i":{"x":[0.3,0.3,0.3],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":5,"s":[97,103,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.6,0.6,0.6],"y":[0,0,0]},"t":11,"s":[105,95,100]},{"i":{"x":[0.3,0.3,0.3],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":16,"s":[97,103,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.6,0.6,0.6],"y":[0,0,0]},"t":23,"s":[105,95,100]},{"i":{"x":[0.3,0.3,0.3],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":29,"s":[97,103,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.6,0.6,0.6],"y":[0,0,0]},"t":36,"s":[105,95,100]},{"i":{"x":[0.3,0.3,0.3],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":43,"s":[97,103,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.6,0.6,0.6],"y":[0,0,0]},"t":50,"s":[105,95,100]},{"i":{"x":[0.4,0.4,0.4],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":57,"s":[95,105,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":66,"s":[102,98,100]},{"i":{"x":[0.2,0.2,0.2],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":78,"s":[95,105,100]},{"t":88,"s":[100,100,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-6.341,0],[0,-6.341],[6.341,0],[0,6.341]],"o":[[6.341,0],[0,6.341],[-6.341,0],[0,-6.341]],"v":[[0,-11.482],[11.482,0],[0,11.482],[-11.482,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"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":"Transform"}],"nm":"Oval","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":5.5,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false}],"ip":0,"op":120,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":3,"nm":"hands","parent":6,"sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.5],"y":[1]},"o":{"x":[0.5],"y":[0]},"t":0,"s":[0]},{"i":{"x":[0.5],"y":[1]},"o":{"x":[0.5],"y":[0]},"t":21,"s":[8]},{"i":{"x":[0.4],"y":[1]},"o":{"x":[0.5],"y":[0]},"t":49,"s":[8]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.6],"y":[0]},"t":65,"s":[8]},{"t":89,"s":[0]}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":0,"s":[-2.341,-17.777,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":10,"s":[-2.341,-9.777,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":21,"s":[-1.541,-17.777,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":35,"s":[-1.541,-11.777,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.4,"y":1},"o":{"x":0.5,"y":0},"t":49,"s":[-1.541,-17.777,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":1},"o":{"x":0.6,"y":0},"t":65,"s":[-1.541,-11.777,0],"to":[0,0,0],"ti":[0,0,0]},{"t":89,"s":[-2.341,-17.777,0]}],"ix":2},"a":{"a":0,"k":[115,115,0],"ix":1},"s":{"a":0,"k":[20,20,100],"ix":6}},"ao":0,"ip":0,"op":120,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"hands 3","parent":3,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[115,115,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[500,500,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.6,"y":0},"t":0,"s":[{"i":[[0,0],[-4.026,-6.73],[-10.797,-1.273],[-2.119,-0.01]],"o":[[0.65,4.552],[3.882,6.488],[3.499,0.413],[1.186,0.005]],"v":[[-34.027,-30.664],[-27.902,-11.553],[-6.28,2.761],[4.392,3.022]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.6,"y":0},"t":10,"s":[{"i":[[0,0],[-0.71,-5.814],[-13.22,1.67],[-6.659,-0.445]],"o":[[-3.232,5.731],[0.566,4.637],[3.994,-0.505],[1.183,0.079]],"v":[[-18.219,-21.464],[-27.079,-2.224],[-7.644,3.484],[10.985,2.86]],"c":false}]},{"i":{"x":0.3,"y":1},"o":{"x":0.167,"y":0},"t":23,"s":[{"i":[[0,0],[-0.961,-3.853],[-4.984,-1.753],[-8.926,0.862]],"o":[[1.597,6.857],[1.517,6.08],[5.484,1.929],[1.18,-0.114]],"v":[[-20.247,-37.336],[-17.926,-14.859],[-7.906,2.713],[8.267,2.627]],"c":false}]},{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":37,"s":[{"i":[[0,0],[-1.803,-4.807],[-6.68,-0.3],[-7.645,-0.484]],"o":[[-1.052,7.028],[2.083,5.552],[5.685,0.256],[1.184,0.075]],"v":[[-20.218,-23.988],[-22.536,-2.921],[-7.359,2.636],[10.433,2.572]],"c":false}]},{"i":{"x":0.4,"y":1},"o":{"x":0.7,"y":0},"t":51,"s":[{"i":[[0,0],[-0.961,-3.853],[-4.984,-1.753],[-8.926,0.862]],"o":[[1.597,6.857],[1.517,6.08],[5.484,1.929],[1.18,-0.114]],"v":[[-20.247,-37.336],[-17.926,-14.859],[-7.906,2.713],[8.267,2.627]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.4,"y":0},"t":67,"s":[{"i":[[0,0],[-1.803,-4.807],[-6.68,-0.3],[-7.645,-0.484]],"o":[[-1.052,7.028],[2.083,5.552],[5.685,0.256],[1.184,0.075]],"v":[[-20.218,-23.988],[-22.536,-2.921],[-7.359,2.636],[10.433,2.572]],"c":false}]},{"i":{"x":0.1,"y":1},"o":{"x":0.167,"y":0.167},"t":78,"s":[{"i":[[0,0],[-1.746,-7.006],[-9.167,-1.369],[-3.018,-0.266]],"o":[[-3.628,5.112],[1.694,6.8],[4.106,0.209],[1.182,0.076]],"v":[[-20.614,-25.461],[-25.11,-7.259],[-6.208,2.731],[7.297,3.206]],"c":false}]},{"t":105,"s":[{"i":[[0,0],[-4.026,-6.73],[-10.797,-1.273],[-2.119,-0.01]],"o":[[0.65,4.552],[3.882,6.488],[3.499,0.413],[1.186,0.005]],"v":[[-34.027,-30.664],[-27.902,-11.553],[-6.28,2.761],[4.392,3.022]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":5.5,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 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":[100,100],"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":"Transform"}],"nm":"Path-8","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":5,"ty":4,"nm":"hands 2","parent":3,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[115,115,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[500,500,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.6,"y":0},"t":0,"s":[{"i":[[-3.103,0.058],[-2.523,0.098],[-2.257,-5.922],[-0.05,-3.076]],"o":[[2.726,-0.051],[10.848,-0.42],[1.636,4.291],[0,0]],"v":[[3.047,3.014],[11.974,3.008],[30.43,17.71],[32.973,31.114]],"c":false}]},{"i":{"x":0.3,"y":1},"o":{"x":0.6,"y":0},"t":10,"s":[{"i":[[-2.832,-0.073],[-2.303,-0.066],[-1.926,-3.085],[-0.239,-5.607]],"o":[[2.794,0.072],[11.894,0.339],[2.109,3.377],[0,0]],"v":[[2.747,2.681],[11.207,2.811],[28.065,7.836],[30.818,22.809]],"c":false}]},{"i":{"x":0.3,"y":1},"o":{"x":0.6,"y":0},"t":22,"s":[{"i":[[-2.805,0.395],[-2.144,-0.845],[-1.574,-3.736],[-3.615,-5.451]],"o":[[4.23,-0.595],[5.323,2.097],[1.94,4.605],[0,0]],"v":[[2.696,3.339],[12.911,2.962],[22.174,19.291],[30.152,32.241]],"c":false}]},{"i":{"x":0.3,"y":1},"o":{"x":0.7,"y":0},"t":36,"s":[{"i":[[-2.83,0.127],[-2.144,-0.845],[-1.985,-3.535],[-1.3,-6.195]],"o":[[5.352,-0.241],[5.323,2.097],[1.964,3.497],[0,0]],"v":[[1.908,2.538],[13.562,3.072],[22.983,13.903],[27.16,28.662]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.6,"y":0},"t":50,"s":[{"i":[[-2.805,0.395],[-2.144,-0.845],[-1.574,-3.736],[-3.615,-5.451]],"o":[[4.23,-0.595],[5.323,2.097],[1.94,4.605],[0,0]],"v":[[2.696,3.339],[12.911,2.962],[22.174,19.291],[30.152,32.241]],"c":false}]},{"i":{"x":0.4,"y":1},"o":{"x":0.167,"y":0.167},"t":58,"s":[{"i":[[-2.818,0.261],[-2.144,-0.845],[-1.78,-3.636],[-2.457,-5.823]],"o":[[4.791,-0.418],[5.323,2.097],[1.952,4.051],[0,0]],"v":[[2.321,3.134],[13.236,3.017],[22.578,16.597],[28.656,30.451]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.4,"y":0},"t":66,"s":[{"i":[[-2.83,0.127],[-2.144,-0.845],[-1.985,-3.535],[-1.3,-6.195]],"o":[[5.352,-0.241],[5.323,2.097],[1.964,3.497],[0,0]],"v":[[1.908,2.538],[13.562,3.072],[22.983,13.903],[27.16,28.662]],"c":false}]},{"i":{"x":0.1,"y":1},"o":{"x":0.167,"y":0.167},"t":76,"s":[{"i":[[-2.803,-0.138],[-2.518,-0.399],[-2.207,-4.957],[2.376,-4.348]],"o":[[3.802,0.187],[4.102,0.65],[1.627,3.656],[0,0]],"v":[[1.941,2.94],[11.88,3.616],[26.524,13.453],[23.943,27.653]],"c":false}]},{"t":97,"s":[{"i":[[-3.103,0.058],[-2.523,0.098],[-2.257,-5.922],[-0.05,-3.076]],"o":[[2.726,-0.051],[10.848,-0.42],[1.636,4.291],[0,0]],"v":[[3.047,3.014],[11.974,3.008],[30.43,17.71],[32.973,31.114]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":5.5,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 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":[100,100],"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":"Transform"}],"nm":"Path-8","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":6,"ty":4,"nm":"body","parent":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.5,"y":1},"o":{"x":0.5,"y":0},"t":0,"s":[116.171,146.724,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":10,"s":[116.171,151.524,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":21,"s":[116.171,140.724,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":35,"s":[116.171,146.724,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.4,"y":1},"o":{"x":0.5,"y":0},"t":49,"s":[116.171,143.924,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":0.2},"o":{"x":0.6,"y":0.6},"t":65,"s":[116.171,146.724,0],"to":[0,0,0],"ti":[0,0,0]},{"t":89,"s":[116.171,146.724,0]}],"ix":2},"a":{"a":0,"k":[0,14.211,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.5,0.5,0.5],"y":[0,0,0]},"t":0,"s":[100,100,100]},{"i":{"x":[0.5,0.5,0.5],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":5,"s":[97,103,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.484,0.484,0.484],"y":[0,0,0]},"t":10,"s":[100,100,100]},{"i":{"x":[0.486,0.486,0.486],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":15,"s":[103,97,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.5,0.5,0.5],"y":[0,0,0]},"t":21,"s":[100,100,100]},{"i":{"x":[0.5,0.5,0.5],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":27,"s":[97,103,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.5,0.5,0.5],"y":[0,0,0]},"t":35,"s":[100,100,100]},{"i":{"x":[0.5,0.5,0.5],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":42,"s":[103,97,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.5,0.5,0.5],"y":[0,0,0]},"t":49,"s":[100,100,100]},{"i":{"x":[0.4,0.4,0.4],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":56,"s":[97,103,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.6,0.6,0.6],"y":[0,0,0]},"t":65,"s":[100,100,100]},{"i":{"x":[0.2,0.2,0.2],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":76,"s":[97,103,100]},{"t":89,"s":[100,100,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":0,"s":[{"i":[[0,0],[-0.741,-5.903],[-1.922,-0.751],[-2.946,0],[-2.468,1.063],[-0.228,3.004],[0,0]],"o":[[0,0],[0.328,2.608],[2.556,0.999],[2.898,0],[1.959,-0.844],[0.638,-8.388],[0,0]],"v":[[-12.432,-14.575],[-11.977,7.814],[-8.487,12.849],[0.4,13.948],[9.223,12.916],[12.656,7.499],[12.482,-13.225]],"c":false}]},{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":10,"s":[{"i":[[0,0],[0,0],[-2.036,-0.83],[-3.175,0],[-2.659,1.063],[-0.093,2.131],[0,0]],"o":[[0,0],[0.446,2.497],[2.698,1.099],[3.123,0],[2.111,-0.844],[0,0],[0,0]],"v":[[-14.932,-5.55],[-13.915,7.864],[-10.056,12.562],[0.354,14.211],[10.626,12.616],[14.232,7.749],[14.732,-4.571]],"c":false}]},{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":21,"s":[{"i":[[0.942,-11.03],[0,0],[-1.748,-0.83],[-2.726,0],[-2.283,1.063],[-0.08,2.131],[-1.162,6.43]],"o":[[-0.818,9.587],[0.13,2.059],[2.317,1.099],[2.681,0],[1.812,-0.844],[0,0],[0.041,-0.17]],"v":[[-11.695,-16.726],[-11.827,7.863],[-8.772,12.562],[0.392,14.211],[9.438,12.616],[12.534,7.749],[14.054,-9.469]],"c":false}]},{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":35,"s":[{"i":[[0.379,-11.085],[0,0],[-1.759,-0.83],[-2.743,0],[-2.297,1.063],[-0.08,2.131],[-1.325,4.892]],"o":[[-0.283,8.264],[0.131,2.059],[2.331,1.099],[2.698,0],[1.823,-0.844],[0,0],[0.268,-0.691]],"v":[[-12.874,-10.003],[-11.908,7.863],[-8.834,12.562],[0.377,14.211],[9.469,12.616],[12.584,7.749],[14.152,-3.848]],"c":false}]},{"i":{"x":0.4,"y":1},"o":{"x":0.5,"y":0},"t":49,"s":[{"i":[[0.942,-11.03],[0,0],[-1.748,-0.83],[-2.726,0],[-2.283,1.063],[-0.08,2.131],[-1.162,6.43]],"o":[[-0.818,9.587],[0.13,2.059],[2.317,1.099],[2.681,0],[1.812,-0.844],[0,0],[0.041,-0.17]],"v":[[-11.695,-17.126],[-11.827,7.863],[-8.772,12.562],[0.392,14.211],[9.438,12.616],[12.534,7.749],[14.054,-9.469]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.6,"y":0},"t":65,"s":[{"i":[[0.379,-11.085],[0,0],[-1.759,-0.83],[-2.743,0],[-2.297,1.063],[-0.08,2.131],[-1.325,4.892]],"o":[[-0.283,8.264],[0.131,2.059],[2.331,1.099],[2.698,0],[1.823,-0.844],[0,0],[0.268,-0.691]],"v":[[-12.874,-10.003],[-11.908,7.863],[-8.834,12.562],[0.377,14.211],[9.469,12.616],[12.584,7.749],[14.152,-4.048]],"c":false}]},{"t":89,"s":[{"i":[[0,0],[-0.741,-5.903],[-1.922,-0.751],[-2.946,0],[-2.468,1.063],[-0.228,3.004],[0,0]],"o":[[0,0],[0.328,2.608],[2.556,0.999],[2.898,0],[1.959,-0.844],[0.638,-8.388],[0,0]],"v":[[-12.432,-14.575],[-11.977,7.814],[-8.487,12.849],[0.4,13.948],[9.223,12.916],[12.656,7.499],[12.482,-13.225]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"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":"Transform"}],"nm":"Path","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":5.5,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false}],"ip":0,"op":120,"st":0,"bm":0}]},{"id":"comp_4","layers":[{"ddd":0,"ind":1,"ty":3,"nm":"NULL CONTROL","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[256.989,275.127,0],"ix":2},"a":{"a":0,"k":[115,115,0],"ix":1},"s":{"a":0,"k":[500,500,100],"ix":6}},"ao":0,"ip":0,"op":119,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"head","parent":3,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.6],"y":[0]},"t":1,"s":[0]},{"i":{"x":[0.3],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":11,"s":[0]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":23,"s":[21]},{"t":107,"s":[0]}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.6,"y":0},"t":1,"s":[122.706,17.15,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.6,"y":0},"t":11,"s":[122.706,68.999,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.589,"y":1},"o":{"x":0.275,"y":0},"t":23,"s":[150.706,5.999,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.206,"y":0},"t":32,"s":[150.706,32.05,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.589,"y":1},"o":{"x":0.275,"y":0},"t":40,"s":[150.706,27.67,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.3,"y":1},"o":{"x":0.206,"y":0},"t":47,"s":[150.706,32.05,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.589,"y":1},"o":{"x":0.275,"y":0},"t":54,"s":[150.706,27.67,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":1},"o":{"x":0.206,"y":0},"t":61,"s":[150.706,32.05,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.1,"y":1},"o":{"x":0.6,"y":0},"t":70,"s":[150.706,27.67,0],"to":[0,0,0],"ti":[0,0,0]},{"t":107,"s":[122.706,17.15,0]}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.6,0.6,0.6],"y":[0,0,0]},"t":1,"s":[500,500,100]},{"i":{"x":[0.3,0.3,0.3],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":5,"s":[485,515,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.6,0.6,0.6],"y":[0,0,0]},"t":11,"s":[525,475,100]},{"i":{"x":[0.3,0.3,0.3],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":16,"s":[485,515,100]},{"i":{"x":[0.3,0.3,0.3],"y":[1,1,1]},"o":{"x":[0.6,0.6,0.6],"y":[0,0,0]},"t":23,"s":[525,475,100]},{"i":{"x":[0.3,0.3,0.3],"y":[1,1,1]},"o":{"x":[0.6,0.6,0.6],"y":[0,0,0]},"t":32,"s":[500,500,100]},{"i":{"x":[0.3,0.3,0.3],"y":[1,1,1]},"o":{"x":[0.6,0.6,0.6],"y":[0,0,0]},"t":40,"s":[500,500,100]},{"i":{"x":[0.3,0.3,0.3],"y":[1,1,1]},"o":{"x":[0.6,0.6,0.6],"y":[0,0,0]},"t":47,"s":[500,500,100]},{"i":{"x":[0.3,0.3,0.3],"y":[1,1,1]},"o":{"x":[0.6,0.6,0.6],"y":[0,0,0]},"t":54,"s":[500,500,100]},{"i":{"x":[0.2,0.2,0.2],"y":[1,1,1]},"o":{"x":[0.6,0.6,0.6],"y":[0,0,0]},"t":61,"s":[500,500,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.6,0.6,0.6],"y":[0,0,0]},"t":70,"s":[500,500,100]},{"i":{"x":[0.1,0.1,0.1],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":77,"s":[480,520,100]},{"t":107,"s":[500,500,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-6.341,0],[0,-6.341],[6.341,0],[0,6.341]],"o":[[6.341,0],[0,6.341],[-6.341,0],[0,-6.341]],"v":[[0,-11.482],[11.482,0],[0,11.482],[-11.482,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"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":"Transform"}],"nm":"Oval","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":5.5,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false}],"ip":0,"op":120,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":3,"nm":"NULL CONTROL","parent":7,"sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.5,"y":0},"t":21,"s":[-1.541,-17.777,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.13,"y":1},"o":{"x":0.6,"y":0},"t":66,"s":[-1.541,-20.177,0],"to":[0,0,0],"ti":[0,0,0]},{"t":94,"s":[-1.541,-17.777,0]}],"ix":2},"a":{"a":0,"k":[115,115,0],"ix":1},"s":{"a":0,"k":[20,20,100],"ix":6}},"ao":0,"ip":0,"op":120,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":3,"nm":"hands","parent":3,"sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.5],"y":[1]},"o":{"x":[0.5],"y":[0]},"t":0,"s":[0]},{"i":{"x":[0.5],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":23,"s":[8]},{"i":{"x":[0.13],"y":[1]},"o":{"x":[0.6],"y":[0]},"t":65,"s":[8]},{"t":94,"s":[0]}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":0,"s":[111,115,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":10,"s":[111,155,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.167,"y":0},"t":23,"s":[115,115,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.167,"y":0},"t":30,"s":[115,121,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.167,"y":0},"t":38,"s":[115,115,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.167,"y":0},"t":45,"s":[115,121,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.167,"y":0},"t":53,"s":[115,115,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":1},"o":{"x":0.167,"y":0},"t":57,"s":[115,121,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.13,"y":1},"o":{"x":0.6,"y":0},"t":65,"s":[115,115,0],"to":[0,0,0],"ti":[0,0,0]},{"t":94,"s":[111,115,0]}],"ix":2},"a":{"a":0,"k":[115,115,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"ip":0,"op":120,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"hands 3","parent":4,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[115,115,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[500,500,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.6,"y":0},"t":0,"s":[{"i":[[0,0],[-4.026,-6.73],[-10.797,-1.273],[-2.119,-0.01]],"o":[[0.65,4.552],[3.882,6.488],[3.499,0.413],[1.186,0.005]],"v":[[-34.027,-30.664],[-27.902,-11.553],[-6.28,2.761],[4.392,3.022]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.6,"y":0},"t":10,"s":[{"i":[[0,0],[-0.71,-5.814],[-13.22,1.67],[-6.666,-0.138]],"o":[[-3.232,5.731],[0.566,4.637],[3.994,-0.505],[1.186,0.024]],"v":[[-18.219,-21.464],[-27.079,-2.224],[-7.644,3.484],[11.485,2.829]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0},"t":23,"s":[{"i":[[0,0],[-0.961,-3.853],[-4.491,-2.782],[-8.28,0.661]],"o":[[1.597,6.857],[1.517,6.08],[4.565,2.828],[1.182,-0.094]],"v":[[-20.247,-37.336],[-17.926,-14.859],[-7.359,2.636],[10.724,2.534]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0.167},"t":26,"s":[{"i":[[0,0],[-0.511,-3.901],[-4.724,-3.504],[-8.291,0.526]],"o":[[2.238,7.884],[0.807,6.156],[4.331,3.14],[1.184,-0.075]],"v":[[-18.139,-37.479],[-16.265,-15.877],[-7.359,2.636],[10.941,2.657]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0},"t":30,"s":[{"i":[[0,0],[0.125,-3.969],[-5.052,-4.524],[-8.435,1.023]],"o":[[-2.798,7.352],[-0.197,6.263],[4,3.582],[1.177,-0.143]],"v":[[-6.969,-38.346],[-13.919,-17.314],[-7.359,2.636],[9.48,3.009]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0.167},"t":34,"s":[{"i":[[0,0],[-0.447,-3.908],[-4.757,-3.608],[-8.213,0.625]],"o":[[-4.119,5.691],[0.705,6.167],[4.297,3.185],[1.182,-0.09]],"v":[[-10.167,-36.731],[-16.027,-16.023],[-7.359,2.636],[11.49,2.929]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0},"t":37.666,"s":[{"i":[[0,0],[-0.961,-3.853],[-4.491,-2.782],[-8.28,0.661]],"o":[[1.597,6.857],[1.517,6.08],[4.565,2.828],[1.182,-0.094]],"v":[[-20.247,-37.336],[-17.926,-14.859],[-7.359,2.636],[10.724,2.534]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0.167},"t":41,"s":[{"i":[[0,0],[-0.511,-3.901],[-4.724,-3.504],[-8.291,0.526]],"o":[[2.238,7.884],[0.807,6.156],[4.331,3.14],[1.184,-0.075]],"v":[[-18.139,-37.479],[-16.265,-15.877],[-7.359,2.636],[10.941,2.657]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0},"t":45,"s":[{"i":[[0,0],[0.125,-3.969],[-5.052,-4.524],[-8.435,1.023]],"o":[[-2.798,7.352],[-0.197,6.263],[4,3.582],[1.177,-0.143]],"v":[[-6.969,-38.346],[-13.919,-17.314],[-7.359,2.636],[9.48,3.009]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0.167},"t":48,"s":[{"i":[[0,0],[-0.447,-3.908],[-4.757,-3.608],[-8.213,0.625]],"o":[[-4.119,5.691],[0.705,6.167],[4.297,3.185],[1.182,-0.09]],"v":[[-10.167,-36.731],[-16.027,-16.023],[-7.359,2.636],[11.49,2.929]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0},"t":52.334,"s":[{"i":[[0,0],[-0.961,-3.853],[-4.491,-2.782],[-8.28,0.661]],"o":[[1.597,6.857],[1.517,6.08],[4.565,2.828],[1.182,-0.094]],"v":[[-20.247,-37.336],[-17.926,-14.859],[-7.359,2.636],[10.724,2.534]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0.167},"t":56,"s":[{"i":[[0,0],[-0.511,-3.901],[-4.724,-3.504],[-8.291,0.526]],"o":[[2.238,7.884],[0.807,6.156],[4.331,3.14],[1.184,-0.075]],"v":[[-18.139,-37.479],[-16.265,-15.877],[-7.359,2.636],[10.941,2.657]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0},"t":60,"s":[{"i":[[0,0],[0.125,-3.969],[-5.052,-4.524],[-8.435,1.023]],"o":[[-2.798,7.352],[-0.197,6.263],[4,3.582],[1.177,-0.143]],"v":[[-6.969,-38.346],[-13.919,-17.314],[-7.359,2.636],[9.48,3.009]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0.167},"t":64,"s":[{"i":[[0,0],[-0.447,-3.908],[-4.757,-3.608],[-8.213,0.625]],"o":[[-4.119,5.691],[0.705,6.167],[4.297,3.185],[1.182,-0.09]],"v":[[-10.167,-36.731],[-16.027,-16.023],[-7.359,2.636],[11.49,2.929]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0},"t":68,"s":[{"i":[[0,0],[-0.961,-3.853],[-4.491,-2.782],[-8.28,0.661]],"o":[[1.597,6.857],[1.517,6.08],[4.565,2.828],[1.182,-0.094]],"v":[[-20.247,-37.336],[-17.926,-14.859],[-7.359,2.636],[10.724,2.534]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0.167},"t":71.666,"s":[{"i":[[0,0],[-0.511,-3.901],[-4.724,-3.504],[-8.522,-0.294]],"o":[[2.238,7.884],[0.807,6.156],[4.331,3.14],[1.185,0.041]],"v":[[-18.139,-37.479],[-16.265,-15.877],[-7.359,2.636],[11.49,2.929]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0},"t":76,"s":[{"i":[[0,0],[0.125,-3.969],[-5.052,-4.524],[-9.764,-0.338]],"o":[[-2.798,7.352],[-0.197,6.263],[4,3.582],[1.185,0.041]],"v":[[-6.969,-38.346],[-13.919,-17.314],[-7.359,2.636],[11.487,2.829]],"c":false}]},{"i":{"x":0.09,"y":1},"o":{"x":0.167,"y":0.167},"t":84,"s":[{"i":[[0,0],[-1.151,-9.822],[-6.924,-2.401],[-5.542,0.051]],"o":[[-3.654,5.5],[0.733,6.255],[4.389,1.538],[1.186,-0.011]],"v":[[-12.896,-35.078],[-19.976,-13.433],[-7.442,2.05],[9.027,3.064]],"c":false}]},{"t":104,"s":[{"i":[[0,0],[-4.026,-6.73],[-10.797,-1.273],[-2.119,-0.01]],"o":[[0.65,4.552],[3.882,6.488],[3.499,0.413],[1.186,0.005]],"v":[[-34.027,-30.664],[-27.902,-11.553],[-6.28,2.761],[4.392,3.022]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":5.5,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 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":[100,100],"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":"Transform"}],"nm":"Path-8","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":6,"ty":4,"nm":"hands 2","parent":4,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[115,115,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[500,500,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.6,"y":0},"t":0,"s":[{"i":[[-3.103,0.058],[-2.523,0.098],[-2.257,-5.922],[-0.05,-3.076]],"o":[[2.726,-0.051],[10.848,-0.42],[1.636,4.291],[0,0]],"v":[[3.047,3.014],[11.974,3.008],[30.43,17.71],[32.973,31.114]],"c":false}]},{"i":{"x":0.5,"y":1},"o":{"x":0.6,"y":0},"t":10,"s":[{"i":[[-2.832,-0.073],[-2.303,-0.066],[-1.926,-3.085],[-0.239,-5.607]],"o":[[2.794,0.072],[11.894,0.339],[2.109,3.377],[0,0]],"v":[[2.758,2.83],[11.206,2.761],[28.065,7.836],[30.818,22.809]],"c":false}]},{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":23,"s":[{"i":[[-2.816,0.314],[-2.144,-0.845],[-1.574,-3.736],[-3.615,-5.451]],"o":[[4.14,-0.462],[5.323,2.097],[1.94,4.605],[0,0]],"v":[[2.456,3.576],[12.827,2.873],[22.174,19.291],[30.152,32.241]],"c":false}]},{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":31,"s":[{"i":[[-2.805,0.396],[-2.168,-0.779],[-1.2,-3.73],[-2.259,-5.223]],"o":[[3.592,-0.507],[6.085,2.162],[1.511,4.741],[0,0]],"v":[[2.725,4.04],[12.869,3.17],[22.316,18.867],[27.587,32.67]],"c":false}]},{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":38,"s":[{"i":[[-2.816,0.314],[-2.144,-0.845],[-1.574,-3.736],[-3.615,-5.451]],"o":[[4.14,-0.462],[5.323,2.097],[1.94,4.605],[0,0]],"v":[[2.456,3.576],[12.827,2.873],[22.174,19.291],[30.152,32.241]],"c":false}]},{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":46,"s":[{"i":[[-2.805,0.396],[-2.168,-0.779],[-1.2,-3.73],[-2.259,-5.223]],"o":[[3.592,-0.507],[6.085,2.162],[1.511,4.741],[0,0]],"v":[[2.725,4.04],[12.869,3.17],[22.316,18.867],[27.587,32.67]],"c":false}]},{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":53,"s":[{"i":[[-2.816,0.314],[-2.144,-0.845],[-1.574,-3.736],[-3.615,-5.451]],"o":[[4.14,-0.462],[5.323,2.097],[1.94,4.605],[0,0]],"v":[[2.456,3.576],[12.827,2.873],[22.174,19.291],[30.152,32.241]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.5,"y":0},"t":61,"s":[{"i":[[-2.805,0.396],[-2.168,-0.779],[-1.2,-3.73],[-2.259,-5.223]],"o":[[3.592,-0.507],[6.085,2.162],[1.511,4.741],[0,0]],"v":[[2.725,4.04],[12.869,3.17],[22.316,18.867],[27.587,32.67]],"c":false}]},{"i":{"x":0.13,"y":1},"o":{"x":0.6,"y":0},"t":69,"s":[{"i":[[-2.816,0.314],[-2.144,-0.845],[-1.574,-3.736],[-3.615,-5.451]],"o":[[4.14,-0.462],[5.323,2.097],[1.94,4.605],[0,0]],"v":[[2.456,3.576],[12.827,2.873],[22.174,19.291],[30.152,32.241]],"c":false}]},{"t":100,"s":[{"i":[[-3.103,0.058],[-2.523,0.098],[-2.257,-5.922],[-0.05,-3.076]],"o":[[2.726,-0.051],[10.848,-0.42],[1.636,4.291],[0,0]],"v":[[3.047,3.014],[11.974,3.008],[30.43,17.71],[32.973,31.114]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":5.5,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 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":[100,100],"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":"Transform"}],"nm":"Path-8","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":7,"ty":4,"nm":"body","parent":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.5,"y":1},"o":{"x":0.5,"y":0},"t":0,"s":[116.171,146.724,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":10,"s":[116.171,151.524,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.723,"y":1},"o":{"x":0.395,"y":0},"t":21,"s":[116.171,146.724,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.361,"y":0},"t":30,"s":[116.171,147.924,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.723,"y":1},"o":{"x":0.395,"y":0},"t":38,"s":[116.171,146.724,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.361,"y":0},"t":45,"s":[116.171,147.924,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.723,"y":1},"o":{"x":0.395,"y":0},"t":52,"s":[116.171,146.724,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.361,"y":0},"t":59,"s":[116.171,147.924,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.104,"y":0.104},"o":{"x":0.395,"y":0.395},"t":66,"s":[116.171,146.724,0],"to":[0,0,0],"ti":[0,0,0]},{"t":94,"s":[116.171,146.724,0]}],"ix":2},"a":{"a":0,"k":[0,14.211,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.5,0.5,0.5],"y":[0,0,0]},"t":0,"s":[100,100,100]},{"i":{"x":[0.5,0.5,0.5],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":5,"s":[97,103,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.484,0.484,0.484],"y":[0,0,0]},"t":10,"s":[100,100,100]},{"i":{"x":[0.486,0.486,0.486],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":15,"s":[103,97,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.5,0.5,0.5],"y":[0,0,0]},"t":21,"s":[100,100,100]},{"i":{"x":[0.486,0.486,0.486],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":25,"s":[98,102,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.5,0.5,0.5],"y":[0,0,0]},"t":30,"s":[100,100,100]},{"i":{"x":[0.486,0.486,0.486],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":34,"s":[103,97,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.48,0.48,0.48],"y":[0,0,0]},"t":38,"s":[100,100,100]},{"i":{"x":[0.471,0.471,0.471],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":41,"s":[98,102,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.537,0.537,0.537],"y":[0,0,0]},"t":45,"s":[100,100,100]},{"i":{"x":[0.582,0.582,0.582],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":49,"s":[103,97,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.48,0.48,0.48],"y":[0,0,0]},"t":52,"s":[100,100,100]},{"i":{"x":[0.471,0.471,0.471],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":55,"s":[98,102,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.468,0.468,0.468],"y":[0,0,0]},"t":59,"s":[100,100,100]},{"i":{"x":[0.2,0.2,0.2],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":62,"s":[103,97,100]},{"i":{"x":[0.13,0.13,0.13],"y":[1,1,1]},"o":{"x":[0.6,0.6,0.6],"y":[0,0,0]},"t":66,"s":[100,100,100]},{"t":94,"s":[100,100,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":0,"s":[{"i":[[0,0],[-0.741,-5.903],[-1.922,-0.751],[-2.946,0],[-2.468,1.063],[-0.228,3.004],[0,0]],"o":[[0,0],[0.328,2.608],[2.556,0.999],[2.898,0],[1.959,-0.844],[0.638,-8.388],[0,0]],"v":[[-12.432,-14.575],[-11.977,7.814],[-8.487,12.849],[0.4,13.948],[9.223,12.916],[12.656,7.499],[12.482,-13.225]],"c":false}]},{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":10,"s":[{"i":[[0,0],[0,0],[-1.89,-0.83],[-2.946,0],[-2.468,1.063],[-0.086,2.131],[0,0]],"o":[[0,0],[0.14,2.059],[2.504,1.099],[2.898,0],[1.959,-0.844],[0,0],[0,0]],"v":[[-14.032,-5.05],[-13.127,7.863],[-9.825,12.562],[0.35,14.211],[10.398,12.616],[13.744,7.749],[14.232,-5.571]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.5,"y":0},"t":23,"s":[{"i":[[2.01,-10.893],[0,0],[-1.89,-0.83],[-2.946,0],[-2.468,1.063],[-0.086,2.131],[-2.532,4.395]],"o":[[-1.585,8.587],[0.14,2.059],[2.504,1.099],[2.898,0],[1.959,-0.844],[0,0],[0.024,-0.319]],"v":[[-11.286,-17.808],[-12.133,7.863],[-8.83,12.562],[0.35,14.211],[9.404,12.616],[12.749,7.749],[13.232,-10.927]],"c":false}]},{"i":{"x":0.5,"y":1},"o":{"x":0.167,"y":0.167},"t":30,"s":[{"i":[[3.203,-10.603],[0,0],[-1.89,-0.83],[-2.946,0],[-2.468,1.063],[-0.086,2.131],[-3.19,3.944]],"o":[[-2.702,8.946],[0.14,2.059],[2.504,1.099],[2.898,0],[1.959,-0.844],[0,0],[0.08,-0.14]],"v":[[-11.366,-17.585],[-11.927,7.863],[-8.625,12.562],[0.35,14.211],[9.398,12.616],[12.744,7.749],[14.152,-8.699]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.5,"y":0},"t":38,"s":[{"i":[[2.01,-10.893],[0,0],[-1.89,-0.83],[-2.946,0],[-2.468,1.063],[-0.086,2.131],[-2.532,4.395]],"o":[[-1.585,8.587],[0.14,2.059],[2.504,1.099],[2.898,0],[1.959,-0.844],[0,0],[0.024,-0.319]],"v":[[-11.286,-19.408],[-12.133,7.863],[-8.83,12.562],[0.35,14.211],[9.404,12.616],[12.749,7.749],[13.232,-10.927]],"c":false}]},{"i":{"x":0.5,"y":1},"o":{"x":0.167,"y":0.167},"t":45,"s":[{"i":[[3.203,-10.603],[0,0],[-1.89,-0.83],[-2.946,0],[-2.468,1.063],[-0.086,2.131],[-3.19,3.944]],"o":[[-2.702,8.946],[0.14,2.059],[2.504,1.099],[2.898,0],[1.959,-0.844],[0,0],[0.08,-0.14]],"v":[[-11.166,-19.585],[-11.927,7.863],[-8.625,12.562],[0.35,14.211],[9.398,12.616],[12.744,7.749],[14.152,-8.699]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.5,"y":0},"t":52,"s":[{"i":[[2.01,-10.893],[0,0],[-1.89,-0.83],[-2.946,0],[-2.468,1.063],[-0.086,2.131],[-2.532,4.395]],"o":[[-1.585,8.587],[0.14,2.059],[2.504,1.099],[2.898,0],[1.959,-0.844],[0,0],[0.024,-0.319]],"v":[[-11.303,-19.608],[-12.133,7.863],[-8.83,12.562],[0.35,14.211],[9.404,12.616],[12.749,7.749],[13.232,-10.927]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.167,"y":0.167},"t":59,"s":[{"i":[[3.203,-10.603],[0,0],[-1.89,-0.83],[-2.946,0],[-2.468,1.063],[-0.086,2.131],[-3.19,3.944]],"o":[[-2.702,8.946],[0.14,2.059],[2.504,1.099],[2.898,0],[1.959,-0.844],[0,0],[0.08,-0.14]],"v":[[-11.164,-19.885],[-11.927,7.863],[-8.625,12.562],[0.35,14.211],[9.398,12.616],[12.744,7.749],[14.152,-8.699]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.6,"y":0},"t":66,"s":[{"i":[[2.01,-10.893],[0,0],[-1.89,-0.83],[-2.946,0],[-2.468,1.063],[-0.086,2.131],[-2.532,4.395]],"o":[[-1.585,8.587],[0.14,2.059],[2.504,1.099],[2.898,0],[1.959,-0.844],[0,0],[0.024,-0.319]],"v":[[-11.303,-20.208],[-12.133,7.863],[-8.83,12.562],[0.35,14.211],[9.404,12.616],[12.749,7.749],[13.232,-10.927]],"c":false}]},{"i":{"x":0.13,"y":1},"o":{"x":0.167,"y":0.167},"t":79,"s":[{"i":[[0.694,-3.313],[0,0],[-1.89,-0.83],[-2.946,0],[-2.468,1.063],[-0.086,2.131],[-0.774,1.343]],"o":[[-0.964,4.6],[0.14,2.059],[2.504,1.099],[2.898,0],[1.959,-0.844],[0,0],[0.007,-0.097]],"v":[[-12.005,-18.139],[-12.127,7.863],[-8.825,12.562],[0.35,14.211],[9.398,12.616],[12.744,7.749],[13.275,-12.666]],"c":false}]},{"t":94,"s":[{"i":[[0,0],[-0.741,-5.903],[-1.922,-0.751],[-2.946,0],[-2.468,1.063],[-0.228,3.004],[0,0]],"o":[[0,0],[0.328,2.608],[2.556,0.999],[2.898,0],[1.959,-0.844],[0.638,-8.388],[0,0]],"v":[[-12.432,-14.575],[-11.977,7.814],[-8.487,12.849],[0.4,13.948],[9.223,12.916],[12.656,7.499],[12.482,-13.225]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"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":"Transform"}],"nm":"Path","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":5.5,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false}],"ip":0,"op":120,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":0,"nm":"hand_7","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[27.573,25.611,0],"ix":2},"a":{"a":0,"k":[256,256,0],"ix":1},"s":{"a":0,"k":[10.898,10.898,100],"ix":6}},"ao":0,"w":512,"h":512,"ip":540,"op":720,"st":540,"bm":0},{"ddd":0,"ind":2,"ty":0,"nm":"hand_4","refId":"comp_1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[27.573,25.611,0],"ix":2},"a":{"a":0,"k":[256,256,0],"ix":1},"s":{"a":0,"k":[10.898,10.898,100],"ix":6}},"ao":0,"w":512,"h":512,"ip":420,"op":540,"st":420,"bm":0},{"ddd":0,"ind":3,"ty":0,"nm":"hand_3","refId":"comp_2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[27.573,25.611,0],"ix":2},"a":{"a":0,"k":[256,256,0],"ix":1},"s":{"a":0,"k":[10.898,10.898,100],"ix":6}},"ao":0,"w":512,"h":512,"ip":240,"op":420,"st":240,"bm":0},{"ddd":0,"ind":4,"ty":0,"nm":"hand_2","refId":"comp_3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[27.573,25.611,0],"ix":2},"a":{"a":0,"k":[256,256,0],"ix":1},"s":{"a":0,"k":[10.898,10.898,100],"ix":6}},"ao":0,"w":512,"h":512,"ip":120,"op":240,"st":120,"bm":0},{"ddd":0,"ind":5,"ty":0,"nm":"hand_1","refId":"comp_4","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[27.573,25.611,0],"ix":2},"a":{"a":0,"k":[256,256,0],"ix":1},"s":{"a":0,"k":[10.898,10.898,100],"ix":6}},"ao":0,"w":512,"h":512,"ip":0,"op":120,"st":0,"bm":0}],"markers":[]} \ No newline at end of file diff --git a/Telegram/Resources/icons/calls/voice.lottie b/Telegram/Resources/icons/calls/voice.lottie new file mode 100644 index 000000000..9f900f8fa --- /dev/null +++ b/Telegram/Resources/icons/calls/voice.lottie @@ -0,0 +1 @@ +{"v":"5.5.7","meta":{"g":"LottieFiles AE 0.1.20","a":"","k":"","d":"","tc":""},"fr":60,"ip":0,"op":404,"w":60,"h":60,"nm":"ALL","ddd":0,"assets":[{"id":"comp_0","layers":[{"ddd":0,"ind":1,"ty":0,"nm":"Pre-comp 1","refId":"comp_1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[30,30,0],"ix":2},"a":{"a":0,"k":[256,256,0],"ix":1},"s":{"a":0,"k":[13.6,13.6,100],"ix":6}},"ao":0,"w":512,"h":512,"ip":0,"op":29,"st":0,"bm":0}]},{"id":"comp_1","layers":[{"ddd":0,"ind":1,"ty":4,"nm":"START","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.3,"y":0},"t":0,"s":[282.33,256.042,0],"to":[0,0,0],"ti":[0,0,0]},{"t":7,"s":[254.33,256.042,0]}],"ix":2},"a":{"a":0,"k":[12.33,0.042,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.3,"y":1},"o":{"x":0.27,"y":0},"t":0,"s":[{"i":[[2.8,-1.7],[0,0],[5.7,9.5],[0,3.6],[0,0],[-11.1,0],[-3.1,-1.9],[0,0],[5.7,-9.4]],"o":[[0,0],[-9.4,5.7],[-1.9,-3.1],[0,0],[0,-11],[3.6,0],[0,0],[9.4,5.7],[-1.7,2.7]],"v":[[103.4,17.1],[-58.1,114.7],[-85.5,107.9],[-88.4,97.6],[-88.4,-97.6],[-68.4,-117.5],[-58.1,-114.6],[103.4,-17],[110.2,10.4]],"c":true}]},{"t":10,"s":[{"i":[[5.225,-7.477],[6.6,-2.2],[5.7,9.5],[0,3.6],[-0.127,11.332],[-7.35,7.25],[-5.65,0.1],[-14.675,-17.878],[0.55,-11.25]],"o":[[-6.15,8.8],[-7.202,2.401],[-1.9,-3.1],[0,0],[0.15,-13.4],[2.563,-2.528],[0,0],[7.1,8.65],[-0.2,7.75]],"v":[[36.4,28.7],[15.4,39.95],[-11,30.15],[-16.4,18.1],[-18.65,0.9],[-8.65,-24],[7.4,-31.85],[36.15,-20.4],[43.7,7.5]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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":"Fill 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":[100,100],"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":"Transform"}],"nm":"Triangle","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":5,"st":-6,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Leg","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[252.45,380.6,0],"ix":2},"a":{"a":0,"k":[-3.55,124.6,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.4,"y":1},"o":{"x":0.31,"y":0},"t":5,"s":[{"i":[[-6.3,0],[0,0],[0,-1.435],[0,0],[6.3,0],[0,0],[0,1.435],[0,0]],"o":[[0,0],[6.3,0],[0,0],[0,1.435],[0,0],[-6.3,0],[0,0],[0,-1.435]],"v":[[-5.2,78.5],[-1.9,78.5],[9.5,81.097],[9.5,86.403],[-1.9,89],[-5.2,89],[-16.6,86.403],[-16.6,81.097]],"c":true}]},{"i":{"x":0.5,"y":1},"o":{"x":0.3,"y":0},"t":13,"s":[{"i":[[-6.3,0],[0,0],[0,-6.3],[0,0],[6.3,0],[0,0],[0,6.3],[0,0]],"o":[[0,0],[6.3,0],[0,0],[0,6.3],[0,0],[-6.3,0],[0,0],[0,-6.3]],"v":[[-5.15,70.5],[-1.85,70.5],[9.55,81.9],[9.5,113.2],[-1.9,124.6],[-5.2,124.6],[-16.6,113.2],[-16.55,81.9]],"c":true}]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":22,"s":[{"i":[[-6.3,0],[0,0],[0,-6.3],[0,0],[6.3,0],[0,0],[0,6.3],[0,0]],"o":[[0,0],[6.3,0],[0,0],[0,6.3],[0,0],[-6.3,0],[0,0],[0,-6.3]],"v":[[-5.15,86.75],[-1.85,86.75],[9.55,98.15],[9.5,113.2],[-1.9,124.6],[-5.2,124.6],[-16.6,113.2],[-16.55,98.15]],"c":true}]},{"t":29,"s":[{"i":[[-6.3,0],[0,0],[0,-6.3],[0,0],[6.3,0],[0,0],[0,6.3],[0,0]],"o":[[0,0],[6.3,0],[0,0],[0,6.3],[0,0],[-6.3,0],[0,0],[0,-6.3]],"v":[[-5.2,78.5],[-1.9,78.5],[9.5,89.9],[9.5,113.2],[-1.9,124.6],[-5.2,124.6],[-16.6,113.2],[-16.6,89.9]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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":"Fill 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":[100,100],"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":"Transform"}],"nm":"Rectangle","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":7,"op":30,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Arc R","parent":4,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[66.05,51.45,0],"ix":2},"a":{"a":0,"k":[66.05,51.45,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.833,"y":0.833},"o":{"x":0.31,"y":0},"t":8,"s":[{"i":[[0,0],[35.3,-21.739]],"o":[[0,15.6],[0,0]],"v":[[78.4,8.4],[36.7,75]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":10,"s":[{"i":[[0,0],[14.8,-11.381]],"o":[[0.03,17.185],[0,0]],"v":[[78.7,5.25],[62.7,54.175]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":12,"s":[{"i":[[0,0],[15.195,-13.612]],"o":[[0.075,19.589],[0,0]],"v":[[79.155,2.974],[63.805,52.795]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":15,"s":[{"i":[[0,0],[9.715,-14.875]],"o":[[0.094,20.585],[0,0]],"v":[[79.372,1.136],[64.285,50.377]],"c":false}]},{"i":{"x":0.5,"y":1},"o":{"x":0.167,"y":0.167},"t":16,"s":[{"i":[[0,0],[9.715,-14.875]],"o":[[0.094,20.585],[0,0]],"v":[[79.372,1.136],[64.285,50.377]],"c":false}]},{"t":23,"s":[{"i":[[0,0],[8.7,-12]],"o":[[0,15.6],[0,0]],"v":[[78.4,8.4],[64.7,50.5]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.711],"y":[6.356]},"o":{"x":[0.392],"y":[0]},"t":8,"s":[100]},{"i":{"x":[0.66],"y":[0.822]},"o":{"x":[0.353],"y":[0.153]},"t":9,"s":[100]},{"i":{"x":[0.544],"y":[0.863]},"o":{"x":[0.254],"y":[0.3]},"t":10,"s":[57.213]},{"i":{"x":[0.564],"y":[1]},"o":{"x":[0.246],"y":[0.217]},"t":12,"s":[19.305]},{"t":15,"s":[0]}],"ix":1},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":22,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 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":[100,100],"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":"Transform"}],"nm":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":5,"op":30,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Arc L","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.4,"y":1},"o":{"x":0.31,"y":0},"t":3,"s":[-3.75,84.5,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.3,"y":0},"t":13,"s":[-3.75,77.5,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":22,"s":[-3.75,87.25,0],"to":[0,0,0],"ti":[0,0,0]},{"t":29,"s":[-3.75,84.5,0]}],"ix":2},"a":{"a":0,"k":[-3.75,84.5,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.4,"y":1},"o":{"x":0.31,"y":0},"t":5,"s":[{"i":[[0,0],[13.1,0],[0,42]],"o":[[-11,5.1],[-45.3,0],[0,0]],"v":[[33,76.5],[-3.5,84.5],[-85.5,6.5]],"c":false}]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":15,"s":[{"i":[[0,0],[13.1,0],[0,42]],"o":[[-11,5.1],[-45.3,0],[0,0]],"v":[[33,76.5],[-3.5,84.5],[-85,2]],"c":false}]},{"t":23,"s":[{"i":[[0,0],[13.1,0],[0,42]],"o":[[-11,5.1],[-45.3,0],[0,0]],"v":[[33,76.5],[-3.5,84.5],[-85.5,6.5]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.3],"y":[1]},"o":{"x":[0.31],"y":[0]},"t":5,"s":[15]},{"t":15,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.723],"y":[5.239]},"o":{"x":[0.4],"y":[0]},"t":5,"s":[15]},{"i":{"x":[0.695],"y":[0.969]},"o":{"x":[0.368],"y":[0.188]},"t":7,"s":[15]},{"t":8,"s":[0]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":22,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 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":[100,100],"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":"Transform"}],"nm":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":5,"op":30,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Line","parent":6,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[3.687,-6.898,0],"ix":2},"a":{"a":0,"k":[3.6,-7,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.4,"y":1},"o":{"x":0.31,"y":0},"t":5,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-101.613,-97],[108.787,112]],"c":false}]},{"i":{"x":0.5,"y":1},"o":{"x":0.3,"y":0},"t":15,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-108.213,-118.069],[115.413,104.069]],"c":false}]},{"t":23,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-101.6,-111.5],[108.8,97.5]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.4],"y":[1]},"o":{"x":[0.31],"y":[0]},"t":5,"s":[34]},{"t":15,"s":[0]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.4],"y":[1]},"o":{"x":[0.31],"y":[0]},"t":5,"s":[59]},{"t":15,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":22,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 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":[100,100],"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":"Transform"}],"nm":"Path-10","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":5,"op":30,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"Head","parent":4,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-3.6,-33.55,0],"ix":2},"a":{"a":0,"k":[-3.6,-33.55,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.833,"y":0.833},"o":{"x":0.31,"y":0},"t":3,"s":[{"i":[[-25.4,0],[0,-14.432],[0,0],[12.95,-6.477],[0,0],[0,0],[0,0]],"o":[[25.4,0],[0,0],[0,6.477],[0,0],[0,0],[0,0],[0,-14.432]],"v":[[-3.6,-45],[42.4,-18.863],[42.4,24.32],[23.55,44.576],[-27.58,17.939],[-49.7,3.439],[-49.6,-18.863]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":6,"s":[{"i":[[-25.217,0],[0,-18.82],[0,0],[12.857,-8.447],[0,0],[0,0],[0,0]],"o":[[25.217,0],[0,0],[0,8.447],[0,0],[0,0],[0,0],[0,-18.82]],"v":[[-3.6,-74.003],[42.068,-39.92],[42.068,16.391],[23.354,42.805],[-22.609,2.243],[-49.345,-21.035],[-49.269,-39.92]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":7,"s":[{"i":[[-25.133,0],[0,-20.833],[0,0],[7.486,-6.386],[0,0],[0,0],[0,0]],"o":[[25.133,0],[0,0],[0,9.35],[0,0],[0,0],[0,0],[0,-20.833]],"v":[[-3.601,-87.309],[41.916,-49.58],[41.916,12.754],[28.014,36.493],[-16.079,-7.707],[-49.113,-39.513],[-49.117,-49.58]],"c":true}]},{"i":{"x":0.4,"y":1},"o":{"x":0.167,"y":0.167},"t":9,"s":[{"i":[[-24.994,0],[0,-24.173],[0,0],[9.026,-7.804],[0,0],[0,0],[0,0]],"o":[[24.994,0],[0,0],[0,10.849],[0,0],[0,0],[0,0],[0,-24.173]],"v":[[-3.601,-109.388],[41.663,-65.611],[41.663,6.718],[27.287,33.135],[-21.692,-14.203],[-48.939,-44.406],[-48.865,-65.611]],"c":true}]},{"i":{"x":0.5,"y":1},"o":{"x":0.3,"y":0},"t":13,"s":[{"i":[[-24.882,0],[0,-26.136],[0,0],[6.004,-6.398],[0,0],[0,0],[0,0]],"o":[[24.882,0],[0,0],[0,11.73],[0,0],[0,0],[0,0],[0,-26.136]],"v":[[-3.601,-123],[41.462,-75.667],[41.462,2.534],[30.496,26.07],[-20.343,-23.873],[-48.773,-48.671],[-48.664,-75.667]],"c":true}]},{"t":22,"s":[{"i":[[-25.4,0],[0,-25.4],[0,0],[6.9,-8],[0,0],[0,0],[0,0]],"o":[[25.4,0],[0,0],[0,11.4],[0,0],[0,0],[0,0],[0,-25.4]],"v":[[-3.6,-117.5],[42.4,-71.5],[42.4,4.5],[31.3,34.4],[-20.33,-17.23],[-49.6,-46.5],[-49.6,-71.5]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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":"Fill 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":[100,100],"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":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.31,"y":0},"t":3,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[5.9,0],[0,14.432],[0,0]],"o":[[0,0],[0,0],[0,0],[-11.85,5.796],[-25.4,0],[0,0],[0,0]],"v":[[-49.7,1.365],[-24.952,12.671],[-23,13.78],[26.35,43.241],[-3.6,50.4],[-49.6,24.263],[-49.7,1.365]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":6,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[5.857,0],[0,18.82],[0,0]],"o":[[0,0],[0,0],[0,0],[-11.765,7.558],[-25.217,0],[0,0],[0,0]],"v":[[-49.345,-23.74],[-20,-4.627],[-18.062,-3.18],[26.134,41.064],[-3.6,50.4],[-49.269,16.317],[-49.345,-23.74]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":7,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[5.838,0],[0,20.833],[0,0]],"o":[[0,0],[0,0],[0,0],[-10.035,6.292],[-25.133,0],[0,0],[0,0]],"v":[[-49.113,-23.507],[-25.728,0.688],[-23.796,2.289],[19.285,43.816],[-3.601,50.4],[-49.117,12.672],[-49.113,-23.257]],"c":true}]},{"i":{"x":0.4,"y":1},"o":{"x":0.167,"y":0.167},"t":9,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[5.806,0],[0,24.173],[0,0]],"o":[[0,0],[0,0],[0,0],[-6.163,2.551],[-24.994,0],[0,0],[0,0]],"v":[[-49.382,-11.516],[-31.116,7.197],[-29.195,9.055],[8.663,46.861],[-3.601,50.4],[-48.865,6.622],[-49.243,-11.446]],"c":true}]},{"i":{"x":0.5,"y":1},"o":{"x":0.3,"y":0},"t":13,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[5.78,0],[0,26.837],[0,0]],"o":[[0,0],[0,0],[0,0],[-4.239,1.413],[-24.882,0],[0,0],[0,0]],"v":[[-48.796,-13.783],[-30.018,6.241],[-28.105,8.304],[9.739,46.087],[-3.601,50.4],[-48.664,1.797],[-48.546,-13.658]],"c":true}]},{"t":22,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[5.9,0],[0,25.4],[0,0]],"o":[[0,0],[0,0],[0,0],[-5.2,2],[-25.4,0],[0,0],[0,0]],"v":[[-49.6,-15.4],[-40.702,-6.502],[-38.75,-4.55],[13.1,47.3],[-3.6,50.4],[-49.6,4.4],[-49.6,-15.4]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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":"Fill 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":[100,100],"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":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":4,"op":30,"st":0,"bm":0}]},{"id":"comp_2","layers":[{"ddd":0,"ind":1,"ty":0,"nm":"Pre-comp 1","refId":"comp_3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[30,30,0],"ix":2},"a":{"a":0,"k":[256,256,0],"ix":1},"s":{"a":0,"k":[13.6,13.6,100],"ix":6}},"ao":0,"w":512,"h":512,"ip":0,"op":33,"st":0,"bm":0}]},{"id":"comp_3","layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Line","parent":2,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[3.687,-6.898,0],"ix":2},"a":{"a":0,"k":[3.6,-7,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.3,"y":1},"o":{"x":0.2,"y":0},"t":14,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-101.6,-111.5],[108.8,97.5]],"c":false}]},{"t":26,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-101.6,-111.5],[108.8,97.5]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.3],"y":[1]},"o":{"x":[0.2],"y":[0]},"t":14,"s":[14]},{"t":26,"s":[0]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.3],"y":[1]},"o":{"x":[0.2],"y":[0]},"t":14,"s":[86]},{"t":26,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":22,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 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":[100,100],"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":"Transform"}],"nm":"Path-10","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":14,"op":34,"st":-1,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Head","parent":3,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-3.6,-33.55,0],"ix":2},"a":{"a":0,"k":[-3.6,-33.55,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":[[-25.4,0],[0,-25.4],[0,0],[6.9,-8],[0,0],[0,0],[0,0]],"o":[[25.4,0],[0,0],[0,11.4],[0,0],[0,0],[0,0],[0,-25.4]],"v":[[-3.6,-117.5],[42.4,-71.5],[42.4,4.5],[31.3,34.4],[-20.33,-17.23],[-49.6,-46.5],[-49.6,-71.5]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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":"Fill 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":[100,100],"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":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[5.9,0],[0,25.4],[0,0]],"o":[[0,0],[0,0],[0,0],[-5.2,2],[-25.4,0],[0,0],[0,0]],"v":[[-49.6,-15.4],[-40.702,-6.502],[-38.75,-4.55],[13.1,47.3],[-3.6,50.4],[-49.6,4.4],[-49.6,-15.4]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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":"Fill 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":[100,100],"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":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":12,"op":34,"st":-1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Arc L","parent":5,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.31,"y":0},"t":13,"s":[-3.75,69.094,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.3,"y":0},"t":25,"s":[-3.75,89.094,0],"to":[0,0,0],"ti":[0,0,0]},{"t":33,"s":[-3.75,84.5,0]}],"ix":2},"a":{"a":0,"k":[-3.75,84.5,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.3,"y":1},"o":{"x":0.29,"y":0},"t":17,"s":[{"i":[[0,0],[13.1,0],[0,42]],"o":[[-11,5.1],[-45.3,0],[0,0]],"v":[[33,76.5],[-3.5,84.5],[-85.5,6.5]],"c":false}]},{"t":27,"s":[{"i":[[0,0],[13.1,0],[0,42]],"o":[[-11,5.1],[-45.3,0],[0,0]],"v":[[33,76.5],[-3.5,84.5],[-85.5,6.5]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.723],"y":[0.572]},"o":{"x":[0.4],"y":[0]},"t":4,"s":[15]},{"i":{"x":[0.695],"y":[0.627]},"o":{"x":[0.368],"y":[0.45]},"t":6,"s":[29]},{"i":{"x":[0.323],"y":[1]},"o":{"x":[0.222],"y":[0.388]},"t":7,"s":[37.868]},{"t":17,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.723],"y":[1.14]},"o":{"x":[0.4],"y":[0]},"t":4,"s":[12]},{"i":{"x":[0.695],"y":[0.945]},"o":{"x":[0.368],"y":[0.061]},"t":6,"s":[17.559]},{"i":{"x":[0.652],"y":[0.857]},"o":{"x":[0.357],"y":[0.122]},"t":7,"s":[9]},{"i":{"x":[0.426],"y":[1]},"o":{"x":[0.183],"y":[-2.717]},"t":9,"s":[0]},{"t":17,"s":[0]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":22,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 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":[100,100],"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":"Transform"}],"nm":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":6,"op":34,"st":-1,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Arc R","parent":3,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[66.05,51.45,0],"ix":2},"a":{"a":0,"k":[66.05,51.45,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.3,"y":1},"o":{"x":0.29,"y":0},"t":17,"s":[{"i":[[0,0],[8.7,-12]],"o":[[0,15.6],[0,0]],"v":[[78.4,8.4],[64.7,50.5]],"c":false}]},{"t":27,"s":[{"i":[[0,0],[8.7,-12]],"o":[[0,15.6],[0,0]],"v":[[78.4,8.4],[64.7,50.5]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.3],"y":[1]},"o":{"x":[0.4],"y":[0]},"t":7,"s":[99]},{"t":17,"s":[0]}],"ix":1},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":22,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 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":[100,100],"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":"Transform"}],"nm":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":10,"op":34,"st":-1,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Leg","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[252.45,380.6,0],"ix":2},"a":{"a":0,"k":[-3.55,124.6,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.3,"y":1},"o":{"x":0.31,"y":0},"t":13,"s":[{"i":[[-6.3,0],[0,0],[0,-6.3],[0,0],[6.3,0],[0,0],[0,6.3],[0,0]],"o":[[0,0],[6.3,0],[0,0],[0,6.3],[0,0],[-6.3,0],[0,0],[0,-6.3]],"v":[[-5.15,64],[-1.85,64],[9.55,75.4],[9.5,113.2],[-1.9,124.6],[-5.2,124.6],[-16.6,113.2],[-16.55,75.4]],"c":true}]},{"i":{"x":0.5,"y":1},"o":{"x":0.3,"y":0},"t":25,"s":[{"i":[[-6.3,0],[0,0],[0,-2.846],[0,0],[6.3,0],[0,0],[0,6.3],[0,0]],"o":[[0,0],[6.3,0],[0,0],[0,6.3],[0,0],[-6.3,0],[0,0],[0,-2.846]],"v":[[-5.2,84.75],[-1.9,84.75],[9.5,89.9],[9.5,113.2],[-1.9,124.6],[-5.2,124.6],[-16.6,113.2],[-16.6,89.9]],"c":true}]},{"t":33,"s":[{"i":[[-6.3,0],[0,0],[0,-6.3],[0,0],[6.3,0],[0,0],[0,6.3],[0,0]],"o":[[0,0],[6.3,0],[0,0],[0,6.3],[0,0],[-6.3,0],[0,0],[0,-6.3]],"v":[[-5.2,78.5],[-1.9,78.5],[9.5,89.9],[9.5,113.2],[-1.9,124.6],[-5.2,124.6],[-16.6,113.2],[-16.6,89.9]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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":"Fill 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":[100,100],"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":"Transform"}],"nm":"Rectangle","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":9,"op":34,"st":-1,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"Line Bell","parent":7,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.28,"y":0},"t":-1,"s":[5.03,-9.859,0],"to":[0,0,0],"ti":[0,0,0]},{"t":14,"s":[4.03,-8.359,0]}],"ix":2},"a":{"a":0,"k":[-0.05,-2.95,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":[[-98.152,-101.129],[98.052,95.229]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.3],"y":[1]},"o":{"x":[0.28],"y":[0]},"t":-1,"s":[0]},{"t":14,"s":[12]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.3],"y":[1]},"o":{"x":[0.28],"y":[0]},"t":-1,"s":[100]},{"t":14,"s":[88]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":22,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 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":[100,100],"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":"Transform"}],"nm":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":14,"st":-1,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":"Bell","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.3,"y":0},"t":-1,"s":[258.564,256.028,0],"to":[0,0,0],"ti":[0,0,0]},{"t":13,"s":[253.564,241.028,0]}],"ix":2},"a":{"a":0,"k":[-1.88,-0.927,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.3,"y":1},"o":{"x":0.5,"y":0},"t":-1,"s":[{"i":[[-4.2,9.7],[0,0],[0,0],[0,0],[0,0],[-7.5,8],[0,0],[0,0],[0,1.8],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[-11.2,0],[0,0],[0,0],[1.2,-1.3],[0,0],[0,0],[0,-11.6]],"v":[[-74.4,-46.7],[32.121,59.821],[34.778,62.478],[51.9,79.6],[-85.9,79.6],[-97.5,58.4],[-97.2,58.1],[-82.6,41.5],[-80.8,36.7],[-80.8,36.7],[-80.8,-14.6]],"c":true}]},{"t":13,"s":[{"i":[[-0.557,1.753],[0,0],[0,0],[0,0],[12.242,3.875],[4.463,6.051],[0,0],[0,0],[0,1.225],[0.586,4.254],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[-8.598,-2.722],[0,0],[0,0],[-0.69,-2.075],[0,0],[-1.67,-12.113],[0.086,-1.777]],"v":[[-48.888,-16.333],[2.602,35.024],[4.48,36.832],[14.055,46.983],[-15.561,47.733],[-39.407,31.182],[-39.266,30.978],[-44.13,22.682],[-46.406,17.666],[-48.156,11.291],[-49.281,-8.366]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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":"Fill 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":[100,100],"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":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.5,"y":0},"t":-1,"s":[{"i":[[6.6,6.5],[0,0],[0,1.8],[0,0],[35.493,9.136],[0,0],[9,0],[0,0],[0,-9.1],[0,0],[10.111,-11.873],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[-1.2,-1.3],[0,0],[0,-37.993],[0,0],[0,-9.1],[0,0],[-9.1,0],[0,0],[-16.061,4.493],[0,0],[0,0],[0,0],[0,0],[10.58,-1.555]],"v":[[88.55,58.6],[73.795,40.25],[71.995,35.45],[71.9,-14.6],[11.5,-91.763],[11.5,-98],[-4.9,-114.4],[-6.1,-114.4],[-22.5,-98],[-22.5,-91.12],[-62.4,-65.9],[-19.568,-22.503],[-18.761,-21.685],[79.699,78.074],[80.6,78.975]],"c":true}]},{"t":13,"s":[{"i":[[-2.182,4.07],[-1.1,3.295],[0,1.8],[0,0],[25.933,14.308],[0,0],[11.842,-0.367],[0,0],[3.626,-2.33],[0,0],[-2.875,-21.055],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0.815,-2.442],[0,0],[0.221,-54.605],[0,0],[-4.442,-2.455],[0,0],[-13.97,-0.305],[0,0],[-19.874,13.29],[0,0],[0,0],[0,0],[0,0],[2.995,-3.055]],"v":[[36.363,27.6],[40.655,19.625],[42.779,11.2],[43.21,-19.85],[21.372,-111.763],[20.684,-112.312],[-3.286,-118.838],[-3.6,-118.9],[-29.821,-110.375],[-31.571,-108.995],[-48.57,-47.15],[-22.056,-18.753],[-21.467,-17.935],[30.402,32.574],[31.31,33.225]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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":"Fill 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":[100,100],"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":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":12,"st":-1,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":"Bottom Bell","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[255.445,394.655,0],"ix":2},"a":{"a":0,"k":[0,137.7,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.3,"y":1},"o":{"x":0.4,"y":0},"t":-1,"s":[{"i":[[-13.1,0],[-2,12],[1.2,0],[20.1,0],[-0.2,-1.3]],"o":[[12.8,0],[0.2,-1.4],[-10.3,0],[-0.9,0],[1.9,12.1]],"v":[[0.5,119.605],[26.1,97.805],[22.8,93.605],[-20.9,93.605],[-25.2,97.405]],"c":true}]},{"t":9,"s":[{"i":[[-8.162,0],[0.625,15.53],[0.51,0.546],[4.907,-5.5],[0.14,-2.002]],"o":[[7.975,0],[0.125,-2.157],[-6.069,-6.5],[-0.373,0.418],[-1.164,16.646]],"v":[[-3.269,123.85],[10.181,105.515],[9.694,64.545],[-15.782,65.045],[-16.531,104.149]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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":"Fill 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":[100,100],"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":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":9,"st":-1,"bm":0}]},{"id":"comp_4","layers":[{"ddd":0,"ind":1,"ty":0,"nm":"Pre-comp 1","refId":"comp_5","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[30,30,0],"ix":2},"a":{"a":0,"k":[256,256,0],"ix":1},"s":{"a":0,"k":[13.6,13.6,100],"ix":6}},"ao":0,"w":512,"h":512,"ip":0,"op":33,"st":0,"bm":0}]},{"id":"comp_5","layers":[{"ddd":0,"ind":1,"ty":4,"nm":"EXAMPLE ON 2","parent":4,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-3.981,2.44,0],"ix":2},"a":{"a":0,"k":[-0.038,1.5,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":[[-9.1,0],[0,0],[0,-9.1],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[9,0],[0,0],[0,0],[0,0],[0,0],[0,-9.1]],"v":[[-1.6,-115.5],[-0.4,-115.5],[16,-99.1],[16,-89.5],[16,-89.5],[-18,-89.5],[-18,-99.1]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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":"Fill 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":[100,100],"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":"Transform"}],"nm":"Rectangle","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-13.1,0],[-2,12],[1.2,0],[20.1,0],[-0.2,-1.3]],"o":[[12.8,0],[0.2,-1.4],[-10.3,0],[-0.9,0],[1.9,12.2]],"v":[[0,118.5],[25.6,96.7],[22.3,92.5],[-21.4,92.5],[-25.7,96.3]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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":"Fill 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":[100,100],"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":"Transform"}],"nm":"Path","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,1.8],[0,0],[0,0],[36.4,8.6],[3.4,0],[5.9,-0.1],[0,-38.8],[0,0],[1.2,-1.3],[0,0],[0,0],[-11.3,0],[0,0],[8.1,7.9],[0,0]],"o":[[0,0],[0,0],[0,-38.6],[-6.7,-0.2],[-3,0],[-36.5,8.6],[0,0],[0,1.8],[0,0],[0,0],[-8,7.9],[0,0],[11.3,0],[0,0],[-1.1,-1.3]],"v":[[76.4,35.6],[76.4,-15.7],[76.4,-15.7],[14.3,-93.3],[-0.8,-93.5],[-14.2,-93.3],[-76.3,-15.7],[-76.3,35.6],[-78.1,40.4],[-92.8,57],[-92.8,57],[-81.5,78.5],[81.3,78.5],[92.7,57],[78.1,40.4]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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":"Fill 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":[100,100],"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":"Transform"}],"nm":"Combined-Shape","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false}],"ip":15,"op":33,"st":-1,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Line Bell","parent":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.3,"y":1},"o":{"x":0.4,"y":0},"t":2,"s":[5.03,-9.859,0],"to":[0,0,0],"ti":[0,0,0]},{"t":23,"s":[12.954,-0.45,0]}],"ix":2},"a":{"a":0,"k":[-0.05,-2.95,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":[[-98.152,-101.129],[98.052,95.229]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.3],"y":[1]},"o":{"x":[0.4],"y":[0]},"t":2,"s":[0]},{"t":23,"s":[100]}],"ix":1},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":22,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 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":[100,100],"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":"Transform"}],"nm":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":21,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Bottom Bell","parent":4,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,137.7,0],"ix":2},"a":{"a":0,"k":[0,137.7,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":[[-13.1,0],[-2,12],[1.2,0],[20.1,0],[-0.2,-1.3]],"o":[[12.8,0],[0.2,-1.4],[-10.3,0],[-0.9,0],[1.9,12.1]],"v":[[-4.5,119.6],[21.1,97.8],[17.8,93.6],[-25.9,93.6],[-30.2,97.4]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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":"Fill 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":[100,100],"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":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":15,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Bell","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[260.562,232.132,0],"ix":2},"a":{"a":0,"k":[0.12,-24.927,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.7,0.7,0.7],"y":[1,1,1]},"o":{"x":[0.3,0.3,0.3],"y":[0,0,0]},"t":0,"s":[100,100,100]},{"i":{"x":[0.7,0.7,0.7],"y":[1,1,1]},"o":{"x":[0.3,0.3,0.3],"y":[0,0,0]},"t":12,"s":[93,93,100]},{"i":{"x":[0.7,0.7,0.7],"y":[1,1,1]},"o":{"x":[0.3,0.3,0.3],"y":[0,0,0]},"t":22,"s":[101,101,100]},{"t":32,"s":[100,100,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":7,"s":[{"i":[[-4.2,9.7],[0,0],[0,0],[0,0],[0,0],[-7.5,8],[0,0],[0,0],[0,1.8],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[-11.2,0],[0,0],[0,0],[1.2,-1.3],[0,0],[0,0],[0,-11.6]],"v":[[-74.4,-46.7],[32.121,59.821],[34.778,62.478],[51.9,79.6],[-85.9,79.6],[-97.5,58.4],[-97.2,58.1],[-82.6,41.5],[-80.8,36.7],[-80.8,36.7],[-80.8,-14.6]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":8,"s":[{"i":[[-14.003,15.695],[-5.659,-3.648],[-10.756,-14.742],[0,0],[0,0],[-7.5,8],[0,0],[0,0],[0,1.8],[0,0],[0,0]],"o":[[0,0],[-6.594,-3.203],[4.9,6.715],[0,0],[-11.2,0],[0,0],[0,0],[1.2,-1.3],[0,0],[0,0],[0,-11.6]],"v":[[-63.408,-63.817],[-47.235,-56.249],[-60.858,-34.829],[50.693,79.614],[-85.9,79.6],[-97.5,58.4],[-97.2,58.1],[-82.6,41.5],[-80.8,36.7],[-80.8,36.7],[-80.8,-14.6]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":9,"s":[{"i":[[-14.003,15.695],[-5.659,-3.648],[-10.756,-14.742],[0,0],[0,0],[-7.5,8],[0,0],[0,0],[0,1.8],[0,0],[0,0]],"o":[[0,0],[-6.594,-3.203],[4.9,6.715],[0,0],[-11.2,0],[0,0],[0,0],[1.2,-1.3],[0,0],[0,0],[0,-11.6]],"v":[[-63.408,-63.817],[-24.266,-35.672],[-39.752,-12.387],[50.693,79.614],[-85.9,79.6],[-97.5,58.4],[-97.2,58.1],[-82.6,41.5],[-80.8,36.7],[-80.8,36.7],[-80.8,-14.6]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":10,"s":[{"i":[[-14.003,15.695],[-5.659,-3.648],[-10.756,-14.742],[0,0],[0,0],[-7.5,8],[0,0],[0,0],[0,1.8],[0,0],[0,0]],"o":[[0,0],[-6.594,-3.203],[4.9,6.715],[0,0],[-11.2,0],[0,0],[0,0],[1.2,-1.3],[0,0],[0,0],[0,-11.6]],"v":[[-63.408,-63.817],[-3.42,-13.502],[-18.646,10.055],[50.693,79.614],[-85.9,79.6],[-97.5,58.4],[-97.2,58.1],[-82.6,41.5],[-80.8,36.7],[-80.8,36.7],[-80.8,-14.6]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":12,"s":[{"i":[[-14.003,15.695],[-5.659,-3.648],[-10.756,-14.742],[0,0],[0,0],[-7.5,8],[0,0],[0,0],[0,1.8],[0,0],[0,0]],"o":[[0,0],[-6.594,-3.203],[4.9,6.715],[0,0],[-11.2,0],[0,0],[0,0],[1.2,-1.3],[0,0],[0,0],[0,-11.6]],"v":[[-63.408,-63.817],[39.053,27.896],[23.827,51.453],[50.693,79.614],[-85.9,79.6],[-97.5,58.4],[-97.2,58.1],[-82.6,41.5],[-80.8,36.7],[-80.8,36.7],[-80.8,-14.6]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":13,"s":[{"i":[[-14.003,15.695],[-5.659,-3.648],[-10.756,-14.742],[0,0],[0,0],[-7.5,8],[0,0],[0,0],[0,1.8],[0,0],[0,0]],"o":[[0,0],[-6.594,-3.203],[4.9,6.715],[0,0],[-11.2,0],[0,0],[0,0],[1.2,-1.3],[0,0],[0,0],[0,-11.6]],"v":[[-63.408,-63.817],[52.725,43.176],[39.376,66.197],[50.693,79.614],[-85.9,79.6],[-97.5,58.4],[-97.2,58.1],[-82.6,41.5],[-80.8,36.7],[-80.8,36.7],[-80.8,-14.6]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":14,"s":[{"i":[[-14.003,15.695],[-5.659,-3.648],[-7.11,-19.386],[0,0],[0,0],[-7.5,8],[0,0],[0,0],[0,1.8],[0,0],[0,0]],"o":[[0,0],[-6.594,-3.203],[1.271,3.465],[0,0],[-11.2,0],[0,0],[0,0],[1.2,-1.3],[0,0],[0,0],[0,-11.6]],"v":[[-63.408,-63.817],[67.899,50.896],[48.96,73.385],[50.693,79.614],[-85.9,79.6],[-97.5,58.4],[-97.2,58.1],[-82.6,41.5],[-80.8,36.7],[-80.8,36.7],[-80.8,-14.6]],"c":true}]},{"t":15,"s":[{"i":[[-7.545,13.495],[0,0],[0,0],[0,0],[0,0],[-7.5,8],[0,0],[0,0],[0,1.8],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[-11.2,0],[0,0],[0,0],[1.2,-1.3],[0,0],[0,0],[0,-11.6]],"v":[[-68.9,-57.2],[46.687,58.223],[49.57,61.102],[68.15,79.655],[-85.9,79.6],[-97.5,58.4],[-97.2,58.1],[-82.6,41.5],[-80.8,36.7],[-80.8,36.7],[-80.8,-14.6]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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":"Fill 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":[100,100],"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":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":7,"s":[{"i":[[6.6,6.5],[0,0],[0,1.8],[0,0],[35.493,9.136],[0,0],[9,0],[0,0],[0,-9.1],[0,0],[10.111,-11.873],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[-1.2,-1.3],[0,0],[0,-37.993],[0,0],[0,-9.1],[0,0],[-9.1,0],[0,0],[-16.061,4.493],[0,0],[0,0],[0,0],[0,0],[10.58,-1.555]],"v":[[88.55,58.6],[73.795,40.25],[71.995,35.45],[71.9,-14.6],[11.5,-91.763],[11.5,-98],[-4.9,-114.4],[-6.1,-114.4],[-22.5,-98],[-22.5,-91.12],[-62.4,-65.9],[-19.568,-22.503],[-18.761,-21.685],[79.699,78.074],[80.6,78.975]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":8,"s":[{"i":[[6.6,6.5],[0,0],[0,1.8],[0,0],[35.493,9.136],[0,0],[9,0],[0,0],[0,-9.1],[0,0],[9.92,-13.055],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[-1.2,-1.3],[0,0],[0,-37.993],[0,0],[0,-9.1],[0,0],[-9.1,0],[0,0],[-16.061,4.493],[0,0],[0,0],[0,0],[0,0],[9.935,-1.566]],"v":[[88.675,58.537],[73.795,40.125],[71.995,35.325],[71.9,-14.6],[11.5,-91.763],[11.5,-98],[-4.9,-114.4],[-6.1,-114.4],[-22.5,-98],[-22.5,-91.12],[-64.104,-63.629],[-25.68,-45.311],[-14.342,-27.377],[81.753,76.333],[80.85,79.006]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":10,"s":[{"i":[[6.6,6.5],[0,0],[0,1.8],[0,0],[35.493,9.136],[0,0],[9,0],[0,0],[0,-9.1],[0,0],[9.92,-13.055],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[-1.2,-1.3],[0,0],[0,-37.993],[0,0],[0,-9.1],[0,0],[-9.1,0],[0,0],[-16.061,4.493],[0,0],[0,0],[0,0],[0,0],[9.935,-1.566]],"v":[[88.675,58.537],[73.795,40.125],[71.995,35.325],[71.9,-14.6],[11.5,-91.763],[11.5,-98],[-4.9,-114.4],[-6.1,-114.4],[-22.5,-98],[-22.5,-91.12],[-64.104,-63.629],[7.983,-0.159],[27.87,17.507],[81.753,76.333],[80.85,79.006]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":12,"s":[{"i":[[6.6,6.5],[0,0],[0,1.8],[0,0],[35.493,9.136],[0,0],[9,0],[0,0],[0,-9.1],[0,0],[9.92,-13.055],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[-1.2,-1.3],[0,0],[0,-37.993],[0,0],[0,-9.1],[0,0],[-9.1,0],[0,0],[-16.061,4.493],[0,0],[0,0],[0,0],[0,0],[9.935,-1.566]],"v":[[88.675,58.537],[73.795,40.125],[71.995,35.325],[71.9,-14.6],[11.5,-91.763],[11.5,-98],[-4.9,-114.4],[-6.1,-114.4],[-22.5,-98],[-22.5,-91.12],[-64.104,-63.629],[50.456,41.238],[70.343,58.905],[81.753,76.333],[80.85,79.006]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":13,"s":[{"i":[[6.6,6.5],[0,0],[0,1.8],[0,0],[35.493,9.136],[0,0],[9,0],[0,0],[0,-9.1],[0,0],[9.92,-13.055],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[-1.2,-1.3],[0,0],[0,-37.993],[0,0],[0,-9.1],[0,0],[-9.1,0],[0,0],[-16.061,4.493],[0,0],[0,0],[0,0],[0,0],[9.935,-1.566]],"v":[[88.675,58.537],[73.795,40.125],[71.995,35.325],[71.9,-14.6],[11.5,-91.763],[11.5,-98],[-4.9,-114.4],[-6.1,-114.4],[-22.5,-98],[-22.5,-91.12],[-64.104,-63.629],[51.394,43.785],[70.343,58.905],[81.753,76.333],[80.85,79.006]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":14,"s":[{"i":[[6.6,6.5],[0,0],[0,1.8],[0,0],[35.493,9.136],[0,0],[9,0],[0,0],[0,-9.1],[0,0],[9.92,-13.055],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[-1.2,-1.3],[0,0],[0,-37.993],[0,0],[0,-9.1],[0,0],[-9.1,0],[0,0],[-16.061,4.493],[0,0],[0,0],[0,0],[0,0],[9.935,-1.566]],"v":[[88.675,58.537],[73.795,40.125],[71.995,35.325],[71.9,-14.6],[11.5,-91.763],[11.5,-98],[-4.9,-114.4],[-6.1,-114.4],[-22.5,-98],[-22.5,-91.12],[-64.104,-63.629],[66.569,51.505],[70.343,58.905],[81.753,76.333],[80.85,79.006]],"c":true}]},{"t":15,"s":[{"i":[[6.6,6.5],[0,0],[0,1.8],[0,0],[35.493,9.136],[0,0],[9,0],[0,0],[0,-9.1],[0,0],[9.346,-16.6],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[-1.2,-1.3],[0,0],[0,-37.993],[0,0],[0,-9.1],[0,0],[-9.1,0],[0,0],[-16.061,4.493],[0,0],[0,0],[0,0],[0,0],[8,-1.6]],"v":[[89.05,58.35],[73.795,39.75],[71.995,34.95],[71.9,-14.6],[11.5,-91.763],[11.5,-98],[-4.9,-114.4],[-6.1,-114.4],[-22.5,-98],[-22.5,-91.12],[-69.215,-56.816],[-28.207,-15.716],[-27.434,-14.941],[66.835,79.54],[81.6,79.1]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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":"Fill 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":[100,100],"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":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":15,"st":0,"bm":0}]},{"id":"comp_6","layers":[{"ddd":0,"ind":1,"ty":0,"nm":"Pre-comp 1","refId":"comp_7","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":0,"s":[30,30,0],"to":[0,0,0],"ti":[0,0,0]},{"t":10,"s":[28.282,29.857,0]}],"ix":2},"a":{"a":0,"k":[256,256,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.7,0.7,0.7],"y":[1,1,1]},"o":{"x":[0.3,0.3,0.3],"y":[0,0,0]},"t":0,"s":[13.6,13.6,100]},{"t":10,"s":[13.5,13.5,100]}],"ix":6}},"ao":0,"w":512,"h":512,"ip":0,"op":37,"st":0,"bm":0}]},{"id":"comp_7","layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Head 2","parent":3,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.39,"y":1},"o":{"x":0.3,"y":0},"t":0,"s":[0.613,-101.537,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.39,"y":1},"o":{"x":0.3,"y":0},"t":12,"s":[11.613,-50.537,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.39,"y":1},"o":{"x":0.3,"y":0},"t":26,"s":[11.613,-89.537,0],"to":[0,0,0],"ti":[0,0,0]},{"t":36,"s":[11.113,-83.037,0]}],"ix":2},"a":{"a":0,"k":[11.613,-84.537,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.833,"y":0.833},"o":{"x":0.3,"y":0},"t":0,"s":[{"i":[[0,-8.774],[8.774,0],[0,8.774],[-8.774,0]],"o":[[0,8.774],[-8.774,0],[0,-8.774],[8.774,0]],"v":[[25.5,-84.537],[9.613,-68.65],[-6.274,-84.537],[9.613,-100.424]],"c":true}]},{"i":{"x":0.39,"y":1},"o":{"x":0.167,"y":0.167},"t":4,"s":[{"i":[[0,-11.72],[13.247,0],[0,11.72],[-13.247,0]],"o":[[0,11.72],[-13.247,0],[0,-11.72],[13.247,0]],"v":[[33.09,-69.693],[9.103,-48.473],[-14.883,-69.693],[9.103,-90.913]],"c":true}]},{"i":{"x":0.39,"y":1},"o":{"x":0.3,"y":0},"t":14,"s":[{"i":[[0,-24.735],[24.735,0],[0,24.735],[-24.735,0]],"o":[[0,24.735],[-24.735,0],[0,-24.735],[24.735,0]],"v":[[56.4,-84.537],[11.613,-39.75],[-33.174,-84.537],[11.613,-129.324]],"c":true}]},{"t":25,"s":[{"i":[[0,-26.924],[26.924,0],[0,26.924],[-26.924,0]],"o":[[0,26.924],[-26.924,0],[0,-26.924],[26.924,0]],"v":[[60.363,-84.537],[11.613,-35.787],[-37.137,-84.537],[11.613,-133.287]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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":"Fill 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":[100,100],"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":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":37,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Hands 2","parent":3,"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.05,"y":0},"t":9,"s":[11.336,13.774,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.4,"y":1},"o":{"x":0.31,"y":0},"t":12,"s":[11.336,18.274,0],"to":[0,0,0],"ti":[0,0,0]},{"t":25,"s":[11.336,11.774,0]}],"ix":2},"a":{"a":0,"k":[11.336,10.774,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.833,"y":0.833},"o":{"x":0.3,"y":0},"t":7,"s":[{"i":[[0,0],[0.875,44.36],[0.012,11.61]],"o":[[0,0],[-0.348,-17.658],[-0.016,-14.955]],"v":[[-33.583,109.659],[-40.875,50.64],[-45.012,-17.11]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":9,"s":[{"i":[[0,0],[-7.48,30.25],[-7.964,9.648]],"o":[[0,0],[2.73,-11.373],[7.228,-11.196]],"v":[[-57.163,97.688],[-57.996,36.651],[-42.036,0.102]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":10,"s":[{"i":[[0,0],[-14.611,30.379],[-23.925,1.795]],"o":[[0,0],[7.389,-12.121],[13.171,-8.112]],"v":[[-77.526,99.758],[-81.889,29.906],[-41.075,4.99]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":11,"s":[{"i":[[0,0],[-27.718,19.589],[-10.136,-1.028]],"o":[[0,0],[21.782,-16.411],[19.114,-5.028]],"v":[[-107.888,77.829],[-93.782,16.161],[-42.114,8.877]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":13,"s":[{"i":[[0,0],[-27.325,4.127],[-14.975,-3.949]],"o":[[0,0],[20.334,-3.734],[35.444,0.066]],"v":[[-136.225,32.858],[-90.834,7.893],[-38.525,10.472]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":14,"s":[{"i":[[0,0],[-27.129,-3.603],[-14.269,-4.157]],"o":[[0,0],[19.61,2.605],[43.609,2.613]],"v":[[-147.393,-5.627],[-89.61,-2.992],[-38.231,9.77]],"c":false}]},{"i":{"x":0.3,"y":1},"o":{"x":0.167,"y":0.167},"t":18,"s":[{"i":[[0,0],[-38.825,-22.336],[-19.04,-2.789]],"o":[[0,0],[14.675,9.164],[62.76,8.586]],"v":[[-144.801,-73.381],[-91.175,-7.197],[-37.96,10.757]],"c":false}]},{"t":28,"s":[{"i":[[0,0],[-46.458,-35.583],[-19.738,-3.567]],"o":[[0,0],[8.352,6.397],[79.039,14.286]],"v":[[-142.333,-130.341],[-88.062,-16.235],[-40.512,7.89]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":22,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 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":[100,100],"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":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.3,"y":0},"t":7,"s":[{"i":[[0,0],[0.583,25.777],[4.5,19.11]],"o":[[0,0],[-0.238,-10.518],[-18.41,-78.181]],"v":[[63,101.64],[63.167,54.973],[56,-16.61]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":9,"s":[{"i":[[0,0],[2.455,25.149],[6.868,13.265]],"o":[[0,0],[-0.66,-9.745],[-29.265,-65.813]],"v":[[68.548,94.42],[70.368,48.723],[56.521,2.29]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":11,"s":[{"i":[[-11.495,10.995],[15.537,39.692],[11.504,1.535]],"o":[[14.034,-13.424],[-3.378,-8.551],[-47.075,-45.52]],"v":[[79.466,115.674],[91.463,38.058],[62.496,12.174]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":13,"s":[{"i":[[-14.54,10.097],[25.244,37.544],[12.466,1.965]],"o":[[15.872,-11.198],[-4.741,-7.952],[-56.007,-35.342]],"v":[[88.294,117.357],[93.089,33.781],[63.701,13.504]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":14,"s":[{"i":[[-16.063,9.648],[30.097,36.471],[11.946,0.457]],"o":[[16.791,-10.086],[-5.422,-7.653],[-60.473,-30.253]],"v":[[92.709,118.199],[93.903,31.642],[61.554,13.67]],"c":false}]},{"i":{"x":0.3,"y":1},"o":{"x":0.167,"y":0.167},"t":18,"s":[{"i":[[-5.509,3.309],[41.86,36.38],[11.653,0.923]],"o":[[5.759,-3.459],[-7.371,-6.796],[-73.243,-15.702]],"v":[[124.602,127.851],[94.405,26.111],[62.332,13.5]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":25,"s":[{"i":[[0,0],[48,36.333],[11.5,1.167]],"o":[[0,0],[-8.388,-6.35],[-79.91,-8.107]],"v":[[141.25,132.89],[94.667,23.223],[63.5,11.89]],"c":false}]},{"t":28,"s":[{"i":[[0,0],[48,36.333],[11.5,1.167]],"o":[[0,0],[-8.388,-6.35],[-79.91,-8.107]],"v":[[140,128.89],[94.667,23.848],[63.5,11.89]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":22,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 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":[100,100],"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":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":9,"op":37,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Body 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.4,"y":1},"o":{"x":0.3,"y":0},"t":12,"s":[266.676,398.378,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.4,"y":1},"o":{"x":0.3,"y":0},"t":23,"s":[266.676,387.378,0],"to":[0,0,0],"ti":[0,0,0]},{"t":34,"s":[266.676,392.378,0]}],"ix":2},"a":{"a":0,"k":[10.676,136.378,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.69,"y":1},"o":{"x":0.31,"y":0},"t":12,"s":[{"i":[[-12.288,0.098],[-12.498,-0.987],[0,0],[3.339,-1.222],[4.897,-9.213],[22.369,0],[7.456,13.82],[-1.892,54.808]],"o":[[8.847,-0.071],[35.724,2.82],[24.706,0],[0,0],[-7.456,13.82],[-22.369,0],[-4.897,-9.213],[-12.353,-3.008]],"v":[[-46.407,7.071],[-18.334,9.585],[61.794,9.679],[70.141,28.952],[68.805,121.176],[13.161,136.5],[-42.484,121.176],[-47.047,25.191]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.31,"y":0},"t":23,"s":[{"i":[[-10.8,-2.3],[-8.2,-0.4],[0,0],[3,-1.3],[4.4,-9.8],[20.1,0],[6.7,14.7],[-1.7,58.3]],"o":[[7.9,1.7],[0,0],[22.2,0],[0,0],[-6.7,14.7],[-20.1,0],[-4.4,-9.8],[-11.1,-3.2]],"v":[[-40.2,-2.3],[-15.1,1.5],[56.9,1.6],[64.4,22.1],[63.2,120.2],[13.2,136.5],[-36.8,120.2],[-40.9,18.1]],"c":true}]},{"t":34,"s":[{"i":[[-10.8,-2.3],[-8.2,-0.4],[0,0],[3,-1.3],[4.4,-9.8],[24.175,0.094],[6.7,14.7],[-1.7,58.3]],"o":[[7.9,1.7],[0,0],[22.2,0],[0,0],[-6.669,14.144],[-20.1,-0.078],[-4.4,-9.8],[-11.1,-3.2]],"v":[[-40.2,-2.3],[-15.1,1.5],[56.9,1.6],[64.4,22.1],[63.044,119.606],[13.2,136.156],[-36.8,120.2],[-40.9,18.1]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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":"Fill 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":[100,100],"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":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":12,"op":37,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Line Bell","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.3,"y":0},"t":0,"s":[264.975,247.096,0],"to":[0,0,0],"ti":[0,0,0]},{"t":12,"s":[260.475,307.096,0]}],"ix":2},"a":{"a":0,"k":[-0.05,-2.95,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":[[-98.152,-101.129],[98.052,95.229]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.3],"y":[1]},"o":{"x":[0.3],"y":[0]},"t":0,"s":[0]},{"t":12,"s":[45]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.3],"y":[1]},"o":{"x":[0.3],"y":[0]},"t":0,"s":[100]},{"t":12,"s":[69]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":22,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 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":[100,100],"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":"Transform"}],"nm":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":13,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Bell","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.4,"y":1},"o":{"x":0.3,"y":0},"t":0,"s":[260.467,231.7,0],"to":[0,0,0],"ti":[0,0,0]},{"t":12,"s":[255.967,270.7,0]}],"ix":2},"a":{"a":0,"k":[-0.033,-25.3,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.833,"y":0.833},"o":{"x":0.3,"y":0},"t":0,"s":[{"i":[[-4.2,9.7],[0,0],[0,0],[0,0],[0,0],[-7.5,8],[0,0],[0,0],[0,1.8],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[-11.2,0],[0,0],[0,0],[1.2,-1.3],[0,0],[0,0],[0,-11.6]],"v":[[-74.4,-46.7],[32.121,59.821],[34.778,62.478],[51.9,79.6],[-85.9,79.6],[-97.5,58.4],[-97.2,58.1],[-82.6,41.5],[-80.8,36.7],[-80.8,36.7],[-80.8,-14.6]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":4,"s":[{"i":[[-8.362,11.23],[0,0],[0,0],[3.762,-1.859],[8.142,-0.264],[0.777,8.157],[0.156,0.224],[1.314,3.134],[0.224,1.943],[0,0],[-0.582,7.711]],"o":[[0,0],[0,0],[0,0],[-19.321,9.547],[-28.463,-1.013],[-0.005,-0.156],[-0.156,-3.776],[0.062,-2.373],[0,0],[-0.045,-4.946],[0,-7.124]],"v":[[-60.638,-49.681],[29.345,35.402],[55.841,63.676],[71.571,79.252],[-31.443,86.874],[-68.777,71.642],[-68.75,71.544],[-67.843,58.677],[-67.812,51.954],[-68.27,48.401],[-68.748,-3.168]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":6,"s":[{"i":[[-1.323,3.262],[0,0],[0,0],[6.275,-3.565],[14.005,-0.455],[0.542,0.356],[1.713,1.084],[2.26,5.391],[0.385,2.046],[0,0],[-1.001,13.264]],"o":[[0,0],[0,0],[0,0],[-4.624,2.627],[-40.891,-1.743],[0.542,-0.578],[-4.048,-2.564],[-0.758,-3.145],[0,0],[-0.077,-8.507],[0,-3.901]],"v":[[-57.609,-43.228],[22.617,23.841],[64.126,70.129],[61.225,80.722],[7.766,92.111],[-48.042,81.338],[-48.213,81.277],[-57.218,71.044],[-58.46,62.937],[-59.249,56.826],[-60.071,5.063]],"c":true}]},{"i":{"x":0.4,"y":1},"o":{"x":0.167,"y":0.167},"t":9,"s":[{"i":[[-0.309,0.762],[0,0],[0,0],[4.568,-5.804],[17.516,-1.439],[9.846,0.175],[5.202,1.836],[3.057,6.91],[0.521,1.988],[0,0],[-1.354,17]],"o":[[0,0],[0,0],[0,0],[-4.183,5.585],[-15.234,1.811],[-9.592,-0.227],[-5.947,-2.247],[-1.448,-3.573],[0,0],[-0.104,-10.903],[0,-0.911]],"v":[[-52.53,-28.79],[11.994,25.222],[67.477,74.382],[64.512,86.679],[25.234,99.127],[-10.469,99.989],[-34.862,95.528],[-48.272,82.948],[-50.586,74.249],[-51.653,66.417],[-52.765,18.153]],"c":true}]},{"t":12,"s":[{"i":[[0,0],[0,0],[0,0],[4.244,-6.392],[19.61,-0.621],[11.618,0.122],[6.498,1.941],[3.306,7.372],[0.563,1.97],[0,0],[-1.464,18.138]],"o":[[0,0],[0,0],[0,0],[-4.244,6.392],[-5.683,0.18],[-11.618,-0.122],[-6.595,-1.969],[-1.661,-3.704],[0,0],[-0.113,-11.633],[0,0]],"v":[[-46.474,-23.891],[12.383,25.893],[71.24,75.677],[67.655,88.306],[34.655,101.868],[5.941,102.334],[-23.997,99.619],[-42.777,86.574],[-45.423,77.695],[-46.577,69.338],[-47.779,22.141]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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":"Fill 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":[100,100],"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":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.3,"y":0},"t":0,"s":[{"i":[[6.6,6.5],[0,0],[0,1.8],[0,0],[35.493,9.136],[0,0],[9,0],[0,0],[0,-9.1],[0,0],[10.111,-11.873],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[-1.2,-1.3],[0,0],[0,-37.993],[0,0],[0,-9.1],[0,0],[-9.1,0],[0,0],[-16.061,4.493],[0,0],[0,0],[0,0],[0,0],[10.58,-1.555]],"v":[[88.55,58.6],[73.795,40.25],[71.995,35.45],[71.9,-14.6],[11.5,-91.763],[11.5,-98],[-4.9,-114.4],[-6.1,-114.4],[-22.5,-98],[-22.5,-91.12],[-62.4,-65.9],[-19.568,-22.503],[-18.761,-21.685],[79.699,78.074],[80.6,78.975]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":6,"s":[{"i":[[0.157,8.024],[0,0],[0,1.922],[0,0],[24.177,-0.77],[0,0],[9,0],[0,0],[5.709,0.272],[0,0],[-0.26,-17.504],[0,0],[0,0],[0,0],[-7.459,5.266]],"o":[[0,0],[-0.323,-1.991],[0,0],[1.096,-24.22],[0,0],[-9.573,2.118],[0,0],[-9.1,0],[0,0],[-6.6,1.142],[0,0],[0,0],[0,0],[0,0],[3.993,-2.819]],"v":[[71.843,56.59],[71.106,36.657],[70.583,30.009],[69.845,-21.987],[38.823,-52.309],[28.073,-50.661],[3.062,-46.678],[1.048,-46.86],[-26.209,-52.017],[-31.319,-53.002],[-58.24,-38.393],[-25.737,-8.451],[-22.131,-4.064],[59.51,80.628],[65.709,78.296]],"c":true}]},{"i":{"x":0.4,"y":1},"o":{"x":0.167,"y":0.167},"t":9,"s":[{"i":[[0.094,4.576],[0,0],[0,1.828],[0,0],[20.355,-2.421],[0,0],[9,0],[0,0],[6.507,0.351],[0,0],[2.835,-13.717],[0,0],[0,0],[0,0],[-1.742,1.23]],"o":[[0,0],[-0.014,-2.094],[0,0],[-0.871,-19.045],[0,0],[-6.547,0.878],[0,0],[-9.1,0],[0,0],[-3.266,-0.12],[0,0],[0,0],[0,0],[0,0],[1.239,-8.208]],"v":[[69.031,57.556],[68.76,39.315],[68.408,33.605],[67.871,-20.767],[25.645,-36.641],[23.135,-36.246],[-0.875,-35.181],[-1.998,-35.415],[-22.891,-36.675],[-31.934,-38.285],[-52.647,-28.157],[-24.554,-1.976],[-21.081,1.4],[63.583,76.467],[67.733,76.517]],"c":true}]},{"t":12,"s":[{"i":[[0.075,3.525],[0,0],[0,1.8],[0,0],[24.798,0.638],[0,0],[9.018,0],[0,0],[6.763,0.375],[0,0],[-1.858,-4.475],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0.08,-2.125],[0,0],[1.607,-13.525],[0,0],[-5.636,0.5],[0,0],[-9.118,0],[0,0],[-2.254,-0.505],[0,0],[0,0],[0,0],[0,0],[0.401,-9.85]],"v":[[71.166,57.85],[71.036,40.125],[70.735,34.7],[70.643,-14.975],[22.897,-23.388],[22.897,-23.375],[0.453,-23.525],[0.253,-23.775],[-17.431,-24.5],[-24.068,-24.87],[-46.892,-24.4],[-21.382,-0.003],[-17.943,3.065],[67.559,75.199],[71.091,75.975]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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":"Fill 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":[100,100],"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":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":13,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"Bottom Bell","parent":5,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.39,"y":1},"o":{"x":0.3,"y":0},"t":0,"s":[0,136.7,0],"to":[0,0,0],"ti":[0,0,0]},{"t":12,"s":[12,91.7,0]}],"ix":2},"a":{"a":0,"k":[0,137.7,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.833,"y":0.833},"o":{"x":0.3,"y":0},"t":0,"s":[{"i":[[-13.1,0],[-2,12],[1.2,0],[20.1,0],[-0.2,-1.3]],"o":[[12.8,0],[0.2,-1.4],[-10.3,0],[-0.9,0],[1.9,12.1]],"v":[[-5.125,120.225],[20.6,99.05],[17.3,94.85],[-26.4,94.85],[-30.7,98.65]],"c":true}]},{"i":{"x":0.39,"y":1},"o":{"x":0.167,"y":0.167},"t":4,"s":[{"i":[[-19.183,0.659],[-2.93,11.67],[1.758,0],[29.45,0],[-0.296,-1.263]],"o":[[23.128,-0.795],[2.186,-3.703],[-15.091,0],[-1.319,0],[2.784,11.767]],"v":[[-2.487,124.469],[35.205,101.877],[29.62,99.793],[-34.409,99.793],[-40.709,103.488]],"c":true}]},{"t":12,"s":[{"i":[[-28.151,0],[-4.298,11.184],[2.579,0],[43.194,0],[-0.438,-1.209]],"o":[[27.506,0],[0.43,-1.305],[-22.134,0],[-1.934,0],[4.083,11.277]],"v":[[-0.461,137.5],[54.82,117.764],[47.729,113.85],[-46.18,113.85],[-55.42,117.392]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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":"Fill 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":[100,100],"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":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":13,"st":0,"bm":0}]},{"id":"comp_8","layers":[{"ddd":0,"ind":1,"ty":0,"nm":"Pre-comp 2","refId":"comp_9","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":0,"s":[30,30,0],"to":[0,0,0],"ti":[0,0,0]},{"t":10,"s":[28.282,29.857,0]}],"ix":2},"a":{"a":0,"k":[256,256,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.7,0.7,0.7],"y":[1,1,1]},"o":{"x":[0.3,0.3,0.3],"y":[0,0,0]},"t":0,"s":[13.6,13.6,100]},{"t":10,"s":[13.5,13.5,100]}],"ix":6}},"ao":0,"w":512,"h":512,"ip":0,"op":37,"st":0,"bm":0}]},{"id":"comp_9","layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Head 2","parent":3,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.39,"y":1},"o":{"x":0.3,"y":0},"t":0,"s":[0.613,-101.537,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.39,"y":1},"o":{"x":0.3,"y":0},"t":12,"s":[11.613,-50.537,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.39,"y":1},"o":{"x":0.3,"y":0},"t":26,"s":[11.613,-87.537,0],"to":[0,0,0],"ti":[0,0,0]},{"t":36,"s":[11.113,-83.037,0]}],"ix":2},"a":{"a":0,"k":[11.613,-84.537,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.833,"y":0.833},"o":{"x":0.3,"y":0},"t":0,"s":[{"i":[[0,-8.774],[8.774,0],[0,8.774],[-8.774,0]],"o":[[0,8.774],[-8.774,0],[0,-8.774],[8.774,0]],"v":[[25.5,-84.537],[9.613,-68.65],[-6.274,-84.537],[9.613,-100.424]],"c":true}]},{"i":{"x":0.39,"y":1},"o":{"x":0.167,"y":0.167},"t":4,"s":[{"i":[[0,-11.72],[13.247,0],[0,11.72],[-13.247,0]],"o":[[0,11.72],[-13.247,0],[0,-11.72],[13.247,0]],"v":[[33.09,-69.693],[9.103,-48.473],[-14.883,-69.693],[9.103,-90.913]],"c":true}]},{"i":{"x":0.39,"y":1},"o":{"x":0.3,"y":0},"t":14,"s":[{"i":[[0,-24.735],[24.735,0],[0,24.735],[-24.735,0]],"o":[[0,24.735],[-24.735,0],[0,-24.735],[24.735,0]],"v":[[56.4,-84.537],[11.613,-39.75],[-33.174,-84.537],[11.613,-129.324]],"c":true}]},{"t":25,"s":[{"i":[[0,-26.924],[26.924,0],[0,26.924],[-26.924,0]],"o":[[0,26.924],[-26.924,0],[0,-26.924],[26.924,0]],"v":[[60.363,-84.537],[11.613,-35.787],[-37.137,-84.537],[11.613,-133.287]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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":"Fill 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":[100,100],"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":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":37,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Hands 2","parent":3,"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.05,"y":0},"t":9,"s":[11.336,13.774,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.4,"y":1},"o":{"x":0.31,"y":0},"t":12,"s":[11.336,18.274,0],"to":[0,0,0],"ti":[0,0,0]},{"t":25,"s":[11.336,11.774,0]}],"ix":2},"a":{"a":0,"k":[11.336,10.774,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.833,"y":0.833},"o":{"x":0.3,"y":0},"t":7,"s":[{"i":[[0,0],[0.875,44.36],[0.012,11.61]],"o":[[0,0],[-0.348,-17.658],[-0.016,-14.955]],"v":[[-33.583,109.659],[-40.875,50.64],[-45.012,-17.11]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":9,"s":[{"i":[[0,0],[-7.48,30.25],[-7.964,9.648]],"o":[[0,0],[2.73,-11.373],[7.228,-11.196]],"v":[[-57.163,97.688],[-57.996,36.651],[-42.036,0.102]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":10,"s":[{"i":[[0,0],[-14.611,30.379],[-23.925,1.795]],"o":[[0,0],[7.389,-12.121],[13.171,-8.112]],"v":[[-77.526,99.758],[-81.889,29.906],[-41.075,4.99]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":11,"s":[{"i":[[0,0],[-27.718,19.589],[-10.136,-1.028]],"o":[[0,0],[21.782,-16.411],[19.114,-5.028]],"v":[[-107.888,77.829],[-93.782,16.161],[-42.114,8.877]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":13,"s":[{"i":[[0,0],[-27.325,4.127],[-14.975,-3.949]],"o":[[0,0],[20.334,-3.734],[35.444,0.066]],"v":[[-136.225,32.858],[-90.834,7.893],[-38.525,10.472]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":14,"s":[{"i":[[0,0],[-27.129,-3.603],[-14.269,-4.157]],"o":[[0,0],[19.61,2.605],[43.609,2.613]],"v":[[-147.393,-5.627],[-89.61,-2.992],[-38.231,9.77]],"c":false}]},{"i":{"x":0.3,"y":1},"o":{"x":0.167,"y":0.167},"t":18,"s":[{"i":[[0,0],[-38.825,-22.336],[-19.04,-2.789]],"o":[[0,0],[14.675,9.164],[62.76,8.586]],"v":[[-144.801,-73.381],[-91.175,-7.197],[-37.96,10.757]],"c":false}]},{"t":28,"s":[{"i":[[0,0],[-46.458,-35.583],[-19.738,-3.567]],"o":[[0,0],[8.352,6.397],[79.039,14.286]],"v":[[-142.333,-130.341],[-88.062,-16.235],[-40.512,7.89]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":22,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 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":[100,100],"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":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.3,"y":0},"t":7,"s":[{"i":[[0,0],[0.583,25.777],[4.5,19.11]],"o":[[0,0],[-0.238,-10.518],[-18.41,-78.181]],"v":[[63,101.64],[63.167,54.973],[56,-16.61]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":9,"s":[{"i":[[0,0],[2.455,25.149],[6.868,13.265]],"o":[[0,0],[-0.66,-9.745],[-29.265,-65.813]],"v":[[68.548,94.42],[70.368,48.723],[56.521,2.29]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":11,"s":[{"i":[[-11.495,10.995],[15.537,39.692],[11.504,1.535]],"o":[[14.034,-13.424],[-3.378,-8.551],[-47.075,-45.52]],"v":[[79.466,115.674],[91.463,38.058],[62.496,12.174]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":13,"s":[{"i":[[-14.54,10.097],[25.244,37.544],[12.466,1.965]],"o":[[15.872,-11.198],[-4.741,-7.952],[-56.007,-35.342]],"v":[[88.294,117.357],[93.089,33.781],[63.701,13.504]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":14,"s":[{"i":[[-16.063,9.648],[30.097,36.471],[11.946,0.457]],"o":[[16.791,-10.086],[-5.422,-7.653],[-60.473,-30.253]],"v":[[92.709,118.199],[93.903,31.642],[61.554,13.67]],"c":false}]},{"i":{"x":0.3,"y":1},"o":{"x":0.167,"y":0.167},"t":18,"s":[{"i":[[-5.509,3.309],[41.86,36.38],[11.653,0.923]],"o":[[5.759,-3.459],[-7.371,-6.796],[-73.243,-15.702]],"v":[[124.602,127.851],[94.405,26.111],[62.332,13.5]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":25,"s":[{"i":[[0,0],[48,36.333],[11.5,1.167]],"o":[[0,0],[-8.388,-6.35],[-79.91,-8.107]],"v":[[141.25,132.89],[94.667,23.223],[63.5,11.89]],"c":false}]},{"t":28,"s":[{"i":[[0,0],[48,36.333],[11.5,1.167]],"o":[[0,0],[-8.388,-6.35],[-79.91,-8.107]],"v":[[140,128.89],[94.667,23.848],[63.5,11.89]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":22,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 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":[100,100],"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":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":9,"op":37,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Body 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.4,"y":1},"o":{"x":0.3,"y":0},"t":12,"s":[266.676,398.378,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.4,"y":1},"o":{"x":0.3,"y":0},"t":23,"s":[266.676,387.378,0],"to":[0,0,0],"ti":[0,0,0]},{"t":34,"s":[266.676,392.378,0]}],"ix":2},"a":{"a":0,"k":[10.676,136.378,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.69,"y":1},"o":{"x":0.31,"y":0},"t":12,"s":[{"i":[[-12.288,0.098],[-12.498,-0.987],[0,0],[3.339,-1.222],[4.897,-9.213],[22.369,0],[7.456,13.82],[-1.892,54.808]],"o":[[8.847,-0.071],[35.724,2.82],[24.706,0],[0,0],[-7.456,13.82],[-22.369,0],[-4.897,-9.213],[-12.353,-3.008]],"v":[[-46.407,7.071],[-18.334,9.585],[61.794,9.679],[70.141,28.952],[68.805,121.176],[13.161,136.5],[-42.484,121.176],[-47.047,25.191]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.31,"y":0},"t":23,"s":[{"i":[[-10.8,-2.3],[-8.2,-0.4],[0,0],[3,-1.3],[4.4,-9.8],[20.1,0],[6.7,14.7],[-1.7,58.3]],"o":[[7.9,1.7],[0,0],[22.2,0],[0,0],[-6.7,14.7],[-20.1,0],[-4.4,-9.8],[-11.1,-3.2]],"v":[[-40.2,-2.3],[-15.1,1.5],[56.9,1.6],[64.4,22.1],[63.2,120.2],[13.2,136.5],[-36.8,120.2],[-40.9,18.1]],"c":true}]},{"t":34,"s":[{"i":[[-10.8,-2.3],[-8.2,-0.4],[0,0],[3,-1.3],[4.4,-9.8],[24.175,0.094],[6.7,14.7],[-1.7,58.3]],"o":[[7.9,1.7],[0,0],[22.2,0],[0,0],[-6.669,14.144],[-20.1,-0.078],[-4.4,-9.8],[-11.1,-3.2]],"v":[[-40.2,-2.3],[-15.1,1.5],[56.9,1.6],[64.4,22.1],[63.044,119.606],[13.2,136.156],[-36.8,120.2],[-40.9,18.1]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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":"Fill 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":[100,100],"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":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":12,"op":37,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"EXAMPLE ON","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[256.462,259.5,0],"ix":2},"a":{"a":0,"k":[-0.038,1.5,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":[[-9.1,0],[0,0],[0,-9.1],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[9,0],[0,0],[0,0],[0,0],[0,0],[0,-9.1]],"v":[[-1.6,-115.5],[-0.4,-115.5],[16,-99.1],[16,-89.5],[16,-89.5],[-18,-89.5],[-18,-99.1]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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":"Fill 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":[100,100],"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":"Transform"}],"nm":"Rectangle","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-13.1,0],[-2,12],[1.2,0],[20.1,0],[-0.2,-1.3]],"o":[[12.8,0],[0.2,-1.4],[-10.3,0],[-0.9,0],[1.9,12.2]],"v":[[0,118.5],[25.6,96.7],[22.3,92.5],[-21.4,92.5],[-25.7,96.3]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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":"Fill 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":[100,100],"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":"Transform"}],"nm":"Path","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,1.8],[0,0],[0,0],[36.4,8.6],[3.4,0],[5.9,-0.1],[0,-38.8],[0,0],[1.2,-1.3],[0,0],[0,0],[-11.3,0],[0,0],[8.1,7.9],[0,0]],"o":[[0,0],[0,0],[0,-38.6],[-6.7,-0.2],[-3,0],[-36.5,8.6],[0,0],[0,1.8],[0,0],[0,0],[-8,7.9],[0,0],[11.3,0],[0,0],[-1.1,-1.3]],"v":[[76.4,35.6],[76.4,-15.7],[76.4,-15.7],[14.3,-93.3],[-0.8,-93.5],[-14.2,-93.3],[-76.3,-15.7],[-76.3,35.6],[-78.1,40.4],[-92.8,57],[-92.8,57],[-81.5,78.5],[81.3,78.5],[92.7,57],[78.1,40.4]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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":"Fill 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":[100,100],"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":"Transform"}],"nm":"Combined-Shape","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":1,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Bell","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.4,"y":1},"o":{"x":0.3,"y":0},"t":0,"s":[261.217,231.2,0],"to":[0,0,0],"ti":[0,0,0]},{"t":12,"s":[255.967,270.7,0]}],"ix":2},"a":{"a":0,"k":[-0.033,-25.3,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.833,"y":0.833},"o":{"x":0.3,"y":0},"t":0,"s":[{"i":[[-11.475,14.325],[0,0],[0,0],[0,0],[0,0],[-7.5,8],[0,0],[0,0],[0,1.8],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[-11.2,0],[0,0],[0,0],[1.2,-1.3],[0,0],[0,0],[0,-11.6]],"v":[[-66.9,-60.7],[52.121,57.296],[54.778,59.953],[70.15,79.575],[-85.9,79.6],[-97.5,58.4],[-97.2,58.1],[-82.6,41.5],[-80.8,36.7],[-80.8,36.7],[-80.8,-14.6]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":4,"s":[{"i":[[-8.362,11.23],[0,0],[0,0],[3.762,-1.859],[8.142,-0.264],[0.62,12.856],[0.156,0.224],[1.314,3.134],[0.224,1.943],[0,0],[-0.582,7.711]],"o":[[0,0],[0,0],[0,0],[-19.321,9.547],[-26.964,1.125],[-0.005,-0.156],[-1.406,-14.295],[0.062,-2.373],[0,0],[-0.045,-4.946],[0,-7.124]],"v":[[-60.638,-49.681],[29.345,35.402],[55.841,63.676],[71.571,79.252],[-31.443,86.874],[-70.777,71.892],[-71,70.044],[-67.748,45.427],[-67.717,38.704],[-68.176,35.151],[-68.748,-3.168]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":6,"s":[{"i":[[-1.323,3.262],[0,0],[0,0],[6.275,-3.565],[14.005,-0.455],[0.542,0.356],[1.713,1.084],[2.26,5.391],[0.385,2.046],[0,0],[-1.001,13.264]],"o":[[0,0],[0,0],[0,0],[-4.624,2.627],[-40.891,-1.743],[0.542,-0.578],[-4.048,-2.564],[-0.758,-3.145],[0,0],[-0.077,-8.507],[0,-3.901]],"v":[[-57.609,-43.228],[22.617,23.841],[64.126,70.129],[61.225,80.722],[7.766,92.111],[-48.042,81.338],[-48.213,81.277],[-57.218,71.044],[-58.46,62.937],[-59.249,56.826],[-60.071,5.063]],"c":true}]},{"i":{"x":0.4,"y":1},"o":{"x":0.167,"y":0.167},"t":9,"s":[{"i":[[-0.309,0.762],[0,0],[0,0],[4.568,-5.804],[17.516,-1.439],[9.846,0.175],[5.202,1.836],[3.057,6.91],[0.521,1.988],[0,0],[-1.354,17]],"o":[[0,0],[0,0],[0,0],[-4.183,5.585],[-15.234,1.811],[-9.592,-0.227],[-5.947,-2.247],[-1.448,-3.573],[0,0],[-0.104,-10.903],[0,-0.911]],"v":[[-52.53,-28.79],[11.994,25.222],[67.477,74.382],[64.512,86.679],[25.234,99.127],[-10.469,99.989],[-34.862,95.528],[-48.272,82.948],[-50.586,74.249],[-51.653,66.417],[-52.765,18.153]],"c":true}]},{"t":12,"s":[{"i":[[0,0],[0,0],[0,0],[4.095,-6.22],[20.07,-1.559],[12.041,0.281],[8.085,2.941],[3.171,6.676],[0.562,1.97],[0,0],[-1.461,18.138]],"o":[[0,0],[0,0],[0,0],[-4.095,6.221],[-5.657,0.44],[-12.041,-0.281],[-6.457,-2.348],[-1.741,-3.665],[0,0],[-0.112,-11.633],[0,0]],"v":[[-46.983,-24.891],[12.039,25.393],[71.061,75.677],[67.888,87.728],[34.61,102.118],[5.478,102.841],[-27.296,98.494],[-42.796,86.574],[-45.437,77.695],[-46.589,69.338],[-47.789,22.141]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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":"Fill 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":[100,100],"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":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.3,"y":0},"t":0,"s":[{"i":[[6.6,6.5],[0,0],[0,1.8],[0,0],[35.493,9.136],[0,0],[9,0],[0,0],[0,-9.1],[0,0],[13.525,-18.6],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[-1.2,-1.3],[0,0],[0,-37.993],[0,0],[0,-9.1],[0,0],[-9.1,0],[0,0],[-16.061,4.493],[0,0],[0,0],[0,0],[0,0],[10.58,-1.555]],"v":[[88.55,58.6],[73.795,40.25],[71.995,35.45],[71.9,-14.6],[11.5,-91.763],[11.5,-98],[-4.9,-114.4],[-6.1,-114.4],[-22.5,-98],[-22.5,-91.12],[-68.65,-58.275],[-25.568,-8.753],[-24.761,-7.935],[67.699,79.699],[80.6,78.975]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":6,"s":[{"i":[[0.157,8.024],[0,0],[0,1.922],[0,0],[24.177,-0.77],[0,0],[9,0],[0,0],[5.709,0.272],[0,0],[-0.26,-17.504],[0,0],[0,0],[0,0],[-7.459,5.266]],"o":[[0,0],[-0.323,-1.991],[0,0],[1.096,-24.22],[0,0],[-9.573,2.118],[0,0],[-9.1,0],[0,0],[-6.6,1.142],[0,0],[0,0],[0,0],[0,0],[3.993,-2.819]],"v":[[71.843,56.59],[71.106,36.657],[70.583,30.009],[69.845,-21.987],[38.823,-52.309],[28.073,-50.661],[3.062,-46.678],[1.048,-46.86],[-26.209,-52.017],[-31.319,-53.002],[-58.24,-38.393],[-25.737,-8.451],[-22.131,-4.064],[59.51,80.628],[65.709,78.296]],"c":true}]},{"i":{"x":0.4,"y":1},"o":{"x":0.167,"y":0.167},"t":9,"s":[{"i":[[0.094,4.576],[0,0],[0,1.828],[0,0],[20.355,-2.421],[0,0],[9,0],[0,0],[6.507,0.351],[0,0],[2.835,-13.717],[0,0],[0,0],[0,0],[-1.742,1.23]],"o":[[0,0],[-0.014,-2.094],[0,0],[-0.871,-19.045],[0,0],[-6.547,0.878],[0,0],[-9.1,0],[0,0],[-3.266,-0.12],[0,0],[0,0],[0,0],[0,0],[1.239,-8.208]],"v":[[69.031,57.556],[68.76,39.315],[68.408,33.605],[67.871,-20.767],[25.645,-36.641],[23.135,-36.246],[-0.875,-35.181],[-1.998,-35.415],[-22.891,-36.675],[-31.934,-38.285],[-52.647,-28.157],[-24.554,-1.976],[-21.081,1.4],[63.583,76.467],[67.733,76.517]],"c":true}]},{"t":12,"s":[{"i":[[0.075,3.525],[0,0],[0,1.8],[0,0],[24.75,0.638],[0,0],[9,0],[0,0],[6.75,0.375],[0,0],[-2.6,-5.6],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0.08,-2.125],[0,0],[1.85,-14.4],[0,0],[-5.625,0.5],[0,0],[-9.1,0],[0,0],[-2.25,-0.505],[0,0],[0,0],[0,0],[0,0],[0.4,-9.85]],"v":[[70.925,57.85],[70.795,40.125],[70.495,34.7],[71.4,-15.1],[22.75,-23.388],[22.75,-23.375],[0.35,-23.525],[0.15,-23.775],[-17.5,-24.5],[-23.5,-24.995],[-47.4,-25.4],[-21.443,-0.003],[-18.011,3.065],[67.387,75.199],[70.912,75.975]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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":"Fill 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":[100,100],"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":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":1,"op":13,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"Bottom Bell","parent":5,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.39,"y":1},"o":{"x":0.3,"y":0},"t":0,"s":[0,137.7,0],"to":[0,0,0],"ti":[0,0,0]},{"t":12,"s":[12,91.7,0]}],"ix":2},"a":{"a":0,"k":[0,137.7,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.833,"y":0.833},"o":{"x":0.3,"y":0},"t":0,"s":[{"i":[[-13.1,0],[-2,12],[1.2,0],[20.1,0],[-0.2,-1.3]],"o":[[12.8,0],[0.2,-1.4],[-10.3,0],[-0.9,0],[1.9,12.1]],"v":[[-5.125,120.225],[20.6,99.05],[17.3,94.85],[-26.4,94.85],[-30.7,98.65]],"c":true}]},{"i":{"x":0.39,"y":1},"o":{"x":0.167,"y":0.167},"t":4,"s":[{"i":[[-19.183,0.659],[-2.93,11.67],[1.758,0],[29.45,0],[-0.296,-1.263]],"o":[[23.128,-0.795],[2.186,-3.703],[-15.091,0],[-1.319,0],[2.784,11.767]],"v":[[-2.487,124.469],[35.205,101.877],[29.62,99.793],[-34.409,99.793],[-40.709,103.488]],"c":true}]},{"t":12,"s":[{"i":[[-28.151,0],[-4.298,11.184],[2.579,0],[43.194,0],[-0.438,-1.209]],"o":[[27.506,0],[0.43,-1.305],[-22.134,0],[-1.934,0],[4.083,11.277]],"v":[[-0.461,137.5],[54.82,117.764],[47.729,113.85],[-46.18,113.85],[-55.42,117.392]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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":"Fill 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":[100,100],"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":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":1,"op":10,"st":0,"bm":0}]},{"id":"comp_10","layers":[{"ddd":0,"ind":1,"ty":0,"nm":"Pre-comp 1","refId":"comp_11","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[30,30,0],"ix":2},"a":{"a":0,"k":[256,256,0],"ix":1},"s":{"a":0,"k":[13.6,13.6,100],"ix":6}},"ao":0,"w":512,"h":512,"ip":0,"op":35,"st":0,"bm":0}]},{"id":"comp_11","layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Line","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.67,"y":1},"o":{"x":0.33,"y":0},"t":6,"s":[1.687,-2.898,0],"to":[0,0,0],"ti":[0,0,0]},{"t":14,"s":[3.687,-6.898,0]}],"ix":2},"a":{"a":0,"k":[3.6,-7,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.3,"y":1},"o":{"x":0.4,"y":0},"t":4,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-101.6,-111.5],[108.8,97.5]],"c":false}]},{"i":{"x":0.3,"y":1},"o":{"x":0.3,"y":0},"t":14,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-109.713,-119.559],[116.913,105.559]],"c":false}]},{"t":27,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-101.6,-111.5],[108.8,97.5]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.3],"y":[1]},"o":{"x":[0.2],"y":[0]},"t":6,"s":[28]},{"t":14,"s":[0]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.3],"y":[1]},"o":{"x":[0.2],"y":[0]},"t":6,"s":[71]},{"t":14,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":22,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 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":[100,100],"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":"Transform"}],"nm":"Path-10","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":6,"op":35,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Head","parent":3,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-3.6,-33.55,0],"ix":2},"a":{"a":0,"k":[-3.6,-33.55,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":[[-25.4,0],[0,-25.4],[0,0],[6.9,-8],[0,0],[0,0],[0,0]],"o":[[25.4,0],[0,0],[0,11.4],[0,0],[0,0],[0,0],[0,-25.4]],"v":[[-3.6,-117.5],[42.4,-71.5],[42.4,4.5],[31.3,34.4],[-20.33,-17.23],[-49.6,-46.5],[-49.6,-71.5]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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":"Fill 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":[100,100],"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":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[5.9,0],[0,25.4],[0,0]],"o":[[0,0],[0,0],[0,0],[-5.2,2],[-25.4,0],[0,0],[0,0]],"v":[[-49.6,-15.4],[-40.702,-6.502],[-38.75,-4.55],[13.1,47.3],[-3.6,50.4],[-49.6,4.4],[-49.6,-15.4]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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":"Fill 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":[100,100],"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":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":13,"op":35,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Arc L","parent":5,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.31,"y":0},"t":14,"s":[-3.75,69.094,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.3,"y":0},"t":26,"s":[-3.75,88.094,0],"to":[0,0,0],"ti":[0,0,0]},{"t":34,"s":[-3.75,84.5,0]}],"ix":2},"a":{"a":0,"k":[-3.75,84.5,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.3,"y":1},"o":{"x":0.29,"y":0},"t":18,"s":[{"i":[[0,0],[13.1,0],[0,42]],"o":[[-11,5.1],[-45.3,0],[0,0]],"v":[[33,76.5],[-3.5,84.5],[-85.5,6.5]],"c":false}]},{"t":28,"s":[{"i":[[0,0],[13.1,0],[0,42]],"o":[[-11,5.1],[-45.3,0],[0,0]],"v":[[33,76.5],[-3.5,84.5],[-85.5,6.5]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.723],"y":[0.572]},"o":{"x":[0.4],"y":[0]},"t":5,"s":[15]},{"i":{"x":[0.695],"y":[0.627]},"o":{"x":[0.368],"y":[0.45]},"t":7,"s":[29]},{"i":{"x":[0.323],"y":[1]},"o":{"x":[0.222],"y":[0.388]},"t":8,"s":[37.868]},{"t":18,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.723],"y":[1.14]},"o":{"x":[0.4],"y":[0]},"t":5,"s":[12]},{"i":{"x":[0.695],"y":[0.945]},"o":{"x":[0.368],"y":[0.061]},"t":7,"s":[17.559]},{"i":{"x":[0.652],"y":[0.857]},"o":{"x":[0.357],"y":[0.122]},"t":8,"s":[9]},{"i":{"x":[0.426],"y":[1]},"o":{"x":[0.183],"y":[-2.717]},"t":10,"s":[0]},{"t":18,"s":[0]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":22,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 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":[100,100],"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":"Transform"}],"nm":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":7,"op":35,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Arc R","parent":3,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[66.05,51.45,0],"ix":2},"a":{"a":0,"k":[66.05,51.45,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.3,"y":1},"o":{"x":0.29,"y":0},"t":18,"s":[{"i":[[0,0],[8.7,-12]],"o":[[0,15.6],[0,0]],"v":[[78.4,8.4],[64.7,50.5]],"c":false}]},{"t":28,"s":[{"i":[[0,0],[8.7,-12]],"o":[[0,15.6],[0,0]],"v":[[78.4,8.4],[64.7,50.5]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.3],"y":[1]},"o":{"x":[0.4],"y":[0]},"t":8,"s":[99]},{"t":18,"s":[0]}],"ix":1},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":22,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 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":[100,100],"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":"Transform"}],"nm":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":11,"op":35,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Leg","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[252.45,380.6,0],"ix":2},"a":{"a":0,"k":[-3.55,124.6,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.3,"y":1},"o":{"x":0.31,"y":0},"t":14,"s":[{"i":[[-6.3,0],[0,0],[0,-6.3],[0,0],[6.3,0],[0,0],[0,6.3],[0,0]],"o":[[0,0],[6.3,0],[0,0],[0,6.3],[0,0],[-6.3,0],[0,0],[0,-6.3]],"v":[[-5.15,64],[-1.85,64],[9.55,75.4],[9.5,113.2],[-1.9,124.6],[-5.2,124.6],[-16.6,113.2],[-16.55,75.4]],"c":true}]},{"i":{"x":0.5,"y":1},"o":{"x":0.3,"y":0},"t":26,"s":[{"i":[[-6.3,0],[0,0],[0,-2.846],[0,0],[6.3,0],[0,0],[0,6.3],[0,0]],"o":[[0,0],[6.3,0],[0,0],[0,6.3],[0,0],[-6.3,0],[0,0],[0,-2.846]],"v":[[-5.2,84.75],[-1.9,84.75],[9.5,89.9],[9.5,113.2],[-1.9,124.6],[-5.2,124.6],[-16.6,113.2],[-16.6,89.9]],"c":true}]},{"t":34,"s":[{"i":[[-6.3,0],[0,0],[0,-6.3],[0,0],[6.3,0],[0,0],[0,6.3],[0,0]],"o":[[0,0],[6.3,0],[0,0],[0,6.3],[0,0],[-6.3,0],[0,0],[0,-6.3]],"v":[[-5.2,78.5],[-1.9,78.5],[9.5,89.9],[9.5,113.2],[-1.9,124.6],[-5.2,124.6],[-16.6,113.2],[-16.6,89.9]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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":"Fill 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":[100,100],"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":"Transform"}],"nm":"Rectangle","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":10,"op":35,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"Bell","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.3,"y":0},"t":0,"s":[259.064,256.028,0],"to":[0,0,0],"ti":[0,0,0]},{"t":14,"s":[253.564,241.028,0]}],"ix":2},"a":{"a":0,"k":[-1.88,-0.927,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.833,"y":0.833},"o":{"x":0.5,"y":0},"t":0,"s":[{"i":[[-16.045,13.245],[0,0],[0,0],[0,0],[0,0],[-7.5,8],[0,0],[0,0],[0,1.8],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[-11.2,0],[0,0],[0,0],[1.2,-1.3],[0,0],[0,0],[0,-11.6]],"v":[[-61.4,-66.7],[44.371,41.071],[47.028,43.728],[73.9,79.64],[-85.9,79.6],[-97.5,58.4],[-97.2,58.1],[-82.6,41.5],[-80.8,36.7],[-80.8,36.7],[-80.8,-14.6]],"c":true}]},{"i":{"x":0.3,"y":1},"o":{"x":0.167,"y":0.167},"t":6,"s":[{"i":[[-4.254,9.376],[0,0],[0,0],[0,0],[6.23,1.972],[-1.412,7.008],[0,0],[0,0],[0,1.507],[0.298,2.165],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[-9.876,-1.385],[0,0],[0,0],[0.238,-1.694],[0,0],[-0.85,-6.164],[0.044,-6.601]],"v":[[-57.532,-45.068],[21.114,38.993],[23.375,41.218],[43.195,62.771],[-50.105,63.383],[-67.187,44.549],[-67.717,44.298],[-64.273,32.174],[-64.047,26.764],[-64.187,23.769],[-63.385,-11.302]],"c":true}]},{"t":14,"s":[{"i":[[-0.557,1.753],[0,0],[0,0],[0,0],[12.242,3.875],[4.463,6.051],[0,0],[0,0],[0,1.225],[0.586,4.254],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[-8.598,-2.722],[0,0],[0,0],[-0.69,-2.075],[0,0],[-1.67,-12.113],[0.086,-1.777]],"v":[[-48.888,-16.333],[2.602,35.024],[4.48,36.832],[14.055,46.983],[-15.561,47.733],[-39.407,31.182],[-39.266,30.978],[-44.13,22.682],[-46.406,17.666],[-48.156,11.291],[-49.281,-8.366]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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":"Fill 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":[100,100],"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":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.5,"y":0},"t":0,"s":[{"i":[[6.6,6.5],[0,0],[0,1.8],[0,0],[35.493,9.136],[0,0],[9,0],[0,0],[0,-9.1],[0,0],[12.143,-14.492],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[-1.2,-1.3],[0,0],[0,-37.993],[0,0],[0,-9.1],[0,0],[-9.1,0],[0,0],[-16.061,4.493],[0,0],[0,0],[0,0],[0,0],[10.58,-1.555]],"v":[[88.55,58.6],[73.795,40.25],[71.995,35.45],[71.9,-14.6],[11.5,-91.763],[11.5,-98],[-4.9,-114.4],[-6.1,-114.4],[-22.5,-98],[-22.5,-91.12],[-63.338,-64.963],[-22.443,-19.878],[-21.636,-19.06],[71.262,79.637],[80.6,78.975]],"c":true}]},{"i":{"x":0.3,"y":1},"o":{"x":0.167,"y":0.167},"t":6,"s":[{"i":[[5.809,9.358],[-0.56,1.677],[0,1.8],[0,0],[30.628,11.768],[0,0],[10.446,-0.187],[0,0],[1.845,-5.655],[0,0],[2.161,-13.67],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[-0.175,-1.881],[0,0],[0.112,-46.447],[0,0],[-2.26,-5.718],[0,0],[-11.578,-0.155],[0,0],[-27.545,10.149],[0,0],[0,0],[0,0],[0,0],[6.72,-2.318]],"v":[[62.53,35.324],[56.968,24.754],[57.164,18.109],[57.299,-17.272],[16.524,-101.941],[16.174,-105.284],[-4.079,-116.658],[-4.828,-116.69],[-26.225,-104.298],[-27.116,-100.217],[-56.572,-52.898],[-23.746,-18.806],[-23.05,-17.988],[50.468,61.936],[58.016,59.693]],"c":true}]},{"t":14,"s":[{"i":[[-2.182,4.07],[-1.1,3.295],[0,1.8],[0,0],[25.933,14.308],[0,0],[11.842,-0.367],[0,0],[3.626,-2.33],[0,0],[-2.875,-21.055],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0.815,-2.442],[0,0],[0.221,-54.605],[0,0],[-4.442,-2.455],[0,0],[-13.97,-0.305],[0,0],[-19.874,13.29],[0,0],[0,0],[0,0],[0,0],[2.995,-3.055]],"v":[[36.363,27.6],[40.655,19.625],[42.779,11.2],[43.21,-19.85],[21.372,-111.763],[20.684,-112.312],[-3.286,-118.838],[-3.6,-118.9],[-29.821,-110.375],[-31.571,-108.995],[-48.57,-47.15],[-22.056,-18.753],[-21.467,-17.935],[30.402,32.574],[31.31,33.225]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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":"Fill 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":[100,100],"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":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":13,"st":0,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":"Bottom Bell","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[255.445,394.655,0],"ix":2},"a":{"a":0,"k":[0,137.7,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.3,"y":1},"o":{"x":0.4,"y":0},"t":0,"s":[{"i":[[-13.1,0],[-2,12],[1.2,0],[20.1,0],[-0.2,-1.3]],"o":[[12.8,0],[0.2,-1.4],[-10.3,0],[-0.9,0],[1.9,12.1]],"v":[[1,119.605],[26.6,97.805],[23.3,93.605],[-20.4,93.605],[-24.7,97.405]],"c":true}]},{"t":10,"s":[{"i":[[-8.162,0],[0.625,15.53],[0.51,0.546],[4.907,-5.5],[0.14,-2.002]],"o":[[7.975,0],[0.125,-2.157],[-6.069,-6.5],[-0.373,0.418],[-1.164,16.646]],"v":[[-3.269,123.85],[10.181,105.515],[9.694,64.545],[-15.782,65.045],[-16.531,104.149]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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":"Fill 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":[100,100],"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":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":10,"st":0,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":"EXAMPLE ON","parent":6,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-4.483,2.545,0],"ix":2},"a":{"a":0,"k":[-0.038,1.5,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":[[-9.1,0],[0,0],[0,-9.1],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[9,0],[0,0],[0,0],[0,0],[0,0],[0,-9.1]],"v":[[-1.6,-115.5],[-0.4,-115.5],[16,-99.1],[16,-89.5],[16,-89.5],[-18,-89.5],[-18,-99.1]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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":"Fill 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":[100,100],"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":"Transform"}],"nm":"Rectangle","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-13.1,0],[-2,12],[1.2,0],[20.1,0],[-0.2,-1.3]],"o":[[12.8,0],[0.2,-1.4],[-10.3,0],[-0.9,0],[1.9,12.2]],"v":[[0,118.5],[25.6,96.7],[22.3,92.5],[-21.4,92.5],[-25.7,96.3]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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":"Fill 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":[100,100],"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":"Transform"}],"nm":"Path","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,1.8],[0,0],[0,0],[36.4,8.6],[3.4,0],[5.9,-0.1],[0,-38.8],[0,0],[1.2,-1.3],[0,0],[0,0],[-11.3,0],[0,0],[8.1,7.9],[0,0]],"o":[[0,0],[0,0],[0,-38.6],[-6.7,-0.2],[-3,0],[-36.5,8.6],[0,0],[0,1.8],[0,0],[0,0],[-8,7.9],[0,0],[11.3,0],[0,0],[-1.1,-1.3]],"v":[[76.4,35.6],[76.4,-15.7],[76.4,-15.7],[14.3,-93.3],[-0.8,-93.5],[-14.2,-93.3],[-76.3,-15.7],[-76.3,35.6],[-78.1,40.4],[-92.8,57],[-92.8,57],[-81.5,78.5],[81.3,78.5],[92.7,57],[78.1,40.4]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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":"Fill 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":[100,100],"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":"Transform"}],"nm":"Combined-Shape","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":2,"st":0,"bm":0}]},{"id":"comp_12","layers":[{"ddd":0,"ind":1,"ty":0,"nm":"Pre-comp 1","refId":"comp_13","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[30,30,0],"ix":2},"a":{"a":0,"k":[256,256,0],"ix":1},"s":{"a":0,"k":[13.6,13.6,100],"ix":6}},"ao":0,"w":512,"h":512,"ip":0,"op":29,"st":0,"bm":0}]},{"id":"comp_13","layers":[{"ddd":0,"ind":1,"ty":4,"nm":"EXAMPLE ON","parent":4,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-3.983,2.545,0],"ix":2},"a":{"a":0,"k":[-0.038,1.5,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":[[-9.1,0],[0,0],[0,-9.1],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[9,0],[0,0],[0,0],[0,0],[0,0],[0,-9.1]],"v":[[-1.6,-115.5],[-0.4,-115.5],[16,-99.1],[16,-89.5],[16,-89.5],[-18,-89.5],[-18,-99.1]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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":"Fill 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":[100,100],"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":"Transform"}],"nm":"Rectangle","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-13.1,0],[-2,12],[1.2,0],[20.1,0],[-0.2,-1.3]],"o":[[12.8,0],[0.2,-1.4],[-10.3,0],[-0.9,0],[1.9,12.2]],"v":[[0,118.5],[25.6,96.7],[22.3,92.5],[-21.4,92.5],[-25.7,96.3]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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":"Fill 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":[100,100],"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":"Transform"}],"nm":"Path","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,1.8],[0,0],[0,0],[36.4,8.6],[3.4,0],[5.9,-0.1],[0,-38.8],[0,0],[1.2,-1.3],[0,0],[0,0],[-11.3,0],[0,0],[8.1,7.9],[0,0]],"o":[[0,0],[0,0],[0,-38.6],[-6.7,-0.2],[-3,0],[-36.5,8.6],[0,0],[0,1.8],[0,0],[0,0],[-8,7.9],[0,0],[11.3,0],[0,0],[-1.1,-1.3]],"v":[[76.4,35.6],[76.4,-15.7],[76.4,-15.7],[14.3,-93.3],[-0.8,-93.5],[-14.2,-93.3],[-76.3,-15.7],[-76.3,35.6],[-78.1,40.4],[-92.8,57],[-92.8,57],[-81.5,78.5],[81.3,78.5],[92.7,57],[78.1,40.4]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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":"Fill 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":[100,100],"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":"Transform"}],"nm":"Combined-Shape","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":3,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Line Bell","parent":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.3,"y":1},"o":{"x":0.4,"y":0},"t":0,"s":[-11.47,-25.859,0],"to":[0,0,0],"ti":[0,0,0]},{"t":17,"s":[5.03,-9.859,0]}],"ix":2},"a":{"a":0,"k":[-0.05,-2.95,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":[[-98.152,-101.129],[98.052,95.229]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.3],"y":[1]},"o":{"x":[0.4],"y":[0]},"t":0,"s":[0]},{"t":17,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":22,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 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":[100,100],"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":"Transform"}],"nm":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":29,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Bottom Bell","parent":4,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,137.7,0],"ix":2},"a":{"a":0,"k":[0,137.7,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":[[-13.1,0],[-2,12],[1.2,0],[20.1,0],[-0.2,-1.3]],"o":[[12.8,0],[0.2,-1.4],[-10.3,0],[-0.9,0],[1.9,12.1]],"v":[[-4.5,119.6],[21.1,97.8],[17.8,93.6],[-25.9,93.6],[-30.2,97.4]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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":"Fill 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":[100,100],"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":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":2,"op":29,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Bell","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[260.562,232.132,0],"ix":2},"a":{"a":0,"k":[0.12,-24.927,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.72,0.72,0.72],"y":[1,1,1]},"o":{"x":[0.28,0.28,0.28],"y":[0,0,0]},"t":1,"s":[100,100,100]},{"i":{"x":[0.72,0.72,0.72],"y":[1,1,1]},"o":{"x":[0.28,0.28,0.28],"y":[0,0,0]},"t":12,"s":[93,93,100]},{"i":{"x":[0.72,0.72,0.72],"y":[1,1,1]},"o":{"x":[0.28,0.28,0.28],"y":[0,0,0]},"t":21,"s":[101,101,100]},{"t":28,"s":[100,100,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":4,"s":[{"i":[[-7.545,13.495],[0,0],[0,0],[0,0],[0,0],[-7.5,8],[0,0],[0,0],[0,1.8],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[-11.2,0],[0,0],[0,0],[1.2,-1.3],[0,0],[0,0],[0,-11.6]],"v":[[-68.9,-57.2],[46.687,58.223],[49.57,61.102],[68.15,79.655],[-85.9,79.6],[-97.5,58.4],[-97.2,58.1],[-82.6,41.5],[-80.8,36.7],[-80.8,36.7],[-80.8,-14.6]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":5,"s":[{"i":[[-5.872,11.598],[-1.72,-1.503],[-1.818,10.555],[0,0],[0,0],[-7.5,8],[0,0],[0,0],[0,1.8],[0,0],[0,0]],"o":[[0,0],[9.034,7.892],[6.962,6.261],[0,0],[-11.2,0],[0,0],[0,0],[1.2,-1.3],[0,0],[0,0],[0,-11.6]],"v":[[-73.235,-48.204],[-65.308,-41.244],[-37.924,-49.058],[74.965,79.428],[-85.9,79.6],[-97.5,58.4],[-97.2,58.1],[-82.6,41.5],[-80.8,36.7],[-80.8,36.7],[-80.8,-14.6]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":6,"s":[{"i":[[-5.872,11.598],[-1.681,-1.546],[-1.818,10.555],[0,0],[0,0],[-7.5,8],[0,0],[0,0],[0,1.8],[0,0],[0,0]],"o":[[0,0],[11.181,10.285],[6.962,6.261],[0,0],[-11.2,0],[0,0],[0,0],[1.2,-1.3],[0,0],[0,0],[0,-11.6]],"v":[[-73.235,-48.204],[-35.586,-11.586],[-5.934,-19.545],[74.965,79.428],[-85.9,79.6],[-97.5,58.4],[-97.2,58.1],[-82.6,41.5],[-80.8,36.7],[-80.8,36.7],[-80.8,-14.6]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":7,"s":[{"i":[[-5.036,10.649],[-2.391,-2.446],[-2.124,14.747],[0,0],[0,0],[-7.5,8],[0,0],[0,0],[0,1.8],[0,0],[0,0]],"o":[[0,0],[6.637,6.293],[10.443,9.392],[0,0],[-11.2,0],[0,0],[0,0],[1.2,-1.3],[0,0],[0,0],[0,-11.6]],"v":[[-73.817,-47.452],[-6.583,20.071],[22.942,7.689],[78.372,79.315],[-85.9,79.6],[-97.5,58.4],[-97.2,58.1],[-82.6,41.5],[-80.8,36.7],[-80.8,36.7],[-80.8,-14.6]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":8,"s":[{"i":[[-4.2,9.7],[-3.062,-3.39],[-2.43,18.938],[0,0],[0,0],[-7.5,8],[0,0],[0,0],[0,1.8],[0,0],[0,0]],"o":[[0,0],[4.241,4.695],[13.924,12.522],[0,0],[-11.2,0],[0,0],[0,0],[1.2,-1.3],[0,0],[0,0],[0,-11.6]],"v":[[-74.4,-46.7],[19.467,48.272],[48.098,35.753],[81.779,79.202],[-85.9,79.6],[-97.5,58.4],[-97.2,58.1],[-82.6,41.5],[-80.8,36.7],[-80.8,36.7],[-80.8,-14.6]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":9,"s":[{"i":[[-4.2,9.7],[-2.2,-2.106],[-1.62,12.626],[0,0],[0,0],[-7.5,8],[0,0],[0,0],[0,1.8],[0,0],[0,0]],"o":[[0,0],[9.752,9.336],[9.282,8.348],[0,0],[-11.2,0],[0,0],[0,0],[1.2,-1.3],[0,0],[0,0],[0,-11.6]],"v":[[-74.4,-46.7],[34.992,63.228],[69.333,53.948],[81.641,79.138],[-85.9,79.6],[-97.5,58.4],[-97.2,58.1],[-82.6,41.5],[-80.8,36.7],[-80.8,36.7],[-80.8,-14.6]],"c":true}]},{"t":10,"s":[{"i":[[-4.2,9.7],[0,0],[0,0],[0,0],[0,0],[-7.5,8],[0,0],[0,0],[0,1.8],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[-11.2,0],[0,0],[0,0],[1.2,-1.3],[0,0],[0,0],[0,-11.6]],"v":[[-74.4,-46.7],[32.121,59.821],[34.778,62.478],[51.9,79.6],[-85.9,79.6],[-97.5,58.4],[-97.2,58.1],[-82.6,41.5],[-80.8,36.7],[-80.8,36.7],[-80.8,-14.6]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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":"Fill 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":[100,100],"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":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":4,"s":[{"i":[[6.6,6.5],[0,0],[0,1.8],[0,0],[35.493,9.136],[0,0],[9,0],[0,0],[0,-9.1],[0,0],[9.346,-16.6],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[-1.2,-1.3],[0,0],[0,-37.993],[0,0],[0,-9.1],[0,0],[-9.1,0],[0,0],[-16.061,4.493],[0,0],[0,0],[0,0],[0,0],[8,-1.6]],"v":[[89.05,58.35],[73.795,39.75],[71.995,34.95],[71.9,-14.6],[11.5,-91.763],[11.5,-98],[-4.9,-114.4],[-6.1,-114.4],[-22.5,-98],[-22.5,-91.12],[-69.215,-56.816],[-28.207,-15.716],[-27.434,-14.941],[66.835,79.54],[81.6,79.1]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":5,"s":[{"i":[[6.6,6.5],[0,0],[0,1.8],[0,0],[35.493,9.136],[0,0],[9,0],[0,0],[0,-9.1],[0,0],[2.875,-7.688],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[-1.2,-1.3],[0,0],[0,-37.993],[0,0],[0,-9.1],[0,0],[-9.1,0],[0,0],[-16.061,4.493],[0,0],[0,0],[0,0],[0,0],[8,-1.6]],"v":[[89.05,58.35],[73.795,39.75],[71.995,34.95],[71.9,-14.6],[11.5,-91.763],[11.5,-98],[-4.9,-114.4],[-6.1,-114.4],[-22.5,-98],[-22.5,-91.12],[-55.392,-66.381],[-37.754,-49.074],[-39.423,-40.52],[73.962,79.404],[81.6,79.1]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":6,"s":[{"i":[[6.6,6.5],[0,0],[0,1.8],[0,0],[35.493,9.136],[0,0],[9,0],[0,0],[0,-9.1],[0,0],[2.875,-7.688],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[-1.2,-1.3],[0,0],[0,-37.993],[0,0],[0,-9.1],[0,0],[-9.1,0],[0,0],[-16.061,4.493],[0,0],[0,0],[0,0],[0,0],[8,-1.6]],"v":[[89.05,58.35],[73.795,39.75],[71.995,34.95],[71.9,-14.6],[11.5,-91.763],[11.5,-98],[-4.9,-114.4],[-6.1,-114.4],[-22.5,-98],[-22.5,-91.12],[-55.392,-66.381],[-4.288,-18.099],[-3.284,-9.024],[73.962,79.404],[81.6,79.1]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":7,"s":[{"i":[[6.6,6.5],[0,0],[0,1.8],[0,0],[35.493,9.136],[0,0],[9,0],[0,0],[0,-9.1],[0,0],[2.047,-7.688],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[-1.2,-1.3],[0,0],[0,-37.993],[0,0],[0,-9.1],[0,0],[-9.1,0],[0,0],[-16.061,4.493],[0,0],[0,0],[0,0],[0,0],[8,-1.6]],"v":[[89.05,58.35],[73.795,39.75],[71.995,34.95],[71.9,-14.6],[11.5,-91.763],[11.5,-98],[-4.9,-114.4],[-6.1,-114.4],[-22.5,-98],[-22.5,-91.12],[-56.464,-65.251],[-10.415,-18.43],[4.841,-14.706],[77.525,79.337],[81.6,79.1]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":8,"s":[{"i":[[6.6,6.5],[0,0],[0,1.8],[0,0],[35.493,9.136],[0,0],[9,0],[0,0],[0,-9.1],[0,0],[1.218,-7.689],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[-1.2,-1.3],[0,0],[0,-37.993],[0,0],[0,-9.1],[0,0],[-9.1,0],[0,0],[-16.061,4.493],[0,0],[0,0],[0,0],[0,0],[8,-1.6]],"v":[[89.05,58.35],[73.795,39.75],[71.995,34.95],[71.9,-14.6],[11.5,-91.763],[11.5,-98],[-4.9,-114.4],[-6.1,-114.4],[-22.5,-98],[-22.5,-91.12],[-57.536,-64.121],[-15.752,-20.901],[36.667,21.906],[81.088,79.269],[81.6,79.1]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":9,"s":[{"i":[[6.6,6.5],[0,0],[0,1.8],[0,0],[35.493,9.136],[0,0],[9,0],[0,0],[0,-9.1],[0,0],[4.182,-9.084],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[-1.2,-1.3],[0,0],[0,-37.993],[0,0],[0,-9.1],[0,0],[-9.1,0],[0,0],[-16.061,4.493],[0,0],[0,0],[0,0],[0,0],[8.86,-1.585]],"v":[[88.883,58.433],[73.795,39.917],[71.995,35.117],[71.9,-14.6],[11.5,-91.763],[11.5,-98],[-4.9,-114.4],[-6.1,-114.4],[-22.5,-98],[-22.5,-91.12],[-59.157,-64.714],[-11.573,-25.425],[-12.451,-25.128],[71.59,57.475],[81.267,79.058]],"c":true}]},{"t":10,"s":[{"i":[[6.6,6.5],[0,0],[0,1.8],[0,0],[35.493,9.136],[0,0],[9,0],[0,0],[0,-9.1],[0,0],[10.111,-11.873],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[-1.2,-1.3],[0,0],[0,-37.993],[0,0],[0,-9.1],[0,0],[-9.1,0],[0,0],[-16.061,4.493],[0,0],[0,0],[0,0],[0,0],[10.58,-1.555]],"v":[[88.55,58.6],[73.795,40.25],[71.995,35.45],[71.9,-14.6],[11.5,-91.763],[11.5,-98],[-4.9,-114.4],[-6.1,-114.4],[-22.5,-98],[-22.5,-91.12],[-62.4,-65.9],[-19.568,-22.503],[-18.761,-21.685],[79.699,78.074],[80.6,78.975]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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":"Fill 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":[100,100],"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":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":2,"op":29,"st":0,"bm":0}]},{"id":"comp_14","layers":[{"ddd":0,"ind":1,"ty":0,"nm":"Pre-comp 2","refId":"comp_15","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":0,"s":[30,30,0],"to":[0,0,0],"ti":[0,0,0]},{"t":10,"s":[28.282,29.857,0]}],"ix":2},"a":{"a":0,"k":[256,256,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.7,0.7,0.7],"y":[1,1,1]},"o":{"x":[0.3,0.3,0.3],"y":[0,0,0]},"t":0,"s":[13.6,13.6,100]},{"t":10,"s":[13.5,13.5,100]}],"ix":6}},"ao":0,"w":512,"h":512,"ip":0,"op":37,"st":0,"bm":0}]},{"id":"comp_15","layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Head 2","parent":3,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.39,"y":1},"o":{"x":0.3,"y":0},"t":0,"s":[0.613,-101.537,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.39,"y":1},"o":{"x":0.3,"y":0},"t":12,"s":[11.613,-50.537,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.39,"y":1},"o":{"x":0.3,"y":0},"t":26,"s":[11.613,-86.537,0],"to":[0,0,0],"ti":[0,0,0]},{"t":36,"s":[11.113,-83.037,0]}],"ix":2},"a":{"a":0,"k":[11.613,-84.537,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.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":3,"s":[{"i":[[0,-17.328],[25.612,0],[0,17.328],[-25.612,0]],"o":[[0,17.328],[-25.612,0],[0,-17.328],[25.612,0]],"v":[[54.039,-62.942],[7.664,-31.567],[-38.711,-62.942],[7.664,-94.317]],"c":true}]},{"i":{"x":0.39,"y":1},"o":{"x":0.167,"y":0.167},"t":7,"s":[{"i":[[0,-23.543],[25.081,0],[0,23.543],[-25.081,0]],"o":[[0,23.543],[-25.081,0],[0,-23.543],[25.081,0]],"v":[[55.47,-76.028],[10.057,-33.4],[-35.356,-76.028],[10.057,-118.656]],"c":true}]},{"i":{"x":0.39,"y":1},"o":{"x":0.3,"y":0},"t":14,"s":[{"i":[[0,-24.735],[24.735,0],[0,24.735],[-24.735,0]],"o":[[0,24.735],[-24.735,0],[0,-24.735],[24.735,0]],"v":[[56.4,-84.537],[11.613,-39.75],[-33.174,-84.537],[11.613,-129.324]],"c":true}]},{"t":25,"s":[{"i":[[0,-26.924],[26.924,0],[0,26.924],[-26.924,0]],"o":[[0,26.924],[-26.924,0],[0,-26.924],[26.924,0]],"v":[[60.363,-84.537],[11.613,-35.787],[-37.137,-84.537],[11.613,-133.287]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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":"Fill 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":[100,100],"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":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":2,"op":37,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Hands 2","parent":3,"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.05,"y":0},"t":9,"s":[11.336,13.774,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.4,"y":1},"o":{"x":0.31,"y":0},"t":12,"s":[11.336,18.274,0],"to":[0,0,0],"ti":[0,0,0]},{"t":25,"s":[11.336,11.774,0]}],"ix":2},"a":{"a":0,"k":[11.336,10.774,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.833,"y":0.833},"o":{"x":0.3,"y":0},"t":7,"s":[{"i":[[0,0],[0.875,44.36],[0.012,11.61]],"o":[[0,0],[-0.348,-17.658],[-0.016,-14.955]],"v":[[-33.583,109.659],[-40.875,50.64],[-45.012,-17.11]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":9,"s":[{"i":[[0,0],[-7.48,30.25],[-7.964,9.648]],"o":[[0,0],[2.73,-11.373],[7.228,-11.196]],"v":[[-57.163,97.688],[-57.996,36.651],[-42.036,0.102]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":10,"s":[{"i":[[0,0],[-14.611,30.379],[-23.925,1.795]],"o":[[0,0],[7.389,-12.121],[13.171,-8.112]],"v":[[-77.526,99.758],[-81.889,29.906],[-41.075,4.99]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":11,"s":[{"i":[[0,0],[-27.718,19.589],[-10.136,-1.028]],"o":[[0,0],[21.782,-16.411],[19.114,-5.028]],"v":[[-107.888,77.829],[-93.782,16.161],[-42.114,8.877]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":13,"s":[{"i":[[0,0],[-27.325,4.127],[-14.975,-3.949]],"o":[[0,0],[20.334,-3.734],[35.444,0.066]],"v":[[-136.225,32.858],[-90.834,7.893],[-38.525,10.472]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":14,"s":[{"i":[[0,0],[-27.129,-3.603],[-14.269,-4.157]],"o":[[0,0],[19.61,2.605],[43.609,2.613]],"v":[[-147.393,-5.627],[-89.61,-2.992],[-38.231,9.77]],"c":false}]},{"i":{"x":0.3,"y":1},"o":{"x":0.167,"y":0.167},"t":18,"s":[{"i":[[0,0],[-38.825,-22.336],[-19.04,-2.789]],"o":[[0,0],[14.675,9.164],[62.76,8.586]],"v":[[-144.801,-73.381],[-91.175,-7.197],[-37.96,10.757]],"c":false}]},{"t":28,"s":[{"i":[[0,0],[-46.458,-35.583],[-19.738,-3.567]],"o":[[0,0],[8.352,6.397],[79.039,14.286]],"v":[[-142.333,-130.341],[-88.062,-16.235],[-40.512,7.89]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":22,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 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":[100,100],"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":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.3,"y":0},"t":7,"s":[{"i":[[0,0],[0.583,25.777],[4.5,19.11]],"o":[[0,0],[-0.238,-10.518],[-18.41,-78.181]],"v":[[63,101.64],[63.167,54.973],[56,-16.61]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":9,"s":[{"i":[[0,0],[2.455,25.149],[6.868,13.265]],"o":[[0,0],[-0.66,-9.745],[-29.265,-65.813]],"v":[[68.548,94.42],[70.368,48.723],[56.521,2.29]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":11,"s":[{"i":[[-11.495,10.995],[15.537,39.692],[11.504,1.535]],"o":[[14.034,-13.424],[-3.378,-8.551],[-47.075,-45.52]],"v":[[79.466,115.674],[91.463,38.058],[62.496,12.174]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":13,"s":[{"i":[[-14.54,10.097],[25.244,37.544],[12.466,1.965]],"o":[[15.872,-11.198],[-4.741,-7.952],[-56.007,-35.342]],"v":[[88.294,117.357],[93.089,33.781],[63.701,13.504]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":14,"s":[{"i":[[-16.063,9.648],[30.097,36.471],[11.946,0.457]],"o":[[16.791,-10.086],[-5.422,-7.653],[-60.473,-30.253]],"v":[[92.709,118.199],[93.903,31.642],[61.554,13.67]],"c":false}]},{"i":{"x":0.3,"y":1},"o":{"x":0.167,"y":0.167},"t":18,"s":[{"i":[[-5.509,3.309],[41.86,36.38],[11.653,0.923]],"o":[[5.759,-3.459],[-7.371,-6.796],[-73.243,-15.702]],"v":[[124.602,127.851],[94.405,26.111],[62.332,13.5]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":25,"s":[{"i":[[0,0],[48,36.333],[11.5,1.167]],"o":[[0,0],[-8.388,-6.35],[-79.91,-8.107]],"v":[[141.25,132.89],[94.667,23.223],[63.5,11.89]],"c":false}]},{"t":28,"s":[{"i":[[0,0],[48,36.333],[11.5,1.167]],"o":[[0,0],[-8.388,-6.35],[-79.91,-8.107]],"v":[[140,128.89],[94.667,23.848],[63.5,11.89]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":22,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 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":[100,100],"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":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":12,"op":37,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Body 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.4,"y":1},"o":{"x":0.3,"y":0},"t":12,"s":[266.676,398.378,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.4,"y":1},"o":{"x":0.3,"y":0},"t":23,"s":[266.676,387.378,0],"to":[0,0,0],"ti":[0,0,0]},{"t":34,"s":[266.676,392.378,0]}],"ix":2},"a":{"a":0,"k":[10.676,136.378,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.69,"y":1},"o":{"x":0.31,"y":0},"t":12,"s":[{"i":[[-12.288,0.098],[-12.498,-0.987],[0,0],[3.339,-1.222],[4.897,-9.213],[22.369,0],[7.456,13.82],[-1.892,54.808]],"o":[[8.847,-0.071],[35.724,2.82],[24.706,0],[0,0],[-7.456,13.82],[-22.369,0],[-4.897,-9.213],[-12.353,-3.008]],"v":[[-46.407,7.071],[-18.334,9.585],[61.794,9.679],[70.141,28.952],[68.805,121.176],[13.161,136.5],[-42.484,121.176],[-47.047,25.191]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.31,"y":0},"t":23,"s":[{"i":[[-10.8,-2.3],[-8.2,-0.4],[0,0],[3,-1.3],[4.4,-9.8],[20.1,0],[6.7,14.7],[-1.7,58.3]],"o":[[7.9,1.7],[0,0],[22.2,0],[0,0],[-6.7,14.7],[-20.1,0],[-4.4,-9.8],[-11.1,-3.2]],"v":[[-40.2,-2.3],[-15.1,1.5],[56.9,1.6],[64.4,22.1],[63.2,120.2],[13.2,136.5],[-36.8,120.2],[-40.9,18.1]],"c":true}]},{"t":34,"s":[{"i":[[-10.8,-2.3],[-8.2,-0.4],[0,0],[3,-1.3],[4.4,-9.8],[24.175,0.094],[6.7,14.7],[-1.7,58.3]],"o":[[7.9,1.7],[0,0],[22.2,0],[0,0],[-6.669,14.144],[-20.1,-0.078],[-4.4,-9.8],[-11.1,-3.2]],"v":[[-40.2,-2.3],[-15.1,1.5],[56.9,1.6],[64.4,22.1],[63.044,119.606],[13.2,136.156],[-36.8,120.2],[-40.9,18.1]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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":"Fill 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":[100,100],"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":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":12,"op":37,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"EXAMPLE ON","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[252.75,259.55,0],"ix":2},"a":{"a":0,"k":[0,3.55,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],[45.3,0],[0,42]],"o":[[0,42],[-45.3,0],[0,0]],"v":[[82,8.4],[0,84.5],[-82,6.5]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":22,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 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":[100,100],"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":"Transform"}],"nm":"Path","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-25.4,0],[0,0],[0,-25.4],[0,0],[25.4,0],[0,0],[0,25.4],[0,0]],"o":[[0,0],[25.4,0],[0,0],[0,25.4],[0,0],[-25.4,0],[0,0],[0,-25.4]],"v":[[0,-117.5],[0,-117.5],[46,-71.5],[46,4.5],[0,50.5],[0,50.5],[-46,4.5],[-46,-71.5]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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":"Fill 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":[100,100],"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":"Transform"}],"nm":"Rectangle","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-6.3,0],[0,0],[0,-6.3],[0,0],[6.3,0],[0,0],[0,6.3],[0,0]],"o":[[0,0],[6.3,0],[0,0],[0,6.3],[0,0],[-6.3,0],[0,0],[0,-6.3]],"v":[[-1.6,78.5],[1.7,78.5],[13.1,89.9],[13.1,113.2],[1.7,124.6],[-1.6,124.6],[-13,113.2],[-13,89.9]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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":"Fill 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":[100,100],"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":"Transform"}],"nm":"Rectangle","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":1,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Head Mic","parent":6,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.39,"y":1},"o":{"x":0.3,"y":0},"t":0,"s":[-3.6,-33.55,0],"to":[0,0,0],"ti":[0,0,0]},{"t":12,"s":[9.4,60.45,0]}],"ix":2},"a":{"a":0,"k":[-3.6,-33.55,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.833,"y":0.833},"o":{"x":0.3,"y":0},"t":0,"s":[{"i":[[-25.4,0],[0,-25.4],[0,0],[11.45,-8.15],[0,0],[0,0],[0,0]],"o":[[25.4,0],[0,0],[0,11.4],[0,0],[0,0],[0,0],[0,-25.4]],"v":[[-3.6,-117.5],[42.4,-71.5],[42.4,4.5],[25.8,40.525],[-28.33,-5.73],[-49.7,-35.75],[-49.6,-71.5]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":4,"s":[{"i":[[-25.393,-0.391],[0.18,-19.791],[-0.243,-7.895],[14.056,-6.795],[0,0],[0,0],[0,0]],"o":[[28.416,0.506],[0,0],[0.243,14.68],[0,0],[0,0],[0,0],[0.052,-15.482]],"v":[[-6.434,-101.102],[47.056,-73.018],[47.664,4.298],[29.68,40.735],[-22.557,-15.51],[-55.36,-50.751],[-55.066,-73.828]],"c":true}]},{"i":{"x":0.39,"y":1},"o":{"x":0.167,"y":0.167},"t":6,"s":[{"i":[[-25.388,-0.636],[-1.714,-14.258],[-0.395,-12.85],[18.974,-7.989],[0,0],[0,0],[0,0]],"o":[[30.309,0.824],[0,0],[0.395,16.737],[0,0],[0,0],[0,0],[2.421,-11.434]],"v":[[-4.713,-90.812],[49.978,-73.971],[50.966,4.171],[29.789,44.635],[-23.955,-14.43],[-58.974,-53.419],[-58.496,-75.289]],"c":true}]},{"t":12,"s":[{"i":[[-25.357,-0.966],[8.591,-7],[-0.599,-19.5],[30.67,-1.65],[0,0],[0,0],[0,0]],"o":[[32.818,1.25],[0,0],[0.599,19.5],[0,0],[0,0],[0,0],[7.976,-0.75]],"v":[[-7.528,-77],[56.909,-75.25],[58.407,4],[30.335,46.65],[-22.743,-12.98],[-60.7,-57],[-59.976,-81.5]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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":"Fill 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":[100,100],"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":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.3,"y":0},"t":0,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[5.9,0],[0,25.4],[0,0]],"o":[[0,0],[0,0],[0,0],[-11.6,9.325],[-25.4,0],[0,0],[0,0]],"v":[[-49.7,-36.15],[-27.202,-14.502],[-25.25,-12.55],[26.975,39.425],[-3.6,50.4],[-49.6,4.4],[-49.7,-31.65]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":4,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[6.435,0.16],[0,28.821],[0,0]],"o":[[0,0],[0,0],[0,0],[-13.044,10.194],[-29.138,-0.769],[0,0],[0,0]],"v":[[-55.579,-50.718],[-28.188,-26.309],[-26.057,-24.356],[32.281,39.246],[-10.39,49.691],[-55.329,4.4],[-55.38,-31.494]],"c":true}]},{"i":{"x":0.39,"y":1},"o":{"x":0.167,"y":0.167},"t":6,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[6.77,0.261],[0,30.968],[0,0]],"o":[[0,0],[0,0],[0,0],[-13.708,7.528],[-31.483,-1.252],[0,0],[0,0]],"v":[[-59.067,-54.691],[-31.67,-27.138],[-29.427,-25.186],[31.261,43.864],[-14.65,49.247],[-58.924,4.4],[-59.007,-41.593]],"c":true}]},{"t":12,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[7.214,0.396],[0,33.85],[0,0]],"o":[[0,0],[0,0],[0,0],[-14.586,3.95],[-34.597,-1.9],[0,0],[0,0]],"v":[[-60.625,-60.025],[-33.246,-28.252],[-30.856,-26.3],[32.633,46.55],[-17.287,48.65],[-60.625,4.4],[-60.75,-55.15]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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":"Fill 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":[100,100],"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":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":1,"op":13,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"Arc L","parent":8,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-3.75,84.5,0],"ix":2},"a":{"a":0,"k":[-3.75,84.5,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.833,"y":0.833},"o":{"x":0.3,"y":0},"t":0,"s":[{"i":[[0,0],[13.1,0],[0,42]],"o":[[-11,5.1],[-45.3,0],[0,0]],"v":[[33,76.5],[-3.5,84.5],[-85.5,6.5]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":2,"s":[{"i":[[0,0],[17.605,8.684],[-3.033,28.67]],"o":[[-19.95,7.814],[-28.895,-17.316],[0,0]],"v":[[24.45,79.436],[-39.105,77.316],[-76.362,0.644]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":3,"s":[{"i":[[0,0],[11.789,14.347],[-5.217,19.073]],"o":[[-50.375,15.159],[-15.78,-19.203],[0,0]],"v":[[26.125,87.591],[-58.72,68.703],[-69.783,-3.573]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":4,"s":[{"i":[[0,0],[3.96,23.737],[-17.216,17.641]],"o":[[-21.241,-5.148],[-3.54,-27.263],[0,0]],"v":[[-26.759,91.648],[-66.96,42.763],[-43.784,-26.641]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":5,"s":[{"i":[[0,0],[0.701,27.677],[-23.215,14.71]],"o":[[-17.607,-9.204],[-0.405,-15.984],[0,0]],"v":[[-32.893,81.704],[-69.201,29.323],[-41.785,-18.71]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":7,"s":[{"i":[[0,0],[-6.372,14.388],[-16.86,4.436]],"o":[[-9.639,-11.784],[6.608,-14.922],[0,0]],"v":[[-74.929,75.894],[-84.108,31.922],[-41.89,3.314]],"c":false}]},{"i":{"x":0.39,"y":1},"o":{"x":0.167,"y":0.167},"t":9,"s":[{"i":[[0,0],[-14.453,13.82],[-10.072,-1.911]],"o":[[-3.391,-12.713],[16.367,-15.651],[0,0]],"v":[[-102.109,72.713],[-93.047,27.18],[-41.962,18.507]],"c":false}]},{"t":12,"s":[{"i":[[0,0],[-10.081,5.804],[-6.5,-5.25]],"o":[[-1.25,-14.5],[24.75,-14.25],[0,0]],"v":[[-121.75,68.75],[-97.75,28.5],[-42,26.5]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":22,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 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":[100,100],"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":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":1,"op":13,"st":0,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":"Arc R","parent":6,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[66.05,51.45,0],"ix":2},"a":{"a":0,"k":[66.05,51.45,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.833,"y":0.833},"o":{"x":0.3,"y":0},"t":0,"s":[{"i":[[0,0],[42.3,-15.5]],"o":[[0,15.6],[0,0]],"v":[[78.4,8.4],[28.45,78.125]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":2,"s":[{"i":[[0,0],[45.259,-17.574]],"o":[[5.569,19.742],[0,0]],"v":[[71.436,-1.265],[27.374,78.492]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":3,"s":[{"i":[[0,0],[47.39,-19.067]],"o":[[9.578,22.724],[0,0]],"v":[[66.422,-8.224],[29.61,86.067]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":4,"s":[{"i":[[0,0],[43.341,-23.274]],"o":[[42.63,43.092],[0,0]],"v":[[43.37,-29.592],[33.159,90.274]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":5,"s":[{"i":[[0,0],[32.542,-17.732]],"o":[[46.682,32.959],[0,0]],"v":[[44.318,-21.959],[47.958,77.232]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":7,"s":[{"i":[[0,0],[29.55,-17.51]],"o":[[48.487,23.807],[0,0]],"v":[[50.036,1.981],[67.878,99.974]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":8,"s":[{"i":[[0,0],[28.054,-17.399]],"o":[[49.39,19.231],[0,0]],"v":[[56.395,12.201],[71.838,111.344]],"c":false}]},{"i":{"x":0.39,"y":1},"o":{"x":0.167,"y":0.167},"t":9,"s":[{"i":[[0,0],[27.199,-17.335]],"o":[[49.907,16.614],[0,0]],"v":[[57.245,18.726],[77.588,118.775]],"c":false}]},{"t":12,"s":[{"i":[[0,0],[26.05,-17.25]],"o":[[47.35,10.35],[0,0]],"v":[[62.15,25.9],[83.95,128.75]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":22,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 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":[100,100],"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":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":13,"st":0,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":"Leg","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[252.45,380.6,0],"ix":2},"a":{"a":0,"k":[-3.55,124.6,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.833,"y":0.833},"o":{"x":0.3,"y":0},"t":0,"s":[{"i":[[-6.3,0],[0,0],[0,-6.3],[0,0],[6.3,0],[0,0],[0,6.3],[0,0]],"o":[[0,0],[6.3,0],[0,0],[0,6.3],[0,0],[-6.3,0],[0,0],[0,-6.3]],"v":[[-5.2,78.5],[-1.9,78.5],[9.5,89.9],[9.5,113.2],[-1.9,124.6],[-5.2,124.6],[-16.6,113.2],[-16.6,89.9]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":3,"s":[{"i":[[-10.717,-0.585],[0,0],[-5.941,-0.906],[0.206,-8.157],[11.865,-1.977],[0,0],[1.529,7.318],[0,0]],"o":[[0,0],[12.64,0.23],[0,0],[-0.717,13.324],[0,0],[-9.832,-0.809],[-1.086,-1.566],[7.288,1.01]],"v":[[-6.991,83.123],[-1.449,83.426],[26.156,78.756],[29.671,97.051],[10.094,121.234],[-4.602,122.184],[-26.436,108.079],[-29.519,79.278]],"c":true}]},{"i":{"x":0.39,"y":1},"o":{"x":0.167,"y":0.167},"t":5,"s":[{"i":[[-15.856,0],[0,0],[-3.721,-3.379],[3.175,-9.561],[10.154,-0.123],[0,0],[3.074,9.411],[0,0]],"o":[[0,0],[17.039,-0.353],[0,0],[-4.368,11.421],[0,0],[-13.978,-0.72],[-2.523,-3.997],[4.562,-2.465]],"v":[[-1.547,83.311],[6.603,83.311],[48.503,77.629],[40.118,101.079],[14.346,125.123],[-0.309,126.215],[-28.324,109.339],[-40.364,80.189]],"c":true}]},{"t":12,"s":[{"i":[[-24.292,0],[0,0],[0,-6.3],[8.795,-7.101],[15.905,-0.35],[0,0],[7.818,10.05],[0,0]],"o":[[0,0],[24.292,0],[0,0],[-9.351,7.55],[0,0],[-20.755,-1.1],[-4.75,-6.106],[0,-6.3]],"v":[[1.971,106.75],[14.403,106.75],[66.25,108.9],[57.149,125.2],[14.987,135.85],[2.263,135.85],[-43.318,122.45],[-49,110.15]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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":"Fill 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":[100,100],"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":"Transform"}],"nm":"Rectangle","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":1,"op":10,"st":0,"bm":0}]},{"id":"comp_16","layers":[{"ddd":0,"ind":1,"ty":0,"nm":"Pre-comp 1","refId":"comp_17","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":0,"s":[30,30,0],"to":[0,0,0],"ti":[0,0,0]},{"t":10,"s":[28.282,29.857,0]}],"ix":2},"a":{"a":0,"k":[256,256,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.7,0.7,0.7],"y":[1,1,1]},"o":{"x":[0.3,0.3,0.3],"y":[0,0,0]},"t":0,"s":[13.6,13.6,100]},{"t":10,"s":[13.5,13.5,100]}],"ix":6}},"ao":0,"w":512,"h":512,"ip":0,"op":37,"st":0,"bm":0}]},{"id":"comp_17","layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Line","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.3,"y":1},"o":{"x":0.3,"y":0},"t":0,"s":[3.687,-6.898,0],"to":[0,0,0],"ti":[0,0,0]},{"t":8,"s":[-3.313,3.102,0]}],"ix":2},"a":{"a":0,"k":[3.6,-7,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":[[-101.6,-111.5],[108.8,97.5]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.3],"y":[1]},"o":{"x":[0.3],"y":[0]},"t":0,"s":[0]},{"t":7,"s":[31]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.3],"y":[1]},"o":{"x":[0.3],"y":[0]},"t":0,"s":[100]},{"t":7,"s":[63]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":22,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 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":[100,100],"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":"Transform"}],"nm":"Path-10","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":9,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Head Mic","parent":3,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.39,"y":1},"o":{"x":0.3,"y":0},"t":0,"s":[-3.6,-33.55,0],"to":[0,0,0],"ti":[0,0,0]},{"t":12,"s":[9.4,60.45,0]}],"ix":2},"a":{"a":0,"k":[-3.6,-33.55,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.833,"y":0.833},"o":{"x":0.3,"y":0},"t":0,"s":[{"i":[[-25.4,0],[0,-25.4],[0,0],[6.9,-8],[0,0],[0,0],[0,0]],"o":[[25.4,0],[0,0],[0,11.4],[0,0],[0,0],[0,0],[0,-25.4]],"v":[[-3.6,-117.5],[42.4,-71.5],[42.4,4.5],[31.3,34.4],[-20.33,-17.23],[-49.6,-46.5],[-49.6,-71.5]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":4,"s":[{"i":[[-25.393,-0.391],[0.18,-19.791],[-0.243,-7.895],[14.056,-6.795],[0,0],[0,0],[0,0]],"o":[[28.416,0.506],[0,0],[0.243,14.68],[0,0],[0,0],[0,0],[0.052,-15.482]],"v":[[-6.434,-101.102],[47.056,-73.018],[47.664,4.298],[29.68,40.735],[-22.557,-15.51],[-55.36,-50.751],[-55.066,-73.828]],"c":true}]},{"i":{"x":0.39,"y":1},"o":{"x":0.167,"y":0.167},"t":6,"s":[{"i":[[-25.388,-0.636],[-1.714,-14.258],[-0.395,-12.85],[18.974,-7.989],[0,0],[0,0],[0,0]],"o":[[30.309,0.824],[0,0],[0.395,16.737],[0,0],[0,0],[0,0],[2.421,-11.434]],"v":[[-4.713,-90.812],[49.978,-73.971],[50.966,4.171],[29.789,44.635],[-23.955,-14.43],[-58.974,-53.419],[-58.496,-75.289]],"c":true}]},{"t":12,"s":[{"i":[[-25.306,-0.966],[8.574,-7],[-0.598,-19.5],[30.609,-1.65],[0,0],[0,0],[0,0]],"o":[[32.753,1.25],[0,0],[0.598,19.5],[0,0],[0,0],[0,0],[5.227,-2.25]],"v":[[-7.633,-77],[56.676,-75.25],[58.171,4],[29.904,46.4],[-22.818,-12.98],[-60.7,-57],[-59.227,-81]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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":"Fill 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":[100,100],"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":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.3,"y":0},"t":0,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[5.9,0],[0,25.4],[0,0]],"o":[[0,0],[0,0],[0,0],[-5.2,2],[-25.4,0],[0,0],[0,0]],"v":[[-49.6,-15.4],[-40.702,-6.502],[-38.75,-4.55],[13.1,47.3],[-3.6,50.4],[-49.6,4.4],[-49.6,-15.4]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":4,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[6.435,0.16],[0,28.821],[0,0]],"o":[[0,0],[0,0],[0,0],[-13.044,10.194],[-29.138,-0.769],[0,0],[0,0]],"v":[[-55.579,-50.718],[-28.188,-26.309],[-26.057,-24.356],[32.281,39.246],[-10.39,49.691],[-55.329,4.4],[-55.38,-31.494]],"c":true}]},{"i":{"x":0.39,"y":1},"o":{"x":0.167,"y":0.167},"t":6,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[6.77,0.261],[0,30.968],[0,0]],"o":[[0,0],[0,0],[0,0],[-13.708,7.528],[-31.483,-1.252],[0,0],[0,0]],"v":[[-59.067,-54.691],[-31.67,-27.138],[-29.427,-25.186],[31.261,43.864],[-14.65,49.247],[-58.924,4.4],[-59.007,-41.593]],"c":true}]},{"t":12,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[7.199,0.396],[0,33.85],[0,0]],"o":[[0,0],[0,0],[0,0],[-14.557,3.95],[-34.528,-1.9],[0,0],[0,0]],"v":[[-60.625,-60.025],[-33.301,-28.252],[-30.915,-26.3],[32.198,46.3],[-17.623,48.4],[-60.625,4.4],[-60.75,-55.15]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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":"Fill 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":[100,100],"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":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":13,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Arc L","parent":5,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-3.75,84.5,0],"ix":2},"a":{"a":0,"k":[-3.75,84.5,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.833,"y":0.833},"o":{"x":0.3,"y":0},"t":0,"s":[{"i":[[0,0],[13.1,0],[0,42]],"o":[[-11,5.1],[-45.3,0],[0,0]],"v":[[33,76.5],[-3.5,84.5],[-85.5,6.5]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":2,"s":[{"i":[[0,0],[17.605,8.684],[-3.033,28.67]],"o":[[-19.95,7.814],[-28.895,-17.316],[0,0]],"v":[[24.45,79.436],[-39.105,77.316],[-76.362,0.644]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":3,"s":[{"i":[[0,0],[11.789,14.347],[-5.217,19.073]],"o":[[-50.375,15.159],[-15.78,-19.203],[0,0]],"v":[[26.125,87.591],[-58.72,68.703],[-69.783,-3.573]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":4,"s":[{"i":[[0,0],[3.96,23.737],[-17.216,17.641]],"o":[[-21.241,-5.148],[-3.54,-27.263],[0,0]],"v":[[-26.759,91.648],[-66.96,42.763],[-43.784,-26.641]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":5,"s":[{"i":[[0,0],[0.701,27.677],[-23.215,14.71]],"o":[[-17.607,-9.204],[-0.405,-15.984],[0,0]],"v":[[-32.893,81.704],[-69.201,29.323],[-41.785,-18.71]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":7,"s":[{"i":[[0,0],[-6.372,14.388],[-16.86,4.436]],"o":[[-9.639,-11.784],[6.608,-14.922],[0,0]],"v":[[-74.929,75.894],[-84.108,31.922],[-41.89,3.314]],"c":false}]},{"i":{"x":0.39,"y":1},"o":{"x":0.167,"y":0.167},"t":9,"s":[{"i":[[0,0],[-14.453,13.82],[-10.072,-1.911]],"o":[[-3.391,-12.713],[16.367,-15.651],[0,0]],"v":[[-102.109,72.713],[-93.047,27.18],[-41.962,18.507]],"c":false}]},{"t":12,"s":[{"i":[[0,0],[-10.081,5.804],[-6.5,-5.25]],"o":[[-1.25,-14.5],[24.75,-14.25],[0,0]],"v":[[-121.75,68.75],[-97.75,28.5],[-42,26.5]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":22,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 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":[100,100],"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":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":13,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Arc R","parent":3,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[66.05,51.45,0],"ix":2},"a":{"a":0,"k":[66.05,51.45,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.833,"y":0.833},"o":{"x":0.3,"y":0},"t":0,"s":[{"i":[[0,0],[8.7,-12]],"o":[[0,15.6],[0,0]],"v":[[78.4,8.4],[64.7,50.5]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":3,"s":[{"i":[[0,0],[16.64,-12.817]],"o":[[15.078,25.724],[0,0]],"v":[[66.422,-8.224],[58.36,66.317]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":4,"s":[{"i":[[0,0],[38.841,-19.274]],"o":[[42.63,43.092],[0,0]],"v":[[43.37,-29.592],[35.659,89.274]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":5,"s":[{"i":[[0,0],[32.542,-17.732]],"o":[[46.682,32.959],[0,0]],"v":[[44.318,-21.959],[47.958,77.232]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":7,"s":[{"i":[[0,0],[29.55,-17.51]],"o":[[48.487,23.807],[0,0]],"v":[[50.036,1.981],[67.878,99.974]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":8,"s":[{"i":[[0,0],[28.054,-17.399]],"o":[[49.39,19.231],[0,0]],"v":[[56.395,12.201],[71.838,111.344]],"c":false}]},{"i":{"x":0.39,"y":1},"o":{"x":0.167,"y":0.167},"t":9,"s":[{"i":[[0,0],[27.199,-17.335]],"o":[[49.907,16.614],[0,0]],"v":[[57.245,18.726],[77.588,118.775]],"c":false}]},{"t":12,"s":[{"i":[[0,0],[26.05,-17.25]],"o":[[47.35,10.35],[0,0]],"v":[[62.15,25.9],[83.95,128.75]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":22,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 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":[100,100],"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":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":13,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Leg","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[252.45,380.6,0],"ix":2},"a":{"a":0,"k":[-3.55,124.6,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.833,"y":0.833},"o":{"x":0.3,"y":0},"t":0,"s":[{"i":[[-6.3,0],[0,0],[0,-6.3],[0,0],[6.3,0],[0,0],[0,6.3],[0,0]],"o":[[0,0],[6.3,0],[0,0],[0,6.3],[0,0],[-6.3,0],[0,0],[0,-6.3]],"v":[[-5.2,78.5],[-1.9,78.5],[9.5,89.9],[9.5,113.2],[-1.9,124.6],[-5.2,124.6],[-16.6,113.2],[-16.6,89.9]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":3,"s":[{"i":[[-10.717,-0.585],[0,0],[-5.941,-0.906],[0.206,-8.157],[11.865,-1.977],[0,0],[1.529,7.318],[0,0]],"o":[[0,0],[12.64,0.23],[0,0],[-0.717,13.324],[0,0],[-9.832,-0.809],[-1.086,-1.566],[7.288,1.01]],"v":[[-6.991,83.123],[-1.449,83.426],[26.156,78.756],[29.671,97.051],[10.094,121.234],[-4.602,122.184],[-26.436,108.079],[-29.519,79.278]],"c":true}]},{"i":{"x":0.39,"y":1},"o":{"x":0.167,"y":0.167},"t":5,"s":[{"i":[[-15.856,0],[0,0],[-3.721,-3.379],[3.175,-9.561],[10.154,-0.123],[0,0],[3.074,9.411],[0,0]],"o":[[0,0],[17.039,-0.353],[0,0],[-4.368,11.421],[0,0],[-13.978,-0.72],[-2.523,-3.997],[4.562,-2.465]],"v":[[-1.547,83.311],[6.603,83.311],[48.503,77.629],[40.118,101.079],[14.346,125.123],[-0.309,126.215],[-28.324,109.339],[-40.364,80.189]],"c":true}]},{"t":12,"s":[{"i":[[-24.292,0],[0,0],[0,-6.3],[8.795,-7.101],[15.905,-0.35],[0,0],[7.818,10.05],[0,0]],"o":[[0,0],[24.292,0],[0,0],[-9.351,7.55],[0,0],[-20.755,-1.1],[-4.75,-6.106],[0,-6.3]],"v":[[1.971,106.75],[14.403,106.75],[66.25,108.9],[57.149,125.2],[14.987,135.85],[2.263,135.85],[-43.318,122.45],[-49,110.15]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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":"Fill 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":[100,100],"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":"Transform"}],"nm":"Rectangle","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":10,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"Head 2","parent":8,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.39,"y":1},"o":{"x":0.3,"y":0},"t":0,"s":[0.613,-101.537,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.39,"y":1},"o":{"x":0.3,"y":0},"t":12,"s":[11.613,-50.537,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.39,"y":1},"o":{"x":0.3,"y":0},"t":26,"s":[11.613,-86.537,0],"to":[0,0,0],"ti":[0,0,0]},{"t":36,"s":[11.113,-83.037,0]}],"ix":2},"a":{"a":0,"k":[11.613,-84.537,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.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":3,"s":[{"i":[[0,-17.328],[25.612,0],[0,17.328],[-25.612,0]],"o":[[0,17.328],[-25.612,0],[0,-17.328],[25.612,0]],"v":[[54.039,-62.942],[7.664,-31.567],[-38.711,-62.942],[7.664,-94.317]],"c":true}]},{"i":{"x":0.39,"y":1},"o":{"x":0.167,"y":0.167},"t":7,"s":[{"i":[[0,-23.543],[25.081,0],[0,23.543],[-25.081,0]],"o":[[0,23.543],[-25.081,0],[0,-23.543],[25.081,0]],"v":[[55.47,-76.028],[10.057,-33.4],[-35.356,-76.028],[10.057,-118.656]],"c":true}]},{"i":{"x":0.39,"y":1},"o":{"x":0.3,"y":0},"t":14,"s":[{"i":[[0,-24.735],[24.735,0],[0,24.735],[-24.735,0]],"o":[[0,24.735],[-24.735,0],[0,-24.735],[24.735,0]],"v":[[56.4,-84.537],[11.613,-39.75],[-33.174,-84.537],[11.613,-129.324]],"c":true}]},{"t":25,"s":[{"i":[[0,-26.924],[26.924,0],[0,26.924],[-26.924,0]],"o":[[0,26.924],[-26.924,0],[0,-26.924],[26.924,0]],"v":[[60.363,-84.537],[11.613,-35.787],[-37.137,-84.537],[11.613,-133.287]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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":"Fill 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":[100,100],"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":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":2,"op":37,"st":0,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":"Hands 2","parent":8,"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.05,"y":0},"t":9,"s":[11.336,13.774,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.4,"y":1},"o":{"x":0.31,"y":0},"t":12,"s":[11.336,18.274,0],"to":[0,0,0],"ti":[0,0,0]},{"t":25,"s":[11.336,11.774,0]}],"ix":2},"a":{"a":0,"k":[11.336,10.774,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.833,"y":0.833},"o":{"x":0.3,"y":0},"t":7,"s":[{"i":[[0,0],[0.875,44.36],[0.012,11.61]],"o":[[0,0],[-0.348,-17.658],[-0.016,-14.955]],"v":[[-33.583,109.659],[-40.875,50.64],[-45.012,-17.11]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":9,"s":[{"i":[[0,0],[-7.48,30.25],[-7.964,9.648]],"o":[[0,0],[2.73,-11.373],[7.228,-11.196]],"v":[[-57.163,97.688],[-57.996,36.651],[-42.036,0.102]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":10,"s":[{"i":[[0,0],[-14.611,30.379],[-23.925,1.795]],"o":[[0,0],[7.389,-12.121],[13.171,-8.112]],"v":[[-77.526,99.758],[-81.889,29.906],[-41.075,4.99]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":11,"s":[{"i":[[0,0],[-27.718,19.589],[-10.136,-1.028]],"o":[[0,0],[21.782,-16.411],[19.114,-5.028]],"v":[[-107.888,77.829],[-93.782,16.161],[-42.114,8.877]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":13,"s":[{"i":[[0,0],[-27.325,4.127],[-14.975,-3.949]],"o":[[0,0],[20.334,-3.734],[35.444,0.066]],"v":[[-136.225,32.858],[-90.834,7.893],[-38.525,10.472]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":14,"s":[{"i":[[0,0],[-27.129,-3.603],[-14.269,-4.157]],"o":[[0,0],[19.61,2.605],[43.609,2.613]],"v":[[-147.393,-5.627],[-89.61,-2.992],[-38.231,9.77]],"c":false}]},{"i":{"x":0.3,"y":1},"o":{"x":0.167,"y":0.167},"t":18,"s":[{"i":[[0,0],[-38.825,-22.336],[-19.04,-2.789]],"o":[[0,0],[14.675,9.164],[62.76,8.586]],"v":[[-144.801,-73.381],[-91.175,-7.197],[-37.96,10.757]],"c":false}]},{"t":28,"s":[{"i":[[0,0],[-46.458,-35.583],[-19.738,-3.567]],"o":[[0,0],[8.352,6.397],[79.039,14.286]],"v":[[-142.333,-130.341],[-88.062,-16.235],[-40.512,7.89]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":22,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 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":[100,100],"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":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.3,"y":0},"t":7,"s":[{"i":[[0,0],[0.583,25.777],[4.5,19.11]],"o":[[0,0],[-0.238,-10.518],[-18.41,-78.181]],"v":[[63,101.64],[63.167,54.973],[56,-16.61]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":9,"s":[{"i":[[0,0],[2.455,25.149],[6.868,13.265]],"o":[[0,0],[-0.66,-9.745],[-29.265,-65.813]],"v":[[68.548,94.42],[70.368,48.723],[56.521,2.29]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":11,"s":[{"i":[[-11.495,10.995],[15.537,39.692],[11.504,1.535]],"o":[[14.034,-13.424],[-3.378,-8.551],[-47.075,-45.52]],"v":[[79.466,115.674],[91.463,38.058],[62.496,12.174]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":13,"s":[{"i":[[-14.54,10.097],[25.244,37.544],[12.466,1.965]],"o":[[15.872,-11.198],[-4.741,-7.952],[-56.007,-35.342]],"v":[[88.294,117.357],[93.089,33.781],[63.701,13.504]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":14,"s":[{"i":[[-16.063,9.648],[30.097,36.471],[11.946,0.457]],"o":[[16.791,-10.086],[-5.422,-7.653],[-60.473,-30.253]],"v":[[92.709,118.199],[93.903,31.642],[61.554,13.67]],"c":false}]},{"i":{"x":0.3,"y":1},"o":{"x":0.167,"y":0.167},"t":18,"s":[{"i":[[-5.509,3.309],[41.86,36.38],[11.653,0.923]],"o":[[5.759,-3.459],[-7.371,-6.796],[-73.243,-15.702]],"v":[[124.602,127.851],[94.405,26.111],[62.332,13.5]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":25,"s":[{"i":[[0,0],[48,36.333],[11.5,1.167]],"o":[[0,0],[-8.388,-6.35],[-79.91,-8.107]],"v":[[141.25,132.89],[94.667,23.223],[63.5,11.89]],"c":false}]},{"t":28,"s":[{"i":[[0,0],[48,36.333],[11.5,1.167]],"o":[[0,0],[-8.388,-6.35],[-79.91,-8.107]],"v":[[140,128.89],[94.667,23.848],[63.5,11.89]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":22,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 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":[100,100],"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":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":12,"op":37,"st":0,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":"Body 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.4,"y":1},"o":{"x":0.3,"y":0},"t":12,"s":[266.676,398.378,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.4,"y":1},"o":{"x":0.3,"y":0},"t":23,"s":[266.676,387.378,0],"to":[0,0,0],"ti":[0,0,0]},{"t":34,"s":[266.676,392.378,0]}],"ix":2},"a":{"a":0,"k":[10.676,136.378,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.69,"y":1},"o":{"x":0.31,"y":0},"t":12,"s":[{"i":[[-12.288,0.098],[-12.498,-0.987],[0,0],[3.339,-1.222],[4.897,-9.213],[22.369,0],[7.456,13.82],[-1.892,54.808]],"o":[[8.847,-0.071],[35.724,2.82],[24.706,0],[0,0],[-7.456,13.82],[-22.369,0],[-4.897,-9.213],[-12.353,-3.008]],"v":[[-46.407,7.071],[-18.334,9.585],[61.794,9.679],[70.141,28.952],[68.805,121.176],[13.161,136.5],[-42.484,121.176],[-47.047,25.191]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.31,"y":0},"t":23,"s":[{"i":[[-10.8,-2.3],[-8.2,-0.4],[0,0],[3,-1.3],[4.4,-9.8],[20.1,0],[6.7,14.7],[-1.7,58.3]],"o":[[7.9,1.7],[0,0],[22.2,0],[0,0],[-6.7,14.7],[-20.1,0],[-4.4,-9.8],[-11.1,-3.2]],"v":[[-40.2,-2.3],[-15.1,1.5],[56.9,1.6],[64.4,22.1],[63.2,120.2],[13.2,136.5],[-36.8,120.2],[-40.9,18.1]],"c":true}]},{"t":34,"s":[{"i":[[-10.8,-2.3],[-8.2,-0.4],[0,0],[3,-1.3],[4.4,-9.8],[24.175,0.094],[6.7,14.7],[-1.7,58.3]],"o":[[7.9,1.7],[0,0],[22.2,0],[0,0],[-6.669,14.144],[-20.1,-0.078],[-4.4,-9.8],[-11.1,-3.2]],"v":[[-40.2,-2.3],[-15.1,1.5],[56.9,1.6],[64.4,22.1],[63.044,119.606],[13.2,136.156],[-36.8,120.2],[-40.9,18.1]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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":"Fill 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":[100,100],"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":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":12,"op":37,"st":0,"bm":0}]},{"id":"comp_18","layers":[{"ddd":0,"ind":1,"ty":0,"nm":"Pre-comp 1","refId":"comp_19","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[30,30,0],"ix":2},"a":{"a":0,"k":[256,256,0],"ix":1},"s":{"a":0,"k":[13.6,13.6,100],"ix":6}},"ao":0,"w":512,"h":512,"ip":0,"op":30,"st":0,"bm":0}]},{"id":"comp_19","layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Null 1","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[254.042,238.57,0],"ix":2},"a":{"a":0,"k":[50,50,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.72,0.72,0.72],"y":[1,1,1]},"o":{"x":[0.28,0.28,0.28],"y":[0,0,0]},"t":0,"s":[100,100,100]},{"i":{"x":[0.72,0.72,0.72],"y":[1,1,1]},"o":{"x":[0.28,0.28,0.28],"y":[0,0,0]},"t":11,"s":[93,93,100]},{"i":{"x":[0.72,0.72,0.72],"y":[1,1,1]},"o":{"x":[0.28,0.28,0.28],"y":[0,0,0]},"t":21,"s":[101,101,100]},{"t":29,"s":[100,100,100]}],"ix":6}},"ao":0,"ip":0,"op":30,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Line","parent":3,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.4,"y":0},"t":0,"s":[-13.538,-23.613,0],"to":[0,0,0],"ti":[0,0,0]},{"t":17,"s":[3.687,-6.898,0]}],"ix":2},"a":{"a":0,"k":[3.6,-7,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":[[-101.6,-111.5],[108.8,97.5]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.3],"y":[1]},"o":{"x":[0.4],"y":[0]},"t":0,"s":[0]},{"t":17,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":22,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 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":[100,100],"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":"Transform"}],"nm":"Path-10","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":2,"op":30,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Head","parent":4,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-3.6,-33.55,0],"ix":2},"a":{"a":0,"k":[-3.6,-33.55,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.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":5,"s":[{"i":[[-25.4,0],[0,-25.4],[0,0],[15.481,-9.962],[0,0],[0,0],[0,0]],"o":[[25.4,0],[0,0],[0,11.4],[0,0],[0,0],[0,0],[0,-25.4]],"v":[[-3.6,-117.5],[42.4,-71.5],[42.4,4.5],[23.519,42.712],[-23.209,-2.257],[-49.7,-27.75],[-49.6,-71.5]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":6,"s":[{"i":[[-25.4,0],[0,-25.4],[0,0],[6.9,-8],[0,0],[0,0],[0,0]],"o":[[25.4,0],[0,0],[0,11.4],[0,0],[0,0],[0,0],[0,-25.4]],"v":[[-3.6,-117.5],[42.4,-71.5],[42.4,4.5],[31.3,34.4],[-10.034,-21.084],[-49.6,-46.5],[-49.6,-71.5]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":7,"s":[{"i":[[-25.4,0],[0,-25.4],[0,0],[6.9,-8],[0,0],[0,0],[0,0]],"o":[[25.4,0],[0,0],[0,11.4],[0,0],[0,0],[0,0],[0,-25.4]],"v":[[-3.6,-117.5],[42.4,-71.5],[42.4,4.5],[31.3,34.4],[19.623,9.37],[-49.6,-46.5],[-49.6,-71.5]],"c":true}]},{"t":8,"s":[{"i":[[-25.4,0],[0,-25.4],[0,0],[6.9,-8],[0,0],[0,0],[0,0]],"o":[[25.4,0],[0,0],[0,11.4],[0,0],[0,0],[0,0],[0,-25.4]],"v":[[-3.6,-117.5],[42.4,-71.5],[42.4,4.5],[31.3,34.4],[-20.33,-17.23],[-49.6,-46.5],[-49.6,-71.5]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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":"Fill 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":[100,100],"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":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":5,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[5.9,0],[0,25.4],[0,0]],"o":[[0,0],[0,0],[0,0],[-12.35,7.7],[-25.4,0],[0,0],[0,0]],"v":[[-49.7,-29.15],[-39.262,-18.975],[-36.972,-16.742],[23.85,42.55],[-3.6,50.4],[-49.6,4.4],[-49.7,-29.15]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":6,"s":[{"i":[[0,0],[-3.331,-2.789],[4.414,24.475],[0,0],[12.582,-0.112],[0,25.4],[0,0]],"o":[[0,0],[4.215,3.528],[21.749,15.62],[-9.968,12.883],[-25.399,0.226],[0,0],[0,0]],"v":[[-49.6,-15.4],[-38.553,-4.468],[-12.248,-24.48],[31.491,33.902],[-3.6,50.4],[-49.6,4.4],[-49.6,-15.4]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":7,"s":[{"i":[[0,0],[-1.647,-1.416],[3.578,20.948],[0,0],[13.582,-0.261],[0,25.4],[0,0]],"o":[[0,0],[7.932,6.822],[10.875,7.81],[-10.683,10.613],[-25.395,0.487],[0,0],[0,0]],"v":[[-49.6,-15.4],[-6.153,27.653],[21.197,9.349],[31.475,35.139],[-3.6,50.4],[-49.6,4.4],[-49.6,-15.4]],"c":true}]},{"t":8,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[5.9,0],[0,25.4],[0,0]],"o":[[0,0],[0,0],[0,0],[-5.2,2],[-25.4,0],[0,0],[0,0]],"v":[[-49.6,-15.4],[-40.702,-6.502],[-38.75,-4.55],[13.1,47.3],[-3.6,50.4],[-49.6,4.4],[-49.6,-15.4]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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":"Fill 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":[100,100],"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":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":30,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Arc L","parent":6,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-3.75,84.5,0],"ix":2},"a":{"a":0,"k":[-3.75,84.5,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],[13.1,0],[0,42]],"o":[[-11,5.1],[-45.3,0],[0,0]],"v":[[33,76.5],[-3.5,84.5],[-85.5,6.5]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":22,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 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":[100,100],"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":"Transform"}],"nm":"Group 1","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":5,"ty":4,"nm":"Arc R","parent":4,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[66.05,51.45,0],"ix":2},"a":{"a":0,"k":[66.05,51.45,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.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":8,"s":[{"i":[[0,0],[34.576,-19.595]],"o":[[0,15.6],[0,0]],"v":[[78.4,8.4],[38.027,74.567]],"c":false}]},{"t":9,"s":[{"i":[[0,0],[8.7,-12]],"o":[[0,15.6],[0,0]],"v":[[78.4,8.4],[64.7,50.5]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":22,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 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":[100,100],"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":"Transform"}],"nm":"Group 1","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":6,"ty":4,"nm":"Leg","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[48.408,192.03,0],"ix":2},"a":{"a":0,"k":[-3.55,124.6,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":[[-6.3,0],[0,0],[0,-6.3],[0,0],[6.3,0],[0,0],[0,6.3],[0,0]],"o":[[0,0],[6.3,0],[0,0],[0,6.3],[0,0],[-6.3,0],[0,0],[0,-6.3]],"v":[[-5.2,78.5],[-1.9,78.5],[9.5,89.9],[9.5,113.2],[-1.9,124.6],[-5.2,124.6],[-16.6,113.2],[-16.6,89.9]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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":"Fill 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":[100,100],"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":"Transform"}],"nm":"Rectangle","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":7,"ty":4,"nm":"EXAMPLE ON","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[48.708,70.98,0],"ix":2},"a":{"a":0,"k":[0,3.55,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],[45.3,0],[0,42]],"o":[[0,42],[-45.3,0],[0,0]],"v":[[82,8.4],[0,84.5],[-82,6.5]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":20.888,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 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":[100,100],"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":"Transform"}],"nm":"Path","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-25.4,0],[0,0],[0,-25.4],[0,0],[25.4,0],[0,0],[0,25.4],[0,0]],"o":[[0,0],[25.4,0],[0,0],[0,25.4],[0,0],[-25.4,0],[0,0],[0,-25.4]],"v":[[0,-117.5],[0,-117.5],[46,-71.5],[46,4.5],[0,50.5],[0,50.5],[-46,4.5],[-46,-71.5]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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":"Fill 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":[100,100],"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":"Transform"}],"nm":"Rectangle","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-6.3,0],[0,0],[0,-6.3],[0,0],[6.3,0],[0,0],[0,6.3],[0,0]],"o":[[0,0],[6.3,0],[0,0],[0,6.3],[0,0],[-6.3,0],[0,0],[0,-6.3]],"v":[[-1.6,78.5],[1.7,78.5],[13.1,89.9],[13.1,113.2],[1.7,124.6],[-1.6,124.6],[-13,113.2],[-13,89.9]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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":"Fill 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":[100,100],"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":"Transform"}],"nm":"Rectangle","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":5,"st":0,"bm":0}]},{"id":"comp_20","layers":[{"ddd":0,"ind":1,"ty":0,"nm":"Pre-comp 1","refId":"comp_21","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[30,30,0],"ix":2},"a":{"a":0,"k":[256,256,0],"ix":1},"s":{"a":0,"k":[13.6,13.6,100],"ix":6}},"ao":0,"w":512,"h":512,"ip":0,"op":33,"st":0,"bm":0}]},{"id":"comp_21","layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Null 1","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[254.062,238.57,0],"ix":2},"a":{"a":0,"k":[50,50,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.7,0.7,0.7],"y":[1,1,1]},"o":{"x":[0.3,0.3,0.3],"y":[0,0,0]},"t":0,"s":[100,100,100]},{"i":{"x":[0.7,0.7,0.7],"y":[1,1,1]},"o":{"x":[0.3,0.3,0.3],"y":[0,0,0]},"t":12,"s":[93,93,100]},{"i":{"x":[0.7,0.7,0.7],"y":[1,1,1]},"o":{"x":[0.3,0.3,0.3],"y":[0,0,0]},"t":22,"s":[101,101,100]},{"t":32,"s":[100,100,100]}],"ix":6}},"ao":0,"ip":0,"op":33,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Line","parent":3,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.4,"y":0},"t":2,"s":[3.687,-6.898,0],"to":[0,0,0],"ti":[0,0,0]},{"t":23,"s":[12.601,2.016,0]}],"ix":2},"a":{"a":0,"k":[3.6,-7,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":[[-101.6,-111.5],[108.8,97.5]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.3],"y":[1]},"o":{"x":[0.4],"y":[0]},"t":2,"s":[0]},{"t":23,"s":[100]}],"ix":1},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":22,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 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":[100,100],"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":"Transform"}],"nm":"Path-10","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":21,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Head","parent":4,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-3.6,-33.55,0],"ix":2},"a":{"a":0,"k":[-3.6,-33.55,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.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":8,"s":[{"i":[[-25.4,0],[0,-25.4],[0,0],[6.9,-8],[0,0],[0,0],[0,0]],"o":[[25.4,0],[0,0],[0,11.4],[0,0],[0,0],[0,0],[0,-25.4]],"v":[[-3.6,-117.5],[42.4,-71.5],[42.4,4.5],[31.3,34.4],[-20.33,-17.23],[-49.6,-46.5],[-49.6,-71.5]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":9,"s":[{"i":[[-25.4,0],[0,-25.4],[0,0],[5.665,-6.576],[0,0],[0,0],[0,0]],"o":[[25.4,0],[0,0],[0,11.4],[0,0],[0,0],[0,0],[0,-25.4]],"v":[[-3.6,-117.5],[42.4,-71.5],[42.4,4.5],[32.133,31.549],[-27.809,-30.43],[-49.62,-42.75],[-49.6,-71.5]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":10,"s":[{"i":[[-25.4,0],[0,-25.4],[0,0],[5.665,-6.576],[0,0],[0,0],[0,0]],"o":[[25.4,0],[0,0],[0,11.4],[0,0],[0,0],[0,0],[0,-25.4]],"v":[[-3.6,-117.5],[42.4,-71.5],[42.4,4.5],[32.133,31.549],[-4.565,-6.652],[-49.62,-42.75],[-49.6,-71.5]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":11,"s":[{"i":[[-25.4,0],[0,-25.4],[0,0],[5.665,-6.576],[0,0],[0,0],[0,0]],"o":[[25.4,0],[0,0],[0,11.4],[0,0],[0,0],[0,0],[0,-25.4]],"v":[[-3.6,-117.5],[42.4,-71.5],[42.4,4.5],[32.133,31.549],[17.977,14.816],[-49.62,-42.75],[-49.6,-71.5]],"c":true}]},{"t":12,"s":[{"i":[[-25.4,0],[0,-25.4],[0,0],[15.481,-9.962],[0,0],[0,0],[0,0]],"o":[[25.4,0],[0,0],[0,11.4],[0,0],[0,0],[0,0],[0,-25.4]],"v":[[-3.6,-117.5],[42.4,-71.5],[42.4,4.5],[23.519,42.712],[-23.209,-2.257],[-49.7,-27.75],[-49.6,-71.5]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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":"Fill 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":[100,100],"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":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":8,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[5.9,0],[0,25.4],[0,0]],"o":[[0,0],[0,0],[0,0],[-5.2,2],[-25.4,0],[0,0],[0,0]],"v":[[-49.6,-15.4],[-40.702,-6.502],[-38.75,-4.55],[13.1,47.3],[-3.6,50.4],[-49.6,4.4],[-49.6,-15.4]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":9,"s":[{"i":[[0,0],[0,0],[-11.683,-16.357],[0,0],[5.9,0],[0,25.4],[0,0]],"o":[[0,0],[0,0],[4.734,6.628],[-6.63,3.14],[-25.4,0],[0,0],[0,0]],"v":[[-49.575,-44.964],[-29.795,-33.421],[-38.394,-6.988],[15.25,46.35],[-3.6,50.4],[-49.6,4.4],[-49.575,-44.167]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":10,"s":[{"i":[[0,0],[0,0],[-11.683,-16.357],[0,0],[5.9,0],[0,25.4],[0,0]],"o":[[0,0],[0,0],[4.734,6.628],[-6.63,3.14],[-25.4,0],[0,0],[0,0]],"v":[[-49.575,-44.964],[-6.551,-9.643],[-15.151,16.79],[15.25,46.35],[-3.6,50.4],[-49.6,4.4],[-49.575,-44.167]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":11,"s":[{"i":[[0,0],[0,0],[-11.683,-16.357],[0,0],[5.9,0],[0,25.4],[0,0]],"o":[[0,0],[0,0],[4.734,6.628],[-6.63,3.14],[-25.4,0],[0,0],[0,0]],"v":[[-49.575,-44.964],[15.99,11.825],[7.391,38.258],[15.25,46.35],[-3.6,50.4],[-49.6,4.4],[-49.575,-44.167]],"c":true}]},{"t":12,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[5.9,0],[0,25.4],[0,0]],"o":[[0,0],[0,0],[0,0],[-12.35,7.7],[-25.4,0],[0,0],[0,0]],"v":[[-49.7,-29.15],[-39.262,-18.975],[-36.972,-16.742],[23.85,42.55],[-3.6,50.4],[-49.6,4.4],[-49.7,-29.15]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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":"Fill 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":[100,100],"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":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":33,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Arc L","parent":6,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-3.75,84.5,0],"ix":2},"a":{"a":0,"k":[-3.75,84.5,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],[13.1,0],[0,42]],"o":[[-11,5.1],[-45.3,0],[0,0]],"v":[[33,76.5],[-3.5,84.5],[-85.5,6.5]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":22,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 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":[100,100],"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":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":33,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Arc R","parent":4,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[66.05,51.45,0],"ix":2},"a":{"a":0,"k":[66.05,51.45,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.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":13,"s":[{"i":[[0,0],[8.7,-12]],"o":[[0,15.6],[0,0]],"v":[[78.4,8.4],[64.7,50.5]],"c":false}]},{"t":14,"s":[{"i":[[0,0],[36.503,-16.928]],"o":[[0,15.6],[0,0]],"v":[[78.4,8.4],[34.73,75.676]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":22,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 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":[100,100],"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":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":33,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"Leg","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[48.408,192.03,0],"ix":2},"a":{"a":0,"k":[-3.55,124.6,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":[[-6.3,0],[0,0],[0,-6.3],[0,0],[6.3,0],[0,0],[0,6.3],[0,0]],"o":[[0,0],[6.3,0],[0,0],[0,6.3],[0,0],[-6.3,0],[0,0],[0,-6.3]],"v":[[-5.2,78.5],[-1.9,78.5],[9.5,89.9],[9.5,113.2],[-1.9,124.6],[-5.2,124.6],[-16.6,113.2],[-16.6,89.9]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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":"Fill 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":[100,100],"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":"Transform"}],"nm":"Rectangle","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":33,"st":0,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":"EXAMPLE ON","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[48.708,70.98,0],"ix":2},"a":{"a":0,"k":[0,3.55,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],[45.3,0],[0,42]],"o":[[0,42],[-45.3,0],[0,0]],"v":[[82,8.4],[0,84.5],[-82,6.5]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":20.888,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 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":[100,100],"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":"Transform"}],"nm":"Path","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-25.4,0],[0,0],[0,-25.4],[0,0],[25.4,0],[0,0],[0,25.4],[0,0]],"o":[[0,0],[25.4,0],[0,0],[0,25.4],[0,0],[-25.4,0],[0,0],[0,-25.4]],"v":[[0,-117.5],[0,-117.5],[46,-71.5],[46,4.5],[0,50.5],[0,50.5],[-46,4.5],[-46,-71.5]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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":"Fill 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":[100,100],"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":"Transform"}],"nm":"Rectangle","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-6.3,0],[0,0],[0,-6.3],[0,0],[6.3,0],[0,0],[0,6.3],[0,0]],"o":[[0,0],[6.3,0],[0,0],[0,6.3],[0,0],[-6.3,0],[0,0],[0,-6.3]],"v":[[-1.6,78.5],[1.7,78.5],[13.1,89.9],[13.1,113.2],[1.7,124.6],[-1.6,124.6],[-13,113.2],[-13,89.9]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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":"Fill 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":[100,100],"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":"Transform"}],"nm":"Rectangle","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false}],"ip":14,"op":33,"st":0,"bm":0}]},{"id":"comp_22","layers":[{"ddd":0,"ind":1,"ty":0,"nm":"Pre-comp 2","refId":"comp_23","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":0,"s":[28.282,29.857,0],"to":[0,0,0],"ti":[0,0,0]},{"t":10,"s":[30,30,0]}],"ix":2},"a":{"a":0,"k":[256,256,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.7,0.7,0.7],"y":[1,1,1]},"o":{"x":[0.3,0.3,0.3],"y":[0,0,0]},"t":0,"s":[13.5,13.5,100]},{"t":10,"s":[13.6,13.6,100]}],"ix":6}},"ao":0,"w":512,"h":512,"ip":0,"op":36,"st":0,"bm":0}]},{"id":"comp_23","layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Head 2","parent":3,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.5,"y":0},"t":0,"s":[11.613,-83.537,0],"to":[0,0,0],"ti":[0,0,0]},{"t":14,"s":[13.613,-4.037,0]}],"ix":2},"a":{"a":0,"k":[11.613,-84.537,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.3,"y":1},"o":{"x":0.5,"y":0},"t":0,"s":[{"i":[[0,-26.924],[26.924,0],[0,26.924],[-26.924,0]],"o":[[0,26.924],[-26.924,0],[0,-26.924],[26.924,0]],"v":[[60.363,-84.537],[11.613,-35.787],[-37.137,-84.537],[11.613,-133.287]],"c":true}]},{"t":14,"s":[{"i":[[0,-25.609],[26.234,0],[0,25.609],[-26.234,0]],"o":[[0,25.609],[-26.234,0],[0,-25.609],[26.234,0]],"v":[[55.5,-92.894],[8,-46.525],[-39.5,-92.894],[8,-139.262]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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":"Fill 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":[100,100],"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":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":13,"st":-2,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Hands 2","parent":3,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[11.336,11.274,0],"ix":2},"a":{"a":0,"k":[11.336,10.774,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.833,"y":0.818},"o":{"x":0.5,"y":0},"t":0,"s":[{"i":[[0,0],[-46.458,-35.583],[-24.488,-5.64]],"o":[[0,0],[8.352,6.397],[78.271,18.027]],"v":[[-141.771,-129.716],[-87.875,-16.36],[-40.512,7.89]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.182},"t":2.742,"s":[{"i":[[0,0],[-36.01,-10.9],[-24.155,1.341]],"o":[[0,0],[17.49,5.6],[69.436,6.533]],"v":[[-154.359,-38.55],[-101.24,-1.379],[-38.096,3.379]],"c":false}]},{"i":{"x":0.833,"y":0.818},"o":{"x":0.167,"y":0.167},"t":4.572,"s":[{"i":[[0,0],[-22.495,10.354],[-22.973,-2.409]],"o":[[0,0],[18.597,-8.552],[52.89,-5.679]],"v":[[-130.223,30.529],[-97.344,4.391],[-37.533,-6.68]],"c":false}]},{"i":{"x":0.833,"y":0.818},"o":{"x":0.167,"y":0.182},"t":5.486,"s":[{"i":[[0,0],[-15.738,20.981],[-22.382,-4.284]],"o":[[0,0],[16.262,-20.519],[44.618,-11.784]],"v":[[-110.655,59.818],[-92.396,11.026],[-37.251,-11.709]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.182},"t":9,"s":[{"i":[[0,0],[15.091,34.278],[-16.072,17.731]],"o":[[0,0],[-7.208,-22.817],[16.812,-10.179]],"v":[[-13.011,129.282],[-62.232,89.104],[-50.644,19.509]],"c":false}]},{"i":{"x":0.833,"y":0.818},"o":{"x":0.167,"y":0.167},"t":10,"s":[{"i":[[0,0],[19.6,34.344],[-11.896,20.735]],"o":[[0,0],[-10.53,-23.142],[12.875,-9.952]],"v":[[0.934,129.625],[-59.674,93.522],[-55.966,25.06]],"c":false}]},{"i":{"x":0.3,"y":1},"o":{"x":0.167,"y":0.182},"t":11,"s":[{"i":[[0,0],[24.108,34.41],[-7.719,23.739]],"o":[[0,0],[-13.853,-23.467],[8.939,-9.725]],"v":[[1.88,129.968],[-57.117,97.94],[-61.289,30.611]],"c":false}]},{"t":14,"s":[{"i":[[0,0],[25.209,29.401],[-7.488,20.11]],"o":[[0,0],[-15.125,-17.64],[3.974,-10.674]],"v":[[6.417,130.659],[-53.875,103.64],[-63.512,36.89]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":22,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 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":[100,100],"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":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.818},"o":{"x":0.5,"y":0},"t":0,"s":[{"i":[[0,0],[48,36.333],[11.5,1.167]],"o":[[0,0],[-8.388,-6.35],[-79.91,-8.107]],"v":[[140.75,128.89],[94.542,23.473],[63.5,11.89]],"c":false}]},{"i":{"x":0.833,"y":0.818},"o":{"x":0.167,"y":0.182},"t":2.742,"s":[{"i":[[-0.107,1.301],[43.705,35.181],[12.499,4.424]],"o":[[1.452,-17.63],[-7.425,-6.513],[-75.279,-8.976]],"v":[[133.797,130.851],[92.055,22.64],[62.256,6.25]],"c":false}]},{"i":{"x":0.833,"y":0.818},"o":{"x":0.167,"y":0.182},"t":5.486,"s":[{"i":[[-6.276,1.09],[19.852,26.915],[21.224,10.921]],"o":[[6.157,-12.218],[-8.148,-12.585],[-54.801,-12.818]],"v":[[109.709,105.725],[104.015,23.092],[59.642,-11.414]],"c":false}]},{"i":{"x":0.833,"y":0.818},"o":{"x":0.167,"y":0.182},"t":9,"s":[{"i":[[-14.16,0.82],[-3.078,22.988],[16.441,25.833]],"o":[[22.052,-4.736],[2.47,-14.595],[-28.631,-17.728]],"v":[[32.732,128.976],[84.858,79.012],[68.64,15.393]],"c":false}]},{"i":{"x":0.3,"y":1},"o":{"x":0.167,"y":0.182},"t":11,"s":[{"i":[[-18.622,0.668],[-13.293,19.932],[12.19,32.742]],"o":[[37.47,-1.344],[5.361,-8.677],[-13.817,-20.508]],"v":[[7.53,130.546],[77.418,98.03],[80.302,30.109]],"c":false}]},{"t":14,"s":[{"i":[[-19.233,1.202],[-15.167,22.777],[7,26.11]],"o":[[30.25,-1.89],[5.831,-8.757],[-6.192,-23.095]],"v":[[11.75,130.39],[78.167,98.723],[84,36.89]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":22,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 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":[100,100],"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":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":12,"st":-2,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Body 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.3,"y":1},"o":{"x":0.5,"y":0},"t":0,"s":[266.426,392.378,0],"to":[0,0,0],"ti":[0,0,0]},{"t":14,"s":[253.676,337.378,0]}],"ix":2},"a":{"a":0,"k":[10.676,136.378,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.833,"y":0.833},"o":{"x":0.5,"y":0},"t":0,"s":[{"i":[[-10.795,-2.32],[-8.2,-0.399],[0,0],[3,-1.298],[6.05,-16.428],[20.1,0],[6.7,14.68],[-1.7,58.223]],"o":[[7.9,1.698],[0,0],[22.2,0],[0,0],[-7.825,16.029],[-20.1,0],[-4.4,-9.787],[-11.1,-3.196]],"v":[[-40.2,-2.303],[-15.1,1.492],[56.9,1.592],[64.619,22.065],[63.2,120.034],[13.2,136.312],[-36.8,120.034],[-40.9,18.07]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":5,"s":[{"i":[[-7.729,4.405],[-14.608,0.316],[-11.697,-7.483],[-0.238,-10.621],[7.036,-14.499],[18.581,0],[6.908,15.026],[-1.237,40.257]],"o":[[7.771,-9.845],[25.241,-0.704],[10.053,2.017],[0,0],[-6.792,14.491],[-18.581,0],[-5.829,-11.041],[-0.133,-10.892]],"v":[[-35.485,-12.023],[0.894,-25.934],[52.233,-18.385],[61.774,26.253],[58.326,106.281],[12.028,125.952],[-35.105,105.616],[-40.582,23.524]],"c":true}]},{"i":{"x":0.3,"y":1},"o":{"x":0.167,"y":0.167},"t":7,"s":[{"i":[[-4.018,5.73],[-11.874,0.331],[-0.482,-11.186],[1.315,-6.931],[7.788,-13.027],[17.423,0],[7.066,15.289],[-0.885,26.553]],"o":[[3.929,-18.735],[44.497,-1.242],[7.668,7.255],[0,0],[-6.004,13.318],[-17.423,0],[-6.92,-11.998],[0.031,-13.41]],"v":[[-38.5,-13.268],[1.196,-38.483],[57.046,-13.793],[59.603,29.448],[54.608,95.788],[11.134,118.049],[-33.812,94.617],[-40.339,27.685]],"c":true}]},{"t":14,"s":[{"i":[[-0.661,9.718],[-13.678,-0.71],[-0.721,-16.729],[0.48,-9.722],[8.649,-11.342],[16.096,0],[7.247,15.59],[-0.48,10.861]],"o":[[1.962,-28.86],[25.305,1.314],[0.467,10.849],[0,0],[-5.101,11.975],[-16.096,0],[-8.168,-13.094],[0.521,-13.007]],"v":[[-37.657,-18.701],[9.27,-58.29],[57.118,-21.416],[57.118,33.106],[50.351,83.775],[10.111,109],[-32.332,82.023],[-37.817,32.45]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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":"Fill 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":[100,100],"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":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":13,"st":-2,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Line","parent":5,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[3.687,-6.898,0],"ix":2},"a":{"a":0,"k":[3.6,-7,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.4,"y":1},"o":{"x":0.1,"y":0},"t":8,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-87.744,-97.736],[94.944,83.736]],"c":false}]},{"t":24,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-101.6,-111.5],[108.8,97.5]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.4],"y":[1]},"o":{"x":[0.2],"y":[0]},"t":8.143,"s":[65]},{"t":23.857421875,"s":[0]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.4],"y":[1]},"o":{"x":[0.2],"y":[0]},"t":8,"s":[22]},{"t":23.857421875,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":22,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 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":[100,100],"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":"Transform"}],"nm":"Path-10","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":15,"op":36,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Head Mic","parent":6,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-3.6,-33.55,0],"ix":2},"a":{"a":0,"k":[-3.6,-33.55,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.833,"y":0.833},"o":{"x":0.32,"y":0},"t":12,"s":[{"i":[[-26.211,0],[-0.75,-24.287],[0,0],[8.887,-9.825],[0,0],[0,0],[0,0]],"o":[[26.211,0],[0,0],[0,11.606],[0,0],[0,0],[0,0],[-1,-21.796]],"v":[[-3.735,-109.75],[45.281,-65.463],[43.991,8.35],[34.343,45.154],[-28.222,-7.666],[-50.958,-26.52],[-50.969,-65.204]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":14,"s":[{"i":[[-25.4,0],[0,-25.4],[0,0],[3.254,-6.84],[0,0],[0,0],[0,0]],"o":[[25.4,0],[0,0],[0,11.4],[0,0],[0,0],[0,0],[0,-25.4]],"v":[[-3.715,-109.619],[43.599,-65.808],[42.504,7.127],[37.527,30.34],[-24.609,-26.91],[-49.585,-52.154],[-50.153,-65.37]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":15,"s":[{"i":[[-25.4,0],[0,-25.4],[0,0],[2.173,-4.255],[0,0],[0,0],[0,0]],"o":[[25.4,0],[0,0],[0,11.4],[0,0],[0,0],[0,0],[0,-25.4]],"v":[[-3.699,-110.688],[43.436,-66.58],[42.49,6.771],[35.692,29.42],[-20.918,-24.458],[-49.597,-54.517],[-50.078,-66.202]],"c":true}]},{"i":{"x":0.4,"y":1},"o":{"x":0.167,"y":0.167},"t":17,"s":[{"i":[[-25.4,0],[0,-25.4],[0,0],[2.511,-3.584],[0,0],[0,0],[0,0]],"o":[[25.4,0],[0,0],[0,11.4],[0,0],[0,0],[0,0],[0,-25.4]],"v":[[-3.668,-112.826],[43.111,-68.124],[42.462,6.058],[35.521,27.954],[-13.534,-19.556],[-49.621,-59.243],[-49.928,-67.865]],"c":true}]},{"t":25,"s":[{"i":[[-25.4,0],[0,-25.4],[0,0],[6.9,-8],[0,0],[0,0],[0,0]],"o":[[25.4,0],[0,0],[0,11.4],[0,0],[0,0],[0,0],[0,-25.4]],"v":[[-3.6,-117.5],[42.4,-71.5],[42.4,4.5],[31.3,34.4],[-20.33,-17.23],[-49.6,-46.5],[-49.6,-71.5]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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":"Fill 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":[100,100],"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":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.32,"y":0},"t":12,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[6.088,0],[-0.503,31.164],[0,0]],"o":[[0,0],[0,0],[0,0],[-10.319,13.723],[-26.211,0],[0,0],[0,0]],"v":[[-50.984,-30.364],[-31.444,-15.166],[-26.333,-11.224],[33.943,44.851],[-3.477,59],[-50.945,10.019],[-50.958,-21.446]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":13,"s":[{"i":[[0,0],[-0.064,2.378],[0,0],[0,0],[6.019,0],[-0.47,30.673],[0,0]],"o":[[0,0],[0.089,-3.285],[0,0],[-10.163,17.808],[-25.911,0],[0,0],[0,0]],"v":[[-50.431,-27.196],[-50.235,-36.056],[-34.964,-26.612],[35.415,38.984],[-3.485,57.977],[-50.411,9.433],[-50.391,-18.196]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":14,"s":[{"i":[[0,0],[-0.174,6.439],[0,0],[0,0],[5.9,0],[-0.415,29.833],[0,0]],"o":[[0,0],[0.241,-8.895],[0,0],[-9.897,24.787],[-25.4,0],[0,0],[0,0]],"v":[[-49.489,-21.787],[-49.835,-35.855],[-49.705,-52.894],[37.928,28.963],[-3.499,56.229],[-49.499,8.433],[-49.422,-12.646]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":15,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[5.9,0],[-0.361,29.258],[0,0]],"o":[[0,0],[0,0],[0,0],[-5.945,8.024],[-25.4,0],[0,0],[0,0]],"v":[[-49.614,-15.443],[-16.569,15.355],[-4.062,27.479],[16.726,47.226],[-3.512,55.472],[-49.512,7.909],[-49.493,-12.332]],"c":true}]},{"i":{"x":0.4,"y":1},"o":{"x":0.167,"y":0.167},"t":17,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[5.9,0],[-0.253,28.106],[0,0]],"o":[[0,0],[0,0],[0,0],[-4.54,4.12],[-25.4,0],[0,0],[0,0]],"v":[[-49.647,-16.255],[-37.036,-2.726],[-33.526,0.224],[15.322,49.25],[-3.538,53.958],[-49.538,6.862],[-49.634,-11.706]],"c":true}]},{"t":25,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[5.9,0],[0,25.4],[0,0]],"o":[[0,0],[0,0],[0,0],[-5.2,2],[-25.4,0],[0,0],[0,0]],"v":[[-49.6,-15.4],[-40.702,-6.502],[-38.75,-4.55],[13.1,47.3],[-3.6,50.4],[-49.6,4.4],[-49.6,-15.4]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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":"Fill 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":[100,100],"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":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":12,"op":36,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"Arc L","parent":8,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.5,"y":1},"o":{"x":0.05,"y":0},"t":15,"s":[-3.781,80.5,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.69,"y":1},"o":{"x":0.31,"y":0},"t":25,"s":[-3.781,89.75,0],"to":[0,0,0],"ti":[0,0,0]},{"t":33,"s":[-3.75,84.5,0]}],"ix":2},"a":{"a":0,"k":[-3.75,84.5,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.4,"y":1},"o":{"x":0.1,"y":0},"t":12,"s":[{"i":[[0,0],[13.1,0],[-24.469,59.5]],"o":[[-11,5.1],[-45.3,0],[0,0]],"v":[[32,72.5],[-6,80],[-74,-20]],"c":false}]},{"t":26,"s":[{"i":[[0,0],[13.1,0],[0,42]],"o":[[-11,5.1],[-45.3,0],[0,0]],"v":[[33,76.5],[-3.5,84.5],[-85.5,6.5]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":22,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 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":[100,100],"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":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":12,"op":36,"st":0,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":"Arc R","parent":6,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[66.05,51.45,0],"ix":2},"a":{"a":0,"k":[66.05,51.45,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.833,"y":0.833},"o":{"x":0.1,"y":0},"t":12,"s":[{"i":[[0,0],[39.331,-15]],"o":[[9.631,29.1],[0,0]],"v":[[68.4,-20.1],[39.2,68.5]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":15,"s":[{"i":[[0,0],[38.51,-16.677]],"o":[[5.914,24.45],[0,0]],"v":[[72.925,-10.242],[39.451,70.103]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":16,"s":[{"i":[[0,0],[10.82,-9.511]],"o":[[4.75,22.994],[0,0]],"v":[[74.342,-7.154],[65.462,49.587]],"c":false}]},{"i":{"x":0.4,"y":1},"o":{"x":0.167,"y":0.167},"t":19,"s":[{"i":[[0,0],[13.536,-11.424]],"o":[[1.257,18.625],[0,0]],"v":[[78.594,2.109],[61.495,50.04]],"c":false}]},{"t":26,"s":[{"i":[[0,0],[8.7,-12]],"o":[[0,15.6],[0,0]],"v":[[78.4,8.4],[64.7,50.5]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":22,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 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":[100,100],"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":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":12,"op":36,"st":0,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":"Leg","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[252.45,380.6,0],"ix":2},"a":{"a":0,"k":[-3.55,124.6,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.4,"y":1},"o":{"x":0.1,"y":0},"t":11,"s":[{"i":[[-11.972,0],[0,0],[0,-2.631],[0,0],[11.972,0],[0,0],[0,2.631],[0,0]],"o":[[0,0],[11.972,0],[0,0],[0,2.631],[0,0],[-11.972,0],[0,0],[0,-2.631]],"v":[[-6.686,71.5],[-0.414,71.5],[21.25,76.26],[21.25,85.99],[-0.414,90.75],[-6.686,90.75],[-28.35,85.99],[-28.35,76.26]],"c":true}]},{"i":{"x":0.69,"y":1},"o":{"x":0.31,"y":0},"t":20,"s":[{"i":[[-6.3,0],[0,0],[0,-4.763],[0,0],[6.3,0],[0,0],[0,4.763],[0,0]],"o":[[0,0],[6.3,0],[0,0],[0,4.763],[0,0],[-6.3,0],[0,0],[0,-4.763]],"v":[[-5.2,89.75],[-1.9,89.75],[9.5,98.368],[9.5,115.982],[-1.9,124.6],[-5.2,124.6],[-16.6,115.982],[-16.6,98.368]],"c":true}]},{"t":33,"s":[{"i":[[-6.3,0],[0,0],[0,-6.3],[0,0],[6.3,0],[0,0],[0,6.3],[0,0]],"o":[[0,0],[6.3,0],[0,0],[0,6.3],[0,0],[-6.3,0],[0,0],[0,-6.3]],"v":[[-5.2,78.5],[-1.9,78.5],[9.5,89.9],[9.5,113.2],[-1.9,124.6],[-5.2,124.6],[-16.6,113.2],[-16.6,89.9]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","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":"Fill 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":[100,100],"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":"Transform"}],"nm":"Rectangle","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":11,"op":36,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":0,"nm":"Start Chat to Mute Android 3","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[30,30,0],"ix":2},"a":{"a":0,"k":[30,30,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"w":60,"h":60,"ip":376,"op":405,"st":376,"bm":0},{"ddd":0,"ind":2,"ty":0,"nm":"Cancel Reminder to Mute Android","refId":"comp_2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[30,30,0],"ix":2},"a":{"a":0,"k":[30,30,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"w":60,"h":60,"ip":344,"op":376,"st":344,"bm":0},{"ddd":0,"ind":3,"ty":0,"nm":"Set Reminder Android","refId":"comp_4","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[30,30,0],"ix":2},"a":{"a":0,"k":[30,30,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"w":60,"h":60,"ip":311,"op":344,"st":311,"bm":0},{"ddd":0,"ind":4,"ty":0,"nm":"Cancel Reminder to Rise Hand","refId":"comp_6","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[30,30,0],"ix":2},"a":{"a":0,"k":[30,30,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"w":60,"h":60,"ip":274,"op":311,"st":274,"bm":0},{"ddd":0,"ind":5,"ty":0,"nm":"Set Reminder to Rise Hand","refId":"comp_8","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[30,30,0],"ix":2},"a":{"a":0,"k":[30,30,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"w":60,"h":60,"ip":237,"op":274,"st":237,"bm":0},{"ddd":0,"ind":6,"ty":0,"nm":"Set Reminder to Mute","refId":"comp_10","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[30,30,0],"ix":2},"a":{"a":0,"k":[30,30,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"w":60,"h":60,"ip":202,"op":237,"st":202,"bm":0},{"ddd":0,"ind":7,"ty":0,"nm":"Cancel Reminder","refId":"comp_12","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[30,30,0],"ix":2},"a":{"a":0,"k":[30,30,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"w":60,"h":60,"ip":173,"op":202,"st":173,"bm":0},{"ddd":0,"ind":8,"ty":0,"nm":"Unmute to Rise Hand","refId":"comp_14","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[30,30,0],"ix":2},"a":{"a":0,"k":[30,30,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"w":60,"h":60,"ip":136,"op":173,"st":136,"bm":0},{"ddd":0,"ind":9,"ty":0,"nm":"Mute to Rise Hand","refId":"comp_16","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[30,30,0],"ix":2},"a":{"a":0,"k":[30,30,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"w":60,"h":60,"ip":99,"op":136,"st":99,"bm":0},{"ddd":0,"ind":10,"ty":0,"nm":"Mute","refId":"comp_18","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[30,30,0],"ix":2},"a":{"a":0,"k":[30,30,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"w":60,"h":60,"ip":69,"op":99,"st":69,"bm":0},{"ddd":0,"ind":11,"ty":0,"nm":"Unmute Android","refId":"comp_20","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[30,30,0],"ix":2},"a":{"a":0,"k":[30,30,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"w":60,"h":60,"ip":36,"op":69,"st":36,"bm":0},{"ddd":0,"ind":12,"ty":0,"nm":"Raise Hand to Mute","refId":"comp_22","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[30,30,0],"ix":2},"a":{"a":0,"k":[30,30,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"w":60,"h":60,"ip":0,"op":36,"st":0,"bm":0}],"markers":[]} \ No newline at end of file diff --git a/Telegram/Resources/icons/inline_button_card.png b/Telegram/Resources/icons/inline_button_card.png new file mode 100644 index 000000000..ce28e2188 Binary files /dev/null and b/Telegram/Resources/icons/inline_button_card.png differ diff --git a/Telegram/Resources/icons/inline_button_card@2x.png b/Telegram/Resources/icons/inline_button_card@2x.png new file mode 100644 index 000000000..6c550d573 Binary files /dev/null and b/Telegram/Resources/icons/inline_button_card@2x.png differ diff --git a/Telegram/Resources/icons/inline_button_card@3x.png b/Telegram/Resources/icons/inline_button_card@3x.png new file mode 100644 index 000000000..0e4758882 Binary files /dev/null and b/Telegram/Resources/icons/inline_button_card@3x.png differ diff --git a/Telegram/Resources/icons/inline_button_switch.png b/Telegram/Resources/icons/inline_button_switch.png index 04c858512..af74ace51 100644 Binary files a/Telegram/Resources/icons/inline_button_switch.png and b/Telegram/Resources/icons/inline_button_switch.png differ diff --git a/Telegram/Resources/icons/inline_button_switch@2x.png b/Telegram/Resources/icons/inline_button_switch@2x.png index 9d805c979..72b7fd4d3 100644 Binary files a/Telegram/Resources/icons/inline_button_switch@2x.png and b/Telegram/Resources/icons/inline_button_switch@2x.png differ diff --git a/Telegram/Resources/icons/inline_button_switch@3x.png b/Telegram/Resources/icons/inline_button_switch@3x.png index 428d328c2..3a5c4f043 100644 Binary files a/Telegram/Resources/icons/inline_button_switch@3x.png and b/Telegram/Resources/icons/inline_button_switch@3x.png differ diff --git a/Telegram/Resources/icons/inline_button_url.png b/Telegram/Resources/icons/inline_button_url.png index 986d15cda..33b1061d1 100644 Binary files a/Telegram/Resources/icons/inline_button_url.png and b/Telegram/Resources/icons/inline_button_url.png differ diff --git a/Telegram/Resources/icons/inline_button_url@2x.png b/Telegram/Resources/icons/inline_button_url@2x.png index 0b364c03c..b0579663b 100644 Binary files a/Telegram/Resources/icons/inline_button_url@2x.png and b/Telegram/Resources/icons/inline_button_url@2x.png differ diff --git a/Telegram/Resources/icons/inline_button_url@3x.png b/Telegram/Resources/icons/inline_button_url@3x.png index 801baba44..d6887d45a 100644 Binary files a/Telegram/Resources/icons/inline_button_url@3x.png and b/Telegram/Resources/icons/inline_button_url@3x.png differ diff --git a/Telegram/Resources/icons/payments/payment_address.png b/Telegram/Resources/icons/payments/payment_address.png new file mode 100644 index 000000000..dcc9b2f81 Binary files /dev/null and b/Telegram/Resources/icons/payments/payment_address.png differ diff --git a/Telegram/Resources/icons/payments/payment_address@2x.png b/Telegram/Resources/icons/payments/payment_address@2x.png new file mode 100644 index 000000000..5b4cea3d3 Binary files /dev/null and b/Telegram/Resources/icons/payments/payment_address@2x.png differ diff --git a/Telegram/Resources/icons/payments/payment_address@3x.png b/Telegram/Resources/icons/payments/payment_address@3x.png new file mode 100644 index 000000000..f15568019 Binary files /dev/null and b/Telegram/Resources/icons/payments/payment_address@3x.png differ diff --git a/Telegram/Resources/icons/payments/payment_card.png b/Telegram/Resources/icons/payments/payment_card.png new file mode 100644 index 000000000..6e80687c6 Binary files /dev/null and b/Telegram/Resources/icons/payments/payment_card.png differ diff --git a/Telegram/Resources/icons/payments/payment_card@2x.png b/Telegram/Resources/icons/payments/payment_card@2x.png new file mode 100644 index 000000000..1fd98a77c Binary files /dev/null and b/Telegram/Resources/icons/payments/payment_card@2x.png differ diff --git a/Telegram/Resources/icons/payments/payment_card@3x.png b/Telegram/Resources/icons/payments/payment_card@3x.png new file mode 100644 index 000000000..ae50fe67d Binary files /dev/null and b/Telegram/Resources/icons/payments/payment_card@3x.png differ diff --git a/Telegram/Resources/icons/payments/payment_email.png b/Telegram/Resources/icons/payments/payment_email.png new file mode 100644 index 000000000..41154157b Binary files /dev/null and b/Telegram/Resources/icons/payments/payment_email.png differ diff --git a/Telegram/Resources/icons/payments/payment_email@2x.png b/Telegram/Resources/icons/payments/payment_email@2x.png new file mode 100644 index 000000000..d6b6ab150 Binary files /dev/null and b/Telegram/Resources/icons/payments/payment_email@2x.png differ diff --git a/Telegram/Resources/icons/payments/payment_email@3x.png b/Telegram/Resources/icons/payments/payment_email@3x.png new file mode 100644 index 000000000..5f113ae76 Binary files /dev/null and b/Telegram/Resources/icons/payments/payment_email@3x.png differ diff --git a/Telegram/Resources/icons/payments/payment_name.png b/Telegram/Resources/icons/payments/payment_name.png new file mode 100644 index 000000000..fdeb96631 Binary files /dev/null and b/Telegram/Resources/icons/payments/payment_name.png differ diff --git a/Telegram/Resources/icons/payments/payment_name@2x.png b/Telegram/Resources/icons/payments/payment_name@2x.png new file mode 100644 index 000000000..2bcacb374 Binary files /dev/null and b/Telegram/Resources/icons/payments/payment_name@2x.png differ diff --git a/Telegram/Resources/icons/payments/payment_name@3x.png b/Telegram/Resources/icons/payments/payment_name@3x.png new file mode 100644 index 000000000..23d2ee243 Binary files /dev/null and b/Telegram/Resources/icons/payments/payment_name@3x.png differ diff --git a/Telegram/Resources/icons/payments/payment_phone.png b/Telegram/Resources/icons/payments/payment_phone.png new file mode 100644 index 000000000..87b185d8c Binary files /dev/null and b/Telegram/Resources/icons/payments/payment_phone.png differ diff --git a/Telegram/Resources/icons/payments/payment_phone@2x.png b/Telegram/Resources/icons/payments/payment_phone@2x.png new file mode 100644 index 000000000..d047fb5b4 Binary files /dev/null and b/Telegram/Resources/icons/payments/payment_phone@2x.png differ diff --git a/Telegram/Resources/icons/payments/payment_phone@3x.png b/Telegram/Resources/icons/payments/payment_phone@3x.png new file mode 100644 index 000000000..ff201d3e8 Binary files /dev/null and b/Telegram/Resources/icons/payments/payment_phone@3x.png differ diff --git a/Telegram/Resources/icons/payments/payment_shipping.png b/Telegram/Resources/icons/payments/payment_shipping.png new file mode 100644 index 000000000..aa618f5fb Binary files /dev/null and b/Telegram/Resources/icons/payments/payment_shipping.png differ diff --git a/Telegram/Resources/icons/payments/payment_shipping@2x.png b/Telegram/Resources/icons/payments/payment_shipping@2x.png new file mode 100644 index 000000000..3315d9a9c Binary files /dev/null and b/Telegram/Resources/icons/payments/payment_shipping@2x.png differ diff --git a/Telegram/Resources/icons/payments/payment_shipping@3x.png b/Telegram/Resources/icons/payments/payment_shipping@3x.png new file mode 100644 index 000000000..8b08421d0 Binary files /dev/null and b/Telegram/Resources/icons/payments/payment_shipping@3x.png differ diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 2b8212d73..76d1acc28 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -216,6 +216,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_code_desc" = "We have sent you a message with activation\ncode to your phone. Please enter it below."; "lng_code_from_telegram" = "Please enter the code you've just received\nin your previous **Telegram** app."; "lng_code_no_telegram" = "Send code via SMS"; +"lng_code_register_phone" = "If you already signed up for Telegram, please enter the code which was sent to your mobile app.\n\nIf you haven’t signed up yet, please register from your phone or tablet first."; "lng_code_call" = "Telegram will call you in {minutes}:{seconds}"; "lng_code_calling" = "Requesting a call from Telegram..."; "lng_code_called" = "Telegram dialed your number"; @@ -453,6 +454,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_settings_sensitive_title" = "Sensitive content"; "lng_settings_sensitive_disable_filtering" = "Disable filtering"; "lng_settings_sensitive_about" = "Display sensitive media in public channels on all your Telegram devices."; +"lng_settings_security_bots" = "Bots and websites"; +"lng_settings_clear_payment_info" = "Clear Payment and Shipping Info"; + +"lng_clear_payment_info_title" = "Clear payment info"; +"lng_clear_payment_info_sure" = "Are you sure you want to clear your payment and shipping info?"; +"lng_clear_payment_info_shipping" = "Shipping info"; +"lng_clear_payment_info_payment" = "Payment info"; +"lng_clear_payment_info_clear" = "Clear"; +"lng_clear_payment_info_confirm" = "Delete your shipping info and instruct all payment providers to remove your saved credit cards? Note that Telegram never stores your credit card data."; "lng_settings_auto_night_mode" = "Auto-Night mode"; "lng_settings_auto_night_enabled" = "Match the system settings"; @@ -1097,8 +1107,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_action_invite_user_chat" = "the voice chat"; "lng_action_invite_users_and_one" = "{accumulated}, {user}"; "lng_action_invite_users_and_last" = "{accumulated} and {user}"; -"lng_action_group_call_started" = "{from} started {chat}"; -"lng_action_group_call_started_chat" = "a voice chat"; +"lng_action_group_call_started_group" = "{from} started a voice chat"; +"lng_action_group_call_started_channel" = "Voice chat started"; +"lng_action_group_call_scheduled_group" = "{from} scheduled a voice chat for {date}"; +"lng_action_group_call_scheduled_channel" = "Voice chat scheduled for {date}"; "lng_action_group_call_finished" = "Voice chat finished ({duration})"; "lng_action_add_user" = "{from} added {user}"; "lng_action_add_users_many" = "{from} added {users}"; @@ -1856,11 +1868,66 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_theme_editor_menu_show" = "Show palette file"; "lng_payments_not_supported" = "Sorry, Telegram Desktop doesn't support payments yet. Please use one of our mobile apps to do this."; +"lng_payments_webview_no_card" = "Unfortunately, you can't add a new card with current system configuration."; +"lng_payments_webview_no_use" = "Unfortunately, you can't use payments with current system configuration."; +"lng_payments_webview_install_edge" = "Please install {link}."; +"lng_payments_webview_install_webkit" = "Please install WebKitGTK 4 (webkit2gtk-4.0) using your package manager."; +"lng_payments_webview_switch_mutter" = "Qt's window embedding doesn't work well with Mutter window manager. Please switch to another window manager or desktop environment."; +"lng_payments_webview_switch_wayland" = "There is no way to embed WebView window on Wayland. Please switch to X11."; +"lng_payments_sure_close" = "Are you sure you want to close this payment form? The changes you made will be lost."; "lng_payments_receipt_label" = "Receipt"; "lng_payments_receipt_label_test" = "Test receipt"; "lng_payments_invoice_label" = "Invoice"; "lng_payments_invoice_label_test" = "Test invoice"; "lng_payments_receipt_button" = "Receipt"; +"lng_payments_success" = "You paid {amount} for {title}."; + +"lng_payments_checkout_title" = "Checkout"; +"lng_payments_receipt_title" = "Receipt"; +"lng_payments_total_label" = "Total"; +"lng_payments_date_label" = "Paid"; +"lng_payments_pay_amount" = "Pay {amount}"; +"lng_payments_payment_method" = "Payment Method"; +"lng_payments_new_card" = "New Card..."; +"lng_payments_shipping_address" = "Shipping Address"; +"lng_payments_address_street1" = "Address 1"; +"lng_payments_address_street2" = "Address 2"; +"lng_payments_address_city" = "City"; +"lng_payments_address_state" = "State"; +"lng_payments_address_country" = "Country"; +"lng_payments_address_postcode" = "Postcode"; + +"lng_payments_shipping_method" = "Shipping Method"; +"lng_payments_info_name" = "Name"; +"lng_payments_info_email" = "Email"; +"lng_payments_info_phone" = "Phone"; +"lng_payments_to_provider_phone_email" = "Phone and Email will be passed to {provider} as billing info."; +"lng_payments_to_provider_email" = "Email will be passed to {provider} as billing info."; +"lng_payments_to_provider_phone" = "Phone will be passed to {provider} as billing info."; +"lng_payments_processed_by" = "Processed by {provider}"; +"lng_payments_warning_title" = "Warning"; +"lng_payments_warning_body" = "Neither Telegram, nor {bot1} will have access to your credit card information. Credit card details will be handled only by the payment system, {provider}.\n\nPayments will go directly to the developer of {bot2}. Telegram cannot provide any guarantees, so proceed at your own risk. In case of problems, please contact the developer of {bot3} or your bank."; +"lng_payments_shipping_address_title" = "Shipping Information"; +"lng_payments_card_title" = "New Card"; +"lng_payments_card_number" = "Card Number"; +"lng_payments_card_holder" = "Cardholder name"; +"lng_payments_billing_address" = "Billing Information"; +"lng_payments_billing_country" = "Country"; +"lng_payments_billing_zip_code" = "Zip Code"; +"lng_payments_save_information" = "Save Information for future use"; +"lng_payments_need_password" = "You can save your payment information for future use. Please turn on Two-Step Verification to enable this."; +"lng_payments_password_title" = "Payment Confirmation"; +"lng_payments_password_description" = "Your card {card} is on file. To pay with this card, please enter your 2-Step-Verification password."; +"lng_payments_password_submit" = "Pay"; +"lng_payments_tips_label" = "Tip (Optional)"; +"lng_payments_tips_box_title" = "Add Tip"; +"lng_payments_tips_max" = "Max possible tip amount: {amount}"; + +"lng_payments_shipping_not_available" = "Shipping to the selected country is not available."; +"lng_payments_card_declined" = "Your card was declined."; +"lng_payments_payment_failed" = "Payment failed. Your card has not been billed."; +"lng_payments_precheckout_failed" = "The bot couldn't process your payment. Your card has not been billed."; +"lng_payments_already_paid" = "You have already paid for this item."; "lng_call_status_incoming" = "is calling you..."; "lng_call_status_connecting" = "connecting..."; @@ -1927,6 +1994,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_group_call_inactive" = "listening"; "lng_group_call_raised_hand_status" = "wants to speak"; "lng_group_call_settings" = "Settings"; +"lng_group_call_share_button" = "Share"; "lng_group_call_unmute" = "Unmute"; "lng_group_call_unmute_sub" = "or hold spacebar to talk"; "lng_group_call_you_are_live" = "You are Live"; @@ -1939,6 +2007,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_group_call_leave" = "Leave"; "lng_group_call_leave_title" = "Leave voice chat"; "lng_group_call_leave_sure" = "Are you sure you want to leave this voice chat?"; +"lng_group_call_close" = "Close"; +"lng_group_call_close_sure" = "Voice chat is scheduled. You can abort it or just close this panel."; +"lng_group_call_also_cancel" = "Abort voice chat"; "lng_group_call_leave_to_other_sure" = "Do you want to leave your active voice chat and join a voice chat in this group?"; "lng_group_call_create_sure" = "Do you really want to start a voice chat in this group?"; "lng_group_call_create_sure_channel" = "Are you sure you want to start a voice chat in this channel as your personal account?"; @@ -1969,6 +2040,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_group_call_copy_speaker_link" = "Copy Speaker Link"; "lng_group_call_copy_listener_link" = "Copy Listener Link"; "lng_group_call_end" = "End Voice Chat"; +"lng_group_call_cancel" = "Abort Voice Chat"; "lng_group_call_join" = "Join"; "lng_group_call_join_confirm" = "Do you want to join the voice chat {chat}?"; "lng_group_call_invite_done_user" = "You invited {user} to the voice chat."; @@ -2003,6 +2075,27 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_group_call_join_as_header" = "Join Voice Chat as..."; "lng_group_call_display_as_header" = "Display me as..."; "lng_group_call_join_as_about" = "Choose whether you want to be displayed as your personal account or as your channel."; +"lng_group_call_or_schedule" = "You can also {link}."; +"lng_group_call_schedule" = "schedule a voice chat"; +"lng_group_call_schedule_title" = "Schedule Voice Chat"; +"lng_group_call_schedule_notified_group" = "The members of the group will be notified that the voice chat will start in {duration}."; +"lng_group_call_schedule_notified_channel" = "The subscribers of the channel will be notified that the voice chat will start in {duration}."; +"lng_group_call_scheduled_status" = "Scheduled"; +"lng_group_call_scheduled_title" = "Scheduled Voice Chat"; +"lng_group_call_starts_short" = "Starts {when}"; +"lng_group_call_starts" = "Voice Chat starts {when}"; +"lng_group_call_starts_today" = "today at {time}"; +"lng_group_call_starts_tomorrow" = "tomorrow at {time}"; +"lng_group_call_starts_date" = "{date} at {time}"; +"lng_group_call_starts_in" = "Starts in"; +"lng_group_call_late_by" = "Late by"; +"lng_group_call_starts_short_today" = "Today, {time}"; +"lng_group_call_starts_short_tomorrow" = "Tomorrow, {time}"; +"lng_group_call_starts_short_date" = "{date}, {time}"; +"lng_group_call_start_now" = "Start Now"; +"lng_group_call_start_now_sure" = "Are you sure you want to start the voice chat now?"; +"lng_group_call_set_reminder" = "Set Reminder"; +"lng_group_call_cancel_reminder" = "Cancel Reminder"; "lng_group_call_join_as_personal" = "personal account"; "lng_group_call_edit_title" = "Edit voice chat title"; "lng_group_call_switch_done" = "Members of this voice chat will now see you as **{user}**"; diff --git a/Telegram/Resources/qrc/telegram/telegram.qrc b/Telegram/Resources/qrc/telegram/telegram.qrc index b6758593b..86d89ba47 100644 --- a/Telegram/Resources/qrc/telegram/telegram.qrc +++ b/Telegram/Resources/qrc/telegram/telegram.qrc @@ -69,9 +69,8 @@ ../../day-blue.tdesktop-theme ../../night.tdesktop-theme ../../night-green.tdesktop-theme - ../../icons/calls/active_hand.json - ../../icons/calls/hand_muted_active.json - ../../icons/calls/raised_hand.json + ../../icons/calls/hands.lottie + ../../icons/calls/voice.lottie ../qmime/freedesktop.org.xml diff --git a/Telegram/Resources/tl/api.tl b/Telegram/Resources/tl/api.tl index 22bc8cf22..93d30d008 100644 --- a/Telegram/Resources/tl/api.tl +++ b/Telegram/Resources/tl/api.tl @@ -68,7 +68,7 @@ inputMediaVenue#c13d1c11 geo_point:InputGeoPoint title:string address:string pro inputMediaPhotoExternal#e5bbfe1a flags:# url:string ttl_seconds:flags.0?int = InputMedia; inputMediaDocumentExternal#fb52dc99 flags:# url:string ttl_seconds:flags.0?int = InputMedia; inputMediaGame#d33f43f3 id:InputGame = InputMedia; -inputMediaInvoice#f4e096c3 flags:# title:string description:string photo:flags.0?InputWebDocument invoice:Invoice payload:bytes provider:string provider_data:DataJSON start_param:string = InputMedia; +inputMediaInvoice#f4e096c3 flags:# multiple_allowed:flags.1?true can_forward:flags.2?true title:string description:string photo:flags.0?InputWebDocument invoice:Invoice payload:bytes provider:string provider_data:DataJSON start_param:string = InputMedia; inputMediaGeoLive#971fa843 flags:# stopped:flags.0?true geo_point:InputGeoPoint heading:flags.2?int period:flags.1?int proximity_notification_radius:flags.3?int = InputMedia; inputMediaPoll#f94e5f1 flags:# poll:Poll correct_answers:flags.0?Vector solution:flags.1?string solution_entities:flags.1?Vector = InputMedia; inputMediaDice#e66fbf7b emoticon:string = InputMedia; @@ -90,8 +90,8 @@ inputSecureFileLocation#cbc7ee28 id:long access_hash:long = InputFileLocation; inputTakeoutFileLocation#29be5899 = InputFileLocation; inputPhotoFileLocation#40181ffe id:long access_hash:long file_reference:bytes thumb_size:string = InputFileLocation; inputPhotoLegacyFileLocation#d83466f3 id:long access_hash:long file_reference:bytes volume_id:long local_id:int secret:long = InputFileLocation; -inputPeerPhotoFileLocation#27d69997 flags:# big:flags.0?true peer:InputPeer volume_id:long local_id:int = InputFileLocation; -inputStickerSetThumb#dbaeae9 stickerset:InputStickerSet volume_id:long local_id:int = InputFileLocation; +inputPeerPhotoFileLocation#37257e99 flags:# big:flags.0?true peer:InputPeer photo_id:long = InputFileLocation; +inputStickerSetThumb#9d84f3db stickerset:InputStickerSet thumb_version:int = InputFileLocation; inputGroupCallStream#bba51639 call:InputGroupCall time_ms:long scale:int = InputFileLocation; peerUser#9db1bc6d user_id:int = Peer; @@ -113,7 +113,7 @@ userEmpty#200250ba id:int = User; user#938458c1 flags:# self:flags.10?true contact:flags.11?true mutual_contact:flags.12?true deleted:flags.13?true bot:flags.14?true bot_chat_history:flags.15?true bot_nochats:flags.16?true verified:flags.17?true restricted:flags.18?true min:flags.20?true bot_inline_geo:flags.21?true support:flags.23?true scam:flags.24?true apply_min_photo:flags.25?true fake:flags.26?true id:int access_hash:flags.0?long first_name:flags.1?string last_name:flags.2?string username:flags.3?string phone:flags.4?string photo:flags.5?UserProfilePhoto status:flags.6?UserStatus bot_info_version:flags.14?int restriction_reason:flags.18?Vector bot_inline_placeholder:flags.19?string lang_code:flags.22?string = User; userProfilePhotoEmpty#4f11bae1 = UserProfilePhoto; -userProfilePhoto#69d3ab26 flags:# has_video:flags.0?true photo_id:long photo_small:FileLocation photo_big:FileLocation dc_id:int = UserProfilePhoto; +userProfilePhoto#82d1f706 flags:# has_video:flags.0?true photo_id:long stripped_thumb:flags.1?bytes dc_id:int = UserProfilePhoto; userStatusEmpty#9d05049 = UserStatus; userStatusOnline#edb93949 expires:int = UserStatus; @@ -139,7 +139,7 @@ chatParticipantsForbidden#fc900c2b flags:# chat_id:int self_participant:flags.0? chatParticipants#3f460fed chat_id:int participants:Vector version:int = ChatParticipants; chatPhotoEmpty#37c1011c = ChatPhoto; -chatPhoto#d20b9f3c flags:# has_video:flags.0?true photo_small:FileLocation photo_big:FileLocation dc_id:int = ChatPhoto; +chatPhoto#1c6e1c11 flags:# has_video:flags.0?true photo_id:long stripped_thumb:flags.1?bytes dc_id:int = ChatPhoto; messageEmpty#90a6ca84 flags:# id:int peer_id:flags.0?Peer = Message; message#bce383d2 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true from_scheduled:flags.18?true legacy:flags.19?true edit_hide:flags.21?true pinned:flags.24?true id:int from_id:flags.8?Peer peer_id:Peer fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?int reply_to:flags.3?MessageReplyHeader date:int message:string media:flags.9?MessageMedia reply_markup:flags.6?ReplyMarkup entities:flags.7?Vector views:flags.10?int forwards:flags.10?int replies:flags.23?MessageReplies edit_date:flags.15?int post_author:flags.16?string grouped_id:flags.17?long restriction_reason:flags.22?Vector ttl_period:flags.25?int = Message; @@ -186,6 +186,7 @@ messageActionGeoProximityReached#98e0d697 from_id:Peer to_id:Peer distance:int = messageActionGroupCall#7a0d7f42 flags:# call:InputGroupCall duration:flags.0?int = MessageAction; messageActionInviteToGroupCall#76b9f11a call:InputGroupCall users:Vector = MessageAction; messageActionSetMessagesTTL#aa1afbfd period:int = MessageAction; +messageActionGroupCallScheduled#b3a07661 call:InputGroupCall schedule_date:int = MessageAction; dialog#2c171f72 flags:# pinned:flags.2?true unread_mark:flags.3?true peer:Peer top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_mentions_count:int notify_settings:PeerNotifySettings pts:flags.0?int draft:flags.1?DraftMessage folder_id:flags.4?int = Dialog; dialogFolder#71bd134c flags:# pinned:flags.2?true folder:Folder peer:Peer top_message:int unread_muted_peers_count:int unread_unmuted_peers_count:int unread_muted_messages_count:int unread_unmuted_messages_count:int = Dialog; @@ -194,10 +195,10 @@ photoEmpty#2331b22d id:long = Photo; photo#fb197a65 flags:# has_stickers:flags.0?true id:long access_hash:long file_reference:bytes date:int sizes:Vector video_sizes:flags.1?Vector dc_id:int = Photo; photoSizeEmpty#e17e23c type:string = PhotoSize; -photoSize#77bfb61b type:string location:FileLocation w:int h:int size:int = PhotoSize; -photoCachedSize#e9a734fa type:string location:FileLocation w:int h:int bytes:bytes = PhotoSize; +photoSize#75c78e60 type:string w:int h:int size:int = PhotoSize; +photoCachedSize#21e1ad6 type:string w:int h:int bytes:bytes = PhotoSize; photoStrippedSize#e0b0bc2e type:string bytes:bytes = PhotoSize; -photoSizeProgressive#5aa86a51 type:string location:FileLocation w:int h:int sizes:Vector = PhotoSize; +photoSizeProgressive#fa3efb95 type:string w:int h:int sizes:Vector = PhotoSize; photoPathSize#d8214d41 type:string bytes:bytes = PhotoSize; geoPointEmpty#1117dd5f = GeoPoint; @@ -554,7 +555,7 @@ inputStickerSetShortName#861cc8a0 short_name:string = InputStickerSet; inputStickerSetAnimatedEmoji#28703c8 = InputStickerSet; inputStickerSetDice#e67f520e emoticon:string = InputStickerSet; -stickerSet#40e237a8 flags:# archived:flags.1?true official:flags.2?true masks:flags.3?true animated:flags.5?true installed_date:flags.0?int id:long access_hash:long title:string short_name:string thumbs:flags.4?Vector thumb_dc_id:flags.4?int count:int hash:int = StickerSet; +stickerSet#d7df217a flags:# archived:flags.1?true official:flags.2?true masks:flags.3?true animated:flags.5?true installed_date:flags.0?int id:long access_hash:long title:string short_name:string thumbs:flags.4?Vector thumb_dc_id:flags.4?int thumb_version:flags.4?int count:int hash:int = StickerSet; messages.stickerSet#b60a24a6 set:StickerSet packs:Vector documents:Vector = messages.StickerSet; @@ -648,6 +649,7 @@ inputBotInlineMessageMediaGeo#96929a85 flags:# geo_point:InputGeoPoint heading:f inputBotInlineMessageMediaVenue#417bbf11 flags:# geo_point:InputGeoPoint title:string address:string provider:string venue_id:string venue_type:string reply_markup:flags.2?ReplyMarkup = InputBotInlineMessage; inputBotInlineMessageMediaContact#a6edbffd flags:# phone_number:string first_name:string last_name:string vcard:string reply_markup:flags.2?ReplyMarkup = InputBotInlineMessage; inputBotInlineMessageGame#4b425864 flags:# reply_markup:flags.2?ReplyMarkup = InputBotInlineMessage; +inputBotInlineMessageMediaInvoice#d5348d85 flags:# multiple_allowed:flags.1?true can_forward:flags.3?true title:string description:string photo:flags.0?InputWebDocument invoice:Invoice payload:bytes provider:string provider_data:DataJSON start_param:string reply_markup:flags.2?ReplyMarkup = InputBotInlineMessage; inputBotInlineResult#88bf9319 flags:# id:string type:string title:flags.1?string description:flags.2?string url:flags.3?string thumb:flags.4?InputWebDocument content:flags.5?InputWebDocument send_message:InputBotInlineMessage = InputBotInlineResult; inputBotInlineResultPhoto#a8d864a7 id:string type:string photo:InputPhoto send_message:InputBotInlineMessage = InputBotInlineResult; @@ -659,6 +661,7 @@ botInlineMessageText#8c7f65e2 flags:# no_webpage:flags.0?true message:string ent botInlineMessageMediaGeo#51846fd flags:# geo:GeoPoint heading:flags.0?int period:flags.1?int proximity_notification_radius:flags.3?int reply_markup:flags.2?ReplyMarkup = BotInlineMessage; botInlineMessageMediaVenue#8a86659c flags:# geo:GeoPoint title:string address:string provider:string venue_id:string venue_type:string reply_markup:flags.2?ReplyMarkup = BotInlineMessage; botInlineMessageMediaContact#18d1cdc2 flags:# phone_number:string first_name:string last_name:string vcard:string reply_markup:flags.2?ReplyMarkup = BotInlineMessage; +botInlineMessageMediaInvoice#354a9b09 flags:# shipping_address_requested:flags.1?true test:flags.3?true title:string description:string photo:flags.0?WebDocument currency:string total_amount:long reply_markup:flags.2?ReplyMarkup = BotInlineMessage; botInlineResult#11965f3a flags:# id:string type:string title:flags.1?string description:flags.2?string url:flags.3?string thumb:flags.4?WebDocument content:flags.5?WebDocument send_message:BotInlineMessage = BotInlineResult; botInlineMediaResult#17db940b flags:# id:string type:string photo:flags.0?Photo document:flags.1?Document title:flags.2?string description:flags.3?string send_message:BotInlineMessage = BotInlineResult; @@ -792,7 +795,7 @@ dataJSON#7d748d04 data:string = DataJSON; labeledPrice#cb296bf8 label:string amount:long = LabeledPrice; -invoice#c30aa358 flags:# test:flags.0?true name_requested:flags.1?true phone_requested:flags.2?true email_requested:flags.3?true shipping_address_requested:flags.4?true flexible:flags.5?true phone_to_provider:flags.6?true email_to_provider:flags.7?true currency:string prices:Vector = Invoice; +invoice#cd886e0 flags:# test:flags.0?true name_requested:flags.1?true phone_requested:flags.2?true email_requested:flags.3?true shipping_address_requested:flags.4?true flexible:flags.5?true phone_to_provider:flags.6?true email_to_provider:flags.7?true currency:string prices:Vector max_tip_amount:flags.8?long suggested_tip_amounts:flags.8?Vector = Invoice; paymentCharge#ea02c27e id:string provider_charge_id:string = PaymentCharge; @@ -812,14 +815,14 @@ inputWebFileGeoPointLocation#9f2221c9 geo_point:InputGeoPoint access_hash:long w upload.webFile#21e753bc size:int mime_type:string file_type:storage.FileType mtime:int bytes:bytes = upload.WebFile; -payments.paymentForm#3f56aea3 flags:# can_save_credentials:flags.2?true password_missing:flags.3?true bot_id:int invoice:Invoice provider_id:int url:string native_provider:flags.4?string native_params:flags.4?DataJSON saved_info:flags.0?PaymentRequestedInfo saved_credentials:flags.1?PaymentSavedCredentials users:Vector = payments.PaymentForm; +payments.paymentForm#8d0b2415 flags:# can_save_credentials:flags.2?true password_missing:flags.3?true form_id:long bot_id:int invoice:Invoice provider_id:int url:string native_provider:flags.4?string native_params:flags.4?DataJSON saved_info:flags.0?PaymentRequestedInfo saved_credentials:flags.1?PaymentSavedCredentials users:Vector = payments.PaymentForm; payments.validatedRequestedInfo#d1451883 flags:# id:flags.0?string shipping_options:flags.1?Vector = payments.ValidatedRequestedInfo; payments.paymentResult#4e5f810d updates:Updates = payments.PaymentResult; payments.paymentVerificationNeeded#d8411139 url:string = payments.PaymentResult; -payments.paymentReceipt#500911e1 flags:# date:int bot_id:int invoice:Invoice provider_id:int info:flags.0?PaymentRequestedInfo shipping:flags.1?ShippingOption currency:string total_amount:long credentials_title:string users:Vector = payments.PaymentReceipt; +payments.paymentReceipt#10b555d0 flags:# date:int bot_id:int provider_id:int title:string description:string photo:flags.2?WebDocument invoice:Invoice info:flags.0?PaymentRequestedInfo shipping:flags.1?ShippingOption tip_amount:flags.3?long currency:string total_amount:long credentials_title:string users:Vector = payments.PaymentReceipt; payments.savedInfo#fb8fe43c flags:# has_saved_credentials:flags.1?true saved_info:flags.0?PaymentRequestedInfo = payments.SavedInfo; @@ -1088,8 +1091,6 @@ emojiURL#a575739d url:string = EmojiURL; emojiLanguage#b3fb5361 lang_code:string = EmojiLanguage; -fileLocationToBeDeprecated#bc7fc6cd volume_id:long local_id:int = FileLocation; - folder#ff544e65 flags:# autofill_new_broadcasts:flags.0?true autofill_public_groups:flags.1?true autofill_new_correspondents:flags.2?true id:int title:string photo:flags.3?ChatPhoto = Folder; inputFolderPeer#fbd2c296 peer:InputPeer folder_id:int = InputFolderPeer; @@ -1169,7 +1170,7 @@ stats.broadcastStats#bdf78394 period:StatsDateRangeDays followers:StatsAbsValueA help.promoDataEmpty#98f6ac75 expires:int = help.PromoData; help.promoData#8c39793f flags:# proxy:flags.0?true expires:int peer:Peer chats:Vector users:Vector psa_type:flags.1?string psa_message:flags.2?string = help.PromoData; -videoSize#e831c556 flags:# type:string location:FileLocation w:int h:int size:int video_start_ts:flags.0?double = VideoSize; +videoSize#de33b094 flags:# type:string w:int h:int size:int video_start_ts:flags.0?double = VideoSize; statsGroupTopPoster#18f3d0f7 user_id:int messages:int avg_chars:int = StatsGroupTopPoster; @@ -1203,11 +1204,11 @@ peerBlocked#e8fd8014 peer_id:Peer date:int = PeerBlocked; stats.messageStats#8999f295 views_graph:StatsGraph = stats.MessageStats; groupCallDiscarded#7780bcb4 id:long access_hash:long duration:int = GroupCall; -groupCall#c0c2052e flags:# join_muted:flags.1?true can_change_join_muted:flags.2?true join_date_asc:flags.6?true id:long access_hash:long participants_count:int params:flags.0?DataJSON title:flags.3?string stream_dc_id:flags.4?int record_start_date:flags.5?int version:int = GroupCall; +groupCall#c95c6654 flags:# join_muted:flags.1?true can_change_join_muted:flags.2?true join_date_asc:flags.6?true schedule_start_subscribed:flags.8?true id:long access_hash:long participants_count:int params:flags.0?DataJSON title:flags.3?string stream_dc_id:flags.4?int record_start_date:flags.5?int schedule_date:flags.7?int version:int = GroupCall; inputGroupCall#d8aa840f id:long access_hash:long = InputGroupCall; -groupCallParticipant#19adba89 flags:# muted:flags.0?true left:flags.1?true can_self_unmute:flags.2?true just_joined:flags.4?true versioned:flags.5?true min:flags.8?true muted_by_you:flags.9?true volume_by_admin:flags.10?true self:flags.12?true peer:Peer date:int active_date:flags.3?int source:int volume:flags.7?int about:flags.11?string raise_hand_rating:flags.13?long = GroupCallParticipant; +groupCallParticipant#b96b25ee flags:# muted:flags.0?true left:flags.1?true can_self_unmute:flags.2?true just_joined:flags.4?true versioned:flags.5?true min:flags.8?true muted_by_you:flags.9?true volume_by_admin:flags.10?true self:flags.12?true peer:Peer date:int active_date:flags.3?int source:int volume:flags.7?int about:flags.11?string raise_hand_rating:flags.13?long params:flags.6?DataJSON = GroupCallParticipant; phone.groupCall#9e727aad call:GroupCall participants:Vector participants_next_offset:string chats:Vector users:Vector = phone.GroupCall; @@ -1592,10 +1593,10 @@ bots.sendCustomRequest#aa2769ed custom_method:string params:DataJSON = DataJSON; bots.answerWebhookJSONQuery#e6213f4d query_id:long data:DataJSON = Bool; bots.setBotCommands#805d46f6 commands:Vector = Bool; -payments.getPaymentForm#99f09745 msg_id:int = payments.PaymentForm; -payments.getPaymentReceipt#a092a980 msg_id:int = payments.PaymentReceipt; -payments.validateRequestedInfo#770a8e74 flags:# save:flags.0?true msg_id:int info:PaymentRequestedInfo = payments.ValidatedRequestedInfo; -payments.sendPaymentForm#2b8879b3 flags:# msg_id:int requested_info_id:flags.0?string shipping_option_id:flags.1?string credentials:InputPaymentCredentials = payments.PaymentResult; +payments.getPaymentForm#8a333c8d flags:# peer:InputPeer msg_id:int theme_params:flags.0?DataJSON = payments.PaymentForm; +payments.getPaymentReceipt#2478d1cc peer:InputPeer msg_id:int = payments.PaymentReceipt; +payments.validateRequestedInfo#db103170 flags:# save:flags.0?true peer:InputPeer msg_id:int info:PaymentRequestedInfo = payments.ValidatedRequestedInfo; +payments.sendPaymentForm#30c3bc9d flags:# form_id:long peer:InputPeer msg_id:int requested_info_id:flags.0?string shipping_option_id:flags.1?string credentials:InputPaymentCredentials tip_amount:flags.2?long = payments.PaymentResult; payments.getSavedInfo#227d824b = payments.SavedInfo; payments.clearSavedInfo#d83d70c1 flags:# credentials:flags.0?true info:flags.1?true = Bool; payments.getBankCardData#2e79d779 number:string = payments.BankCardData; @@ -1615,7 +1616,7 @@ phone.discardCall#b2cbc1c0 flags:# video:flags.0?true peer:InputPhoneCall durati phone.setCallRating#59ead627 flags:# user_initiative:flags.0?true peer:InputPhoneCall rating:int comment:string = Updates; phone.saveCallDebug#277add7e peer:InputPhoneCall debug:DataJSON = Bool; phone.sendSignalingData#ff7a9383 peer:InputPhoneCall data:bytes = Bool; -phone.createGroupCall#bd3dabe0 peer:InputPeer random_id:int = Updates; +phone.createGroupCall#48cdc6d8 flags:# peer:InputPeer random_id:int title:flags.0?string schedule_date:flags.1?int = Updates; phone.joinGroupCall#b132ff7b flags:# muted:flags.0?true call:InputGroupCall join_as:InputPeer invite_hash:flags.1?string params:DataJSON = Updates; phone.leaveGroupCall#500377f9 call:InputGroupCall source:int = Updates; phone.inviteToGroupCall#7b393160 call:InputGroupCall users:Vector = Updates; @@ -1629,6 +1630,9 @@ phone.editGroupCallParticipant#d975eb80 flags:# muted:flags.0?true call:InputGro phone.editGroupCallTitle#1ca6ac0a call:InputGroupCall title:string = Updates; phone.getGroupCallJoinAs#ef7c213a peer:InputPeer = phone.JoinAsPeers; phone.exportGroupCallInvite#e6aa647f flags:# can_self_unmute:flags.0?true call:InputGroupCall = phone.ExportedGroupCallInvite; +phone.toggleGroupCallStartSubscription#219c34e6 call:InputGroupCall subscribed:Bool = Updates; +phone.startScheduledGroupCall#5680e342 call:InputGroupCall = Updates; +phone.saveDefaultGroupCallJoinAs#575e1f8c peer:InputPeer join_as:InputPeer = Bool; langpack.getLangPack#f2f2330a lang_pack:string lang_code:string = LangPackDifference; langpack.getStrings#efea3803 lang_pack:string lang_code:string keys:Vector = Vector; @@ -1645,4 +1649,4 @@ stats.getMegagroupStats#dcdf8607 flags:# dark:flags.0?true channel:InputChannel stats.getMessagePublicForwards#5630281b channel:InputChannel msg_id:int offset_rate:int offset_peer:InputPeer offset_id:int limit:int = messages.Messages; stats.getMessageStats#b6e0a3f5 flags:# dark:flags.0?true channel:InputChannel msg_id:int = stats.MessageStats; -// LAYER 126 +// LAYER 128 diff --git a/Telegram/Resources/uwp/AppX/AppxManifest.xml b/Telegram/Resources/uwp/AppX/AppxManifest.xml index 609b6d122..d37ec5f62 100644 --- a/Telegram/Resources/uwp/AppX/AppxManifest.xml +++ b/Telegram/Resources/uwp/AppX/AppxManifest.xml @@ -9,7 +9,7 @@ + Version="2.7.2.0" /> Telegram Desktop Telegram Messenger LLP diff --git a/Telegram/Resources/winrc/Telegram.rc b/Telegram/Resources/winrc/Telegram.rc index cc8798581..aead1df4f 100644 --- a/Telegram/Resources/winrc/Telegram.rc +++ b/Telegram/Resources/winrc/Telegram.rc @@ -44,8 +44,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico" // VS_VERSION_INFO VERSIONINFO - FILEVERSION 2,7,1,0 - PRODUCTVERSION 2,7,1,0 + FILEVERSION 2,7,2,0 + PRODUCTVERSION 2,7,2,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -62,10 +62,10 @@ BEGIN BEGIN VALUE "CompanyName", "Telegram FZ-LLC" VALUE "FileDescription", "Telegram Desktop" - VALUE "FileVersion", "2.7.1.0" + VALUE "FileVersion", "2.7.2.0" VALUE "LegalCopyright", "Copyright (C) 2014-2021" VALUE "ProductName", "Telegram Desktop" - VALUE "ProductVersion", "2.7.1.0" + VALUE "ProductVersion", "2.7.2.0" END END BLOCK "VarFileInfo" diff --git a/Telegram/Resources/winrc/Updater.rc b/Telegram/Resources/winrc/Updater.rc index 4492de97f..817dc2afe 100644 --- a/Telegram/Resources/winrc/Updater.rc +++ b/Telegram/Resources/winrc/Updater.rc @@ -35,8 +35,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US // VS_VERSION_INFO VERSIONINFO - FILEVERSION 2,7,1,0 - PRODUCTVERSION 2,7,1,0 + FILEVERSION 2,7,2,0 + PRODUCTVERSION 2,7,2,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -53,10 +53,10 @@ BEGIN BEGIN VALUE "CompanyName", "Telegram FZ-LLC" VALUE "FileDescription", "Telegram Desktop Updater" - VALUE "FileVersion", "2.7.1.0" + VALUE "FileVersion", "2.7.2.0" VALUE "LegalCopyright", "Copyright (C) 2014-2021" VALUE "ProductName", "Telegram Desktop" - VALUE "ProductVersion", "2.7.1.0" + VALUE "ProductVersion", "2.7.2.0" END END BLOCK "VarFileInfo" diff --git a/Telegram/SourceFiles/api/api_invite_links.cpp b/Telegram/SourceFiles/api/api_invite_links.cpp index cf14c9599..2b6a11f43 100644 --- a/Telegram/SourceFiles/api/api_invite_links.cpp +++ b/Telegram/SourceFiles/api/api_invite_links.cpp @@ -54,7 +54,7 @@ JoinedByLinkSlice ParseJoinedByLinkSlice( for (const auto &importer : data.vimporters().v) { importer.match([&](const MTPDchatInviteImporter &data) { result.users.push_back({ - .user = owner.user(data.vuser_id().v), + .user = owner.user(data.vuser_id()), .date = data.vdate().v, }); }); @@ -623,7 +623,7 @@ auto InviteLinks::parse( return invite.match([&](const MTPDchatInviteExported &data) { return Link{ .link = qs(data.vlink()), - .admin = peer->session().data().user(data.vadmin_id().v), + .admin = peer->session().data().user(data.vadmin_id()), .date = data.vdate().v, .startDate = data.vstart_date().value_or_empty(), .expireDate = data.vexpire_date().value_or_empty(), diff --git a/Telegram/SourceFiles/api/api_single_message_search.cpp b/Telegram/SourceFiles/api/api_single_message_search.cpp index 9f39f372d..c6d6e146d 100644 --- a/Telegram/SourceFiles/api/api_single_message_search.cpp +++ b/Telegram/SourceFiles/api/api_single_message_search.cpp @@ -37,7 +37,7 @@ Key ExtractKey(const QString &query) { const auto params = parse(); const auto channel = params.value("channel"); const auto post = params.value("post").toInt(); - return (channel.toInt() && post) ? Key{ channel, post } : Key(); + return (channel.toULongLong() && post) ? Key{ channel, post } : Key(); } else if (check.startsWith(qstr("tg://resolve"), Qt::CaseInsensitive)) { const auto params = parse(); const auto domain = params.value("domain"); @@ -112,7 +112,7 @@ std::optional SingleMessageSearch::performLookupByChannel( && received.messageIds.front() == postId) { _cache.emplace( _requestKey, - FullMsgId(channel->bareId(), postId)); + FullMsgId(peerToChannel(channel->id), postId)); ready(); } else { fail(); @@ -142,7 +142,7 @@ std::optional SingleMessageSearch::performLookupById( _requestId = _session->api().request(MTPchannels_GetChannels( MTP_vector( 1, - MTP_inputChannel(MTP_int(channelId), MTP_long(0))) + MTP_inputChannel(MTP_int(channelId.bare), MTP_long(0))) // #TODO ids )).done([=](const MTPmessages_Chats &result) { result.match([&](const auto &data) { const auto peer = _session->data().processChats(data.vchats()); @@ -212,7 +212,7 @@ std::optional SingleMessageSearch::performLookup( if (!_requestKey.domainOrId[0].isDigit()) { return performLookupByUsername(_requestKey.domainOrId, ready); } - const auto channelId = _requestKey.domainOrId.toInt(); + const auto channelId = ChannelId(_requestKey.domainOrId.toULongLong()); return performLookupById(channelId, ready); } diff --git a/Telegram/SourceFiles/api/api_text_entities.cpp b/Telegram/SourceFiles/api/api_text_entities.cpp index 04b3ec8c5..b98b1fefa 100644 --- a/Telegram/SourceFiles/api/api_text_entities.cpp +++ b/Telegram/SourceFiles/api/api_text_entities.cpp @@ -37,15 +37,17 @@ EntitiesInText EntitiesFromMTP( case mtpc_messageEntityMention: { auto &d = entity.c_messageEntityMention(); result.push_back({ EntityType::Mention, d.voffset().v, d.vlength().v }); } break; case mtpc_messageEntityMentionName: { const auto &d = entity.c_messageEntityMentionName(); + const auto userId = UserId(d.vuser_id()); const auto data = [&] { if (session) { - if (const auto user = session->data().userLoaded(d.vuser_id().v)) { + if (const auto user = session->data().userLoaded(userId)) { return MentionNameDataFromFields({ - d.vuser_id().v, - user->accessHash() }); + userId.bare, + user->accessHash() + }); } } - return MentionNameDataFromFields(d.vuser_id().v); + return MentionNameDataFromFields(userId.bare); }(); result.push_back({ EntityType::MentionName, d.voffset().v, d.vlength().v, data }); } break; @@ -53,10 +55,11 @@ EntitiesInText EntitiesFromMTP( const auto &d = entity.c_inputMessageEntityMentionName(); const auto data = [&] { if (session && d.vuser_id().type() == mtpc_inputUserSelf) { - return MentionNameDataFromFields(session->userId()); + return MentionNameDataFromFields(session->userId().bare); } else if (d.vuser_id().type() == mtpc_inputUser) { auto &user = d.vuser_id().c_inputUser(); - return MentionNameDataFromFields({ user.vuser_id().v, user.vaccess_hash().v }); + const auto userId = UserId(user.vuser_id()); + return MentionNameDataFromFields({ userId.bare, user.vaccess_hash().v }); } return QString(); }(); @@ -130,7 +133,7 @@ MTPVector EntitiesToMTP( if (uid == session->userId()) { return MTP_inputUserSelf(); } else if (const auto user = session->data().userLoaded(uid)) { - return MTP_inputUser(MTP_int(uid), MTP_long(user->accessHash())); + return MTP_inputUser(MTP_int(uid.bare), MTP_long(user->accessHash())); } } return MTP_inputUserEmpty(); @@ -148,7 +151,7 @@ MTPVector EntitiesToMTP( case EntityType::MentionName: { auto inputUser = [&](const QString &data) -> MTPInputUser { auto fields = MentionNameDataToFields(data); - if (session && fields.userId == session->userId()) { + if (session && fields.userId == session->userId().bare) { return MTP_inputUserSelf(); } else if (fields.userId) { return MTP_inputUser(MTP_int(fields.userId), MTP_long(fields.accessHash)); diff --git a/Telegram/SourceFiles/api/api_updates.cpp b/Telegram/SourceFiles/api/api_updates.cpp index 1402acdc9..021811c23 100644 --- a/Telegram/SourceFiles/api/api_updates.cpp +++ b/Telegram/SourceFiles/api/api_updates.cpp @@ -131,13 +131,13 @@ bool MentionUsersLoaded( for (const auto &entity : entities.v) { auto type = entity.type(); if (type == mtpc_messageEntityMentionName) { - if (!session->data().userLoaded(entity.c_messageEntityMentionName().vuser_id().v)) { + if (!session->data().userLoaded(entity.c_messageEntityMentionName().vuser_id())) { return false; } } else if (type == mtpc_inputMessageEntityMentionName) { auto &inputUser = entity.c_inputMessageEntityMentionName().vuser_id(); if (inputUser.type() == mtpc_inputUser) { - if (!session->data().userLoaded(inputUser.c_inputUser().vuser_id().v)) { + if (!session->data().userLoaded(inputUser.c_inputUser().vuser_id())) { return false; } } @@ -157,7 +157,7 @@ DataIsLoadedResult AllDataLoadedForMessage( } } if (const auto viaBotId = message.vvia_bot_id()) { - if (!session->data().userLoaded(viaBotId->v)) { + if (!session->data().userLoaded(*viaBotId)) { return DataIsLoadedResult::NotLoaded; } } @@ -181,19 +181,19 @@ DataIsLoadedResult AllDataLoadedForMessage( } return message.vaction().match( [&](const MTPDmessageActionChatAddUser &action) { - for (const MTPint &userId : action.vusers().v) { - if (!session->data().userLoaded(userId.v)) { + for (const auto &userId : action.vusers().v) { + if (!session->data().userLoaded(userId)) { return DataIsLoadedResult::NotLoaded; } } return DataIsLoadedResult::Ok; }, [&](const MTPDmessageActionChatJoinedByLink &action) { - if (!session->data().userLoaded(action.vinviter_id().v)) { + if (!session->data().userLoaded(action.vinviter_id())) { return DataIsLoadedResult::NotLoaded; } return DataIsLoadedResult::Ok; }, [&](const MTPDmessageActionChatDeleteUser &action) { - if (!session->data().userLoaded(action.vuser_id().v)) { + if (!session->data().userLoaded(action.vuser_id())) { return DataIsLoadedResult::NotLoaded; } return DataIsLoadedResult::Ok; @@ -1020,7 +1020,7 @@ void Updates::applyUpdatesNoPtsCheck(const MTPUpdates &updates) { | MTPDmessage::Flag::f_from_id; const auto peerUserId = d.is_out() ? d.vuser_id() - : MTP_int(_session->userId()); + : MTP_int(_session->userId().bare); // #TODO ids _session->data().addNewMessage( MTP_message( MTP_flags(flags), @@ -1309,8 +1309,8 @@ void Updates::applyUpdates( const auto viaBotId = d.vvia_bot_id(); const auto entities = d.ventities(); const auto fwd = d.vfwd_from(); - if (!session().data().userLoaded(d.vuser_id().v) - || (viaBotId && !session().data().userLoaded(viaBotId->v)) + if (!session().data().userLoaded(d.vuser_id()) + || (viaBotId && !session().data().userLoaded(*viaBotId)) || (entities && !MentionUsersLoaded(&session(), *entities)) || (fwd && !ForwardedInfoDataLoaded(&session(), *fwd))) { MTP_LOG(0, ("getDifference " @@ -1326,14 +1326,14 @@ void Updates::applyUpdates( case mtpc_updateShortChatMessage: { auto &d = updates.c_updateShortChatMessage(); - const auto noFrom = !session().data().userLoaded(d.vfrom_id().v); - const auto chat = session().data().chatLoaded(d.vchat_id().v); + const auto noFrom = !session().data().userLoaded(d.vfrom_id()); + const auto chat = session().data().chatLoaded(d.vchat_id()); const auto viaBotId = d.vvia_bot_id(); const auto entities = d.ventities(); const auto fwd = d.vfwd_from(); if (!chat || noFrom - || (viaBotId && !session().data().userLoaded(viaBotId->v)) + || (viaBotId && !session().data().userLoaded(*viaBotId)) || (entities && !MentionUsersLoaded(&session(), *entities)) || (fwd && !ForwardedInfoDataLoaded(&session(), *fwd))) { MTP_LOG(0, ("getDifference " @@ -1491,7 +1491,7 @@ void Updates::feedUpdate(const MTPUpdate &update) { case mtpc_updateChannelReadMessagesContents: { auto &d = update.c_updateChannelReadMessagesContents(); - auto channel = session().data().channelLoaded(d.vchannel_id().v); + auto channel = session().data().channelLoaded(d.vchannel_id()); if (!channel) { if (!_byMinChannelTimer.isActive()) { // getDifference after timeout. @@ -1537,7 +1537,7 @@ void Updates::feedUpdate(const MTPUpdate &update) { case mtpc_updatePinnedChannelMessages: { auto &d = update.c_updatePinnedChannelMessages(); - auto channel = session().data().channelLoaded(d.vchannel_id().v); + auto channel = session().data().channelLoaded(d.vchannel_id()); if (channel && !_handlingChannelDifference) { if (channel->ptsRequesting()) { // skip global updates while getting channel difference @@ -1623,7 +1623,7 @@ void Updates::feedUpdate(const MTPUpdate &update) { case mtpc_updateDeleteChannelMessages: { auto &d = update.c_updateDeleteChannelMessages(); - auto channel = session().data().channelLoaded(d.vchannel_id().v); + auto channel = session().data().channelLoaded(d.vchannel_id()); if (channel && !_handlingChannelDifference) { if (channel->ptsRequesting()) { // skip global updates while getting channel difference @@ -1662,7 +1662,7 @@ void Updates::feedUpdate(const MTPUpdate &update) { session().data().processWebpage(d.vwebpage()); session().data().sendWebPageGamePollNotifications(); - auto channel = session().data().channelLoaded(d.vchannel_id().v); + auto channel = session().data().channelLoaded(d.vchannel_id()); if (channel && !_handlingChannelDifference) { if (channel->ptsRequesting()) { // skip global updates while getting channel difference return; @@ -1683,27 +1683,25 @@ void Updates::feedUpdate(const MTPUpdate &update) { handleSendActionUpdate( peerFromUser(d.vuser_id()), 0, - d.vuser_id().v, + peerFromUser(d.vuser_id()), d.vaction()); } break; case mtpc_updateChatUserTyping: { auto &d = update.c_updateChatUserTyping(); - const auto fromId = peerFromMTP(d.vfrom_id()); handleSendActionUpdate( peerFromChat(d.vchat_id()), 0, - fromId, + peerFromMTP(d.vfrom_id()), d.vaction()); } break; case mtpc_updateChannelUserTyping: { const auto &d = update.c_updateChannelUserTyping(); - const auto fromId = peerFromMTP(d.vfrom_id()); handleSendActionUpdate( peerFromChannel(d.vchannel_id()), d.vtop_msg_id().value_or_empty(), - fromId, + peerFromMTP(d.vfrom_id()), d.vaction()); } break; @@ -1729,7 +1727,7 @@ void Updates::feedUpdate(const MTPUpdate &update) { case mtpc_updateUserStatus: { auto &d = update.c_updateUserStatus(); - if (auto user = session().data().userLoaded(d.vuser_id().v)) { + if (auto user = session().data().userLoaded(d.vuser_id())) { switch (d.vstatus().type()) { case mtpc_userStatusEmpty: user->onlineTill = 0; break; case mtpc_userStatusRecently: @@ -1746,7 +1744,7 @@ void Updates::feedUpdate(const MTPUpdate &update) { user, Data::PeerUpdate::Flag::OnlineStatus); } - if (d.vuser_id().v == session().userId()) { + if (UserId(d.vuser_id()) == session().userId()) { if (d.vstatus().type() == mtpc_userStatusOffline || d.vstatus().type() == mtpc_userStatusEmpty) { updateOnline(true); @@ -1763,7 +1761,7 @@ void Updates::feedUpdate(const MTPUpdate &update) { case mtpc_updateUserName: { auto &d = update.c_updateUserName(); - if (auto user = session().data().userLoaded(d.vuser_id().v)) { + if (auto user = session().data().userLoaded(d.vuser_id())) { if (!user->isContact()) { user->setName( TextUtilities::SingleLine(qs(d.vfirst_name())), @@ -1782,7 +1780,7 @@ void Updates::feedUpdate(const MTPUpdate &update) { case mtpc_updateUserPhoto: { auto &d = update.c_updateUserPhoto(); - if (auto user = session().data().userLoaded(d.vuser_id().v)) { + if (auto user = session().data().userLoaded(d.vuser_id())) { user->setPhoto(d.vphoto()); user->loadUserpic(); // After that update we don't have enough information to @@ -1793,11 +1791,11 @@ void Updates::feedUpdate(const MTPUpdate &update) { // //if (mtpIsTrue(d.vprevious()) || !user->userpicPhotoId()) { session().storage().remove(Storage::UserPhotosRemoveAfter( - user->bareId(), + peerToUser(user->id), user->userpicPhotoId())); //} else { // session().storage().add(Storage::UserPhotosAddNew( - // user->bareId(), + // peerToUser(user->id), // user->userpicPhotoId())); //} } @@ -1831,7 +1829,7 @@ void Updates::feedUpdate(const MTPUpdate &update) { case mtpc_updateUserPhone: { const auto &d = update.c_updateUserPhone(); - if (const auto user = session().data().userLoaded(d.vuser_id().v)) { + if (const auto user = session().data().userLoaded(d.vuser_id())) { const auto newPhone = qs(d.vphone()); if (newPhone != user->phone()) { user->setPhone(newPhone); @@ -1914,8 +1912,8 @@ void Updates::feedUpdate(const MTPUpdate &update) { auto &d = update.c_updatePrivacy(); const auto allChatsLoaded = [&](const MTPVector &ids) { for (const auto &chatId : ids.v) { - if (!session().data().chatLoaded(chatId.v) - && !session().data().channelLoaded(chatId.v)) { + if (!session().data().chatLoaded(chatId) + && !session().data().channelLoaded(chatId)) { return false; } } @@ -1999,7 +1997,7 @@ void Updates::feedUpdate(const MTPUpdate &update) { } DEBUG_LOG(("API Error: " "pinned chat not loaded for peer %1, folder: %2" - ).arg(id + ).arg(id.value ).arg(folderId )); return false; @@ -2027,7 +2025,7 @@ void Updates::feedUpdate(const MTPUpdate &update) { case mtpc_updateChannel: { auto &d = update.c_updateChannel(); - if (const auto channel = session().data().channelLoaded(d.vchannel_id().v)) { + if (const auto channel = session().data().channelLoaded(d.vchannel_id())) { channel->inviter = UserId(0); if (channel->amIn()) { if (channel->isMegagroup() @@ -2049,7 +2047,7 @@ void Updates::feedUpdate(const MTPUpdate &update) { case mtpc_updateChannelTooLong: { const auto &d = update.c_updateChannelTooLong(); - if (const auto channel = session().data().channelLoaded(d.vchannel_id().v)) { + if (const auto channel = session().data().channelLoaded(d.vchannel_id())) { const auto pts = d.vpts(); if (!pts || channel->pts() < pts->v) { getChannelDifference(channel); @@ -2108,7 +2106,7 @@ void Updates::feedUpdate(const MTPUpdate &update) { case mtpc_updateChannelAvailableMessages: { auto &d = update.c_updateChannelAvailableMessages(); - if (const auto channel = session().data().channelLoaded(d.vchannel_id().v)) { + if (const auto channel = session().data().channelLoaded(d.vchannel_id())) { channel->setAvailableMinId(d.vavailable_min_id().v); if (const auto history = session().data().historyLoaded(channel)) { history->clearUpTill(d.vavailable_min_id().v); diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index 285ecca62..160d4dd59 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -692,7 +692,7 @@ QString ApiWrap::exportDirectMessageLink( if (inRepliesContext) { if (const auto rootId = item->replyToTop()) { const auto root = item->history()->owner().message( - channel->bareId(), + peerToChannel(channel->id), rootId); const auto sender = root ? root->discussionPostOriginalSender() @@ -715,7 +715,7 @@ QString ApiWrap::exportDirectMessageLink( } const auto base = linkChannel->hasUsername() ? linkChannel->username - : "c/" + QString::number(linkChannel->bareId()); + : "c/" + QString::number(peerToChannel(linkChannel->id).bare); const auto query = base + '/' + QString::number(linkItemId) @@ -776,7 +776,7 @@ void ApiWrap::requestContacts() { for (const auto &contact : d.vcontacts().v) { if (contact.type() != mtpc_contact) continue; - const auto userId = contact.c_contact().vuser_id().v; + const auto userId = UserId(contact.c_contact().vuser_id()); if (userId == _session->userId()) { _session->user()->setIsContact(true); } @@ -2267,7 +2267,7 @@ void ApiWrap::updatePrivacyLastSeens(const QVector &rules) { for (const auto &item : result.v) { Assert(item.type() == mtpc_contactStatus); auto &data = item.c_contactStatus(); - if (auto user = _session->data().userLoaded(data.vuser_id().v)) { + if (auto user = _session->data().userLoaded(data.vuser_id())) { auto oldOnlineTill = user->onlineTill; auto newOnlineTill = OnlineTillFromStatus(data.vstatus(), oldOnlineTill); if (oldOnlineTill != newOnlineTill) { @@ -3566,7 +3566,7 @@ void ApiWrap::userPhotosDone( } } _session->storage().add(Storage::UserPhotosAddSlice( - user->id, + peerToUser(user->id), std::move(photoIds), fullCount )); @@ -4297,7 +4297,7 @@ void ApiWrap::sendSharedContact( MTP_string(firstName), MTP_string(lastName), MTP_string(vcard), - MTP_int(userId)), + MTP_int(userId.bare)), // #TODO ids MTPReplyMarkup(), MTPVector(), MTP_int(views), @@ -5114,7 +5114,7 @@ void ApiWrap::clearPeerPhoto(not_null photo) { MTP_vector(1, photo->mtpInput()) )).send(); _session->storage().remove(Storage::UserPhotosRemoveOne( - self->bareId(), + peerToUser(self->id), photo->id)); } } @@ -5291,11 +5291,11 @@ auto ApiWrap::parsePrivacy(const QVector &rules) }, [&](const MTPDprivacyValueAllowChatParticipants &data) { const auto &chats = data.vchats().v; always.reserve(always.size() + chats.size()); - for (const auto chatId : chats) { - const auto chat = _session->data().chatLoaded(chatId.v); + for (const auto &chatId : chats) { + const auto chat = _session->data().chatLoaded(chatId); const auto peer = chat ? static_cast(chat) - : _session->data().channelLoaded(chatId.v); + : _session->data().channelLoaded(chatId); if (peer && !base::contains(never, peer) && !base::contains(always, peer)) { @@ -5319,11 +5319,11 @@ auto ApiWrap::parsePrivacy(const QVector &rules) }, [&](const MTPDprivacyValueDisallowChatParticipants &data) { const auto &chats = data.vchats().v; never.reserve(never.size() + chats.size()); - for (const auto chatId : chats) { - const auto chat = _session->data().chatLoaded(chatId.v); + for (const auto &chatId : chats) { + const auto chat = _session->data().chatLoaded(chatId); const auto peer = chat ? static_cast(chat) - : _session->data().channelLoaded(chatId.v); + : _session->data().channelLoaded(chatId); if (peer && !base::contains(always, peer) && !base::contains(never, peer)) { diff --git a/Telegram/SourceFiles/apiwrap.h b/Telegram/SourceFiles/apiwrap.h index 0fe53e261..fd53cd978 100644 --- a/Telegram/SourceFiles/apiwrap.h +++ b/Telegram/SourceFiles/apiwrap.h @@ -76,6 +76,15 @@ inline QString ToString(uint64 value) { return QString::number(value); } +template +inline QString ToString(ChatIdType value) { + return QString::number(value.bare); +} + +inline QString ToString(PeerId value) { + return QString::number(value.value); +} + } // namespace details template < diff --git a/Telegram/SourceFiles/boxes/add_contact_box.cpp b/Telegram/SourceFiles/boxes/add_contact_box.cpp index a69e40b96..e051d4013 100644 --- a/Telegram/SourceFiles/boxes/add_contact_box.cpp +++ b/Telegram/SourceFiles/boxes/add_contact_box.cpp @@ -94,7 +94,7 @@ void ChatCreateDone( } | [&](auto chats) { return navigation->session().data().chat( - chats->front().c_chat().vid().v); + chats->front().c_chat().vid()); } | [&](not_null chat) { if (!image.isNull()) { @@ -268,7 +268,7 @@ AddContactBox::AddContactBox( this, st::defaultInputField, tr::lng_contact_phone(), - ExtractPhonePrefix(session->user()->phone()), + Ui::ExtractPhonePrefix(session->user()->phone()), phone) , _invertOrder(langFirstNameGoesSecond()) { if (!phone.isEmpty()) { @@ -401,7 +401,7 @@ void AddContactBox::save() { const auto extractUser = [&](const MTPImportedContact &data) { return data.match([&](const MTPDimportedContact &data) { return (data.vclient_id().v == _contactId) - ? _session->data().userLoaded(data.vuser_id().v) + ? _session->data().userLoaded(data.vuser_id()) : nullptr; }); }; @@ -687,7 +687,7 @@ void GroupInfoBox::createChannel(const QString &title, const QString &descriptio } | [&](auto chats) { return _navigation->session().data().channel( - chats->front().c_channel().vid().v); + chats->front().c_channel().vid()); } | [&](not_null channel) { auto image = _photo->takeResultImage(); diff --git a/Telegram/SourceFiles/boxes/background_preview_box.cpp b/Telegram/SourceFiles/boxes/background_preview_box.cpp index 807ca7c68..3877a9057 100644 --- a/Telegram/SourceFiles/boxes/background_preview_box.cpp +++ b/Telegram/SourceFiles/boxes/background_preview_box.cpp @@ -295,7 +295,7 @@ AdminLog::OwnedItem GenerateTextItem( | (out ? Flag::f_out : Flag(0)); const auto clientFlags = MTPDmessage_ClientFlag::f_fake_history_item; const auto replyTo = 0; - const auto viaBotId = 0; + const auto viaBotId = UserId(0); const auto item = history->makeMessage( ++id, flags, @@ -402,14 +402,12 @@ BackgroundPreviewBox::BackgroundPreviewBox( , _controller(controller) , _text1(GenerateTextItem( delegate(), - _controller->session().data().history( - peerFromUser(PeerData::kServiceNotificationsId)), + _controller->session().data().history(PeerData::kServiceNotificationsId), tr::lng_background_text1(tr::now), false)) , _text2(GenerateTextItem( delegate(), - _controller->session().data().history( - peerFromUser(PeerData::kServiceNotificationsId)), + _controller->session().data().history(PeerData::kServiceNotificationsId), tr::lng_background_text2(tr::now), true)) , _paper(paper) diff --git a/Telegram/SourceFiles/boxes/change_phone_box.cpp b/Telegram/SourceFiles/boxes/change_phone_box.cpp index 727c8b93c..199ebeab5 100644 --- a/Telegram/SourceFiles/boxes/change_phone_box.cpp +++ b/Telegram/SourceFiles/boxes/change_phone_box.cpp @@ -151,7 +151,7 @@ void ChangePhoneBox::EnterPhone::prepare() { this, st::defaultInputField, tr::lng_change_phone_new_title(), - ExtractPhonePrefix(_session->user()->phone()), + Ui::ExtractPhonePrefix(_session->user()->phone()), phoneValue); _phone->resize(st::boxWidth - 2 * st::boxPadding.left(), _phone->height()); diff --git a/Telegram/SourceFiles/boxes/confirm_box.cpp b/Telegram/SourceFiles/boxes/confirm_box.cpp index bc43a797d..6f14bceee 100644 --- a/Telegram/SourceFiles/boxes/confirm_box.cpp +++ b/Telegram/SourceFiles/boxes/confirm_box.cpp @@ -304,9 +304,10 @@ void ConfirmBox::mouseReleaseEvent(QMouseEvent *e) { _lastMousePos = e->globalPos(); updateHover(); if (const auto activated = ClickHandler::unpressed()) { - const auto guard = window(); - Ui::hideLayer(); - ActivateClickHandler(guard, activated, e->button()); + ActivateClickHandler(window(), activated, e->button()); + crl::on_main(this, [=] { + closeBox(); + }); return; } BoxContent::mouseReleaseEvent(e); diff --git a/Telegram/SourceFiles/boxes/confirm_phone_box.cpp b/Telegram/SourceFiles/boxes/confirm_phone_box.cpp index e0d4bf803..3d960f668 100644 --- a/Telegram/SourceFiles/boxes/confirm_phone_box.cpp +++ b/Telegram/SourceFiles/boxes/confirm_phone_box.cpp @@ -71,14 +71,6 @@ void ShowPhoneBannedError(const QString &phone) { [=] { SendToBannedHelp(phone); close(); })); } -QString ExtractPhonePrefix(const QString &phone) { - const auto pattern = phoneNumberParse(phone); - if (!pattern.isEmpty()) { - return phone.mid(0, pattern[0]); - } - return QString(); -} - SentCodeField::SentCodeField( QWidget *parent, const style::InputField &st, diff --git a/Telegram/SourceFiles/boxes/confirm_phone_box.h b/Telegram/SourceFiles/boxes/confirm_phone_box.h index cb3b15de8..5448a78f8 100644 --- a/Telegram/SourceFiles/boxes/confirm_phone_box.h +++ b/Telegram/SourceFiles/boxes/confirm_phone_box.h @@ -22,7 +22,6 @@ class Session; } // namespace Main void ShowPhoneBannedError(const QString &phone); -[[nodiscard]] QString ExtractPhonePrefix(const QString &phone); class SentCodeField : public Ui::InputField { public: diff --git a/Telegram/SourceFiles/boxes/edit_privacy_box.cpp b/Telegram/SourceFiles/boxes/edit_privacy_box.cpp index 453840772..d6e2a26c7 100644 --- a/Telegram/SourceFiles/boxes/edit_privacy_box.cpp +++ b/Telegram/SourceFiles/boxes/edit_privacy_box.cpp @@ -182,11 +182,11 @@ QVector EditPrivacyBox::collectResult() { return result; }; const auto collectInputChats = [](const auto &peers) { - auto result = QVector(); + auto result = QVector(); // #TODO ids result.reserve(peers.size()); for (const auto peer : peers) { if (!peer->isUser()) { - result.push_back(MTP_int(peer->bareId())); + result.push_back(peerToBareMTPInt(peer->id)); } } return result; diff --git a/Telegram/SourceFiles/boxes/filters/edit_filter_chats_list.cpp b/Telegram/SourceFiles/boxes/filters/edit_filter_chats_list.cpp index a3dcd1189..d9d6c41db 100644 --- a/Telegram/SourceFiles/boxes/filters/edit_filter_chats_list.cpp +++ b/Telegram/SourceFiles/boxes/filters/edit_filter_chats_list.cpp @@ -125,7 +125,7 @@ private: } [[nodiscard]] uint64 TypeId(Flag flag) { - return PeerIdFakeShift | static_cast(flag); + return PeerId(FakeChatId(static_cast(flag))).value; } TypeRow::TypeRow(Flag flag) : PeerListRow(TypeId(flag)) { diff --git a/Telegram/SourceFiles/boxes/passcode_box.cpp b/Telegram/SourceFiles/boxes/passcode_box.cpp index 18fb3788a..a6ce44819 100644 --- a/Telegram/SourceFiles/boxes/passcode_box.cpp +++ b/Telegram/SourceFiles/boxes/passcode_box.cpp @@ -361,7 +361,11 @@ void PasscodeBox::closeReplacedBy() { } void PasscodeBox::setPasswordFail(const MTP::Error &error) { - if (MTP::IsFloodError(error)) { + setPasswordFail(error.type()); +} + +void PasscodeBox::setPasswordFail(const QString &type) { + if (MTP::IsFloodError(type)) { closeReplacedBy(); _setRequest = 0; @@ -378,20 +382,19 @@ void PasscodeBox::setPasswordFail(const MTP::Error &error) { closeReplacedBy(); _setRequest = 0; - const auto &err = error.type(); - if (err == qstr("PASSWORD_HASH_INVALID") - || err == qstr("SRP_PASSWORD_CHANGED")) { + if (type == qstr("PASSWORD_HASH_INVALID") + || type == qstr("SRP_PASSWORD_CHANGED")) { if (_oldPasscode->isHidden()) { _passwordReloadNeeded.fire({}); closeBox(); } else { badOldPasscode(); } - } else if (err == qstr("SRP_ID_INVALID")) { + } else if (type == qstr("SRP_ID_INVALID")) { handleSrpIdInvalid(); - //} else if (err == qstr("NEW_PASSWORD_BAD")) { - //} else if (err == qstr("NEW_SALT_INVALID")) { - } else if (err == qstr("EMAIL_INVALID")) { + //} else if (type == qstr("NEW_PASSWORD_BAD")) { + //} else if (type == qstr("NEW_SALT_INVALID")) { + } else if (type == qstr("EMAIL_INVALID")) { _emailError = tr::lng_cloud_password_bad_email(tr::now); _recoverEmail->setFocus(); _recoverEmail->showError(); @@ -682,12 +685,15 @@ void PasscodeBox::serverError() { } bool PasscodeBox::handleCustomCheckError(const MTP::Error &error) { - const auto &type = error.type(); - if (MTP::IsFloodError(error) + return handleCustomCheckError(error.type()); +} + +bool PasscodeBox::handleCustomCheckError(const QString &type) { + if (MTP::IsFloodError(type) || type == qstr("PASSWORD_HASH_INVALID") || type == qstr("SRP_PASSWORD_CHANGED") || type == qstr("SRP_ID_INVALID")) { - setPasswordFail(error); + setPasswordFail(type); return true; } return false; diff --git a/Telegram/SourceFiles/boxes/passcode_box.h b/Telegram/SourceFiles/boxes/passcode_box.h index 80644c2be..875bed383 100644 --- a/Telegram/SourceFiles/boxes/passcode_box.h +++ b/Telegram/SourceFiles/boxes/passcode_box.h @@ -56,6 +56,7 @@ public: rpl::producer<> clearUnconfirmedPassword() const; bool handleCustomCheckError(const MTP::Error &error); + bool handleCustomCheckError(const QString &type); protected: void prepare() override; @@ -82,6 +83,7 @@ private: void setPasswordDone(const QByteArray &newPasswordBytes); void setPasswordFail(const MTP::Error &error); + void setPasswordFail(const QString &type); void setPasswordFail( const QByteArray &newPasswordBytes, const QString &email, diff --git a/Telegram/SourceFiles/boxes/peer_list_box.cpp b/Telegram/SourceFiles/boxes/peer_list_box.cpp index b5f5a12dd..b78e4ead5 100644 --- a/Telegram/SourceFiles/boxes/peer_list_box.cpp +++ b/Telegram/SourceFiles/boxes/peer_list_box.cpp @@ -89,8 +89,8 @@ void PeerListBox::createMultiSelect() { if (_controller->handleDeselectForeignRow(itemId)) { return; } - if (const auto peer = _controller->session().data().peerLoaded(itemId)) { - if (const auto row = peerListFindRow(peer->id)) { + if (const auto peer = _controller->session().data().peerLoaded(PeerId(itemId))) { + if (const auto row = peerListFindRow(itemId)) { content()->changeCheckState(row, false, anim::type::normal); update(); } @@ -278,11 +278,11 @@ void PeerListController::search(const QString &query) { } void PeerListController::peerListSearchAddRow(not_null peer) { - if (auto row = delegate()->peerListFindRow(peer->id)) { - Assert(row->id() == row->peer()->id); + if (auto row = delegate()->peerListFindRow(peer->id.value)) { + Assert(row->id() == row->peer()->id.value); delegate()->peerListAppendFoundRow(row); } else if (auto row = createSearchRow(peer)) { - Assert(row->id() == row->peer()->id); + Assert(row->id() == row->peer()->id.value); delegate()->peerListAppendSearchRow(std::move(row)); } } @@ -356,7 +356,7 @@ void PeerListBox::addSelectItem( ? tr::lng_replies_messages(tr::now) : peer->shortName(); addSelectItem( - peer->id, + peer->id.value, text, PaintUserpicCallback(peer, respect), animated); @@ -423,7 +423,7 @@ auto PeerListBox::collectSelectedRows() result.reserve(items.size()); for (const auto itemId : items) { if (!_controller->isForeignRow(itemId)) { - result.push_back(_controller->session().data().peer(itemId)); + result.push_back(_controller->session().data().peer(PeerId(itemId))); } } } @@ -431,7 +431,7 @@ auto PeerListBox::collectSelectedRows() } PeerListRow::PeerListRow(not_null peer) -: PeerListRow(peer, peer->id) { +: PeerListRow(peer, peer->id.value) { } PeerListRow::PeerListRow(not_null peer, PeerListRowId id) @@ -914,7 +914,7 @@ void PeerListContent::addRowEntry(not_null row) { addToSearchIndex(row); } if (_controller->isRowSelected(row)) { - Assert(row->special() || row->id() == row->peer()->id); + Assert(row->special() || row->id() == row->peer()->id.value); changeCheckState(row, true, anim::type::instant); } } @@ -1772,7 +1772,7 @@ void PeerListContent::restoreState( auto searchWords = TextUtilities::PrepareSearchWords(query); setSearchQuery(query, searchWords.join(' ')); for (auto peer : state->filterResults) { - if (auto existingRow = findRow(peer->id)) { + if (auto existingRow = findRow(peer->id.value)) { _filterResults.push_back(existingRow); } else if (auto row = _controller->createSearchRow(peer)) { appendSearchRow(std::move(row)); diff --git a/Telegram/SourceFiles/boxes/peer_list_controllers.cpp b/Telegram/SourceFiles/boxes/peer_list_controllers.cpp index ce619fd32..f00b4dc86 100644 --- a/Telegram/SourceFiles/boxes/peer_list_controllers.cpp +++ b/Telegram/SourceFiles/boxes/peer_list_controllers.cpp @@ -369,7 +369,7 @@ std::unique_ptr ChatsListBoxController::createSearchRow(not_null history) { - if (auto row = delegate()->peerListFindRow(history->peer->id)) { + if (auto row = delegate()->peerListFindRow(history->peer->id.value)) { updateRowHook(static_cast(row)); return false; } @@ -451,7 +451,7 @@ void ContactsBoxController::rowClicked(not_null row) { } bool ContactsBoxController::appendRow(not_null user) { - if (auto row = delegate()->peerListFindRow(user->id)) { + if (auto row = delegate()->peerListFindRow(user->id.value)) { updateRowHook(row); return false; } diff --git a/Telegram/SourceFiles/boxes/peer_lists_box.cpp b/Telegram/SourceFiles/boxes/peer_lists_box.cpp index 7ea2c91b4..010dba90a 100644 --- a/Telegram/SourceFiles/boxes/peer_lists_box.cpp +++ b/Telegram/SourceFiles/boxes/peer_lists_box.cpp @@ -46,7 +46,7 @@ auto PeerListsBox::collectSelectedRows() return false; }(); if (!foreign) { - result.push_back(session->data().peer(itemId)); + result.push_back(session->data().peer(PeerId(itemId))); } } } @@ -108,10 +108,10 @@ void PeerListsBox::createMultiSelect() { } } const auto session = &firstController()->session(); - if (const auto peer = session->data().peerLoaded(itemId)) { + if (const auto peer = session->data().peerLoaded(PeerId(itemId))) { const auto id = peer->id; for (const auto &list : _lists) { - if (const auto row = list.delegate->peerListFindRow(id)) { + if (const auto row = list.delegate->peerListFindRow(id.value)) { list.content->changeCheckState( row, false, @@ -385,7 +385,7 @@ void PeerListsBox::addSelectItem( not_null peer, anim::type animated) { addSelectItem( - peer->id, + peer->id.value, peer->shortName(), PaintUserpicCallback(peer, false), animated); diff --git a/Telegram/SourceFiles/boxes/peers/add_participants_box.cpp b/Telegram/SourceFiles/boxes/peers/add_participants_box.cpp index acf78bdf9..42e70faf0 100644 --- a/Telegram/SourceFiles/boxes/peers/add_participants_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/add_participants_box.cpp @@ -661,12 +661,12 @@ void AddSpecialBoxController::editAdminDone( using Flag = MTPDchannelParticipantCreator::Flag; _additional.applyParticipant(MTP_channelParticipantCreator( MTP_flags(rank.isEmpty() ? Flag(0) : Flag::f_rank), - MTP_int(user->bareId()), + peerToBareMTPInt(user->id), rights, MTP_string(rank))); } else if (rights.c_chatAdminRights().vflags().v == 0) { _additional.applyParticipant(MTP_channelParticipant( - MTP_int(user->bareId()), + peerToBareMTPInt(user->id), MTP_int(date))); } else { using Flag = MTPDchannelParticipantAdmin::Flag; @@ -674,11 +674,11 @@ void AddSpecialBoxController::editAdminDone( _additional.applyParticipant(MTP_channelParticipantAdmin( MTP_flags(Flag::f_can_edit | (rank.isEmpty() ? Flag(0) : Flag::f_rank)), - MTP_int(user->bareId()), + peerToBareMTPInt(user->id), MTPint(), // inviter_id - MTP_int(alreadyPromotedBy - ? alreadyPromotedBy->bareId() - : user->session().userId()), + peerToBareMTPInt(alreadyPromotedBy + ? alreadyPromotedBy->id + : user->session().userPeerId()), MTP_int(date), rights, MTP_string(rank))); @@ -763,7 +763,7 @@ void AddSpecialBoxController::editRestrictedDone( if (Data::ChatBannedRightsFlags(rights) == 0) { if (const auto user = participant->asUser()) { _additional.applyParticipant(MTP_channelParticipant( - MTP_int(user->bareId()), + peerToBareMTPInt(user->id), MTP_int(date))); } else { _additional.setExternal(participant); @@ -777,14 +777,10 @@ void AddSpecialBoxController::editRestrictedDone( MTP_flags(kicked ? MTPDchannelParticipantBanned::Flag::f_left : MTPDchannelParticipantBanned::Flag(0)), - (participant->isUser() - ? MTP_peerUser(MTP_int(participant->bareId())) - : participant->isChat() - ? MTP_peerChat(MTP_int(participant->bareId())) - : MTP_peerChannel(MTP_int(participant->bareId()))), - MTP_int(alreadyRestrictedBy - ? alreadyRestrictedBy->bareId() - : participant->session().userId()), + peerToMTP(participant->id), + peerToBareMTPInt(alreadyRestrictedBy + ? alreadyRestrictedBy->id + : participant->session().userPeerId()), MTP_int(date), rights)); } @@ -861,7 +857,7 @@ void AddSpecialBoxController::kickUser( } bool AddSpecialBoxController::appendRow(not_null participant) { - if (delegate()->peerListFindRow(participant->id) + if (delegate()->peerListFindRow(participant->id.value) || (_excludeSelf && participant->isSelf())) { return false; } @@ -870,7 +866,7 @@ bool AddSpecialBoxController::appendRow(not_null participant) { } bool AddSpecialBoxController::prependRow(not_null user) { - if (delegate()->peerListFindRow(user->id)) { + if (delegate()->peerListFindRow(user->id.value)) { return false; } delegate()->peerListPrependRow(createRow(user)); diff --git a/Telegram/SourceFiles/boxes/peers/edit_contact_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_contact_box.cpp index 168abd6d8..95ccfac92 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_contact_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_contact_box.cpp @@ -31,7 +31,7 @@ constexpr auto kMaxUserFirstLastName = 64; // See also add_contact_box. QString UserPhone(not_null user) { const auto phone = user->phone(); return phone.isEmpty() - ? user->owner().findContactPhone(user->bareId()) + ? user->owner().findContactPhone(peerToUser(user->id)) : phone; } diff --git a/Telegram/SourceFiles/boxes/peers/edit_linked_chat_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_linked_chat_box.cpp index 2495d2017..96e2f4bf4 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_linked_chat_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_linked_chat_box.cpp @@ -82,7 +82,7 @@ int Controller::contentWidth() const { void Controller::prepare() { const auto appendRow = [&](not_null chat) { - if (delegate()->peerListFindRow(chat->id)) { + if (delegate()->peerListFindRow(chat->id.value)) { return; } auto row = std::make_unique(chat); diff --git a/Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp index 90776d311..e57f6917e 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp @@ -609,7 +609,7 @@ UserData *ParticipantsAdditionalData::applyCreator( UserData *ParticipantsAdditionalData::applyAdmin( const MTPDchannelParticipantAdmin &data) { - const auto user = _peer->owner().userLoaded(data.vuser_id().v); + const auto user = _peer->owner().userLoaded(UserId(data.vuser_id().v)); if (!user) { return nullptr; } else if (const auto chat = _peer->asChat()) { @@ -633,7 +633,7 @@ UserData *ParticipantsAdditionalData::applyAdmin( } else { _adminRanks.remove(user); } - if (const auto by = _peer->owner().userLoaded(data.vpromoted_by().v)) { + if (const auto by = _peer->owner().userLoaded(data.vpromoted_by())) { const auto i = _adminPromotedBy.find(user); if (i == _adminPromotedBy.end()) { _adminPromotedBy.emplace(user, by); @@ -648,7 +648,7 @@ UserData *ParticipantsAdditionalData::applyAdmin( } UserData *ParticipantsAdditionalData::applyRegular(MTPint userId) { - const auto user = _peer->owner().userLoaded(userId.v); + const auto user = _peer->owner().userLoaded(userId); if (!user) { return nullptr; } else if (const auto chat = _peer->asChat()) { @@ -687,7 +687,7 @@ PeerData *ParticipantsAdditionalData::applyBanned( _kicked.erase(participant); } _restrictedRights[participant] = data.vbanned_rights(); - if (const auto by = _peer->owner().userLoaded(data.vkicked_by().v)) { + if (const auto by = _peer->owner().userLoaded(data.vkicked_by())) { const auto i = _restrictedBy.find(participant); if (i == _restrictedBy.end()) { _restrictedBy.emplace(participant, by); @@ -727,7 +727,7 @@ ParticipantsOnlineSorter::ParticipantsOnlineSorter( Data::PeerUpdate::Flag::OnlineStatus ) | rpl::start_with_next([=](const Data::PeerUpdate &update) { const auto peerId = update.peer->id; - if (const auto row = _delegate->peerListFindRow(peerId)) { + if (const auto row = _delegate->peerListFindRow(peerId.value)) { row->refreshStatus(); sortDelayed(); } @@ -829,7 +829,7 @@ void ParticipantsBoxController::setupListChangeViewers() { return; } } - if (const auto row = delegate()->peerListFindRow(user->id)) { + if (const auto row = delegate()->peerListFindRow(user->id.value)) { delegate()->peerListPartitionRows([&](const PeerListRow &row) { return (row.peer() == user); }); @@ -845,7 +845,7 @@ void ParticipantsBoxController::setupListChangeViewers() { channel->owner().megagroupParticipantRemoved( channel ) | rpl::start_with_next([=](not_null user) { - if (const auto row = delegate()->peerListFindRow(user->id)) { + if (const auto row = delegate()->peerListFindRow(user->id.value)) { delegate()->peerListRemoveRow(row); } delegate()->peerListRefreshRows(); @@ -1459,8 +1459,6 @@ void ParticipantsBoxController::rowActionClicked( base::unique_qptr ParticipantsBoxController::rowContextMenu( QWidget *parent, not_null row) { - Expects(row->peer()->isUser()); - const auto chat = _peer->asChat(); const auto channel = _peer->asChannel(); const auto participant = row->peer(); @@ -1468,7 +1466,11 @@ base::unique_qptr ParticipantsBoxController::rowContextMenu( auto result = base::make_unique_q(parent); if (_navigation) { result->addAction( - tr::lng_context_view_profile(tr::now), + (participant->isUser() + ? tr::lng_context_view_profile + : participant->isBroadcast() + ? tr::lng_context_view_channel + : tr::lng_context_view_group)(tr::now), crl::guard(this, [=] { _navigation->showPeerInfo(participant); })); } @@ -1509,12 +1511,12 @@ base::unique_qptr ParticipantsBoxController::rowContextMenu( : tr::lng_context_promote_admin)(tr::now), crl::guard(this, [=] { showAdmin(user); })); } - if (_additional.canRestrictParticipant(participant)) { + if (user && _additional.canRestrictParticipant(participant)) { const auto canRestrictWithoutKick = [&] { if (const auto chat = _peer->asChat()) { return chat->amCreator(); } - return _peer->isMegagroup(); + return _peer->isMegagroup() && !_peer->isGigagroup(); }(); if (canRestrictWithoutKick) { result->addAction( @@ -1522,7 +1524,7 @@ base::unique_qptr ParticipantsBoxController::rowContextMenu( crl::guard(this, [=] { showRestricted(user); })); } } - if (_additional.canRemoveParticipant(participant)) { + if (user && _additional.canRemoveParticipant(participant)) { if (!_additional.isKicked(participant)) { const auto isGroup = _peer->isChat() || _peer->isMegagroup(); result->addAction( @@ -1591,12 +1593,12 @@ void ParticipantsBoxController::editAdminDone( using Flag = MTPDchannelParticipantCreator::Flag; _additional.applyParticipant(MTP_channelParticipantCreator( MTP_flags(rank.isEmpty() ? Flag(0) : Flag::f_rank), - MTP_int(user->bareId()), + peerToBareMTPInt(user->id), rights, MTP_string(rank))); } else if (rights.c_chatAdminRights().vflags().v == 0) { _additional.applyParticipant(MTP_channelParticipant( - MTP_int(user->bareId()), + peerToBareMTPInt(user->id), MTP_int(date))); if (_role == Role::Admins) { removeRow(user); @@ -1607,11 +1609,11 @@ void ParticipantsBoxController::editAdminDone( _additional.applyParticipant(MTP_channelParticipantAdmin( MTP_flags(Flag::f_can_edit | (rank.isEmpty() ? Flag(0) : Flag::f_rank)), - MTP_int(user->bareId()), + peerToBareMTPInt(user->id), MTPint(), // inviter_id - MTP_int(alreadyPromotedBy - ? alreadyPromotedBy->bareId() - : user->session().userId()), + peerToBareMTPInt(alreadyPromotedBy + ? alreadyPromotedBy->id + : user->session().userPeerId()), MTP_int(date), rights, MTP_string(rank))); @@ -1667,7 +1669,7 @@ void ParticipantsBoxController::editRestrictedDone( if (Data::ChatBannedRightsFlags(rights) == 0) { if (user) { _additional.applyParticipant(MTP_channelParticipant( - MTP_int(user->bareId()), + peerToBareMTPInt(user->id), MTP_int(date))); } else { _additional.setExternal(participant); @@ -1684,14 +1686,10 @@ void ParticipantsBoxController::editRestrictedDone( MTP_flags(kicked ? MTPDchannelParticipantBanned::Flag::f_left : MTPDchannelParticipantBanned::Flag(0)), - (participant->isUser() - ? MTP_peerUser(MTP_int(participant->bareId())) - : participant->isChat() - ? MTP_peerChat(MTP_int(participant->bareId())) - : MTP_peerChannel(MTP_int(participant->bareId()))), - MTP_int(alreadyRestrictedBy - ? alreadyRestrictedBy->bareId() - : participant->session().userId()), + peerToMTP(participant->id), + peerToBareMTPInt(alreadyRestrictedBy + ? alreadyRestrictedBy->id + : participant->session().userPeerId()), MTP_int(date), rights)); if (kicked) { @@ -1734,7 +1732,7 @@ void ParticipantsBoxController::kickParticipant(not_null participant) void ParticipantsBoxController::unkickParticipant(not_null user) { _editBox = nullptr; - if (const auto row = delegate()->peerListFindRow(user->id)) { + if (const auto row = delegate()->peerListFindRow(user->id.value)) { delegate()->peerListRemoveRow(row); delegate()->peerListRefreshRows(); } @@ -1750,7 +1748,7 @@ void ParticipantsBoxController::kickParticipantSure( ? *restrictedRights : ChannelData::EmptyRestrictedRights(participant); - if (const auto row = delegate()->peerListFindRow(participant->id)) { + if (const auto row = delegate()->peerListFindRow(participant->id.value)) { delegate()->peerListRemoveRow(row); delegate()->peerListRefreshRows(); } @@ -1800,7 +1798,7 @@ void ParticipantsBoxController::removeAdminSure(not_null user) { void ParticipantsBoxController::removeKickedWithRow( not_null participant) { - if (const auto row = delegate()->peerListFindRow(participant->id)) { + if (const auto row = delegate()->peerListFindRow(participant->id.value)) { removeKicked(row, participant); } else { removeKicked(participant); @@ -1826,7 +1824,7 @@ void ParticipantsBoxController::removeKicked( } bool ParticipantsBoxController::appendRow(not_null participant) { - if (delegate()->peerListFindRow(participant->id)) { + if (delegate()->peerListFindRow(participant->id.value)) { recomputeTypeFor(participant); return false; } else if (auto row = createRow(participant)) { @@ -1840,7 +1838,7 @@ bool ParticipantsBoxController::appendRow(not_null participant) { } bool ParticipantsBoxController::prependRow(not_null participant) { - if (const auto row = delegate()->peerListFindRow(participant->id)) { + if (const auto row = delegate()->peerListFindRow(participant->id.value)) { recomputeTypeFor(participant); refreshCustomStatus(row); if (_role == Role::Admins) { @@ -1859,7 +1857,7 @@ bool ParticipantsBoxController::prependRow(not_null participant) { } bool ParticipantsBoxController::removeRow(not_null participant) { - if (auto row = delegate()->peerListFindRow(participant->id)) { + if (auto row = delegate()->peerListFindRow(participant->id.value)) { if (_role == Role::Admins) { // Perhaps we are removing an admin from search results. row->setCustomStatus(tr::lng_channel_admin_status_not_admin(tr::now)); @@ -1945,7 +1943,7 @@ void ParticipantsBoxController::recomputeTypeFor( if (_role != Role::Profile) { return; } - if (const auto row = delegate()->peerListFindRow(participant->id)) { + if (const auto row = delegate()->peerListFindRow(participant->id.value)) { static_cast(row)->setType(computeType(participant)); } } diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp index 46981585b..bece3bebe 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp @@ -11,7 +11,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "main/main_session.h" #include "boxes/add_contact_box.h" #include "boxes/confirm_box.h" -#include "boxes/single_choice_box.h" #include "boxes/peer_list_controllers.h" #include "boxes/peers/edit_participants_box.h" #include "boxes/peers/edit_peer_type_box.h" @@ -20,6 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "boxes/peers/edit_peer_invite_links.h" #include "boxes/peers/edit_linked_chat_box.h" #include "boxes/stickers_box.h" +#include "ui/boxes/single_choice_box.h" #include "chat_helpers/emoji_suggestions_widget.h" #include "core/application.h" #include "core/core_settings.h" diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp index 080dccfaa..5d2bb6ef9 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp @@ -766,7 +766,7 @@ void AdminsController::prepare() { owner.processUsers(data.vusers()); for (const auto &admin : data.vadmins().v) { admin.match([&](const MTPDchatAdminWithInvites &data) { - const auto adminId = data.vadmin_id().v; + const auto adminId = data.vadmin_id(); if (const auto user = owner.userLoaded(adminId)) { if (!user->isSelf()) { appendRow(user, data.vinvites_count().v); diff --git a/Telegram/SourceFiles/boxes/share_box.cpp b/Telegram/SourceFiles/boxes/share_box.cpp index e4cdaad64..cdcc3710e 100644 --- a/Telegram/SourceFiles/boxes/share_box.cpp +++ b/Telegram/SourceFiles/boxes/share_box.cpp @@ -264,7 +264,7 @@ void ShareBox::prepare() { applyFilterUpdate(query); }); _select->setItemRemovedCallback([=](uint64 itemId) { - if (const auto peer = _descriptor.session->data().peerLoaded(itemId)) { + if (const auto peer = _descriptor.session->data().peerLoaded(PeerId(itemId))) { _inner->peerUnselected(peer); selectedChanged(); update(); @@ -610,7 +610,7 @@ void ShareBox::addPeerToMultiSelect(PeerData *peer, bool skipAnimation) { using AddItemWay = Ui::MultiSelect::AddItemWay; auto addItemWay = skipAnimation ? AddItemWay::SkipAnimation : AddItemWay::Default; _select->addItem( - peer->id, + peer->id.value, peer->isSelf() ? tr::lng_saved_short(tr::now) : peer->shortName(), st::activeButtonBg, PaintUserpicCallback(peer, true), @@ -622,7 +622,7 @@ void ShareBox::innerSelectedChanged(PeerData *peer, bool checked) { addPeerToMultiSelect(peer); //_select->clearQuery(); } else { - _select->removeItem(peer->id); + _select->removeItem(peer->id.value); } selectedChanged(); update(); @@ -1285,25 +1285,25 @@ QString AppendShareGameScoreUrl( not_null session, const QString &url, const FullMsgId &fullId) { - auto shareHashData = QByteArray(0x10, Qt::Uninitialized); - auto shareHashDataInts = reinterpret_cast(shareHashData.data()); + auto shareHashData = QByteArray(0x20, Qt::Uninitialized); + auto shareHashDataInts = reinterpret_cast(shareHashData.data()); auto channel = fullId.channel ? session->data().channelLoaded(fullId.channel) : static_cast(nullptr); - auto channelAccessHash = channel ? channel->access : 0ULL; + auto channelAccessHash = uint64(channel ? channel->access : 0); auto channelAccessHashInts = reinterpret_cast(&channelAccessHash); - shareHashDataInts[0] = session->userId(); - shareHashDataInts[1] = fullId.channel; + shareHashDataInts[0] = session->userId().bare; + shareHashDataInts[1] = fullId.channel.bare; shareHashDataInts[2] = fullId.msg; - shareHashDataInts[3] = channelAccessHashInts[0]; + shareHashDataInts[3] = channelAccessHash; // Count SHA1() of data. auto key128Size = 0x10; auto shareHashEncrypted = QByteArray(key128Size + shareHashData.size(), Qt::Uninitialized); hashSha1(shareHashData.constData(), shareHashData.size(), shareHashEncrypted.data()); - // Mix in channel access hash to the first 64 bits of SHA1 of data. - *reinterpret_cast(shareHashEncrypted.data()) ^= *reinterpret_cast(channelAccessHashInts); + //// Mix in channel access hash to the first 64 bits of SHA1 of data. + //*reinterpret_cast(shareHashEncrypted.data()) ^= channelAccessHash; // Encrypt data. if (!session->local().encrypt(shareHashData.constData(), shareHashEncrypted.data() + key128Size, shareHashData.size(), shareHashEncrypted.constData())) { @@ -1335,7 +1335,7 @@ void ShareGameScoreByHash( auto key128Size = 0x10; auto hashEncrypted = QByteArray::fromBase64(hash.toLatin1(), QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals); - if (hashEncrypted.size() <= key128Size || (hashEncrypted.size() % 0x10) != 0) { + if (hashEncrypted.size() <= key128Size || (hashEncrypted.size() != key128Size + 0x20)) { Ui::show(Box(tr::lng_confirm_phone_link_invalid(tr::now))); return; } @@ -1350,37 +1350,46 @@ void ShareGameScoreByHash( char dataSha1[20] = { 0 }; hashSha1(hashData.constData(), hashData.size(), dataSha1); - // Mix out channel access hash from the first 64 bits of SHA1 of data. - auto channelAccessHash = *reinterpret_cast(hashEncrypted.data()) ^ *reinterpret_cast(dataSha1); + //// Mix out channel access hash from the first 64 bits of SHA1 of data. + //auto channelAccessHash = *reinterpret_cast(hashEncrypted.data()) ^ *reinterpret_cast(dataSha1); - // Check next 64 bits of SHA1() of data. - auto skipSha1Part = sizeof(channelAccessHash); - if (memcmp(dataSha1 + skipSha1Part, hashEncrypted.constData() + skipSha1Part, key128Size - skipSha1Part) != 0) { + //// Check next 64 bits of SHA1() of data. + //auto skipSha1Part = sizeof(channelAccessHash); + //if (memcmp(dataSha1 + skipSha1Part, hashEncrypted.constData() + skipSha1Part, key128Size - skipSha1Part) != 0) { + // Ui::show(Box(tr::lng_share_wrong_user(tr::now))); + // return; + //} + + // Check 128 bits of SHA1() of data. + if (memcmp(dataSha1, hashEncrypted.constData(), key128Size) != 0) { Ui::show(Box(tr::lng_share_wrong_user(tr::now))); return; } - auto hashDataInts = reinterpret_cast(hashData.data()); - if (hashDataInts[0] != session->userId()) { + auto hashDataInts = reinterpret_cast(hashData.data()); + if (hashDataInts[0] != session->userId().bare) { Ui::show(Box(tr::lng_share_wrong_user(tr::now))); return; } // Check first 32 bits of channel access hash. - auto channelAccessHashInts = reinterpret_cast(&channelAccessHash); - if (channelAccessHashInts[0] != hashDataInts[3]) { - Ui::show(Box(tr::lng_share_wrong_user(tr::now))); - return; - } + auto channelAccessHash = hashDataInts[3]; + //auto channelAccessHashInts = reinterpret_cast(&channelAccessHash); + //if (channelAccessHashInts[0] != hashDataInts[3]) { + // Ui::show(Box(tr::lng_share_wrong_user(tr::now))); + // return; + //} - auto channelId = hashDataInts[1]; - auto msgId = hashDataInts[2]; - if (!channelId && channelAccessHash) { + if (((hashDataInts[1] >> 40) != 0) + || ((hashDataInts[2] >> 32) != 0) + || (!hashDataInts[1] && channelAccessHash)) { // If there is no channel id, there should be no channel access_hash. Ui::show(Box(tr::lng_share_wrong_user(tr::now))); return; } + auto channelId = ChannelId(hashDataInts[1]); + auto msgId = MsgId(hashDataInts[2]); if (const auto item = session->data().message(channelId, msgId)) { FastShareMessage(item); } else { @@ -1406,7 +1415,7 @@ void ShareGameScoreByHash( MTP_vector( 1, MTP_inputChannel( - MTP_int(channelId), + MTP_int(channelId.bare), // #TODO ids MTP_long(channelAccessHash))) )).done([=](const MTPmessages_Chats &result) { result.match([&](const auto &data) { diff --git a/Telegram/SourceFiles/calls/calls.style b/Telegram/SourceFiles/calls/calls.style index 4c07ab924..3368a28f1 100644 --- a/Telegram/SourceFiles/calls/calls.style +++ b/Telegram/SourceFiles/calls/calls.style @@ -393,8 +393,8 @@ callErrorToast: Toast(defaultToast) { groupCallWidth: 380px; groupCallHeight: 580px; -groupCallMuteButtonIconSize: size(55px, 55px); -groupCallMuteButtonIconTop: 42px; +groupCallMuteButtonIconSize: size(67px, 67px); +groupCallMuteButtonIconTop: 35px; groupCallRipple: RippleAnimation(defaultRippleAnimation) { color: groupCallMembersBgRipple; } @@ -685,13 +685,19 @@ groupCallMemberInvited: icon {{ "calls/group_calls_invited", groupCallMemberInac groupCallMemberInvitedPosition: point(2px, 12px); groupCallMemberRaisedHand: icon {{ "calls/group_calls_raised_hand", groupCallMemberInactiveStatus }}; +groupCallSettingsInner: IconButton(callButton) { + iconPosition: point(-1px, 22px); + icon: icon {{ "calls/call_settings", groupCallIconFg }}; + ripple: RippleAnimation(defaultRippleAnimation) { + color: callMuteRipple; + } +} groupCallSettings: CallButton(callMicrophoneMute) { - button: IconButton(callButton) { - iconPosition: point(-1px, 22px); - icon: icon {{ "calls/call_settings", groupCallIconFg }}; - ripple: RippleAnimation(defaultRippleAnimation) { - color: callMuteRipple; - } + button: groupCallSettingsInner; +} +groupCallShare: CallButton(groupCallSettings) { + button: IconButton(groupCallSettingsInner) { + icon: icon {{ "calls/group_calls_share", groupCallIconFg }}; } } groupCallHangup: CallButton(callHangup) { @@ -720,6 +726,11 @@ groupCallTopBarJoin: RoundButton(defaultActiveButton) { height: 26px; textTop: 4px; } +groupCallTopBarOpen: RoundButton(groupCallTopBarJoin) { + ripple: RippleAnimation(defaultRippleAnimation) { + color: shadowFg; + } +} groupCallBox: Box(defaultBox) { button: RoundButton(defaultBoxButton) { textFg: groupCallActiveFg; @@ -940,3 +951,18 @@ callTopBarMuteCrossLine: CrossLineAnimation { endPosition: point(26px, 23px); stroke: 2px; } + +groupCallStartsIn: FlatLabel(defaultFlatLabel) { + style: TextStyle(defaultTextStyle) { + font: font(20px semibold); + linkFont: font(20px semibold); + linkFontOver: font(20px semibold underline); + } + textFg: groupCallMembersFg; +} +groupCallScheduledBodyHeight: 200px; +groupCallStartsWhen: groupCallStartsIn; +groupCallStartsInTop: 10px; +groupCallStartsWhenTop: 160px; +groupCallCountdownFont: font(64px semibold); +groupCallCountdownTop: 52px; diff --git a/Telegram/SourceFiles/calls/calls_box_controller.cpp b/Telegram/SourceFiles/calls/calls_box_controller.cpp index 6a3773412..19e0c6a7b 100644 --- a/Telegram/SourceFiles/calls/calls_box_controller.cpp +++ b/Telegram/SourceFiles/calls/calls_box_controller.cpp @@ -409,7 +409,8 @@ void BoxController::receivedCalls(const QVector &result) { NewMessageType::Existing); insertRow(item, InsertWay::Append); } else { - LOG(("API Error: a search results with not loaded peer %1").arg(peerId)); + LOG(("API Error: a search results with not loaded peer %1" + ).arg(peerId.value)); } _offsetId = msgId; } diff --git a/Telegram/SourceFiles/calls/calls_call.cpp b/Telegram/SourceFiles/calls/calls_call.cpp index 2c2269329..cbf41b004 100644 --- a/Telegram/SourceFiles/calls/calls_call.cpp +++ b/Telegram/SourceFiles/calls/calls_call.cpp @@ -490,13 +490,13 @@ bool Call::handleUpdate(const MTPPhoneCall &call) { auto &data = call.c_phoneCallRequested(); if (_type != Type::Incoming || _id != 0 - || peerToUser(_user->id) != data.vadmin_id().v) { + || peerToUser(_user->id) != UserId(data.vadmin_id())) { Unexpected("phoneCallRequested call inside an existing call handleUpdate()"); } - if (_user->session().userId() != data.vparticipant_id().v) { + if (_user->session().userId() != UserId(data.vparticipant_id())) { LOG(("Call Error: Wrong call participant_id %1, expected %2." ).arg(data.vparticipant_id().v - ).arg(_user->session().userId())); + ).arg(_user->session().userId().bare)); finish(FinishType::Failed); return true; } @@ -891,12 +891,12 @@ bool Call::checkCallCommonFields(const T &call) { } auto adminId = (_type == Type::Outgoing) ? _user->session().userId() : peerToUser(_user->id); auto participantId = (_type == Type::Outgoing) ? peerToUser(_user->id) : _user->session().userId(); - if (call.vadmin_id().v != adminId) { - LOG(("Call Error: Wrong call admin_id %1, expected %2.").arg(call.vadmin_id().v).arg(adminId)); + if (UserId(call.vadmin_id()) != adminId) { + LOG(("Call Error: Wrong call admin_id %1, expected %2.").arg(call.vadmin_id().v).arg(adminId.bare)); return checkFailed(); } - if (call.vparticipant_id().v != participantId) { - LOG(("Call Error: Wrong call participant_id %1, expected %2.").arg(call.vparticipant_id().v).arg(participantId)); + if (UserId(call.vparticipant_id()) != participantId) { + LOG(("Call Error: Wrong call participant_id %1, expected %2.").arg(call.vparticipant_id().v).arg(participantId.bare)); return checkFailed(); } return true; diff --git a/Telegram/SourceFiles/calls/calls_choose_join_as.cpp b/Telegram/SourceFiles/calls/calls_choose_join_as.cpp index aa02d788b..d58850b4b 100644 --- a/Telegram/SourceFiles/calls/calls_choose_join_as.cpp +++ b/Telegram/SourceFiles/calls/calls_choose_join_as.cpp @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "calls/calls_choose_join_as.h" #include "calls/calls_group_common.h" +#include "calls/calls_group_menu.h" #include "data/data_peer.h" #include "data/data_user.h" #include "data/data_channel.h" @@ -18,15 +19,21 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "lang/lang_keys.h" #include "apiwrap.h" #include "ui/layers/generic_box.h" +#include "ui/boxes/choose_date_time.h" #include "ui/text/text_utilities.h" #include "boxes/peer_list_box.h" -#include "boxes/confirm_box.h" +#include "base/unixtime.h" +#include "base/timer_rpl.h" #include "styles/style_boxes.h" +#include "styles/style_layers.h" #include "styles/style_calls.h" namespace Calls::Group { namespace { +constexpr auto kDefaultScheduleDuration = 60 * TimeId(60); +constexpr auto kLabelRefreshInterval = 10 * crl::time(1000); + using Context = ChooseJoinAsProcess::Context; class ListController : public PeerListController { @@ -98,7 +105,7 @@ void ListController::rowClicked(not_null row) { if (peer == _selected) { return; } - const auto previous = delegate()->peerListFindRow(_selected->id); + const auto previous = delegate()->peerListFindRow(_selected->id.value); Assert(previous != nullptr); delegate()->peerListSetRowChecked(previous, false); delegate()->peerListSetRowChecked(row, true); @@ -109,6 +116,79 @@ not_null ListController::selected() const { return _selected; } +void ScheduleGroupCallBox( + not_null box, + const JoinInfo &info, + Fn done) { + const auto send = [=](TimeId date) { + box->closeBox(); + + auto copy = info; + copy.scheduleDate = date; + done(std::move(copy)); + }; + const auto duration = box->lifetime().make_state< + rpl::variable>(); + auto description = (info.peer->isBroadcast() + ? tr::lng_group_call_schedule_notified_channel + : tr::lng_group_call_schedule_notified_group)( + lt_duration, + duration->value()); + + const auto now = QDateTime::currentDateTime(); + const auto min = [] { + return base::unixtime::serialize( + QDateTime::currentDateTime().addSecs(12)); + }; + const auto max = [] { + return base::unixtime::serialize( + QDateTime(QDate::currentDate().addDays(8), QTime(0, 0))) - 1; + }; + + // At least half an hour later, at zero minutes/seconds. + const auto schedule = QDateTime( + now.date(), + QTime(now.time().hour(), 0) + ).addSecs(60 * 60 * (now.time().minute() < 30 ? 1 : 2)); + + auto descriptor = Ui::ChooseDateTimeBox(box, { + .title = tr::lng_group_call_schedule_title(), + .submit = tr::lng_schedule_button(), + .done = send, + .min = min, + .time = base::unixtime::serialize(schedule), + .max = max, + .description = std::move(description), + }); + + using namespace rpl::mappers; + *duration = rpl::combine( + rpl::single( + rpl::empty_value() + ) | rpl::then(base::timer_each(kLabelRefreshInterval)), + std::move(descriptor.values) | rpl::filter(_1 != 0), + _2 + ) | rpl::map([](TimeId date) { + const auto now = base::unixtime::now(); + const auto duration = (date - now); + if (duration >= 24 * 60 * 60) { + return tr::lng_group_call_duration_days( + tr::now, + lt_count, + duration / (24 * 60 * 60)); + } else if (duration >= 60 * 60) { + return tr::lng_group_call_duration_hours( + tr::now, + lt_count, + duration / (60 * 60)); + } + return tr::lng_group_call_duration_minutes( + tr::now, + lt_count, + std::max(duration / 60, 1)); + }); +} + void ChooseJoinAsBox( not_null box, Context context, @@ -124,12 +204,13 @@ void ChooseJoinAsBox( } Unexpected("Context in ChooseJoinAsBox."); }()); + const auto &labelSt = (context == Context::Switch) + ? st::groupCallJoinAsLabel + : st::confirmPhoneAboutLabel; box->addRow(object_ptr( box, tr::lng_group_call_join_as_about(), - (context == Context::Switch - ? st::groupCallJoinAsLabel - : st::confirmPhoneAboutLabel))); + labelSt)); auto &lifetime = box->lifetime(); const auto delegate = lifetime.make_state< @@ -155,6 +236,25 @@ void ChooseJoinAsBox( auto next = (context == Context::Switch) ? tr::lng_settings_save() : tr::lng_continue(); + if (context == Context::Create) { + const auto makeLink = [](const QString &text) { + return Ui::Text::Link(text); + }; + const auto label = box->addRow(object_ptr( + box, + tr::lng_group_call_or_schedule( + lt_link, + tr::lng_group_call_schedule(makeLink), + Ui::Text::WithEntities), + labelSt)); + label->setClickHandlerFilter([=](const auto&...) { + auto withJoinAs = info; + withJoinAs.joinAs = controller->selected(); + box->getDelegate()->show( + Box(ScheduleGroupCallBox, withJoinAs, done)); + return false; + }); + } box->addButton(std::move(next), [=] { auto copy = info; copy.joinAs = controller->selected(); @@ -296,7 +396,7 @@ void ChooseJoinAsProcess::start( && (peer->groupCall() != nullptr); if (!changingJoinAsFrom && (onlyByMe || byAlreadyUsed)) { - const auto confirmation = CreateOrJoinConfirmation( + auto confirmation = CreateOrJoinConfirmation( peer, context, byAlreadyUsed); @@ -304,12 +404,36 @@ void ChooseJoinAsProcess::start( finish(info); return; } - auto box = Box<::ConfirmBox>( - confirmation, - (peer->groupCall() - ? tr::lng_group_call_join(tr::now) - : tr::lng_create_group_create(tr::now)), - crl::guard(&_request->guard, [=] { finish(info); })); + const auto creating = !peer->groupCall(); + if (creating) { + confirmation + .append("\n\n") + .append(tr::lng_group_call_or_schedule( + tr::now, + lt_link, + Ui::Text::Link(tr::lng_group_call_schedule(tr::now)), + Ui::Text::WithEntities)); + } + const auto guard = base::make_weak(&_request->guard); + const auto safeFinish = crl::guard(guard, [=] { finish(info); }); + const auto filter = [=](const auto &...) { + if (guard) { + _request->showBox(Box( + ScheduleGroupCallBox, + info, + crl::guard(guard, finish))); + } + return false; + }; + auto box = ConfirmBox({ + .text = confirmation, + .button = (creating + ? tr::lng_create_group_create() + : tr::lng_group_call_join()), + .callback = crl::guard(guard, [=] { finish(info); }), + .st = &st::boxLabel, + .filter = filter, + }); box->boxClosing( ) | rpl::start_with_next([=] { _request = nullptr; diff --git a/Telegram/SourceFiles/calls/calls_group_call.cpp b/Telegram/SourceFiles/calls/calls_group_call.cpp index 176d68f6a..050f2cc42 100644 --- a/Telegram/SourceFiles/calls/calls_group_call.cpp +++ b/Telegram/SourceFiles/calls/calls_group_call.cpp @@ -112,7 +112,7 @@ private: } if (const auto chat = peer->asChat()) { return chat->admins.contains(user) - || (chat->creator == user->bareId()); + || (chat->creator == peerToUser(user->id)); } else if (const auto group = peer->asChannel()) { if (const auto mgInfo = group->mgInfo.get()) { if (mgInfo->creator == user) { @@ -184,6 +184,8 @@ GroupCall::GroupCall( , _joinAs(info.joinAs) , _possibleJoinAs(std::move(info.possibleJoinAs)) , _joinHash(info.joinHash) +, _id(inputCall.c_inputGroupCall().vid().v) +, _scheduleDate(info.scheduleDate) , _lastSpokeCheckTimer([=] { checkLastSpoke(); }) , _checkJoinedTimer([=] { checkJoined(); }) , _pushToTalkCancelTimer([=] { pushToTalkCancel(); }) @@ -215,17 +217,35 @@ GroupCall::GroupCall( checkGlobalShortcutAvailability(); - const auto id = inputCall.c_inputGroupCall().vid().v; - if (id) { - if (const auto call = _peer->groupCall(); call && call->id() == id) { - if (!_peer->canManageGroupCall() && call->joinMuted()) { - _muted = MuteState::ForceMuted; - } + if (const auto real = lookupReal()) { + subscribeToReal(real); + if (!_peer->canManageGroupCall() && real->joinMuted()) { + _muted = MuteState::ForceMuted; } - _state = State::Joining; + } else { + _peer->session().changes().peerFlagsValue( + _peer, + Data::PeerUpdate::Flag::GroupCall + ) | rpl::map([=] { + return lookupReal(); + }) | rpl::filter([](Data::GroupCall *real) { + return real != nullptr; + }) | rpl::map([](Data::GroupCall *real) { + return not_null{ real }; + }) | rpl::take( + 1 + ) | rpl::start_with_next([=](not_null real) { + subscribeToReal(real); + _realChanges.fire_copy(real); + }, _lifetime); + } + if (_id) { join(inputCall); } else { - start(); + start(info.scheduleDate); + } + if (_scheduleDate) { + saveDefaultJoinAs(_joinAs); } _mediaDevices->audioInputId( @@ -249,6 +269,17 @@ GroupCall::~GroupCall() { destroyController(); } +void GroupCall::subscribeToReal(not_null real) { + real->scheduleDateValue( + ) | rpl::start_with_next([=](TimeId date) { + const auto was = _scheduleDate; + _scheduleDate = date; + if (was && !date) { + join(inputCall()); + } + }, _lifetime); +} + void GroupCall::checkGlobalShortcutAvailability() { auto &settings = Core::App().settings(); if (!settings.groupCallPushToTalk()) { @@ -326,10 +357,33 @@ bool GroupCall::showChooseJoinAs() const { && !_possibleJoinAs.front()->isSelf()); } -void GroupCall::start() { +bool GroupCall::scheduleStartSubscribed() const { + if (const auto real = lookupReal()) { + return real->scheduleStartSubscribed(); + } + return false; +} + +Data::GroupCall *GroupCall::lookupReal() const { + const auto real = _peer->groupCall(); + return (real && real->id() == _id) ? real : nullptr; +} + +rpl::producer> GroupCall::real() const { + if (const auto real = lookupReal()) { + return rpl::single(not_null{ real }); + } + return _realChanges.events(); +} + +void GroupCall::start(TimeId scheduleDate) { + using Flag = MTPphone_CreateGroupCall::Flag; _createRequestId = _api.request(MTPphone_CreateGroupCall( + MTP_flags(scheduleDate ? Flag::f_schedule_date : Flag(0)), _peer->input, - MTP_int(openssl::RandomValue()) + MTP_int(openssl::RandomValue()), + MTPstring(), // title + MTP_int(scheduleDate) )).done([=](const MTPUpdates &result) { _acceptFields = true; _peer->session().api().applyUpdates(result); @@ -347,20 +401,16 @@ void GroupCall::start() { } void GroupCall::join(const MTPInputGroupCall &inputCall) { - setState(State::Joining); - if (const auto chat = _peer->asChat()) { - chat->setGroupCall(inputCall); - } else if (const auto group = _peer->asChannel()) { - group->setGroupCall(inputCall); - } else { - Unexpected("Peer type in GroupCall::join."); - } - inputCall.match([&](const MTPDinputGroupCall &data) { _id = data.vid().v; _accessHash = data.vaccess_hash().v; - rejoin(); }); + setState(_scheduleDate ? State::Waiting : State::Joining); + + if (_scheduleDate) { + return; + } + rejoin(); using Update = Data::GroupCall::ParticipantUpdate; _peer->groupCall()->participantUpdated( @@ -409,6 +459,23 @@ void GroupCall::rejoinWithHash(const QString &hash) { } } +void GroupCall::setJoinAs(not_null as) { + _joinAs = as; + if (const auto chat = _peer->asChat()) { + chat->setGroupCallDefaultJoinAs(_joinAs->id); + } else if (const auto channel = _peer->asChannel()) { + channel->setGroupCallDefaultJoinAs(_joinAs->id); + } +} + +void GroupCall::saveDefaultJoinAs(not_null as) { + setJoinAs(as); + _api.request(MTPphone_SaveDefaultGroupCallJoinAs( + _peer->input, + _joinAs->input + )).send(); +} + void GroupCall::rejoin(not_null as) { if (state() != State::Joining && state() != State::Joined @@ -424,12 +491,7 @@ void GroupCall::rejoin(not_null as) { applyMeInCallLocally(); LOG(("Call Info: Requesting join payload.")); - _joinAs = as; - if (const auto chat = _peer->asChat()) { - chat->setGroupCallDefaultJoinAs(_joinAs->id); - } else if (const auto channel = _peer->asChannel()) { - channel->setGroupCallDefaultJoinAs(_joinAs->id); - } + setJoinAs(as); const auto weak = base::make_weak(this); _instance->emitJoinPayload([=](tgcalls::GroupJoinPayload payload) { @@ -479,6 +541,7 @@ void GroupCall::rejoin(not_null as) { applyMeInCallLocally(); maybeSendMutedUpdate(wasMuteState); _peer->session().api().applyUpdates(updates); + applyQueuedSelfUpdates(); checkFirstTimeJoined(); }).fail([=](const MTP::Error &error) { const auto type = error.type(); @@ -561,7 +624,8 @@ void GroupCall::applyMeInCallLocally() { MTP_int(_mySsrc), MTP_int(volume), MTPstring(), // Don't update about text in local updates. - MTP_long(raisedHandRating))), + MTP_long(raisedHandRating), + MTPDataJSON())), MTP_int(0)).c_updateGroupCallParticipants()); } @@ -606,7 +670,8 @@ void GroupCall::applyParticipantLocally( MTP_int(participant->ssrc), MTP_int(volume.value_or(participant->volume)), MTPstring(), // Don't update about text in local updates. - MTP_long(participant->raisedHandRating))), + MTP_long(participant->raisedHandRating), + MTPDataJSON())), MTP_int(0)).c_updateGroupCallParticipants()); } @@ -641,8 +706,12 @@ void GroupCall::rejoinAs(Group::JoinInfo info) { .wasJoinAs = _joinAs, .nowJoinAs = info.joinAs, }; - setState(State::Joining); - rejoin(info.joinAs); + if (_scheduleDate) { + saveDefaultJoinAs(info.joinAs); + } else { + setState(State::Joining); + rejoin(info.joinAs); + } _rejoinEvents.fire_copy(event); } @@ -686,6 +755,29 @@ void GroupCall::finish(FinishType type) { })).send(); } +void GroupCall::startScheduledNow() { + if (!lookupReal()) { + return; + } + _api.request(MTPphone_StartScheduledGroupCall( + inputCall() + )).done([=](const MTPUpdates &result) { + _peer->session().api().applyUpdates(result); + }).send(); +} + +void GroupCall::toggleScheduleStartSubscribed(bool subscribed) { + if (!lookupReal()) { + return; + } + _api.request(MTPphone_ToggleGroupCallStartSubscription( + inputCall(), + MTP_bool(subscribed) + )).done([=](const MTPUpdates &result) { + _peer->session().api().applyUpdates(result); + }).send(); +} + void GroupCall::setMuted(MuteState mute) { const auto set = [=] { const auto wasMuted = (muted() == MuteState::Muted) @@ -729,9 +821,25 @@ void GroupCall::handlePossibleCreateOrJoinResponse( void GroupCall::handlePossibleCreateOrJoinResponse( const MTPDgroupCall &data) { + if (const auto date = data.vschedule_date()) { + _scheduleDate = date->v; + } else { + _scheduleDate = 0; + } if (_acceptFields) { if (!_instance && !_id) { - join(MTP_inputGroupCall(data.vid(), data.vaccess_hash())); + const auto input = MTP_inputGroupCall( + data.vid(), + data.vaccess_hash()); + const auto scheduleDate = data.vschedule_date().value_or_empty(); + if (const auto chat = _peer->asChat()) { + chat->setGroupCall(input, scheduleDate); + } else if (const auto group = _peer->asChannel()) { + group->setGroupCall(input, scheduleDate); + } else { + Unexpected("Peer type in GroupCall::join."); + } + join(input); } return; } else if (_id != data.vid().v || !_instance) { @@ -825,10 +933,8 @@ void GroupCall::handlePossibleDiscarded(const MTPDgroupCallDiscarded &data) { } void GroupCall::addParticipantsToInstance() { - const auto real = _peer->groupCall(); - if (!real - || (real->id() != _id) - || (_instanceMode == InstanceMode::None)) { + const auto real = lookupReal(); + if (!real || (_instanceMode == InstanceMode::None)) { return; } for (const auto &participant : real->participants()) { @@ -850,7 +956,7 @@ void GroupCall::addPreparedParticipants() { if (!_preparedParticipants.empty()) { _instance->addParticipants(base::take(_preparedParticipants)); } - if (const auto real = _peer->groupCall(); real && real->id() == _id) { + if (const auto real = lookupReal()) { if (!_unresolvedSsrcs.empty()) { real->resolveParticipants(base::take(_unresolvedSsrcs)); } @@ -890,91 +996,105 @@ void GroupCall::handleUpdate(const MTPDupdateGroupCallParticipants &data) { return; } const auto state = _state.current(); - if (state != State::Joined && state != State::Connecting) { - return; - } - - const auto handleOtherParticipants = [=]( - const MTPDgroupCallParticipant &data) { - if (data.is_min()) { - // No real information about mutedByMe or my custom volume. - return; - } - const auto participantPeer = _peer->owner().peer( - peerFromMTP(data.vpeer())); - const auto participant = LookupParticipant( - _peer, - _id, - participantPeer); - if (!participant) { - return; - } - _otherParticipantStateValue.fire(Group::ParticipantState{ - .peer = participantPeer, - .volume = data.vvolume().value_or_empty(), - .mutedByMe = data.is_muted_by_you(), - }); - }; - + const auto joined = (state == State::Joined) + || (state == State::Connecting); for (const auto &participant : data.vparticipants().v) { participant.match([&](const MTPDgroupCallParticipant &data) { const auto isSelf = data.is_self() || (data.is_min() && peerFromMTP(data.vpeer()) == _joinAs->id); if (!isSelf) { - handleOtherParticipants(data); - return; - } - if (data.is_left()) { - if (data.vsource().v == _mySsrc) { - // I was removed from the call, rejoin. - LOG(("Call Info: " - "Rejoin after got 'left' with my ssrc.")); - setState(State::Joining); - rejoin(); - } - return; - } else if (data.vsource().v != _mySsrc) { - if (!_mySsrcs.contains(data.vsource().v)) { - // I joined from another device, hangup. - LOG(("Call Info: " - "Hangup after '!left' with ssrc %1, my %2." - ).arg(data.vsource().v - ).arg(_mySsrc)); - _mySsrc = 0; - hangup(); - } else { - LOG(("Call Info: " - "Some old 'self' with '!left' and ssrc %1, my %2." - ).arg(data.vsource().v - ).arg(_mySsrc)); - } - return; - } - if (data.is_muted() && !data.is_can_self_unmute()) { - setMuted(data.vraise_hand_rating().value_or_empty() - ? MuteState::RaisedHand - : MuteState::ForceMuted); - } else if (_instanceMode == InstanceMode::Stream) { - LOG(("Call Info: Rejoin after unforcemute in stream mode.")); - setState(State::Joining); - rejoin(); - } else if (muted() == MuteState::ForceMuted - || muted() == MuteState::RaisedHand) { - setMuted(MuteState::Muted); - if (!_instanceTransitioning) { - notifyAboutAllowedToSpeak(); - } - } else if (data.is_muted() && muted() != MuteState::Muted) { - setMuted(MuteState::Muted); + applyOtherParticipantUpdate(data); + } else if (joined) { + applySelfUpdate(data); + } else { + _queuedSelfUpdates.push_back(participant); } }); } } +void GroupCall::applyQueuedSelfUpdates() { + const auto weak = base::make_weak(this); + while (weak + && !_queuedSelfUpdates.empty() + && (_state.current() == State::Joined + || _state.current() == State::Connecting)) { + const auto update = _queuedSelfUpdates.front(); + _queuedSelfUpdates.erase(_queuedSelfUpdates.begin()); + update.match([&](const MTPDgroupCallParticipant &data) { + applySelfUpdate(data); + }); + } +} + +void GroupCall::applySelfUpdate(const MTPDgroupCallParticipant &data) { + if (data.is_left()) { + if (data.vsource().v == _mySsrc) { + // I was removed from the call, rejoin. + LOG(("Call Info: " + "Rejoin after got 'left' with my ssrc.")); + setState(State::Joining); + rejoin(); + } + return; + } else if (data.vsource().v != _mySsrc) { + if (!_mySsrcs.contains(data.vsource().v)) { + // I joined from another device, hangup. + LOG(("Call Info: " + "Hangup after '!left' with ssrc %1, my %2." + ).arg(data.vsource().v + ).arg(_mySsrc)); + _mySsrc = 0; + hangup(); + } else { + LOG(("Call Info: " + "Some old 'self' with '!left' and ssrc %1, my %2." + ).arg(data.vsource().v + ).arg(_mySsrc)); + } + return; + } + if (data.is_muted() && !data.is_can_self_unmute()) { + setMuted(data.vraise_hand_rating().value_or_empty() + ? MuteState::RaisedHand + : MuteState::ForceMuted); + } else if (_instanceMode == InstanceMode::Stream) { + LOG(("Call Info: Rejoin after unforcemute in stream mode.")); + setState(State::Joining); + rejoin(); + } else if (muted() == MuteState::ForceMuted + || muted() == MuteState::RaisedHand) { + setMuted(MuteState::Muted); + if (!_instanceTransitioning) { + notifyAboutAllowedToSpeak(); + } + } else if (data.is_muted() && muted() != MuteState::Muted) { + setMuted(MuteState::Muted); + } +} + +void GroupCall::applyOtherParticipantUpdate( + const MTPDgroupCallParticipant &data) { + if (data.is_min()) { + // No real information about mutedByMe or my custom volume. + return; + } + const auto participantPeer = _peer->owner().peer( + peerFromMTP(data.vpeer())); + if (!LookupParticipant(_peer, _id, participantPeer)) { + return; + } + _otherParticipantStateValue.fire(Group::ParticipantState{ + .peer = participantPeer, + .volume = data.vvolume().value_or_empty(), + .mutedByMe = data.is_muted_by_you(), + }); +} + void GroupCall::changeTitle(const QString &title) { - const auto real = _peer->groupCall(); - if (!real || real->id() != _id || real->title() == title) { + const auto real = lookupReal(); + if (!real || real->title() == title) { return; } @@ -989,8 +1109,8 @@ void GroupCall::changeTitle(const QString &title) { } void GroupCall::toggleRecording(bool enabled, const QString &title) { - const auto real = _peer->groupCall(); - if (!real || real->id() != _id) { + const auto real = lookupReal(); + if (!real) { return; } @@ -1169,10 +1289,8 @@ void GroupCall::broadcastPartCancel(not_null task) { void GroupCall::requestParticipantsInformation( const std::vector &ssrcs) { - const auto real = _peer->groupCall(); - if (!real - || (real->id() != _id) - || (_instanceMode == InstanceMode::None)) { + const auto real = lookupReal(); + if (!real || (_instanceMode == InstanceMode::None)) { for (const auto ssrc : ssrcs) { _unresolvedSsrcs.emplace(ssrc); } @@ -1206,8 +1324,8 @@ void GroupCall::updateInstanceMuteState() { } void GroupCall::updateInstanceVolumes() { - const auto real = _peer->groupCall(); - if (!real || real->id() != _id) { + const auto real = lookupReal(); + if (!real) { return; } @@ -1283,8 +1401,8 @@ void GroupCall::audioLevelsUpdated(const tgcalls::GroupLevelsUpdate &data) { } void GroupCall::checkLastSpoke() { - const auto real = _peer->groupCall(); - if (!real || real->id() != _id) { + const auto real = lookupReal(); + if (!real) { return; } @@ -1495,8 +1613,8 @@ void GroupCall::editParticipant( std::variant> GroupCall::inviteUsers( const std::vector> &users) { - const auto real = _peer->groupCall(); - if (!real || real->id() != _id) { + const auto real = lookupReal(); + if (!real) { return 0; } const auto owner = &_peer->owner(); diff --git a/Telegram/SourceFiles/calls/calls_group_call.h b/Telegram/SourceFiles/calls/calls_group_call.h index 09781aab9..6e029418a 100644 --- a/Telegram/SourceFiles/calls/calls_group_call.h +++ b/Telegram/SourceFiles/calls/calls_group_call.h @@ -34,6 +34,7 @@ class MediaDevices; namespace Data { struct LastSpokeTimes; struct GroupCallParticipant; +class GroupCall; } // namespace Data namespace Calls { @@ -109,8 +110,15 @@ public: return _joinAs; } [[nodiscard]] bool showChooseJoinAs() const; + [[nodiscard]] TimeId scheduleDate() const { + return _scheduleDate; + } + [[nodiscard]] bool scheduleStartSubscribed() const; - void start(); + [[nodiscard]] Data::GroupCall *lookupReal() const; + [[nodiscard]] rpl::producer> real() const; + + void start(TimeId scheduleDate); void hangup(); void discard(); void rejoinAs(Group::JoinInfo info); @@ -123,6 +131,8 @@ public: [[nodiscard]] bool recordingStoppedByMe() const { return _recordingStoppedByMe; } + void startScheduledNow(); + void toggleScheduleStartSubscribed(bool subscribed); void setMuted(MuteState mute); void setMutedAndUpdate(MuteState mute); @@ -138,6 +148,7 @@ public: enum State { Creating, + Waiting, Joining, Connecting, Joined, @@ -245,6 +256,9 @@ private: void applyMeInCallLocally(); void rejoin(); void rejoin(not_null as); + void setJoinAs(not_null as); + void saveDefaultJoinAs(not_null as); + void subscribeToReal(not_null real); void audioLevelsUpdated(const tgcalls::GroupLevelsUpdate &data); void setInstanceConnected(tgcalls::GroupNetworkState networkState); @@ -276,6 +290,9 @@ private: not_null participantPeer, bool mute, std::optional volume); + void applyQueuedSelfUpdates(); + void applySelfUpdate(const MTPDgroupCallParticipant &data); + void applyOtherParticipantUpdate(const MTPDgroupCallParticipant &data); [[nodiscard]] MTPInputGroupCall inputCall() const; @@ -284,6 +301,7 @@ private: rpl::event_stream _peerStream; not_null _history; // Can change in legacy group migration. MTP::Sender _api; + rpl::event_stream> _realChanges; rpl::variable _state = State::Creating; rpl::variable _instanceState = InstanceState::Disconnected; @@ -306,10 +324,12 @@ private: bool _acceptFields = false; rpl::event_stream _otherParticipantStateValue; + std::vector _queuedSelfUpdates; uint64 _id = 0; uint64 _accessHash = 0; uint32 _mySsrc = 0; + TimeId _scheduleDate = 0; base::flat_set _mySsrcs; mtpRequestId _createRequestId = 0; mtpRequestId _updateMuteRequestId = 0; diff --git a/Telegram/SourceFiles/calls/calls_group_common.h b/Telegram/SourceFiles/calls/calls_group_common.h index 371f290b0..0cfa4d9fe 100644 --- a/Telegram/SourceFiles/calls/calls_group_common.h +++ b/Telegram/SourceFiles/calls/calls_group_common.h @@ -44,6 +44,7 @@ struct JoinInfo { not_null joinAs; std::vector> possibleJoinAs; QString joinHash; + TimeId scheduleDate = 0; }; } // namespace Calls::Group diff --git a/Telegram/SourceFiles/calls/calls_group_members.cpp b/Telegram/SourceFiles/calls/calls_group_members.cpp index 2d1d29ba6..5ad91316f 100644 --- a/Telegram/SourceFiles/calls/calls_group_members.cpp +++ b/Telegram/SourceFiles/calls/calls_group_members.cpp @@ -317,7 +317,7 @@ private: not_null participantPeer, bool participantIsCallAdmin, not_null row); - void setupListChangeViewers(not_null call); + void setupListChangeViewers(); void subscribeToChanges(not_null real); void updateRow( const std::optional &was, @@ -335,16 +335,11 @@ private: uint64 raiseHandRating) const; Row *findRow(not_null participantPeer) const; - [[nodiscard]] Data::GroupCall *resolvedRealCall() const; void appendInvitedUsers(); void scheduleRaisedHandStatusRemove(); - const base::weak_ptr _call; + const not_null _call; not_null _peer; - - // Use only resolvedRealCall() method, not this value directly. - Data::GroupCall *_realCallRawValue = nullptr; - uint64 _realId = 0; bool _prepared = false; rpl::event_stream _toggleMuteRequests; @@ -909,7 +904,7 @@ MembersController::MembersController( , _raisedHandStatusRemoveTimer([=] { scheduleRaisedHandStatusRemove(); }) , _inactiveCrossLine(st::groupCallMemberInactiveCrossLine) , _coloredCrossLine(st::groupCallMemberColoredCrossLine) { - setupListChangeViewers(call); + setupListChangeViewers(); style::PaletteChanged( ) | rpl::start_with_next([=] { @@ -964,32 +959,20 @@ MembersController::~MembersController() { base::take(_menu); } -void MembersController::setupListChangeViewers(not_null call) { - const auto peer = call->peer(); - peer->session().changes().peerFlagsValue( - peer, - Data::PeerUpdate::Flag::GroupCall - ) | rpl::map([=] { - return peer->groupCall(); - }) | rpl::filter([=](Data::GroupCall *real) { - const auto call = _call.get(); - return call && real && (real->id() == call->id()); - }) | rpl::take( - 1 +void MembersController::setupListChangeViewers() { + _call->real( ) | rpl::start_with_next([=](not_null real) { subscribeToChanges(real); }, _lifetime); - call->stateValue( + _call->stateValue( ) | rpl::start_with_next([=] { - const auto call = _call.get(); - const auto real = peer->groupCall(); - if (call && real && (real->id() == call->id())) { + if (const auto real = _call->lookupReal()) { //updateRow(channel->session().user()); } }, _lifetime); - call->levelUpdates( + _call->levelUpdates( ) | rpl::start_with_next([=](const LevelUpdate &update) { const auto i = _soundingRowBySsrc.find(update.ssrc); if (i != end(_soundingRowBySsrc)) { @@ -997,7 +980,7 @@ void MembersController::setupListChangeViewers(not_null call) { } }, _lifetime); - call->rejoinEvents( + _call->rejoinEvents( ) | rpl::start_with_next([=](const Group::RejoinEvent &event) { const auto guard = gsl::finally([&] { delegate()->peerListRefreshRows(); @@ -1014,9 +997,6 @@ void MembersController::setupListChangeViewers(not_null call) { } void MembersController::subscribeToChanges(not_null real) { - _realCallRawValue = real; - _realId = real->id(); - _fullCount = real->fullCountValue(); real->participantsSliceAdded( @@ -1053,17 +1033,19 @@ void MembersController::subscribeToChanges(not_null real) { } void MembersController::appendInvitedUsers() { - for (const auto user : _peer->owner().invitedToCallUsers(_realId)) { - if (auto row = createInvitedRow(user)) { - delegate()->peerListAppendRow(std::move(row)); + if (const auto id = _call->id()) { + for (const auto user : _peer->owner().invitedToCallUsers(id)) { + if (auto row = createInvitedRow(user)) { + delegate()->peerListAppendRow(std::move(row)); + } } + delegate()->peerListRefreshRows(); } - delegate()->peerListRefreshRows(); using Invite = Data::Session::InviteToCall; _peer->owner().invitesToCalls( ) | rpl::filter([=](const Invite &invite) { - return (invite.id == _realId); + return (invite.id == _call->id()); }) | rpl::start_with_next([=](const Invite &invite) { if (auto row = createInvitedRow(invite.user)) { delegate()->peerListAppendRow(std::move(row)); @@ -1120,7 +1102,7 @@ void MembersController::updateRow( if (checkPosition) { checkRowPosition(checkPosition); } else if (addedToBottom) { - const auto real = resolvedRealCall(); + const auto real = _call->lookupReal(); if (real && real->joinedToTop()) { const auto proj = [&](const PeerListRow &other) { const auto &real = static_cast(other); @@ -1311,15 +1293,7 @@ void MembersController::updateRowLevel( Row *MembersController::findRow(not_null participantPeer) const { return static_cast( - delegate()->peerListFindRow(participantPeer->id)); -} - -Data::GroupCall *MembersController::resolvedRealCall() const { - return (_realCallRawValue - && (_peer->groupCall() == _realCallRawValue) - && (_realCallRawValue->id() == _realId)) - ? _realCallRawValue - : nullptr; + delegate()->peerListFindRow(participantPeer->id.value)); } Main::Session &MembersController::session() const { @@ -1332,9 +1306,7 @@ void MembersController::prepare() { setDescriptionText(tr::lng_contacts_loading(tr::now)); setSearchNoResultsText(tr::lng_blocked_list_not_found(tr::now)); - const auto call = _call.get(); - if (const auto real = _peer->groupCall() - ; real && call && real->id() == call->id()) { + if (const auto real = _call->lookupReal()) { prepareRows(real); } else if (auto row = createRowForMe()) { delegate()->peerListAppendRow(std::move(row)); @@ -1342,15 +1314,12 @@ void MembersController::prepare() { } loadMoreRows(); - if (_realId) { - appendInvitedUsers(); - } + appendInvitedUsers(); _prepared = true; } bool MembersController::isMe(not_null participantPeer) const { - const auto call = _call.get(); - return call && (call->joinAs() == participantPeer); + return (_call->joinAs() == participantPeer); } void MembersController::prepareRows(not_null real) { @@ -1379,19 +1348,17 @@ void MembersController::prepareRows(not_null real) { } } if (!foundMe) { - if (const auto call = _call.get()) { - const auto me = call->joinAs(); - const auto i = ranges::find( - participants, - me, - &Data::GroupCall::Participant::peer); - auto row = (i != end(participants)) - ? createRow(*i) - : createRowForMe(); - if (row) { - changed = true; - delegate()->peerListAppendRow(std::move(row)); - } + const auto me = _call->joinAs(); + const auto i = ranges::find( + participants, + me, + &Data::GroupCall::Participant::peer); + auto row = (i != end(participants)) + ? createRow(*i) + : createRowForMe(); + if (row) { + changed = true; + delegate()->peerListAppendRow(std::move(row)); } } for (const auto &participant : participants) { @@ -1406,7 +1373,7 @@ void MembersController::prepareRows(not_null real) { } void MembersController::loadMoreRows() { - if (const auto real = _peer->groupCall()) { + if (const auto real = _call->lookupReal()) { real->requestParticipants(); } } @@ -1435,7 +1402,7 @@ void MembersController::rowUpdateRow(not_null row) { void MembersController::rowScheduleRaisedHandStatusRemove( not_null row) { - const auto id = row->peer()->id; + const auto id = row->id(); const auto when = crl::now() + kKeepRaisedHandStatusDuration; const auto i = _raisedHandStatusRemoveAt.find(id); if (i != _raisedHandStatusRemoveAt.end()) { @@ -1634,12 +1601,10 @@ base::unique_qptr MembersController::createRowContextMenu( } if (isMe(participantPeer)) { - if (const auto strong = _call.get() - ; strong && strong->muted() == MuteState::RaisedHand) { + if (_call->muted() == MuteState::RaisedHand) { const auto removeHand = [=] { - if (const auto strong = _call.get() - ; strong && strong->muted() == MuteState::RaisedHand) { - strong->setMutedAndUpdate(MuteState::ForceMuted); + if (_call->muted() == MuteState::RaisedHand) { + _call->setMutedAndUpdate(MuteState::ForceMuted); } }; result->addAction( @@ -1728,14 +1693,12 @@ void MembersController::addMuteActionsToContextMenu( auto mutesFromVolume = rpl::never() | rpl::type_erased(); - const auto call = _call.get(); - if (!isMuted || (call && call->joinAs() == participantPeer)) { - auto otherParticipantStateValue = call - ? call->otherParticipantStateValue( - ) | rpl::filter([=](const Group::ParticipantState &data) { - return data.peer == participantPeer; - }) - : rpl::never() | rpl::type_erased(); + if (!isMuted || _call->joinAs() == participantPeer) { + auto otherParticipantStateValue + = _call->otherParticipantStateValue( + ) | rpl::filter([=](const Group::ParticipantState &data) { + return data.peer == participantPeer; + }); auto volumeItem = base::make_unique_q( menu->menu(), @@ -1814,11 +1777,7 @@ void MembersController::addMuteActionsToContextMenu( } std::unique_ptr MembersController::createRowForMe() { - const auto call = _call.get(); - if (!call) { - return nullptr; - } - auto result = std::make_unique(this, call->joinAs()); + auto result = std::make_unique(this, _call->joinAs()); updateRow(result.get(), nullptr); return result; } @@ -1877,12 +1836,8 @@ auto Members::kickParticipantRequests() const int Members::desiredHeight() const { const auto top = _addMember ? _addMember->height() : 0; auto count = [&] { - if (const auto call = _call.get()) { - if (const auto real = call->peer()->groupCall()) { - if (call->id() == real->id()) { - return real->fullCount(); - } - } + if (const auto real = _call->lookupReal()) { + return real->fullCount(); } return 0; }(); @@ -1911,16 +1866,7 @@ void Members::setupAddMember(not_null call) { if (const auto channel = peer->asBroadcast()) { _canAddMembers = rpl::single( false - ) | rpl::then(peer->session().changes().peerFlagsValue( - peer, - Data::PeerUpdate::Flag::GroupCall - ) | rpl::map([=] { - return peer->groupCall(); - }) | rpl::filter([=](Data::GroupCall *real) { - const auto call = _call.get(); - return call && real && (real->id() == call->id()); - }) | rpl::take( - 1 + ) | rpl::then(_call->real( ) | rpl::map([=] { return Data::PeerFlagValue( channel, diff --git a/Telegram/SourceFiles/calls/calls_group_members.h b/Telegram/SourceFiles/calls/calls_group_members.h index 23dc33717..20064879c 100644 --- a/Telegram/SourceFiles/calls/calls_group_members.h +++ b/Telegram/SourceFiles/calls/calls_group_members.h @@ -75,7 +75,7 @@ private: void updateControlsGeometry(); - const base::weak_ptr _call; + const not_null _call; object_ptr _scroll; std::unique_ptr _listController; object_ptr _addMember = { nullptr }; diff --git a/Telegram/SourceFiles/calls/calls_group_menu.cpp b/Telegram/SourceFiles/calls/calls_group_menu.cpp index 43c6205ce..3887503bc 100644 --- a/Telegram/SourceFiles/calls/calls_group_menu.cpp +++ b/Telegram/SourceFiles/calls/calls_group_menu.cpp @@ -514,15 +514,6 @@ base::unique_qptr MakeRecordingAction( std::move(callback)); } -base::unique_qptr MakeFinishAction( - not_null menu, - Fn callback) { - return MakeAttentionAction( - menu, - tr::lng_group_call_end(tr::now), - std::move(callback)); -} - } // namespace void LeaveBox( @@ -530,16 +521,25 @@ void LeaveBox( not_null call, bool discardChecked, BoxContext context) { - box->setTitle(tr::lng_group_call_leave_title()); + const auto scheduled = (call->scheduleDate() != 0); + if (!scheduled) { + box->setTitle(tr::lng_group_call_leave_title()); + } const auto inCall = (context == BoxContext::GroupCallPanel); - box->addRow(object_ptr( - box.get(), - tr::lng_group_call_leave_sure(), - (inCall ? st::groupCallBoxLabel : st::boxLabel))); + box->addRow( + object_ptr( + box.get(), + (scheduled + ? tr::lng_group_call_close_sure() + : tr::lng_group_call_leave_sure()), + (inCall ? st::groupCallBoxLabel : st::boxLabel)), + scheduled ? st::boxPadding : st::boxRowPadding); const auto discard = call->peer()->canManageGroupCall() ? box->addRow(object_ptr( box.get(), - tr::lng_group_call_end(), + (scheduled + ? tr::lng_group_call_also_cancel() + : tr::lng_group_call_also_end()), discardChecked, (inCall ? st::groupCallCheckbox : st::defaultBoxCheckbox), (inCall ? st::groupCallCheck : st::defaultCheck)), @@ -550,7 +550,10 @@ void LeaveBox( st::boxRowPadding.bottom())) : nullptr; const auto weak = base::make_weak(call.get()); - box->addButton(tr::lng_group_call_leave(), [=] { + auto label = scheduled + ? tr::lng_group_call_close() + : tr::lng_group_call_leave(); + box->addButton(std::move(label), [=] { const auto discardCall = (discard && discard->checked()); box->closeBox(); @@ -565,19 +568,20 @@ void LeaveBox( box->addButton(tr::lng_cancel(), [=] { box->closeBox(); }); } -void ConfirmBox( +void ConfirmBoxBuilder( not_null box, - const TextWithEntities &text, - rpl::producer button, - Fn callback) { - box->addRow( + ConfirmBoxArgs &&args) { + const auto label = box->addRow( object_ptr( box.get(), - rpl::single(text), - st::groupCallBoxLabel), + rpl::single(args.text), + args.st ? *args.st : st::groupCallBoxLabel), st::boxPadding); - if (callback) { - box->addButton(std::move(button), callback); + if (args.callback) { + box->addButton(std::move(args.button), std::move(args.callback)); + } + if (args.filter) { + label->setClickHandlerFilter(std::move(args.filter)); } box->addButton(tr::lng_cancel(), [=] { box->closeBox(); }); } @@ -603,7 +607,8 @@ void FillMenu( const auto addEditJoinAs = call->showChooseJoinAs(); const auto addEditTitle = peer->canManageGroupCall(); - const auto addEditRecording = peer->canManageGroupCall(); + const auto addEditRecording = peer->canManageGroupCall() + && !real->scheduleDate(); if (addEditJoinAs) { menu->addAction(MakeJoinAsAction( menu->menu(), @@ -660,7 +665,7 @@ void FillMenu( showBox(Box(SettingsBox, strong)); } }); - menu->addAction(MakeFinishAction(menu->menu(), [=] { + const auto finish = [=] { if (const auto strong = weak.get()) { showBox(Box( LeaveBox, @@ -668,7 +673,13 @@ void FillMenu( true, BoxContext::GroupCallPanel)); } - })); + }; + menu->addAction(MakeAttentionAction( + menu->menu(), + (real->scheduleDate() + ? tr::lng_group_call_cancel(tr::now) + : tr::lng_group_call_end(tr::now)), + finish)); } base::unique_qptr MakeAttentionAction( diff --git a/Telegram/SourceFiles/calls/calls_group_menu.h b/Telegram/SourceFiles/calls/calls_group_menu.h index 7eeed21fc..484974493 100644 --- a/Telegram/SourceFiles/calls/calls_group_menu.h +++ b/Telegram/SourceFiles/calls/calls_group_menu.h @@ -9,6 +9,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/object_ptr.h" #include "base/unique_qptr.h" +#include "ui/layers/generic_box.h" + +namespace style { +struct FlatLabel; +} // namespace style namespace Ui { class DropdownMenu; @@ -38,11 +43,19 @@ void LeaveBox( bool discardChecked, BoxContext context); -void ConfirmBox( - not_null box, - const TextWithEntities &text, - rpl::producer button, - Fn callback); +struct ConfirmBoxArgs { + TextWithEntities text; + rpl::producer button; + Fn callback; + const style::FlatLabel *st = nullptr; + Fn filter; +}; + +void ConfirmBoxBuilder(not_null box, ConfirmBoxArgs &&args); + +inline auto ConfirmBox(ConfirmBoxArgs &&args) { + return Box(ConfirmBoxBuilder, std::move(args)); +} void FillMenu( not_null menu, diff --git a/Telegram/SourceFiles/calls/calls_group_panel.cpp b/Telegram/SourceFiles/calls/calls_group_panel.cpp index 78071a92b..caa700953 100644 --- a/Telegram/SourceFiles/calls/calls_group_panel.cpp +++ b/Telegram/SourceFiles/calls/calls_group_panel.cpp @@ -20,6 +20,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/checkbox.h" #include "ui/widgets/dropdown_menu.h" #include "ui/widgets/input_fields.h" +#include "ui/chat/group_call_bar.h" #include "ui/layers/layer_manager.h" #include "ui/layers/generic_box.h" #include "ui/text/text_utilities.h" @@ -34,12 +35,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_group_call.h" #include "data/data_session.h" #include "data/data_changes.h" +#include "data/data_peer_values.h" #include "main/main_session.h" #include "base/event_filter.h" #include "boxes/peers/edit_participants_box.h" #include "boxes/peers/add_participants_box.h" #include "boxes/peer_lists_box.h" #include "boxes/confirm_box.h" +#include "base/unixtime.h" +#include "base/timer_rpl.h" #include "app.h" #include "apiwrap.h" // api().kickParticipant. #include "styles/style_calls.h" @@ -55,6 +59,7 @@ namespace { constexpr auto kSpacePushToTalkDelay = crl::time(250); constexpr auto kRecordingAnimationDuration = crl::time(1200); constexpr auto kRecordingOpacity = 0.6; +constexpr auto kStartNoConfirmation = TimeId(10); class InviteController final : public ParticipantsBoxController { public: @@ -115,6 +120,123 @@ private: }; +[[nodiscard]] rpl::producer StartsWhenText( + rpl::producer date) { + return std::move( + date + ) | rpl::map([](TimeId date) -> rpl::producer { + const auto parsedDate = base::unixtime::parse(date); + const auto dateDay = QDateTime(parsedDate.date(), QTime(0, 0)); + const auto previousDay = QDateTime( + parsedDate.date().addDays(-1), + QTime(0, 0)); + const auto now = QDateTime::currentDateTime(); + const auto kDay = int64(24 * 60 * 60); + const auto tillTomorrow = int64(now.secsTo(previousDay)); + const auto tillToday = tillTomorrow + kDay; + const auto tillAfter = tillToday + kDay; + + const auto time = parsedDate.time().toString( + QLocale::system().timeFormat(QLocale::ShortFormat)); + auto exact = tr::lng_group_call_starts_short_date( + lt_date, + rpl::single(langDayOfMonthFull(dateDay.date())), + lt_time, + rpl::single(time) + ) | rpl::type_erased(); + auto tomorrow = tr::lng_group_call_starts_short_tomorrow( + lt_time, + rpl::single(time)); + auto today = tr::lng_group_call_starts_short_today( + lt_time, + rpl::single(time)); + + auto todayAndAfter = rpl::single( + std::move(today) + ) | rpl::then(base::timer_once( + std::min(tillAfter, kDay) * crl::time(1000) + ) | rpl::map([=] { + return rpl::duplicate(exact); + })) | rpl::flatten_latest() | rpl::type_erased(); + + auto tomorrowAndAfter = rpl::single( + std::move(tomorrow) + ) | rpl::then(base::timer_once( + std::min(tillToday, kDay) * crl::time(1000) + ) | rpl::map([=] { + return rpl::duplicate(todayAndAfter); + })) | rpl::flatten_latest() | rpl::type_erased(); + + auto full = rpl::single( + rpl::duplicate(exact) + ) | rpl::then(base::timer_once( + tillTomorrow * crl::time(1000) + ) | rpl::map([=] { + return rpl::duplicate(tomorrowAndAfter); + })) | rpl::flatten_latest() | rpl::type_erased(); + + if (tillTomorrow > 0) { + return full; + } else if (tillToday > 0) { + return tomorrowAndAfter; + } else if (tillAfter > 0) { + return todayAndAfter; + } else { + return exact; + } + }) | rpl::flatten_latest(); +} + +[[nodiscard]] object_ptr CreateGradientLabel( + QWidget *parent, + rpl::producer text) { + struct State { + QBrush brush; + QPainterPath path; + }; + auto result = object_ptr(parent); + const auto raw = result.data(); + const auto state = raw->lifetime().make_state(); + + std::move( + text + ) | rpl::start_with_next([=](const QString &text) { + state->path = QPainterPath(); + const auto &font = st::groupCallCountdownFont; + state->path.addText(0, font->ascent, font->f, text); + const auto width = font->width(text); + raw->resize(width, font->height); + auto gradient = QLinearGradient(QPoint(width, 0), QPoint()); + gradient.setStops(QGradientStops{ + { 0.0, st::groupCallForceMutedBar1->c }, + { .7, st::groupCallForceMutedBar2->c }, + { 1.0, st::groupCallForceMutedBar3->c } + }); + state->brush = QBrush(std::move(gradient)); + raw->update(); + }, raw->lifetime()); + + raw->paintRequest( + ) | rpl::start_with_next([=] { + auto p = QPainter(raw); + auto hq = PainterHighQualityEnabler(p); + const auto skip = st::groupCallWidth / 20; + const auto available = parent->width() - 2 * skip; + const auto full = raw->width(); + if (available > 0 && full > available) { + const auto scale = available / float64(full); + const auto shift = raw->rect().center(); + p.translate(shift); + p.scale(scale, scale); + p.translate(-shift); + } + p.setPen(Qt::NoPen); + p.setBrush(state->brush); + p.drawPath(state->path); + }, raw->lifetime()); + return result; +} + [[nodiscard]] object_ptr CreateSectionSubtitle( QWidget *parent, rpl::producer text) { @@ -181,7 +303,7 @@ void InviteController::itemDeselectedHook(not_null peer) { } bool InviteController::hasRowFor(not_null peer) const { - return (delegate()->peerListFindRow(peer->id) != nullptr); + return (delegate()->peerListFindRow(peer->id.value) != nullptr); } bool InviteController::isAlreadyIn(not_null user) const { @@ -235,7 +357,7 @@ void InviteContactsController::prepareViewHook() { std::move( _discoveredInGroup ) | rpl::start_with_next([=](not_null user) { - if (auto row = delegate()->peerListFindRow(user->id)) { + if (auto row = delegate()->peerListFindRow(user->id.value)) { delegate()->peerListRemoveRow(row); } }, _lifetime); @@ -260,25 +382,30 @@ Panel::Panel(not_null call) _window->body(), st::groupCallTitle)) #endif // !Q_OS_MAC -, _members(widget(), call) -, _settings(widget(), st::groupCallSettings) , _mute(std::make_unique( widget(), Core::App().appDeactivatedValue(), Ui::CallMuteButtonState{ - .text = tr::lng_group_call_connecting(tr::now), - .type = Ui::CallMuteButtonType::Connecting, + .text = (_call->scheduleDate() + ? tr::lng_group_call_start_now(tr::now) + : tr::lng_group_call_connecting(tr::now)), + .type = (!_call->scheduleDate() + ? Ui::CallMuteButtonType::Connecting + : _peer->canManageGroupCall() + ? Ui::CallMuteButtonType::ScheduledCanStart + : _call->scheduleStartSubscribed() + ? Ui::CallMuteButtonType::ScheduledNotify + : Ui::CallMuteButtonType::ScheduledSilent), })) , _hangup(widget(), st::groupCallHangup) { _layerBg->setStyleOverrides(&st::groupCallBox, &st::groupCallLayerBox); - _settings->setColorOverrides(_mute->colorOverrides()); _layerBg->setHideByBackgroundClick(true); SubscribeToMigration( _peer, _window->lifetime(), [=](not_null channel) { migrate(channel); }); - setupRealCallViewers(call); + setupRealCallViewers(); initWindow(); initWidget(); @@ -287,30 +414,7 @@ Panel::Panel(not_null call) showAndActivate(); setupJoinAsChangedToasts(); setupTitleChangedToasts(); - - call->allowedToSpeakNotifications( - ) | rpl::start_with_next([=] { - if (isActive()) { - Ui::ShowMultilineToast({ - .parentOverride = widget(), - .text = { tr::lng_group_call_can_speak_here(tr::now) }, - }); - } else { - const auto real = _peer->groupCall(); - const auto name = (real - && (real->id() == call->id()) - && !real->title().isEmpty()) - ? real->title() - : _peer->name; - Ui::ShowMultilineToast({ - .text = tr::lng_group_call_can_speak( - tr::now, - lt_chat, - Ui::Text::Bold(name), - Ui::Text::WithEntities), - }); - } - }, widget()->lifetime()); + setupAllowedToSpeakToasts(); } Panel::~Panel() { @@ -319,17 +423,8 @@ Panel::~Panel() { } } -void Panel::setupRealCallViewers(not_null call) { - const auto peer = call->peer(); - peer->session().changes().peerFlagsValue( - peer, - Data::PeerUpdate::Flag::GroupCall - ) | rpl::map([=] { - return peer->groupCall(); - }) | rpl::filter([=](Data::GroupCall *real) { - return _call && real && (real->id() == _call->id()); - }) | rpl::take( - 1 +void Panel::setupRealCallViewers() { + _call->real( ) | rpl::start_with_next([=](not_null real) { subscribeToChanges(real); }, _window->lifetime()); @@ -394,11 +489,9 @@ void Panel::initWindow() { } else if (e->type() == QEvent::KeyPress || e->type() == QEvent::KeyRelease) { if (static_cast(e.get())->key() == Qt::Key_Space) { - if (_call) { - _call->pushToTalk( - e->type() == QEvent::KeyPress, - kSpacePushToTalkDelay); - } + _call->pushToTalk( + e->type() == QEvent::KeyPress, + kSpacePushToTalkDelay); } } return base::EventFilterResult::Continue; @@ -440,9 +533,7 @@ void Panel::initWidget() { } void Panel::endCall() { - if (!_call) { - return; - } else if (!_call->peer()->canManageGroupCall()) { + if (!_call->peer()->canManageGroupCall()) { _call->hangup(); return; } @@ -453,11 +544,45 @@ void Panel::endCall() { BoxContext::GroupCallPanel)); } +void Panel::startScheduledNow() { + const auto date = _call->scheduleDate(); + const auto now = base::unixtime::now(); + if (!date) { + return; + } else if (now + kStartNoConfirmation >= date) { + _call->startScheduledNow(); + } else { + const auto box = std::make_shared>(); + const auto done = [=] { + if (*box) { + (*box)->closeBox(); + } + _call->startScheduledNow(); + }; + auto owned = ConfirmBox({ + .text = { tr::lng_group_call_start_now_sure(tr::now) }, + .button = tr::lng_group_call_start_now(), + .callback = done, + }); + *box = owned.data(); + _layerBg->showBox(std::move(owned)); + } +} + void Panel::initControls() { _mute->clicks( ) | rpl::filter([=](Qt::MouseButton button) { - return (button == Qt::LeftButton) && (_call != nullptr); + return (button == Qt::LeftButton); }) | rpl::start_with_next([=] { + if (_call->scheduleDate()) { + if (_peer->canManageGroupCall()) { + startScheduledNow(); + } else if (const auto real = _call->lookupReal()) { + _call->toggleScheduleStartSubscribed( + !real->scheduleStartSubscribed()); + } + return; + } const auto oldState = _call->muted(); const auto newState = (oldState == MuteState::ForceMuted) ? MuteState::RaisedHand @@ -469,34 +594,47 @@ void Panel::initControls() { _call->setMutedAndUpdate(newState); }, _mute->lifetime()); + initShareAction(); + refreshLeftButton(); + _hangup->setClickedCallback([=] { endCall(); }); - _settings->setClickedCallback([=] { - if (_call) { - _layerBg->showBox(Box(SettingsBox, _call)); - } - }); - _settings->setText(tr::lng_group_call_settings()); - _hangup->setText(tr::lng_group_call_leave()); + const auto scheduleDate = _call->scheduleDate(); + _hangup->setText(scheduleDate + ? tr::lng_group_call_close() + : tr::lng_group_call_leave()); + if (scheduleDate) { + auto changes = _call->real( + ) | rpl::map([=](not_null real) { + return real->scheduleDateValue(); + }) | rpl::flatten_latest(); - _members->desiredHeightValue( - ) | rpl::start_with_next([=] { - updateControlsGeometry(); - }, _members->lifetime()); + setupScheduledLabels(rpl::single( + scheduleDate + ) | rpl::then(rpl::duplicate(changes))); - initWithCall(_call); -} + auto started = std::move(changes) | rpl::filter([](TimeId date) { + return (date == 0); + }) | rpl::take(1); -void Panel::initWithCall(GroupCall *call) { - _callLifetime.destroy(); - _call = call; - if (!_call) { - return; + rpl::merge( + rpl::duplicate(started) | rpl::to_empty, + _peer->session().changes().peerFlagsValue( + _peer, + Data::PeerUpdate::Flag::Username + ) | rpl::skip(1) | rpl::to_empty + ) | rpl::start_with_next([=] { + refreshLeftButton(); + updateControlsGeometry(); + }, _callLifetime); + + std::move(started) | rpl::start_with_next([=] { + _hangup->setText(tr::lng_group_call_leave()); + setupMembers(); + }, _callLifetime); } - _peer = _call->peer(); - - call->stateValue( + _call->stateValue( ) | rpl::filter([](State state) { return (state == State::HangingUp) || (state == State::Ended) @@ -506,13 +644,214 @@ void Panel::initWithCall(GroupCall *call) { closeBeforeDestroy(); }, _callLifetime); - call->levelUpdates( + _call->levelUpdates( ) | rpl::filter([=](const LevelUpdate &update) { return update.me; }) | rpl::start_with_next([=](const LevelUpdate &update) { _mute->setLevel(update.value); }, _callLifetime); + _call->real( + ) | rpl::start_with_next([=](not_null real) { + setupRealMuteButtonState(real); + }, _callLifetime); +} + +void Panel::refreshLeftButton() { + const auto share = _call->scheduleDate() + && _peer->isBroadcast() + && _peer->asChannel()->hasUsername(); + if ((share && _share) || (!share && _settings)) { + return; + } + if (share) { + _settings.destroy(); + _share.create(widget(), st::groupCallShare); + _share->setClickedCallback(_shareLinkCallback); + _share->setText(tr::lng_group_call_share_button()); + } else { + _share.destroy(); + _settings.create(widget(), st::groupCallSettings); + _settings->setClickedCallback([=] { + _layerBg->showBox(Box(SettingsBox, _call)); + }); + _settings->setText(tr::lng_group_call_settings()); + } + const auto raw = _share ? _share.data() : _settings.data(); + raw->show(); + raw->setColorOverrides(_mute->colorOverrides()); +} + +void Panel::initShareAction() { + const auto showBox = [=](object_ptr next) { + _layerBg->showBox(std::move(next)); + }; + const auto showToast = [=](QString text) { + Ui::ShowMultilineToast({ + .parentOverride = widget(), + .text = { text }, + }); + }; + auto [shareLinkCallback, shareLinkLifetime] = ShareInviteLinkAction( + _peer, + showBox, + showToast); + _shareLinkCallback = [=, callback = std::move(shareLinkCallback)] { + if (_call->lookupReal()) { + callback(); + } + }; + widget()->lifetime().add(std::move(shareLinkLifetime)); +} + +void Panel::setupRealMuteButtonState(not_null real) { + using namespace rpl::mappers; + rpl::combine( + _call->mutedValue() | MapPushToTalkToActive(), + _call->instanceStateValue(), + real->scheduleDateValue(), + real->scheduleStartSubscribedValue(), + Data::CanManageGroupCallValue(_peer) + ) | rpl::distinct_until_changed( + ) | rpl::filter( + _2 != GroupCall::InstanceState::TransitionToRtc + ) | rpl::start_with_next([=]( + MuteState mute, + GroupCall::InstanceState state, + TimeId scheduleDate, + bool scheduleStartSubscribed, + bool canManage) { + using Type = Ui::CallMuteButtonType; + _mute->setState(Ui::CallMuteButtonState{ + .text = (scheduleDate + ? (canManage + ? tr::lng_group_call_start_now(tr::now) + : scheduleStartSubscribed + ? tr::lng_group_call_cancel_reminder(tr::now) + : tr::lng_group_call_set_reminder(tr::now)) + : state == GroupCall::InstanceState::Disconnected + ? tr::lng_group_call_connecting(tr::now) + : mute == MuteState::ForceMuted + ? tr::lng_group_call_force_muted(tr::now) + : mute == MuteState::RaisedHand + ? tr::lng_group_call_raised_hand(tr::now) + : mute == MuteState::Muted + ? tr::lng_group_call_unmute(tr::now) + : tr::lng_group_call_you_are_live(tr::now)), + .subtext = (scheduleDate + ? QString() + : state == GroupCall::InstanceState::Disconnected + ? QString() + : mute == MuteState::ForceMuted + ? tr::lng_group_call_raise_hand_tip(tr::now) + : mute == MuteState::RaisedHand + ? tr::lng_group_call_raised_hand_sub(tr::now) + : mute == MuteState::Muted + ? tr::lng_group_call_unmute_sub(tr::now) + : QString()), + .type = (scheduleDate + ? (canManage + ? Type::ScheduledCanStart + : scheduleStartSubscribed + ? Type::ScheduledNotify + : Type::ScheduledSilent) + : state == GroupCall::InstanceState::Disconnected + ? Type::Connecting + : mute == MuteState::ForceMuted + ? Type::ForceMuted + : mute == MuteState::RaisedHand + ? Type::RaisedHand + : mute == MuteState::Muted + ? Type::Muted + : Type::Active), + }); + }, _callLifetime); +} + +void Panel::setupScheduledLabels(rpl::producer date) { + using namespace rpl::mappers; + date = std::move(date) | rpl::take_while(_1 != 0); + _startsWhen.create( + widget(), + StartsWhenText(rpl::duplicate(date)), + st::groupCallStartsWhen); + auto countdownCreated = std::move( + date + ) | rpl::map([=](TimeId date) { + _countdownData = std::make_shared(date); + return rpl::empty_value(); + }) | rpl::start_spawning(widget()->lifetime()); + + _countdown = CreateGradientLabel(widget(), rpl::duplicate( + countdownCreated + ) | rpl::map([=] { + return _countdownData->text( + Ui::GroupCallScheduledLeft::Negative::Ignore); + }) | rpl::flatten_latest()); + + _startsIn.create( + widget(), + rpl::conditional( + std::move( + countdownCreated + ) | rpl::map( + [=] { return _countdownData->late(); } + ) | rpl::flatten_latest(), + tr::lng_group_call_late_by(), + tr::lng_group_call_starts_in()), + st::groupCallStartsIn); + + const auto top = [=] { + const auto muteTop = widget()->height() - st::groupCallMuteBottomSkip; + const auto membersTop = st::groupCallMembersTop; + const auto height = st::groupCallScheduledBodyHeight; + return (membersTop + (muteTop - membersTop - height) / 2); + }; + rpl::combine( + widget()->sizeValue(), + _startsIn->widthValue() + ) | rpl::start_with_next([=](QSize size, int width) { + _startsIn->move( + (size.width() - width) / 2, + top() + st::groupCallStartsInTop); + }, _startsIn->lifetime()); + + rpl::combine( + widget()->sizeValue(), + _startsWhen->widthValue() + ) | rpl::start_with_next([=](QSize size, int width) { + _startsWhen->move( + (size.width() - width) / 2, + top() + st::groupCallStartsWhenTop); + }, _startsWhen->lifetime()); + + rpl::combine( + widget()->sizeValue(), + _countdown->widthValue() + ) | rpl::start_with_next([=](QSize size, int width) { + _countdown->move( + (size.width() - width) / 2, + top() + st::groupCallCountdownTop); + }, _startsWhen->lifetime()); +} + +void Panel::setupMembers() { + if (_members) { + return; + } + + _startsIn.destroy(); + _countdown.destroy(); + _startsWhen.destroy(); + + _members.create(widget(), _call); + _members->show(); + + _members->desiredHeightValue( + ) | rpl::start_with_next([=] { + updateMembersGeometry(); + }, _members->lifetime()); + _members->toggleMuteRequests( ) | rpl::start_with_next([=](MuteRequest request) { if (_call) { @@ -532,73 +871,14 @@ void Panel::initWithCall(GroupCall *call) { kickParticipant(participantPeer); }, _callLifetime); - const auto showBox = [=](object_ptr next) { - _layerBg->showBox(std::move(next)); - }; - const auto showToast = [=](QString text) { - Ui::ShowMultilineToast({ - .parentOverride = widget(), - .text = { text }, - }); - }; - auto [shareLinkCallback, shareLinkLifetime] = ShareInviteLinkAction( - _peer, - showBox, - showToast); - auto shareLink = std::move(shareLinkCallback); - _members->lifetime().add(std::move(shareLinkLifetime)); - _members->addMembersRequests( ) | rpl::start_with_next([=] { - if (_call) { - if (_peer->isBroadcast() && _peer->asChannel()->hasUsername()) { - shareLink(); - } else { - addMembers(); - } + if (_peer->isBroadcast() && _peer->asChannel()->hasUsername()) { + _shareLinkCallback(); + } else { + addMembers(); } }, _callLifetime); - - using namespace rpl::mappers; - rpl::combine( - _call->mutedValue() | MapPushToTalkToActive(), - _call->instanceStateValue() - ) | rpl::distinct_until_changed( - ) | rpl::filter( - _2 != GroupCall::InstanceState::TransitionToRtc - ) | rpl::start_with_next([=]( - MuteState mute, - GroupCall::InstanceState state) { - _mute->setState(Ui::CallMuteButtonState{ - .text = (state == GroupCall::InstanceState::Disconnected - ? tr::lng_group_call_connecting(tr::now) - : mute == MuteState::ForceMuted - ? tr::lng_group_call_force_muted(tr::now) - : mute == MuteState::RaisedHand - ? tr::lng_group_call_raised_hand(tr::now) - : mute == MuteState::Muted - ? tr::lng_group_call_unmute(tr::now) - : tr::lng_group_call_you_are_live(tr::now)), - .subtext = (state == GroupCall::InstanceState::Disconnected - ? QString() - : mute == MuteState::ForceMuted - ? tr::lng_group_call_raise_hand_tip(tr::now) - : mute == MuteState::RaisedHand - ? tr::lng_group_call_raised_hand_sub(tr::now) - : mute == MuteState::Muted - ? tr::lng_group_call_unmute_sub(tr::now) - : QString()), - .type = (state == GroupCall::InstanceState::Disconnected - ? Ui::CallMuteButtonType::Connecting - : mute == MuteState::ForceMuted - ? Ui::CallMuteButtonType::ForceMuted - : mute == MuteState::RaisedHand - ? Ui::CallMuteButtonType::RaisedHand - : mute == MuteState::Muted - ? Ui::CallMuteButtonType::Muted - : Ui::CallMuteButtonType::Active), - }); - }, _callLifetime); } void Panel::setupJoinAsChangedToasts() { @@ -624,7 +904,7 @@ void Panel::setupJoinAsChangedToasts() { void Panel::setupTitleChangedToasts() { _call->titleChanged( ) | rpl::filter([=] { - return _peer->groupCall() && _peer->groupCall()->id() == _call->id(); + return (_call->lookupReal() != nullptr); }) | rpl::map([=] { return _peer->groupCall()->title().isEmpty() ? _peer->name @@ -641,9 +921,31 @@ void Panel::setupTitleChangedToasts() { }, widget()->lifetime()); } -void Panel::subscribeToChanges(not_null real) { - _titleText = real->titleValue(); +void Panel::setupAllowedToSpeakToasts() { + _call->allowedToSpeakNotifications( + ) | rpl::start_with_next([=] { + if (isActive()) { + Ui::ShowMultilineToast({ + .parentOverride = widget(), + .text = { tr::lng_group_call_can_speak_here(tr::now) }, + }); + } else { + const auto real = _call->lookupReal(); + const auto name = (real && !real->title().isEmpty()) + ? real->title() + : _peer->name; + Ui::ShowMultilineToast({ + .text = tr::lng_group_call_can_speak( + tr::now, + lt_chat, + Ui::Text::Bold(name), + Ui::Text::WithEntities), + }); + } + }, widget()->lifetime()); +} +void Panel::subscribeToChanges(not_null real) { const auto validateRecordingMark = [=](bool recording) { if (!recording && _recordingMark) { _recordingMark.destroy(); @@ -703,7 +1005,7 @@ void Panel::subscribeToChanges(not_null real) { .parentOverride = widget(), .text = (recorded ? tr::lng_group_call_recording_started - : (_call && _call->recordingStoppedByMe()) + : _call->recordingStoppedByMe() ? tr::lng_group_call_recording_saved : tr::lng_group_call_recording_stopped)( tr::now, @@ -752,9 +1054,7 @@ void Panel::subscribeToChanges(not_null real) { void Panel::chooseJoinAs() { const auto context = ChooseJoinAsProcess::Context::Switch; const auto callback = [=](JoinInfo info) { - if (_call) { - _call->rejoinAs(info); - } + _call->rejoinAs(info); }; const auto showBox = [=](object_ptr next) { _layerBg->showBox(std::move(next)); @@ -775,7 +1075,7 @@ void Panel::chooseJoinAs() { } void Panel::showMainMenu() { - if (_menu || !_call) { + if (_menu) { return; } _menu.create(widget(), st::groupCallDropdownMenu); @@ -822,8 +1122,8 @@ void Panel::showMainMenu() { } void Panel::addMembers() { - const auto real = _peer->groupCall(); - if (!_call || !real || real->id() != _call->id()) { + const auto real = _call->lookupReal(); + if (!real) { return; } auto alreadyIn = _peer->owner().invitedToCallUsers(real->id()); @@ -849,7 +1149,7 @@ void Panel::addMembers() { &st::groupCallInviteMembersList, &st::groupCallMultiSelect); - const auto weak = base::make_weak(_call); + const auto weak = base::make_weak(_call.get()); const auto invite = [=](const std::vector> &users) { const auto call = weak.get(); if (!call) { @@ -916,11 +1216,14 @@ void Panel::addMembers() { } finish(); }; - auto box = Box( - ConfirmBox, - TextWithEntities{ text }, - tr::lng_participant_invite(), - [=] { inviteWithAdd(users, nonMembers, finishWithConfirm); }); + const auto done = [=] { + inviteWithAdd(users, nonMembers, finishWithConfirm); + }; + auto box = ConfirmBox({ + .text = { text }, + .button = tr::lng_participant_invite(), + .callback = done, + }); *shared = box.data(); _layerBg->showBox(std::move(box)); }; @@ -1032,7 +1335,7 @@ void Panel::showControls() { void Panel::closeBeforeDestroy() { _window->close(); - initWithCall(nullptr); + _callLifetime.destroy(); } void Panel::initGeometry() { @@ -1064,38 +1367,26 @@ QRect Panel::computeTitleRect() const { } void Panel::updateControlsGeometry() { - if (widget()->size().isEmpty()) { + if (widget()->size().isEmpty() || (!_settings && !_share)) { return; } - const auto desiredHeight = _members->desiredHeight(); - const auto membersWidthAvailable = widget()->width() - - st::groupCallMembersMargin.left() - - st::groupCallMembersMargin.right(); - const auto membersWidthMin = st::groupCallWidth - - st::groupCallMembersMargin.left() - - st::groupCallMembersMargin.right(); - const auto membersWidth = std::clamp( - membersWidthAvailable, - membersWidthMin, - st::groupCallMembersWidthMax); const auto muteTop = widget()->height() - st::groupCallMuteBottomSkip; const auto buttonsTop = widget()->height() - st::groupCallButtonBottomSkip; - const auto membersTop = st::groupCallMembersTop; - const auto availableHeight = muteTop - - membersTop - - st::groupCallMembersMargin.bottom(); - _members->setGeometry( - (widget()->width() - membersWidth) / 2, - membersTop, - membersWidth, - std::min(desiredHeight, availableHeight)); const auto muteSize = _mute->innerSize().width(); const auto fullWidth = muteSize - + 2 * _settings->width() + + 2 * (_settings ? _settings : _share)->width() + 2 * st::groupCallButtonSkip; _mute->moveInner({ (widget()->width() - muteSize) / 2, muteTop }); - _settings->moveToLeft((widget()->width() - fullWidth) / 2, buttonsTop); - _hangup->moveToRight((widget()->width() - fullWidth) / 2, buttonsTop); + const auto leftButtonLeft = (widget()->width() - fullWidth) / 2; + if (_settings) { + _settings->moveToLeft(leftButtonLeft, buttonsTop); + } + if (_share) { + _share->moveToLeft(leftButtonLeft, buttonsTop); + } + _hangup->moveToRight(leftButtonLeft, buttonsTop); + + updateMembersGeometry(); refreshTitle(); #ifdef Q_OS_MAC @@ -1121,11 +1412,43 @@ void Panel::updateControlsGeometry() { } } +void Panel::updateMembersGeometry() { + if (!_members) { + return; + } + const auto muteTop = widget()->height() - st::groupCallMuteBottomSkip; + const auto membersTop = st::groupCallMembersTop; + const auto availableHeight = muteTop + - membersTop + - st::groupCallMembersMargin.bottom(); + const auto desiredHeight = _members->desiredHeight(); + const auto membersWidthAvailable = widget()->width() + - st::groupCallMembersMargin.left() + - st::groupCallMembersMargin.right(); + const auto membersWidthMin = st::groupCallWidth + - st::groupCallMembersMargin.left() + - st::groupCallMembersMargin.right(); + const auto membersWidth = std::clamp( + membersWidthAvailable, + membersWidthMin, + st::groupCallMembersWidthMax); + _members->setGeometry( + (widget()->width() - membersWidth) / 2, + membersTop, + membersWidth, + std::min(desiredHeight, availableHeight)); +} + void Panel::refreshTitle() { if (!_title) { auto text = rpl::combine( Info::Profile::NameValue(_peer), - _titleText.value() + rpl::single( + QString() + ) | rpl::then(_call->real( + ) | rpl::map([=](not_null real) { + return real->titleValue(); + }) | rpl::flatten_latest()) ) | rpl::map([=]( const TextWithEntities &name, const QString &title) { @@ -1144,11 +1467,25 @@ void Panel::refreshTitle() { if (!_subtitle) { _subtitle.create( widget(), - tr::lng_group_call_members( - lt_count_decimal, - _members->fullCountValue() | rpl::map([](int value) { - return (value > 0) ? float64(value) : 1.; - })), + rpl::single( + _call->scheduleDate() + ) | rpl::then( + _call->real( + ) | rpl::map([=](not_null real) { + return real->scheduleDateValue(); + }) | rpl::flatten_latest() + ) | rpl::map([=](TimeId scheduleDate) { + if (scheduleDate) { + return tr::lng_group_call_scheduled_status(); + } else if (!_members) { + setupMembers(); + } + return tr::lng_group_call_members( + lt_count_decimal, + _members->fullCountValue() | rpl::map([](int value) { + return (value > 0) ? float64(value) : 1.; + })); + }) | rpl::flatten_latest(), st::groupCallSubtitleLabel); _subtitle->show(); _subtitle->setAttribute(Qt::WA_TransparentForMouseEvents); diff --git a/Telegram/SourceFiles/calls/calls_group_panel.h b/Telegram/SourceFiles/calls/calls_group_panel.h index bc966b605..71d68042c 100644 --- a/Telegram/SourceFiles/calls/calls_group_panel.h +++ b/Telegram/SourceFiles/calls/calls_group_panel.h @@ -38,6 +38,7 @@ class Window; class ScrollArea; class GenericBox; class LayerManager; +class GroupCallScheduledLeft; namespace Platform { class TitleControls; } // namespace Platform @@ -73,16 +74,23 @@ private: void initWindow(); void initWidget(); void initControls(); - void initWithCall(GroupCall *call); + void initShareAction(); void initLayout(); void initGeometry(); + void setupScheduledLabels(rpl::producer date); + void setupMembers(); void setupJoinAsChangedToasts(); void setupTitleChangedToasts(); + void setupAllowedToSpeakToasts(); + void setupRealMuteButtonState(not_null real); bool handleClose(); + void startScheduledNow(); void updateControlsGeometry(); + void updateMembersGeometry(); void showControls(); + void refreshLeftButton(); void endCall(); @@ -94,13 +102,13 @@ private: [[nodiscard]] QRect computeTitleRect() const; void refreshTitle(); void refreshTitleGeometry(); - void setupRealCallViewers(not_null call); + void setupRealCallViewers(); void subscribeToChanges(not_null real); void migrate(not_null channel); void subscribeToPeerChanges(); - GroupCall *_call = nullptr; + const not_null _call; not_null _peer; const std::unique_ptr _window; @@ -118,13 +126,18 @@ private: object_ptr _menuToggle = { nullptr }; object_ptr _menu = { nullptr }; object_ptr _joinAsToggle = { nullptr }; - object_ptr _members; - rpl::variable _titleText; + object_ptr _members = { nullptr }; + object_ptr _startsIn = { nullptr }; + object_ptr _countdown = { nullptr }; + std::shared_ptr _countdownData; + object_ptr _startsWhen = { nullptr }; ChooseJoinAsProcess _joinAsProcess; - object_ptr _settings; + object_ptr _settings = { nullptr }; + object_ptr _share = { nullptr }; std::unique_ptr _mute; object_ptr _hangup; + Fn _shareLinkCallback; rpl::lifetime _peerLifetime; diff --git a/Telegram/SourceFiles/calls/calls_group_settings.cpp b/Telegram/SourceFiles/calls/calls_group_settings.cpp index 42491c72d..9ae8bbcae 100644 --- a/Telegram/SourceFiles/calls/calls_group_settings.cpp +++ b/Telegram/SourceFiles/calls/calls_group_settings.cpp @@ -35,7 +35,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_group_call.h" #include "data/data_changes.h" #include "core/application.h" -#include "boxes/single_choice_box.h" +#include "ui/boxes/single_choice_box.h" #include "webrtc/webrtc_audio_input_tester.h" #include "webrtc/webrtc_media_devices.h" #include "settings/settings_common.h" @@ -149,8 +149,7 @@ object_ptr ShareInviteLinkBox( } text.append(error.first); if (const auto weak = *box) { - weak->getDelegate()->show( - Box(ConfirmBox, text, nullptr, nullptr)); + weak->getDelegate()->show(ConfirmBox({ .text = text })); } return; } @@ -677,7 +676,7 @@ std::pair, rpl::lifetime> ShareInviteLinkAction( return true; }; auto callback = [=] { - const auto real = peer->groupCall(); + const auto real = peer->migrateToOrMe()->groupCall(); if (shareReady() || state->generatingLink || !real) { return; } @@ -702,11 +701,11 @@ std::pair, rpl::lifetime> ShareInviteLinkAction( state->linkSpeakerRequestId = peer->session().api().request( MTPphone_ExportGroupCallInvite( MTP_flags(Flag::f_can_self_unmute), - real->input() - )).done([=](const MTPphone_ExportedGroupCallInvite &result) { + real->input()) + ).done([=](const MTPphone_ExportedGroupCallInvite &result) { state->linkSpeakerRequestId = 0; result.match([&]( - const MTPDphone_exportedGroupCallInvite &data) { + const MTPDphone_exportedGroupCallInvite &data) { state->linkSpeaker = qs(data.vlink()); shareReady(); }); diff --git a/Telegram/SourceFiles/calls/calls_instance.cpp b/Telegram/SourceFiles/calls/calls_instance.cpp index 444d0101d..8e1826a95 100644 --- a/Telegram/SourceFiles/calls/calls_instance.cpp +++ b/Telegram/SourceFiles/calls/calls_instance.cpp @@ -374,7 +374,7 @@ void Instance::handleCallUpdate( const MTPPhoneCall &call) { if (call.type() == mtpc_phoneCallRequested) { auto &phoneCall = call.c_phoneCallRequested(); - auto user = session->data().userLoaded(phoneCall.vadmin_id().v); + auto user = session->data().userLoaded(phoneCall.vadmin_id()); if (!user) { LOG(("API Error: User not loaded for phoneCallRequested.")); } else if (user->isSelf()) { @@ -411,6 +411,14 @@ void Instance::handleCallUpdate( void Instance::handleGroupCallUpdate( not_null session, const MTPUpdate &update) { + if (_currentGroupCall + && (&_currentGroupCall->peer()->session() == session)) { + update.match([&](const MTPDupdateGroupCall &data) { + _currentGroupCall->handlePossibleCreateOrJoinResponse(data); + }, [](const auto &) { + }); + } + const auto callId = update.match([](const MTPDupdateGroupCall &data) { return data.vcall().match([](const auto &data) { return data.vid().v; @@ -427,14 +435,6 @@ void Instance::handleGroupCallUpdate( } else { applyGroupCallUpdateChecked(session, update); } - - if (_currentGroupCall - && (&_currentGroupCall->peer()->session() == session)) { - update.match([&](const MTPDupdateGroupCall &data) { - _currentGroupCall->handlePossibleCreateOrJoinResponse(data); - }, [](const auto &) { - }); - } } void Instance::applyGroupCallUpdateChecked( diff --git a/Telegram/SourceFiles/calls/calls_top_bar.cpp b/Telegram/SourceFiles/calls/calls_top_bar.cpp index 982b834a9..f598bb20b 100644 --- a/Telegram/SourceFiles/calls/calls_top_bar.cpp +++ b/Telegram/SourceFiles/calls/calls_top_bar.cpp @@ -57,8 +57,13 @@ constexpr auto kHideBlobsDuration = crl::time(500); constexpr auto kBlobLevelDuration = crl::time(250); constexpr auto kBlobUpdateInterval = crl::time(100); -auto BarStateFromMuteState(MuteState state, GroupCall::InstanceState instanceState) { - return (instanceState == GroupCall::InstanceState::Disconnected) +auto BarStateFromMuteState( + MuteState state, + GroupCall::InstanceState instanceState, + TimeId scheduledDate) { + return scheduledDate + ? BarState::ForceMuted + : (instanceState == GroupCall::InstanceState::Disconnected) ? BarState::Connecting : (state == MuteState::ForceMuted || state == MuteState::RaisedHand) ? BarState::ForceMuted @@ -293,19 +298,27 @@ void TopBar::initControls() { _call ? mapToState(_call->muted()) : _groupCall->muted(), - GroupCall::InstanceState::Connected)); + GroupCall::InstanceState::Connected, + _call ? TimeId(0) : _groupCall->scheduleDate())); using namespace rpl::mappers; auto muted = _call ? rpl::combine( _call->mutedValue() | rpl::map(mapToState), - rpl::single(GroupCall::InstanceState::Connected) + rpl::single(GroupCall::InstanceState::Connected), + rpl::single(TimeId(0)) ) | rpl::type_erased() : rpl::combine( (_groupCall->mutedValue() | MapPushToTalkToActive() | rpl::distinct_until_changed() | rpl::type_erased()), - _groupCall->instanceStateValue() + _groupCall->instanceStateValue(), + rpl::single( + _groupCall->scheduleDate() + ) | rpl::then(_groupCall->real( + ) | rpl::map([](not_null call) { + return call->scheduleDateValue(); + }) | rpl::flatten_latest()) ) | rpl::filter(_2 != GroupCall::InstanceState::TransitionToRtc); std::move( muted diff --git a/Telegram/SourceFiles/chat_helpers/emoji_list_widget.cpp b/Telegram/SourceFiles/chat_helpers/emoji_list_widget.cpp index dce48190c..c7da2d589 100644 --- a/Telegram/SourceFiles/chat_helpers/emoji_list_widget.cpp +++ b/Telegram/SourceFiles/chat_helpers/emoji_list_widget.cpp @@ -399,7 +399,7 @@ EmojiListWidget::EmojiListWidget( for (auto i = 0; i != kEmojiSectionCount; ++i) { const auto section = static_cast
(i); _counts[i] = (section == Section::Recent) - ? GetRecentEmoji().size() + ? int(Core::App().settings().recentEmoji().size()) : Ui::Emoji::GetSectionCount(section); } @@ -500,17 +500,18 @@ void EmojiListWidget::ensureLoaded(int section) { return; } _emoji[section] = (static_cast
(section) == Section::Recent) - ? GetRecentEmojiSection() + ? Core::App().settings().recentEmojiSection() : Ui::Emoji::GetSection(static_cast
(section)); _counts[section] = _emoji[section].size(); if (static_cast
(section) == Section::Recent) { return; } + const auto &variants = Core::App().settings().emojiVariants(); for (auto &emoji : _emoji[section]) { if (emoji->hasVariants()) { - auto j = cEmojiVariants().constFind(emoji->nonColoredId()); - if (j != cEmojiVariants().cend()) { - emoji = emoji->variant(j.value()); + const auto j = variants.find(emoji->nonColoredId()); + if (j != end(variants)) { + emoji = emoji->variant(j->second); } } } @@ -594,10 +595,13 @@ void EmojiListWidget::mousePressEvent(QMouseEvent *e) { if (_selected >= 0) { auto section = (_selected / MatrixRowShift); auto sel = _selected % MatrixRowShift; - if (section < kEmojiSectionCount && sel < _emoji[section].size() && _emoji[section][sel]->hasVariants()) { + if (section < kEmojiSectionCount + && sel < _emoji[section].size() + && _emoji[section][sel]->hasVariants()) { _pickerSel = _selected; setCursor(style::cur_default); - if (!cEmojiVariants().contains(_emoji[section][sel]->nonColoredId())) { + const auto &variants = Core::App().settings().emojiVariants(); + if (!variants.contains(_emoji[section][sel]->nonColoredId())) { showPicker(); } else { _showPickerTimer.callOnce(500); @@ -617,8 +621,11 @@ void EmojiListWidget::mouseReleaseEvent(QMouseEvent *e) { } else if (_pickerSel >= 0) { auto section = (_pickerSel / MatrixRowShift); auto sel = _pickerSel % MatrixRowShift; - if (section < kEmojiSectionCount && sel < _emoji[section].size() && _emoji[section][sel]->hasVariants()) { - if (cEmojiVariants().contains(_emoji[section][sel]->nonColoredId())) { + if (section < kEmojiSectionCount + && sel < _emoji[section].size() + && _emoji[section][sel]->hasVariants()) { + const auto &variants = Core::App().settings().emojiVariants(); + if (variants.contains(_emoji[section][sel]->nonColoredId())) { _picker->hideAnimated(); _pickerSel = -1; } @@ -650,7 +657,7 @@ void EmojiListWidget::mouseReleaseEvent(QMouseEvent *e) { } void EmojiListWidget::selectEmoji(EmojiPtr emoji) { - AddRecentEmoji(emoji); + Core::App().settings().incrementRecentEmoji(emoji); _chosen.fire_copy(emoji); } @@ -698,10 +705,7 @@ QRect EmojiListWidget::emojiRect(int section, int sel) { void EmojiListWidget::colorChosen(EmojiPtr emoji) { if (emoji->hasVariants()) { - cRefEmojiVariants().insert( - emoji->nonColoredId(), - emoji->variantIndex(emoji)); - controller()->session().saveSettingsDelayed(); + Core::App().settings().saveEmojiVariant(emoji); } if (_pickerSel >= 0) { auto section = (_pickerSel / MatrixRowShift); @@ -790,7 +794,7 @@ void EmojiListWidget::processHideFinished() { void EmojiListWidget::refreshRecent() { clearSelection(); - _emoji[0] = GetRecentEmojiSection(); + _emoji[0] = Core::App().settings().recentEmojiSection(); _counts[0] = _emoji[0].size(); resizeToWidth(width()); } diff --git a/Telegram/SourceFiles/chat_helpers/emoji_suggestions_widget.cpp b/Telegram/SourceFiles/chat_helpers/emoji_suggestions_widget.cpp index 09cb1224d..aa554e19c 100644 --- a/Telegram/SourceFiles/chat_helpers/emoji_suggestions_widget.cpp +++ b/Telegram/SourceFiles/chat_helpers/emoji_suggestions_widget.cpp @@ -116,11 +116,11 @@ auto SuggestionsWidget::getRowsByQuery() const -> std::vector { }) | ranges::to_vector; auto lastRecent = begin(result); - const auto &recent = GetRecentEmoji(); + const auto &recent = Core::App().settings().recentEmoji(); for (const auto &item : recent) { - const auto emoji = item.first->original() - ? item.first->original() - : item.first; + const auto emoji = item.emoji->original() + ? item.emoji->original() + : item.emoji; const auto it = ranges::find(result, emoji, [](const Row &row) { return row.emoji.get(); }); @@ -133,12 +133,12 @@ auto SuggestionsWidget::getRowsByQuery() const -> std::vector { for (auto &item : result) { item.emoji = [&] { const auto result = item.emoji; - const auto &variants = cEmojiVariants(); + const auto &variants = Core::App().settings().emojiVariants(); const auto i = result->hasVariants() - ? variants.constFind(result->nonColoredId()) - : variants.cend(); - return (i != variants.cend()) - ? result->variant(i.value()) + ? variants.find(result->nonColoredId()) + : end(variants); + return (i != end(variants)) + ? result->variant(i->second) : result.get(); }(); } diff --git a/Telegram/SourceFiles/chat_helpers/message_field.cpp b/Telegram/SourceFiles/chat_helpers/message_field.cpp index 12037e961..e4d70743f 100644 --- a/Telegram/SourceFiles/chat_helpers/message_field.cpp +++ b/Telegram/SourceFiles/chat_helpers/message_field.cpp @@ -93,7 +93,7 @@ QString FieldTagMimeProcessor::tagFromMimeTag(const QString &mimeTag) { const auto userId = _controller->session().userId(); auto match = QRegularExpression(":(\\d+)$").match(mimeTag); if (!match.hasMatch() - || match.capturedRef(1).toInt() != userId) { + || match.capturedRef(1).toULongLong() != userId.bare) { return QString(); } return mimeTag.mid(0, mimeTag.size() - match.capturedLength()); @@ -249,7 +249,7 @@ TextWithEntities StripSupportHashtag(TextWithEntities &&text) { QString PrepareMentionTag(not_null user) { return TextUtilities::kMentionTagStart - + QString::number(user->bareId()) + + QString::number(user->id.value) + '.' + QString::number(user->accessHash()); } diff --git a/Telegram/SourceFiles/config.h b/Telegram/SourceFiles/config.h index d75dd74e8..cee565414 100644 --- a/Telegram/SourceFiles/config.h +++ b/Telegram/SourceFiles/config.h @@ -14,9 +14,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL enum { MaxSelectedItems = 100, - MaxPhoneCodeLength = 4, // max length of country phone code - MaxPhoneTailLength = 32, // rest of the phone number, without country code (seen 12 at least), need more for service numbers - LocalEncryptIterCount = 4000, // key derivation iteration count LocalEncryptNoPwdIterCount = 4, // key derivation iteration count without pwd (not secure anyway) LocalEncryptSaltSize = 32, // 256 bit diff --git a/Telegram/SourceFiles/core/application.cpp b/Telegram/SourceFiles/core/application.cpp index 48e224418..1c6b54568 100644 --- a/Telegram/SourceFiles/core/application.cpp +++ b/Telegram/SourceFiles/core/application.cpp @@ -63,6 +63,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "storage/storage_domain.h" #include "storage/storage_databases.h" #include "storage/localstorage.h" +#include "payments/payments_checkout_process.h" #include "export/export_manager.h" #include "window/window_session_controller.h" #include "window/window_controller.h" @@ -169,6 +170,15 @@ Application::~Application() { _window = nullptr; _mediaView = nullptr; _notifications->clearAllFast(); + + // We must manually destroy all windows before going further. + // DestroyWindow on Windows (at least with an active WebView) enters + // event loop and invoke scheduled crl::on_main callbacks. + // + // For example Domain::removeRedundantAccounts() is called from + // Domain::finish() and there is a violation on Ensures(started()). + Payments::CheckoutProcess::ClearAll(); + _domain->finish(); Local::finish(); @@ -550,6 +560,9 @@ void Application::badMtprotoConfigurationError() { void Application::startLocalStorage() { Local::start(); _saveSettingsTimer.emplace([=] { saveSettings(); }); + _settings.saveDelayedRequests() | rpl::start_with_next([=] { + saveSettingsDelayed(); + }, _lifetime); } void Application::startEmojiImageLoader() { diff --git a/Telegram/SourceFiles/core/base_integration.cpp b/Telegram/SourceFiles/core/base_integration.cpp index 114af7ca7..8c2ae1f43 100644 --- a/Telegram/SourceFiles/core/base_integration.cpp +++ b/Telegram/SourceFiles/core/base_integration.cpp @@ -21,17 +21,20 @@ void BaseIntegration::enterFromEventLoop(FnMut &&method) { std::move(method)); } +bool BaseIntegration::logSkipDebug() { + return !Logs::DebugEnabled() && Logs::started(); +} + +void BaseIntegration::logMessageDebug(const QString &message) { + Logs::writeDebug(message); +} + void BaseIntegration::logMessage(const QString &message) { -#ifdef DEBUG_LOG - DEBUG_LOG((message)); -#endif // DEBUG_LOG + Logs::writeMain(message); } void BaseIntegration::logAssertionViolation(const QString &info) { -#ifdef LOG - LOG(("Assertion Failed! ") + info); -#endif // LOG - + Logs::writeMain("Assertion Failed! " + info); CrashReports::SetAnnotation("Assertion", info); } diff --git a/Telegram/SourceFiles/core/base_integration.h b/Telegram/SourceFiles/core/base_integration.h index e2a60cf54..11c95f958 100644 --- a/Telegram/SourceFiles/core/base_integration.h +++ b/Telegram/SourceFiles/core/base_integration.h @@ -11,11 +11,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Core { -class BaseIntegration : public base::Integration { +class BaseIntegration final : public base::Integration { public: BaseIntegration(int argc, char *argv[]); void enterFromEventLoop(FnMut &&method) override; + bool logSkipDebug() override; + void logMessageDebug(const QString &message) override; void logMessage(const QString &message) override; void logAssertionViolation(const QString &info) override; [[nodiscard]] bool gtkIntegrationEnabled() const override; diff --git a/Telegram/SourceFiles/core/click_handler_types.cpp b/Telegram/SourceFiles/core/click_handler_types.cpp index 7a416836c..265869a4a 100644 --- a/Telegram/SourceFiles/core/click_handler_types.cpp +++ b/Telegram/SourceFiles/core/click_handler_types.cpp @@ -87,12 +87,12 @@ void BotGameUrlClickHandler::onClick(ClickContext context) const { open(); } else if (!_bot || _bot->isVerified() - || _bot->session().local().isBotTrusted(_bot)) { + || _bot->session().local().isBotTrustedOpenGame(_bot->id)) { open(); } else { const auto callback = [=, bot = _bot] { Ui::hideLayer(); - bot->session().local().markBotTrusted(bot); + bot->session().local().markBotTrustedOpenGame(bot->id); open(); }; Ui::show(Box( @@ -137,7 +137,9 @@ void MentionNameClickHandler::onClick(ClickContext context) const { } auto MentionNameClickHandler::getTextEntity() const -> TextEntity { - auto data = QString::number(_userId) + '.' + QString::number(_accessHash); + const auto data = QString::number(_userId.bare) + + '.' + + QString::number(_accessHash); return { EntityType::MentionName, data }; } diff --git a/Telegram/SourceFiles/core/core_settings.cpp b/Telegram/SourceFiles/core/core_settings.cpp index 7e1ccb578..b90da5917 100644 --- a/Telegram/SourceFiles/core/core_settings.cpp +++ b/Telegram/SourceFiles/core/core_settings.cpp @@ -19,6 +19,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Core { namespace { +constexpr auto kRecentEmojiLimit = 42; + [[nodiscard]] WindowPosition Deserialize(const QByteArray &data) { QDataStream stream(data); stream.setVersion(QDataStream::Qt_5_1); @@ -75,6 +77,17 @@ QByteArray Settings::serialize() const { const auto themesAccentColors = _themesAccentColors.serialize(); const auto windowPosition = Serialize(_windowPosition); + auto recentEmojiPreloadGenerated = std::vector(); + if (_recentEmojiPreload.empty()) { + recentEmojiPreloadGenerated.reserve(_recentEmoji.size()); + for (const auto [emoji, rating] : _recentEmoji) { + recentEmojiPreloadGenerated.push_back({ emoji->id(), rating }); + } + } + const auto &recentEmojiPreloadData = _recentEmojiPreload.empty() + ? recentEmojiPreloadGenerated + : _recentEmojiPreload; + auto size = Serialize::bytearraySize(themesAccentColors) + sizeof(qint32) * 5 + Serialize::stringSize(_downloadPath.current()) @@ -83,10 +96,16 @@ QByteArray Settings::serialize() const { + Serialize::stringSize(_callOutputDeviceId) + Serialize::stringSize(_callInputDeviceId) + Serialize::stringSize(_callVideoInputDeviceId) - + sizeof(qint32) * 3; + + sizeof(qint32) * 5; for (const auto &[key, value] : _soundOverrides) { size += Serialize::stringSize(key) + Serialize::stringSize(value); } + for (const auto &[id, rating] : recentEmojiPreloadData) { + size += Serialize::stringSize(id) + sizeof(quint16); + } + for (const auto &[id, variant] : _emojiVariants) { + size += Serialize::stringSize(id) + sizeof(quint8); + } size += Serialize::bytearraySize(_videoPipGeometry); size += Serialize::bytearraySize(windowPosition); @@ -165,7 +184,16 @@ QByteArray Settings::serialize() const { << qint64(_groupCallPushToTalkDelay) << qint32(0) // Call audio backend << qint32(_disableCalls ? 1 : 0) - << windowPosition; + << windowPosition + << qint32(recentEmojiPreloadData.size()); + for (const auto &[id, rating] : recentEmojiPreloadData) { + stream << id << quint16(rating); + } + stream + << qint32(_emojiVariants.size()); + for (const auto &[id, variant] : _emojiVariants) { + stream << id << quint8(variant); + } } return result; } @@ -239,6 +267,8 @@ void Settings::addFromSerialized(const QByteArray &serialized) { qint32 callAudioBackend = 0; qint32 disableCalls = _disableCalls ? 1 : 0; QByteArray windowPosition; + std::vector recentEmojiPreload; + base::flat_map emojiVariants; stream >> themesAccentColors; if (!stream.atEnd()) { @@ -343,6 +373,30 @@ void Settings::addFromSerialized(const QByteArray &serialized) { if (!stream.atEnd()) { stream >> windowPosition; } + if (!stream.atEnd()) { + auto recentCount = qint32(0); + stream >> recentCount; + if (recentCount > 0 && recentCount < 10000) { + recentEmojiPreload.reserve(recentCount); + for (auto i = 0; i != recentCount; ++i) { + auto id = QString(); + auto rating = quint16(); + stream >> id >> rating; + recentEmojiPreload.push_back({ id, rating }); + } + } + auto variantsCount = qint32(0); + stream >> variantsCount; + if (variantsCount > 0 && variantsCount < 10000) { + emojiVariants.reserve(variantsCount); + for (auto i = 0; i != variantsCount; ++i) { + auto id = QString(); + auto variant = quint8(); + stream >> id >> variant; + emojiVariants.emplace(id, variant); + } + } + } if (stream.status() != QDataStream::Ok) { LOG(("App Error: " "Bad data for Core::Settings::constructFromSerialized()")); @@ -446,6 +500,8 @@ void Settings::addFromSerialized(const QByteArray &serialized) { if (!windowPosition.isEmpty()) { _windowPosition = Deserialize(windowPosition); } + _recentEmojiPreload = std::move(recentEmojiPreload); + _emojiVariants = std::move(emojiVariants); } bool Settings::chatWide() const { @@ -525,6 +581,118 @@ rpl::producer Settings::thirdColumnWidthChanges() const { return _thirdColumnWidth.changes(); } +const std::vector &Settings::recentEmoji() const { + if (_recentEmoji.empty()) { + resolveRecentEmoji(); + } + return _recentEmoji; +} + +void Settings::resolveRecentEmoji() const { + const auto haveAlready = [&](EmojiPtr emoji) { + return ranges::contains( + _recentEmoji, + emoji->id(), + [](const RecentEmoji &data) { return data.emoji->id(); }); + }; + if (!_recentEmojiPreload.empty()) { + _recentEmoji.reserve(_recentEmojiPreload.size()); + for (const auto &[id, rating] : base::take(_recentEmojiPreload)) { + if (const auto emoji = Ui::Emoji::Find(id)) { + if (!haveAlready(emoji)) { + _recentEmoji.push_back({ emoji, rating }); + } + } + } + _recentEmojiPreload.clear(); + } + for (const auto emoji : Ui::Emoji::GetDefaultRecent()) { + if (_recentEmoji.size() >= kRecentEmojiLimit) { + break; + } else if (!haveAlready(emoji)) { + _recentEmoji.push_back({ emoji, 1 }); + } + } +} + +EmojiPack Settings::recentEmojiSection() const { + const auto &recent = recentEmoji(); + + auto result = EmojiPack(); + result.reserve(recent.size()); + for (const auto [emoji, rating] : recent) { + result.push_back(emoji); + } + return result; +} + +void Settings::incrementRecentEmoji(EmojiPtr emoji) { + resolveRecentEmoji(); + + auto i = _recentEmoji.begin(), e = _recentEmoji.end(); + for (; i != e; ++i) { + if (i->emoji == emoji) { + ++i->rating; + if (i->rating > 0x8000) { + for (auto j = _recentEmoji.begin(); j != e; ++j) { + if (j->rating > 1) { + j->rating /= 2; + } else { + j->rating = 1; + } + } + } + for (; i != _recentEmoji.begin(); --i) { + if ((i - 1)->rating > i->rating) { + break; + } + std::swap(*i, *(i - 1)); + } + break; + } + } + if (i == e) { + while (_recentEmoji.size() >= kRecentEmojiLimit) { + _recentEmoji.pop_back(); + } + _recentEmoji.push_back({ emoji, 1 }); + for (i = _recentEmoji.end() - 1; i != _recentEmoji.begin(); --i) { + if ((i - 1)->rating > i->rating) { + break; + } + std::swap(*i, *(i - 1)); + } + } + _recentEmojiUpdated.fire({}); + _saveDelayed.fire({}); +} + +void Settings::setLegacyRecentEmojiPreload( + QVector> data) { + if (!_recentEmojiPreload.empty() || data.isEmpty()) { + return; + } + _recentEmojiPreload.reserve(data.size()); + for (const auto &[id, rating] : data) { + _recentEmojiPreload.push_back({ id, rating }); + } +} + +void Settings::saveEmojiVariant(EmojiPtr emoji) { + _emojiVariants[emoji->nonColoredId()] = emoji->variantIndex(emoji); + _saveDelayed.fire({}); +} + +void Settings::setLegacyEmojiVariants(QMap data) { + if (!_emojiVariants.empty() || data.isEmpty()) { + return; + } + _emojiVariants.reserve(data.size()); + for (auto i = data.begin(), e = data.end(); i != e; ++i) { + _emojiVariants.emplace(i.key(), i.value()); + } +} + void Settings::resetOnLastLogout() { _adaptiveForWide = true; _moderateModeEnabled = false; @@ -592,6 +760,10 @@ void Settings::resetOnLastLogout() { _notifyFromAll = true; _tabbedReplacedWithInfo = false; // per-window _systemDarkModeEnabled = false; + + _recentEmojiPreload.clear(); + _recentEmoji.clear(); + _emojiVariants.clear(); } bool Settings::ThirdColumnByDefault() { diff --git a/Telegram/SourceFiles/core/core_settings.h b/Telegram/SourceFiles/core/core_settings.h index 0b9420b61..cb9f84e63 100644 --- a/Telegram/SourceFiles/core/core_settings.h +++ b/Telegram/SourceFiles/core/core_settings.h @@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "window/themes/window_themes_embedded.h" #include "ui/chat/attach/attach_send_files_way.h" #include "platform/platform_notifications_manager.h" +#include "emoji.h" enum class RectPart; @@ -53,6 +54,10 @@ public: Settings(); + [[nodiscard]] rpl::producer<> saveDelayedRequests() const { + return _saveDelayed.events(); + } + [[nodiscard]] static bool IsLeftCorner(ScreenCorner corner) { return (corner == ScreenCorner::TopLeft) || (corner == ScreenCorner::BottomLeft); @@ -510,8 +515,26 @@ public: _windowPosition = position; } + struct RecentEmoji { + EmojiPtr emoji = nullptr; + ushort rating = 0; + }; + [[nodiscard]] const std::vector &recentEmoji() const; + [[nodiscard]] EmojiPack recentEmojiSection() const; + void incrementRecentEmoji(EmojiPtr emoji); + void setLegacyRecentEmojiPreload(QVector> data); + [[nodiscard]] rpl::producer<> recentEmojiUpdated() const { + return _recentEmojiUpdated.events(); + } + + [[nodiscard]] const base::flat_map &emojiVariants() const { + return _emojiVariants; + } + void saveEmojiVariant(EmojiPtr emoji); + void setLegacyEmojiVariants(QMap data); + [[nodiscard]] static bool ThirdColumnByDefault(); - [[nodiscard]] float64 DefaultDialogsWidthRatio(); + [[nodiscard]] static float64 DefaultDialogsWidthRatio(); [[nodiscard]] static qint32 SerializePlaybackSpeed(float64 speed) { return int(std::round(std::clamp(speed, 0.5, 2.0) * 100)); } @@ -527,10 +550,17 @@ public: void resetOnLastLogout(); private: + void resolveRecentEmoji() const; + static constexpr auto kDefaultThirdColumnWidth = 0; static constexpr auto kDefaultDialogsWidthRatio = 5. / 14; static constexpr auto kDefaultBigDialogsWidthRatio = 0.275; + struct RecentEmojiId { + QString emoji; + ushort rating = 0; + }; + bool _adaptiveForWide = true; bool _moderateModeEnabled = false; rpl::variable _songVolume = kDefaultVolume; @@ -578,6 +608,10 @@ private: rpl::variable> _dictionariesEnabled; rpl::variable _autoDownloadDictionaries = true; rpl::variable _mainMenuAccountsShown = true; + mutable std::vector _recentEmojiPreload; + mutable std::vector _recentEmoji; + base::flat_map _emojiVariants; + rpl::event_stream<> _recentEmojiUpdated; bool _tabbedSelectorSectionEnabled = false; // per-window Window::Column _floatPlayerColumn = Window::Column(); // per-window RectPart _floatPlayerCorner = RectPart(); // per-window @@ -595,6 +629,7 @@ private: bool _tabbedReplacedWithInfo = false; // per-window rpl::event_stream _tabbedReplacedWithInfoValue; // per-window + rpl::event_stream<> _saveDelayed; float64 _rememberedSongVolume = kDefaultVolume; bool _rememberedSoundNotifyFromTray = false; bool _rememberedFlashBounceNotifyFromTray = false; diff --git a/Telegram/SourceFiles/core/launcher.cpp b/Telegram/SourceFiles/core/launcher.cpp index 27d06d274..a23982767 100644 --- a/Telegram/SourceFiles/core/launcher.cpp +++ b/Telegram/SourceFiles/core/launcher.cpp @@ -283,19 +283,7 @@ void Launcher::init() { _arguments = readArguments(_argc, _argv); prepareSettings(); - - static QtMessageHandler originalMessageHandler = nullptr; - originalMessageHandler = qInstallMessageHandler([]( - QtMsgType type, - const QMessageLogContext &context, - const QString &msg) { - if (originalMessageHandler) { - originalMessageHandler(type, context, msg); - } - if (Logs::DebugEnabled() || !Logs::started()) { - LOG((msg)); - } - }); + initQtMessageLogging(); QApplication::setApplicationName(qsl("KotatogramDesktop")); @@ -451,6 +439,26 @@ void Launcher::prepareSettings() { processArguments(); } +void Launcher::initQtMessageLogging() { + static QtMessageHandler OriginalMessageHandler = nullptr; + static bool WritingQtMessage = false; + OriginalMessageHandler = qInstallMessageHandler([]( + QtMsgType type, + const QMessageLogContext &context, + const QString &msg) { + if (OriginalMessageHandler) { + OriginalMessageHandler(type, context, msg); + } + if (Logs::DebugEnabled() || !Logs::started()) { + if (!WritingQtMessage) { + WritingQtMessage = true; + LOG((msg)); + WritingQtMessage = false; + } + } + }); +} + uint64 Launcher::installationTag() const { return InstallationTag; } diff --git a/Telegram/SourceFiles/core/launcher.h b/Telegram/SourceFiles/core/launcher.h index 8847fec81..0d6959b0c 100644 --- a/Telegram/SourceFiles/core/launcher.h +++ b/Telegram/SourceFiles/core/launcher.h @@ -41,6 +41,7 @@ protected: private: void prepareSettings(); + void initQtMessageLogging(); void processArguments(); QStringList readArguments(int argc, char *argv[]) const; diff --git a/Telegram/SourceFiles/core/local_url_handlers.cpp b/Telegram/SourceFiles/core/local_url_handlers.cpp index 925eb6535..5ef33749a 100644 --- a/Telegram/SourceFiles/core/local_url_handlers.cpp +++ b/Telegram/SourceFiles/core/local_url_handlers.cpp @@ -192,7 +192,7 @@ bool ShowPassportForm( if (!controller) { return false; } - const auto botId = params.value("bot_id", QString()).toInt(); + const auto botId = params.value("bot_id", QString()).toULongLong(); const auto scope = params.value("scope", QString()); const auto callback = params.value("callback_url", QString()); const auto publicKey = params.value("public_key", QString()); @@ -323,7 +323,8 @@ bool ResolvePrivatePost( const auto params = url_parse_params( match->captured(1), qthelp::UrlParamNameTransform::ToLower); - const auto channelId = params.value(qsl("channel")).toInt(); + const auto channelId = ChannelId( + params.value(qsl("channel")).toULongLong()); const auto msgId = params.value(qsl("post")).toInt(); const auto commentParam = params.value(qsl("comment")); const auto commentId = commentParam.toInt(); diff --git a/Telegram/SourceFiles/core/ui_integration.cpp b/Telegram/SourceFiles/core/ui_integration.cpp index e1bebb678..be2e3b621 100644 --- a/Telegram/SourceFiles/core/ui_integration.cpp +++ b/Telegram/SourceFiles/core/ui_integration.cpp @@ -98,10 +98,6 @@ void UiIntegration::unregisterLeaveSubscription(not_null widget) { Core::App().unregisterLeaveSubscription(widget); } -void UiIntegration::writeLogEntry(const QString &entry) { - Logs::writeMain(entry); -} - QString UiIntegration::emojiCacheFolder() { return cWorkingDir() + "tdata/emoji"; } @@ -116,6 +112,7 @@ void UiIntegration::activationFromTopPanel() { Platform::IgnoreApplicationActivationRightNow(); } +/* void UiIntegration::startFontsBegin() { if (!cMainFont().isEmpty()) { style::internal::CustomMainFont = cMainFont(); @@ -136,9 +133,7 @@ void UiIntegration::startFontsBegin() { style::internal::UseOriginalMetrics = cUseOriginalMetrics(); } } - -void UiIntegration::startFontsEnd() { -} +*/ QString UiIntegration::timeFormat() { return cTimeFormat(); @@ -250,7 +245,7 @@ rpl::producer<> UiIntegration::forcePopupMenuHideRequests() { QString UiIntegration::convertTagToMimeTag(const QString &tagId) { if (TextUtilities::IsMentionLink(tagId)) { if (const auto session = Core::App().activeAccount().maybeSession()) { - return tagId + ':' + QString::number(session->userId()); + return tagId + ':' + QString::number(session->userId().bare); } } return tagId; @@ -262,11 +257,12 @@ const Ui::Emoji::One *UiIntegration::defaultEmojiVariant( return emoji; } const auto nonColored = emoji->nonColoredId(); - const auto it = cEmojiVariants().constFind(nonColored); - const auto result = (it != cEmojiVariants().cend()) - ? emoji->variant(it.value()) + const auto &variants = Core::App().settings().emojiVariants(); + const auto i = variants.find(nonColored); + const auto result = (i != end(variants)) + ? emoji->variant(i->second) : emoji; - AddRecentEmoji(result); + Core::App().settings().incrementRecentEmoji(result); return result; } diff --git a/Telegram/SourceFiles/core/ui_integration.h b/Telegram/SourceFiles/core/ui_integration.h index 2aac9a0aa..2bcf54fda 100644 --- a/Telegram/SourceFiles/core/ui_integration.h +++ b/Telegram/SourceFiles/core/ui_integration.h @@ -30,20 +30,17 @@ struct MarkedTextContext { HashtagMentionType type = HashtagMentionType::Telegram; }; -class UiIntegration : public Ui::Integration { +class UiIntegration final : public Ui::Integration { public: void postponeCall(FnMut &&callable) override; void registerLeaveSubscription(not_null widget) override; void unregisterLeaveSubscription(not_null widget) override; - void writeLogEntry(const QString &entry) override; QString emojiCacheFolder() override; void textActionsUpdated() override; void activationFromTopPanel() override; - void startFontsBegin() override; - void startFontsEnd() override; QString timeFormat() override; std::shared_ptr createLinkHandler( diff --git a/Telegram/SourceFiles/core/version.h b/Telegram/SourceFiles/core/version.h index 00b7a8bbf..1fa247c5a 100644 --- a/Telegram/SourceFiles/core/version.h +++ b/Telegram/SourceFiles/core/version.h @@ -23,7 +23,7 @@ constexpr auto AppId = "{C4A4AE8F-B9F7-4CC7-8A6C-BF7EEE87ACA5}"_cs; constexpr auto AppNameOld = "Telegram Win (Unofficial)"_cs; constexpr auto AppName = "Kotatogram Desktop"_cs; constexpr auto AppFile = "Kotatogram"_cs; -constexpr auto AppVersion = 2007001; -constexpr auto AppVersionStr = "2.7.1"; +constexpr auto AppVersion = 2007002; +constexpr auto AppVersionStr = "2.7.2"; constexpr auto AppBetaVersion = false; constexpr auto AppAlphaVersion = TDESKTOP_ALPHA_VERSION; diff --git a/Telegram/SourceFiles/data/data_channel.cpp b/Telegram/SourceFiles/data/data_channel.cpp index d2f00f610..a0921ef4b 100644 --- a/Telegram/SourceFiles/data/data_channel.cpp +++ b/Telegram/SourceFiles/data/data_channel.cpp @@ -49,7 +49,8 @@ void MegagroupInfo::setLocation(const ChannelLocation &location) { ChannelData::ChannelData(not_null owner, PeerId id) : PeerData(owner, id) -, inputChannel(MTP_inputChannel(MTP_int(bareId()), MTP_long(0))) +, inputChannel( + MTP_inputChannel(MTP_int(peerToChannel(id).bare), MTP_long(0))) , _ptsWaiter(&owner->session().updates()) { _flags.changes( ) | rpl::start_with_next([=](const Flags::Change &change) { @@ -78,12 +79,8 @@ ChannelData::ChannelData(not_null owner, PeerId id) } void ChannelData::setPhoto(const MTPChatPhoto &photo) { - setPhoto(userpicPhotoId(), photo); -} - -void ChannelData::setPhoto(PhotoId photoId, const MTPChatPhoto &photo) { photo.match([&](const MTPDchatPhoto & data) { - updateUserpic(photoId, data.vdc_id().v, data.vphoto_small()); + updateUserpic(data.vphoto_id().v, data.vdc_id().v); }, [&](const MTPDchatPhotoEmpty &) { clearUserpic(); }); @@ -95,8 +92,8 @@ void ChannelData::setName(const QString &newName, const QString &newUsername) { void ChannelData::setAccessHash(uint64 accessHash) { access = accessHash; - input = MTP_inputPeerChannel(MTP_int(bareId()), MTP_long(accessHash)); - inputChannel = MTP_inputChannel(MTP_int(bareId()), MTP_long(accessHash)); + input = MTP_inputPeerChannel(MTP_int(peerToChannel(id).bare), MTP_long(accessHash)); // #TODO ids + inputChannel = MTP_inputChannel(MTP_int(peerToChannel(id).bare), MTP_long(accessHash)); } void ChannelData::setInviteLink(const QString &newInviteLink) { @@ -353,7 +350,7 @@ void ChannelData::markForbidden() { MTP_flags(isMegagroup() ? MTPDchannelForbidden::Flag::f_megagroup : MTPDchannelForbidden::Flag::f_broadcast), - MTP_int(bareId()), + MTP_int(peerToChannel(id).bare), MTP_long(access), MTP_string(name), MTPint())); @@ -721,7 +718,9 @@ void ChannelData::migrateCall(std::unique_ptr call) { addFlags(MTPDchannel::Flag::f_call_active); } -void ChannelData::setGroupCall(const MTPInputGroupCall &call) { +void ChannelData::setGroupCall( + const MTPInputGroupCall &call, + TimeId scheduleDate) { call.match([&](const MTPDinputGroupCall &data) { if (_call && _call->id() == data.vid().v) { return; @@ -738,7 +737,8 @@ void ChannelData::setGroupCall(const MTPInputGroupCall &call) { _call = std::make_unique( this, data.vid().v, - data.vaccess_hash().v); + data.vaccess_hash().v, + scheduleDate); owner().registerGroupCall(_call.get()); session().changes().peerUpdated(this, UpdateFlag::GroupCall); addFlags(MTPDchannel::Flag::f_call_active); diff --git a/Telegram/SourceFiles/data/data_channel.h b/Telegram/SourceFiles/data/data_channel.h index 11d9d6acc..a9df04045 100644 --- a/Telegram/SourceFiles/data/data_channel.h +++ b/Telegram/SourceFiles/data/data_channel.h @@ -126,10 +126,8 @@ public: ChannelData(not_null owner, PeerId id); - void setPhoto(const MTPChatPhoto &photo); - void setPhoto(PhotoId photoId, const MTPChatPhoto &photo); - void setName(const QString &name, const QString &username); + void setPhoto(const MTPChatPhoto &photo); void setAccessHash(uint64 accessHash); void setFlags(MTPDchannel::Flags which) { @@ -413,7 +411,9 @@ public: return _call.get(); } void migrateCall(std::unique_ptr call); - void setGroupCall(const MTPInputGroupCall &call); + void setGroupCall( + const MTPInputGroupCall &call, + TimeId scheduleDate = 0); void clearGroupCall(); void setGroupCallDefaultJoinAs(PeerId peerId); [[nodiscard]] PeerId groupCallDefaultJoinAs() const; diff --git a/Telegram/SourceFiles/data/data_chat.cpp b/Telegram/SourceFiles/data/data_chat.cpp index c12fdb867..e01bc6ed5 100644 --- a/Telegram/SourceFiles/data/data_chat.cpp +++ b/Telegram/SourceFiles/data/data_chat.cpp @@ -25,7 +25,7 @@ using UpdateFlag = Data::PeerUpdate::Flag; ChatData::ChatData(not_null owner, PeerId id) : PeerData(owner, id) -, inputChat(MTP_int(bareId())) { +, inputChat(MTP_int(peerToChat(id).bare)) { _flags.changes( ) | rpl::start_with_next([=](const Flags::Change &change) { if (change.diff & MTPDchat::Flag::f_call_not_empty) { @@ -37,19 +37,15 @@ ChatData::ChatData(not_null owner, PeerId id) } void ChatData::setPhoto(const MTPChatPhoto &photo) { - setPhoto(userpicPhotoId(), photo); -} - -void ChatData::setPhoto(PhotoId photoId, const MTPChatPhoto &photo) { photo.match([&](const MTPDchatPhoto &data) { - updateUserpic(photoId, data.vdc_id().v, data.vphoto_small()); + updateUserpic(data.vphoto_id().v, data.vdc_id().v); }, [&](const MTPDchatPhotoEmpty &) { clearUserpic(); }); } auto ChatData::defaultAdminRights(not_null user) -> AdminRights { - const auto isCreator = (creator == user->bareId()) + const auto isCreator = (creator == peerToUser(user->id)) || (user->isSelf() && amCreator()); using Flag = AdminRight; return Flag::f_other @@ -198,7 +194,9 @@ void ChatData::setMigrateToChannel(ChannelData *channel) { } } -void ChatData::setGroupCall(const MTPInputGroupCall &call) { +void ChatData::setGroupCall( + const MTPInputGroupCall &call, + TimeId scheduleDate) { if (migrateTo()) { return; } @@ -218,7 +216,8 @@ void ChatData::setGroupCall(const MTPInputGroupCall &call) { _call = std::make_unique( this, data.vid().v, - data.vaccess_hash().v); + data.vaccess_hash().v, + scheduleDate); owner().registerGroupCall(_call.get()); session().changes().peerUpdated(this, UpdateFlag::GroupCall); addFlags(MTPDchat::Flag::f_call_active); @@ -280,7 +279,7 @@ void ApplyChatUpdate( chat->botStatus = 0; } else { chat->participants.emplace(user); - if (update.vinviter_id().v == session->userId()) { + if (UserId(update.vinviter_id()) == session->userId()) { chat->invitedByMe.insert(user); } else { chat->invitedByMe.remove(user); @@ -467,9 +466,9 @@ void ApplyChatUpdate( const auto inviterId = participant.match([&]( const MTPDchatParticipantCreator &data) { - return 0; + return UserId(0); }, [&](const auto &data) { - return data.vinviter_id().v; + return UserId(data.vinviter_id()); }); if (inviterId == selfUserId) { chat->invitedByMe.insert(user); diff --git a/Telegram/SourceFiles/data/data_chat.h b/Telegram/SourceFiles/data/data_chat.h index 595a50542..0c7df890d 100644 --- a/Telegram/SourceFiles/data/data_chat.h +++ b/Telegram/SourceFiles/data/data_chat.h @@ -39,10 +39,8 @@ public: ChatData(not_null owner, PeerId id); - void setPhoto(const MTPChatPhoto &photo); - void setPhoto(PhotoId photoId, const MTPChatPhoto &photo); - void setName(const QString &newName); + void setPhoto(const MTPChatPhoto &photo); void invalidateParticipants(); [[nodiscard]] bool noParticipantInfo() const { @@ -168,7 +166,9 @@ public: [[nodiscard]] Data::GroupCall *groupCall() const { return _call.get(); } - void setGroupCall(const MTPInputGroupCall &call); + void setGroupCall( + const MTPInputGroupCall &call, + TimeId scheduleDate = 0); void clearGroupCall(); void setGroupCallDefaultJoinAs(PeerId peerId); [[nodiscard]] PeerId groupCallDefaultJoinAs() const; diff --git a/Telegram/SourceFiles/data/data_chat_filters.cpp b/Telegram/SourceFiles/data/data_chat_filters.cpp index 9c99a20b6..987948d74 100644 --- a/Telegram/SourceFiles/data/data_chat_filters.cpp +++ b/Telegram/SourceFiles/data/data_chat_filters.cpp @@ -63,17 +63,18 @@ ChatFilter ChatFilter::local( not_null owner) { const auto flags = Flag(data.flags); auto &&to_histories = ranges::view::transform([&]( - const LocalFolder::Peer &filterPeer) { + const uint64 &filterPeer) { PeerData *peer = nullptr; + auto peerId = PeerId(filterPeer); - if (filterPeer.type == LocalFolder::Peer::Type::User) { - const auto user = owner->user(filterPeer.id); + if (peerIsUser(peerId)) { + const auto user = owner->user(peerToUser(peerId).bare); peer = (PeerData *)user; - } else if (filterPeer.type == LocalFolder::Peer::Type::Chat) { - const auto chat = owner->chat(filterPeer.id); + } else if (peerIsChat(peerId)) { + const auto chat = owner->chat(peerToChat(peerId).bare); peer = (PeerData *)chat; - } else if (filterPeer.type == LocalFolder::Peer::Type::Channel) { - const auto channel = owner->channel(filterPeer.id); + } else if (peerIsChannel(peerId)) { + const auto channel = owner->channel(peerToChannel(peerId).bare); peer = (PeerData *)channel; } return peer ? owner->history(peer).get() : nullptr; @@ -215,70 +216,30 @@ MTPDialogFilter ChatFilter::tl(FilterId replaceId) const { LocalFolder ChatFilter::toLocal(int cloudOrder, FilterId replaceId) const { auto always = _always; - auto pinned = std::vector(); + auto pinned = std::vector(); pinned.reserve(_pinned.size()); for (const auto history : _pinned) { const auto &peer = history->peer; - const auto hash = peer->isChannel() - ? peer->asChannel()->access - : peer->isUser() - ? peer->asUser()->accessHash() - : 0; - - pinned.push_back({ - .type = history->peer->isChannel() - ? LocalFolder::Peer::Type::Channel - : history->peer->isChat() - ? LocalFolder::Peer::Type::Chat - : LocalFolder::Peer::Type::User, - .id = peerToBareInt(peer->id) - }); + pinned.push_back(peer->id.value); always.remove(history); } - auto include = std::vector(); + auto include = std::vector(); include.reserve(always.size()); for (const auto history : always) { const auto &peer = history->peer; - const auto hash = peer->isChannel() - ? peer->asChannel()->access - : peer->isUser() - ? peer->asUser()->accessHash() - : 0; - - include.push_back({ - .type = history->peer->isChannel() - ? LocalFolder::Peer::Type::Channel - : history->peer->isChat() - ? LocalFolder::Peer::Type::Chat - : LocalFolder::Peer::Type::User, - .id = peerToBareInt(peer->id) - }); + include.push_back(peer->id.value); } - auto never = std::vector(); + auto never = std::vector(); never.reserve(_never.size()); for (const auto history : _never) { const auto &peer = history->peer; - const auto hash = peer->isChannel() - ? peer->asChannel()->access - : peer->isUser() - ? peer->asUser()->accessHash() - : 0; - - never.push_back({ - .type = history->peer->isChannel() - ? LocalFolder::Peer::Type::Channel - : history->peer->isChat() - ? LocalFolder::Peer::Type::Chat - : LocalFolder::Peer::Type::User, - .id = peerToBareInt(peer->id) - }); + never.push_back(peer->id.value); } const auto &session = App::main()->session(); return { .id = replaceId ? replaceId : _id, - .ownerId = session.mtp().isTestMode() - ? -session.userId() - : session.userId(), + .ownerId = session.userId().bare, + .isTest = session.mtp().isTestMode(), .cloudOrder = cloudOrder, .name = _title, .emoticon = _iconEmoji, @@ -490,7 +451,7 @@ void ChatFilters::received(const QVector &list) { // First we're adding cloud filters and corresponding local filters. for (const auto &filter : list) { for (const auto &localFilter : localFilters) { - if (!account->isCurrent(localFilter.ownerId) + if (!account->isCurrent(localFilter.ownerId, localFilter.isTest) || localFilter.cloudOrder != originalPosition) { continue; } @@ -503,7 +464,7 @@ void ChatFilters::received(const QVector &list) { // Then we adding local filters, retaining cloud order while (originalPosition < kFiltersLimit) { for (const auto &localFilter : localFilters) { - if (!account->isCurrent(localFilter.ownerId) + if (!account->isCurrent(localFilter.ownerId, localFilter.isTest) || localFilter.cloudOrder != originalPosition) { continue; } @@ -514,7 +475,7 @@ void ChatFilters::received(const QVector &list) { // And finally we adding other filters for (const auto &localFilter : localFilters) { - if (!account->isCurrent(localFilter.ownerId) + if (!account->isCurrent(localFilter.ownerId, localFilter.isTest) || localFilter.cloudOrder < kFiltersLimit) { continue; } @@ -740,7 +701,7 @@ void ChatFilters::saveOrder( if ((*i).isLocal()) { auto j = ranges::find_if(localFolders, [id, account](LocalFolder localFolder) { return (id == localFolder.id - && account->isCurrent(localFolder.ownerId)); + && account->isCurrent(localFolder.ownerId, localFolder.isTest)); }); (*j).cloudOrder = cloudIds.size(); std::rotate(j, j+1, localFolders.end()); @@ -884,7 +845,7 @@ void ChatFilters::saveLocal(FilterId filterId) { const auto account = &_owner->session().account(); const auto j = ranges::find_if(localFolders, [filterId, account](LocalFolder localFolder) { return (filterId == localFolder.id - && account->isCurrent(localFolder.ownerId)); + && account->isCurrent(localFolder.ownerId, localFolder.isTest)); }); Assert(i != end(_list)); Assert(j != end(localFolders)); diff --git a/Telegram/SourceFiles/data/data_group_call.cpp b/Telegram/SourceFiles/data/data_group_call.cpp index 0c9fd8947..1ec22213c 100644 --- a/Telegram/SourceFiles/data/data_group_call.cpp +++ b/Telegram/SourceFiles/data/data_group_call.cpp @@ -38,12 +38,14 @@ constexpr auto kWaitForUpdatesTimeout = 3 * crl::time(1000); GroupCall::GroupCall( not_null peer, uint64 id, - uint64 accessHash) + uint64 accessHash, + TimeId scheduleDate) : _id(id) , _accessHash(accessHash) , _peer(peer) , _reloadByQueuedUpdatesTimer([=] { reload(); }) -, _speakingByActiveFinishTimer([=] { checkFinishSpeakingByActive(); }) { +, _speakingByActiveFinishTimer([=] { checkFinishSpeakingByActive(); }) +, _scheduleDate(scheduleDate) { } GroupCall::~GroupCall() { @@ -270,7 +272,9 @@ void GroupCall::discard(const MTPDgroupCallDiscarded &data) { Core::App().calls().applyGroupCallUpdateChecked( &peer->session(), MTP_updateGroupCall( - MTP_int(peer->bareId()), + MTP_int(peer->isChat() + ? peerToChat(peer->id).bare + : peerToChannel(peer->id).bare), MTP_groupCallDiscarded( data.vid(), data.vaccess_hash(), @@ -329,6 +333,8 @@ void GroupCall::applyCallFields(const MTPDgroupCall &data) { changePeerEmptyCallFlag(); _title = qs(data.vtitle().value_or_empty()); _recordStartDate = data.vrecord_start_date().value_or_empty(); + _scheduleDate = data.vschedule_date().value_or_empty(); + _scheduleStartSubscribed = data.is_schedule_start_subscribed(); _allParticipantsLoaded = (_serverParticipantsCount == _participants.size()); } @@ -738,12 +744,12 @@ void GroupCall::requestUnknownParticipants() { for (const auto &[participantPeerId, when] : participantPeerIds) { if (const auto userId = peerToUser(participantPeerId)) { peerInputs.push_back( - MTP_inputPeerUser(MTP_int(userId), MTP_long(0))); + MTP_inputPeerUser(MTP_int(userId.bare), MTP_long(0))); // #TODO ids } else if (const auto chatId = peerToChat(participantPeerId)) { - peerInputs.push_back(MTP_inputPeerChat(MTP_int(chatId))); + peerInputs.push_back(MTP_inputPeerChat(MTP_int(chatId.bare))); // #TODO ids } else if (const auto channelId = peerToChannel(participantPeerId)) { peerInputs.push_back( - MTP_inputPeerChannel(MTP_int(channelId), MTP_long(0))); + MTP_inputPeerChannel(MTP_int(channelId.bare), MTP_long(0))); // #TODO ids } } _unknownParticipantPeersRequestId = api().request( diff --git a/Telegram/SourceFiles/data/data_group_call.h b/Telegram/SourceFiles/data/data_group_call.h index 882678610..078f5634e 100644 --- a/Telegram/SourceFiles/data/data_group_call.h +++ b/Telegram/SourceFiles/data/data_group_call.h @@ -38,7 +38,11 @@ struct GroupCallParticipant { class GroupCall final { public: - GroupCall(not_null peer, uint64 id, uint64 accessHash); + GroupCall( + not_null peer, + uint64 id, + uint64 accessHash, + TimeId scheduleDate); ~GroupCall(); [[nodiscard]] uint64 id() const; @@ -63,6 +67,21 @@ public: [[nodiscard]] rpl::producer recordStartDateChanges() const { return _recordStartDate.changes(); } + [[nodiscard]] TimeId scheduleDate() const { + return _scheduleDate.current(); + } + [[nodiscard]] rpl::producer scheduleDateValue() const { + return _scheduleDate.value(); + } + [[nodiscard]] rpl::producer scheduleDateChanges() const { + return _scheduleDate.changes(); + } + [[nodiscard]] bool scheduleStartSubscribed() const { + return _scheduleStartSubscribed.current(); + } + [[nodiscard]] rpl::producer scheduleStartSubscribedValue() const { + return _scheduleStartSubscribed.value(); + } void setPeer(not_null peer); @@ -163,6 +182,8 @@ private: int _serverParticipantsCount = 0; rpl::variable _fullCount = 0; rpl::variable _recordStartDate = 0; + rpl::variable _scheduleDate = 0; + rpl::variable _scheduleStartSubscribed = false; base::flat_map _unknownSpokenSsrcs; base::flat_map _unknownSpokenPeerIds; diff --git a/Telegram/SourceFiles/data/data_media_types.cpp b/Telegram/SourceFiles/data/data_media_types.cpp index 109d4dd19..ce9dadcaf 100644 --- a/Telegram/SourceFiles/data/data_media_types.cpp +++ b/Telegram/SourceFiles/data/data_media_types.cpp @@ -80,19 +80,19 @@ constexpr auto kFastRevokeRestriction = 24 * 60 * TimeId(60); [[nodiscard]] Invoice ComputeInvoiceData( not_null item, const MTPDmessageMediaInvoice &data) { - auto result = Invoice(); - result.isTest = data.is_test(); - result.amount = data.vtotal_amount().v; - result.currency = qs(data.vcurrency()); - result.description = qs(data.vdescription()); - result.title = TextUtilities::SingleLine(qs(data.vtitle())); - result.receiptMsgId = data.vreceipt_msg_id().value_or_empty(); - if (const auto photo = data.vphoto()) { - result.photo = item->history()->owner().photoFromWeb( - *photo, - ImageLocation()); - } - return result; + return { + .receiptMsgId = data.vreceipt_msg_id().value_or_empty(), + .amount = data.vtotal_amount().v, + .currency = qs(data.vcurrency()), + .title = TextUtilities::SingleLine(qs(data.vtitle())), + .description = qs(data.vdescription()), + .photo = (data.vphoto() + ? item->history()->owner().photoFromWeb( + *data.vphoto(), + ImageLocation()) + : nullptr), + .isTest = data.is_test(), + }; } [[nodiscard]] QString WithCaptionDialogsText( @@ -786,11 +786,12 @@ bool MediaContact::updateSentMedia(const MTPMessageMedia &media) { if (media.type() != mtpc_messageMediaContact) { return false; } - if (_contact.userId != media.c_messageMediaContact().vuser_id().v) { + const auto userId = UserId(media.c_messageMediaContact().vuser_id()); + if (_contact.userId != userId) { parent()->history()->owner().unregisterContactItem( _contact.userId, parent()); - _contact.userId = media.c_messageMediaContact().vuser_id().v; + _contact.userId = userId; parent()->history()->owner().registerContactItem( _contact.userId, parent()); diff --git a/Telegram/SourceFiles/data/data_peer.cpp b/Telegram/SourceFiles/data/data_peer.cpp index 91e6af21d..51f2dbffc 100644 --- a/Telegram/SourceFiles/data/data_peer.cpp +++ b/Telegram/SourceFiles/data/data_peer.cpp @@ -54,14 +54,14 @@ using UpdateFlag = Data::PeerUpdate::Flag; namespace Data { -int PeerColorIndex(int32 bareId) { - const auto index = std::abs(bareId) % 7; +int PeerColorIndex(BareId bareId) { + const auto index = bareId % 7; const int map[] = { 0, 7, 4, 1, 6, 3, 5 }; return map[index]; } int PeerColorIndex(PeerId peerId) { - return PeerColorIndex(peerToBareInt(peerId)); + return PeerColorIndex(peerId.value & PeerId::kChatTypeMask); } style::color PeerUserpicColor(PeerId peerId) { @@ -415,28 +415,23 @@ Data::FileOrigin PeerData::userpicOrigin() const { Data::FileOrigin PeerData::userpicPhotoOrigin() const { return (isUser() && userpicPhotoId()) - ? Data::FileOriginUserPhoto(bareId(), userpicPhotoId()) + ? Data::FileOriginUserPhoto(peerToUser(id).bare, userpicPhotoId()) : Data::FileOrigin(); } -void PeerData::updateUserpic( - PhotoId photoId, - MTP::DcId dcId, - const MTPFileLocation &location) { - setUserpicChecked(photoId, location.match([&]( - const MTPDfileLocationToBeDeprecated &deprecated) { - return ImageLocation( +void PeerData::updateUserpic(PhotoId photoId, MTP::DcId dcId) { + setUserpicChecked( + photoId, + ImageLocation( { StorageFileLocation( dcId, - isSelf() ? peerToUser(id) : 0, + isSelf() ? peerToUser(id) : UserId(), MTP_inputPeerPhotoFileLocation( MTP_flags(0), input, - deprecated.vvolume_id(), - deprecated.vlocal_id())) }, + MTP_long(photoId))) }, kUserpicSize, - kUserpicSize); - })); + kUserpicSize)); } void PeerData::clearUserpic() { diff --git a/Telegram/SourceFiles/data/data_peer.h b/Telegram/SourceFiles/data/data_peer.h index 78a0df5ef..7a9fe2256 100644 --- a/Telegram/SourceFiles/data/data_peer.h +++ b/Telegram/SourceFiles/data/data_peer.h @@ -38,7 +38,7 @@ class GroupCall; class CloudImageView; int PeerColorIndex(PeerId peerId); -int PeerColorIndex(int32 bareId); +int PeerColorIndex(BareId bareId); style::color PeerUserpicColor(PeerId peerId); PeerId FakePeerIdForJustName(const QString &name); @@ -172,7 +172,7 @@ public: || (id == kServiceNotificationsId); } [[nodiscard]] bool isServiceUser() const { - return isUser() && !(id % 1000); + return isUser() && !(id.value % 1000); } [[nodiscard]] std::optional notifyMuteUntil() const { @@ -239,10 +239,6 @@ public: [[nodiscard]] const Ui::Text::String &topBarNameText() const; [[nodiscard]] QString userName() const; - [[nodiscard]] int32 bareId() const { - return int32(uint32(id & 0xFFFFFFFFULL)); - } - [[nodiscard]] const base::flat_set &nameWords() const { return _nameWords; } @@ -416,10 +412,7 @@ protected: const QString &newName, const QString &newNameOrPhone, const QString &newUsername); - void updateUserpic( - PhotoId photoId, - MTP::DcId dcId, - const MTPFileLocation &small); + void updateUserpic(PhotoId photoId, MTP::DcId dcId); void clearUserpic(); private: diff --git a/Telegram/SourceFiles/data/data_peer_id.cpp b/Telegram/SourceFiles/data/data_peer_id.cpp new file mode 100644 index 000000000..c6e1be66a --- /dev/null +++ b/Telegram/SourceFiles/data/data_peer_id.cpp @@ -0,0 +1,58 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "data/data_peer_id.h" + +PeerId peerFromMTP(const MTPPeer &peer) { + return peer.match([](const MTPDpeerUser &data) { + return peerFromUser(data.vuser_id()); + }, [](const MTPDpeerChat &data) { + return peerFromChat(data.vchat_id()); + }, [](const MTPDpeerChannel &data) { + return peerFromChannel(data.vchannel_id()); + }); +} + +MTPpeer peerToMTP(PeerId id) { + if (peerIsUser(id)) { + return MTP_peerUser(peerToBareMTPInt(id)); + } else if (peerIsChat(id)) { + return MTP_peerChat(peerToBareMTPInt(id)); + } else if (peerIsChannel(id)) { + return MTP_peerChannel(peerToBareMTPInt(id)); + } + return MTP_peerUser(MTP_int(0)); +} + +PeerId DeserializePeerId(quint64 serialized) { + const auto flag = (UserId::kReservedBit << 48); + const auto legacy = !(serialized & (UserId::kReservedBit << 48)); + if (!legacy) { + return PeerId(serialized & (~flag)); + } + constexpr auto PeerIdMask = uint64(0xFFFFFFFFULL); + constexpr auto PeerIdTypeMask = uint64(0xF00000000ULL); + constexpr auto PeerIdUserShift = uint64(0x000000000ULL); + constexpr auto PeerIdChatShift = uint64(0x100000000ULL); + constexpr auto PeerIdChannelShift = uint64(0x200000000ULL); + constexpr auto PeerIdFakeShift = uint64(0xF00000000ULL); + return ((serialized & PeerIdTypeMask) == PeerIdUserShift) + ? peerFromUser(UserId(serialized & PeerIdMask)) + : ((serialized & PeerIdTypeMask) == PeerIdChatShift) + ? peerFromChat(ChatId(serialized & PeerIdMask)) + : ((serialized & PeerIdTypeMask) == PeerIdChannelShift) + ? peerFromChannel(ChannelId(serialized & PeerIdMask)) + : ((serialized & PeerIdTypeMask) == PeerIdFakeShift) + ? PeerId(FakeChatId(serialized & PeerIdMask)) + : PeerId(0); +} + +quint64 SerializePeerId(PeerId id) { + Expects(!(id.value & (UserId::kReservedBit << 48))); + + return id.value | (UserId::kReservedBit << 48); +} diff --git a/Telegram/SourceFiles/data/data_peer_id.h b/Telegram/SourceFiles/data/data_peer_id.h new file mode 100644 index 000000000..1bebe95c9 --- /dev/null +++ b/Telegram/SourceFiles/data/data_peer_id.h @@ -0,0 +1,318 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +using BareId = uint64; + +struct PeerIdZeroHelper { +}; +using PeerIdZero = void(PeerIdZeroHelper::*)(); + +template +struct ChatIdType { + BareId bare = 0; + static constexpr BareId kShift = Shift; + static constexpr BareId kReservedBit = BareId(0x80); + static_assert((Shift & kReservedBit) == 0, "Last bit is reserved."); + + constexpr ChatIdType() noexcept = default; + //constexpr ChatIdType(PeerIdZero) noexcept { // UserId id = 0; + //} + constexpr ChatIdType(BareId value) noexcept : bare(value) { + } + constexpr ChatIdType(int32 value) noexcept : bare(value) { // #TODO ids remove + } + constexpr ChatIdType(MTPint value) noexcept : bare(value.v) { // #TODO ids + } + + [[nodiscard]] constexpr explicit operator bool() const noexcept { + return (bare != 0); + } + [[nodiscard]] constexpr bool operator!() const noexcept { + return !bare; + } + +}; + +template +[[nodiscard]] inline constexpr bool operator==( + ChatIdType a, + ChatIdType b) noexcept { + return (a.bare == b.bare); +} + +template +[[nodiscard]] inline constexpr bool operator!=( + ChatIdType a, + ChatIdType b) noexcept { + return (a.bare != b.bare); +} + +template +[[nodiscard]] inline constexpr bool operator<( + ChatIdType a, + ChatIdType b) noexcept { + return (a.bare < b.bare); +} + +template +[[nodiscard]] inline constexpr bool operator>( + ChatIdType a, + ChatIdType b) noexcept { + return (a.bare > b.bare); +} + +template +[[nodiscard]] inline constexpr bool operator<=( + ChatIdType a, + ChatIdType b) noexcept { + return (a.bare <= b.bare); +} + +template +[[nodiscard]] inline constexpr bool operator>=( + ChatIdType a, + ChatIdType b) noexcept { + return (a.bare >= b.bare); +} + +template +[[nodiscard]] inline constexpr bool operator==( + ChatIdType a, + PeerIdZero) noexcept { + return (a.bare == 0); +} + +template +[[nodiscard]] inline constexpr bool operator==( + PeerIdZero, + ChatIdType a) noexcept { + return (0 == a.bare); +} + +template +[[nodiscard]] inline constexpr bool operator!=( + ChatIdType a, + PeerIdZero) noexcept { + return (a.bare != 0); +} + +template +[[nodiscard]] inline constexpr bool operator!=( + PeerIdZero, + ChatIdType a) noexcept { + return (0 != a.bare); +} + +template +bool operator<(ChatIdType, PeerIdZero) = delete; + +template +bool operator<(PeerIdZero, ChatIdType) = delete; + +template +bool operator>(ChatIdType, PeerIdZero) = delete; + +template +bool operator>(PeerIdZero, ChatIdType) = delete; + +template +bool operator<=(ChatIdType, PeerIdZero) = delete; + +template +bool operator<=(PeerIdZero, ChatIdType) = delete; + +template +bool operator>=(ChatIdType, PeerIdZero) = delete; + +template +bool operator>=(PeerIdZero, ChatIdType) = delete; + +using UserId = ChatIdType<0>; +using ChatId = ChatIdType<1>; +using ChannelId = ChatIdType<2>; +using FakeChatId = ChatIdType<0x7F>; + +inline constexpr auto NoChannel = ChannelId(0); + +struct PeerIdHelper { + BareId value = 0; + constexpr PeerIdHelper(BareId value) noexcept : value(value) { + } +}; + +struct PeerId { + BareId value = 0; + static constexpr BareId kChatTypeMask = BareId(0xFFFFFFFFFFFFULL); + + constexpr PeerId() noexcept = default; + constexpr PeerId(PeerIdZero) noexcept { // PeerId id = 0; + } + template + constexpr PeerId(ChatIdType id) noexcept + : value(id.bare | (BareId(Shift) << 48)) { + } + // This instead of explicit PeerId(BareId) allows to use both + // PeerId(uint64(..)) and PeerId(0). + constexpr PeerId(PeerIdHelper value) noexcept : value(value.value) { + } + + template + [[nodiscard]] constexpr bool is() const noexcept { + return ((value >> 48) & BareId(0xFF)) == SomeChatIdType::kShift; + } + + template + [[nodiscard]] constexpr SomeChatIdType to() const noexcept { + return is() ? (value & kChatTypeMask) : 0; + } + + [[nodiscard]] constexpr explicit operator bool() const noexcept { + return (value != 0); + } + [[nodiscard]] constexpr bool operator!() const noexcept { + return !value; + } + +}; + +[[nodiscard]] inline constexpr bool operator==(PeerId a, PeerId b) noexcept { + return (a.value == b.value); +} + +[[nodiscard]] inline constexpr bool operator!=(PeerId a, PeerId b) noexcept { + return (a.value != b.value); +} + +[[nodiscard]] inline constexpr bool operator<(PeerId a, PeerId b) noexcept { + return (a.value < b.value); +} + +[[nodiscard]] inline constexpr bool operator>(PeerId a, PeerId b) noexcept { + return (a.value > b.value); +} + +[[nodiscard]] inline constexpr bool operator<=(PeerId a, PeerId b) noexcept { + return (a.value <= b.value); +} + +[[nodiscard]] inline constexpr bool operator>=(PeerId a, PeerId b) noexcept { + return (a.value >= b.value); +} + +[[nodiscard]] inline constexpr bool operator==( + PeerId a, + PeerIdZero) noexcept { + return (a.value == 0); +} + +[[nodiscard]] inline constexpr bool operator==( + PeerIdZero, + PeerId a) noexcept { + return (0 == a.value); +} + +[[nodiscard]] inline constexpr bool operator!=( + PeerId a, + PeerIdZero) noexcept { + return (a.value != 0); +} + +[[nodiscard]] inline constexpr bool operator!=( + PeerIdZero, + PeerId a) noexcept { + return (0 != a.value); +} + +bool operator<(PeerId, PeerIdZero) = delete; +bool operator<(PeerIdZero, PeerId) = delete; +bool operator>(PeerId, PeerIdZero) = delete; +bool operator>(PeerIdZero, PeerId) = delete; +bool operator<=(PeerId, PeerIdZero) = delete; +bool operator<=(PeerIdZero, PeerId) = delete; +bool operator>=(PeerId, PeerIdZero) = delete; +bool operator>=(PeerIdZero, PeerId) = delete; + +[[nodiscard]] inline constexpr bool peerIsUser(PeerId id) noexcept { + return id.is(); +} + +[[nodiscard]] inline constexpr bool peerIsChat(PeerId id) noexcept { + return id.is(); +} + +[[nodiscard]] inline constexpr bool peerIsChannel(PeerId id) noexcept { + return id.is(); +} + +[[nodiscard]] inline constexpr PeerId peerFromUser(UserId userId) noexcept { + return userId; +} + +[[nodiscard]] inline constexpr PeerId peerFromChat(ChatId chatId) noexcept { + return chatId; +} + +[[nodiscard]] inline constexpr PeerId peerFromChannel( + ChannelId channelId) noexcept { + return channelId; +} + +[[nodiscard]] inline constexpr PeerId peerFromUser(MTPint userId) noexcept { // #TODO ids + return peerFromUser(userId.v); +} + +[[nodiscard]] inline constexpr PeerId peerFromChat(MTPint chatId) noexcept { + return peerFromChat(chatId.v); +} + +[[nodiscard]] inline constexpr PeerId peerFromChannel( + MTPint channelId) noexcept { + return peerFromChannel(channelId.v); +} + +[[nodiscard]] inline constexpr UserId peerToUser(PeerId id) noexcept { + return id.to(); +} + +[[nodiscard]] inline constexpr ChatId peerToChat(PeerId id) noexcept { + return id.to(); +} + +[[nodiscard]] inline constexpr ChannelId peerToChannel(PeerId id) noexcept { + return id.to(); +} + +[[nodiscard]] inline MTPint peerToBareMTPInt(PeerId id) { // #TODO ids + return MTP_int(id.value & PeerId::kChatTypeMask); +} + +[[nodiscard]] PeerId peerFromMTP(const MTPPeer &peer); +[[nodiscard]] MTPpeer peerToMTP(PeerId id); + +// Supports both modern and legacy serializations. +[[nodiscard]] PeerId DeserializePeerId(quint64 serialized); +[[nodiscard]] quint64 SerializePeerId(PeerId id); + +namespace std { + +template +struct hash> : private hash { + size_t operator()(ChatIdType value) const noexcept { + return hash::operator()(value.bare); + } +}; + +template <> +struct hash : private hash { + size_t operator()(PeerId value) const noexcept { + return hash::operator()(value.value); + } +}; + +} // namespace std diff --git a/Telegram/SourceFiles/data/data_peer_values.cpp b/Telegram/SourceFiles/data/data_peer_values.cpp index 46bdbb3a3..5aaabb62b 100644 --- a/Telegram/SourceFiles/data/data_peer_values.cpp +++ b/Telegram/SourceFiles/data/data_peer_values.cpp @@ -322,6 +322,20 @@ rpl::producer CanPinMessagesValue(not_null peer) { Unexpected("Peer type in CanPinMessagesValue."); } +rpl::producer CanManageGroupCallValue(not_null peer) { + const auto flag = MTPDchatAdminRights::Flag::f_manage_call; + if (const auto chat = peer->asChat()) { + return chat->amCreator() + ? (rpl::single(true) | rpl::type_erased()) + : AdminRightValue(chat, flag); + } else if (const auto channel = peer->asChannel()) { + return channel->amCreator() + ? (rpl::single(true) | rpl::type_erased()) + : AdminRightValue(channel, flag); + } + return rpl::single(false); +} + TimeId SortByOnlineValue(not_null user, TimeId now) { if (user->isServiceUser() || user->isBot()) { return -1; diff --git a/Telegram/SourceFiles/data/data_peer_values.h b/Telegram/SourceFiles/data/data_peer_values.h index 04d12b391..24605bf07 100644 --- a/Telegram/SourceFiles/data/data_peer_values.h +++ b/Telegram/SourceFiles/data/data_peer_values.h @@ -111,6 +111,7 @@ inline auto PeerFullFlagValue( [[nodiscard]] rpl::producer CanWriteValue(ChannelData *channel); [[nodiscard]] rpl::producer CanWriteValue(not_null peer); [[nodiscard]] rpl::producer CanPinMessagesValue(not_null peer); +[[nodiscard]] rpl::producer CanManageGroupCallValue(not_null peer); [[nodiscard]] TimeId SortByOnlineValue(not_null user, TimeId now); [[nodiscard]] crl::time OnlineChangeTimeout(TimeId online, TimeId now); diff --git a/Telegram/SourceFiles/data/data_poll.cpp b/Telegram/SourceFiles/data/data_poll.cpp index 26777a50c..3680566ee 100644 --- a/Telegram/SourceFiles/data/data_poll.cpp +++ b/Telegram/SourceFiles/data/data_poll.cpp @@ -133,12 +133,15 @@ bool PollData::applyResults(const MTPPollResults &results) { } } if (const auto recent = results.vrecent_voters()) { + const auto bareProj = [](not_null user) { + return peerToUser(user->id).bare; + }; const auto recentChanged = !ranges::equal( recentVoters, recent->v, ranges::equal_to(), - &UserData::id, - &MTPint::v); + bareProj, + &MTPint::v); // #TODO ids if (recentChanged) { changed = true; recentVoters = ranges::views::all( diff --git a/Telegram/SourceFiles/data/data_session.cpp b/Telegram/SourceFiles/data/data_session.cpp index 6284063fd..1bdd340cf 100644 --- a/Telegram/SourceFiles/data/data_session.cpp +++ b/Telegram/SourceFiles/data/data_session.cpp @@ -3528,7 +3528,7 @@ HistoryItem *Session::findWebPageItem(not_null page) const { QString Session::findContactPhone(not_null contact) const { const auto result = contact->phone(); return result.isEmpty() - ? findContactPhone(contact->bareId()) + ? findContactPhone(peerToUser(contact->id)) : App::formatPhone(result); } @@ -3973,7 +3973,7 @@ void Session::serviceNotification( | MTPDuser::Flag::f_phone | MTPDuser::Flag::f_status | MTPDuser::Flag::f_verified), - MTP_int(peerToUser(PeerData::kServiceNotificationsId)), + MTP_int(peerToUser(PeerData::kServiceNotificationsId).bare), // #TODO ids MTPlong(), // access_hash MTP_string("Telegram"), MTPstring(), // last_name diff --git a/Telegram/SourceFiles/data/data_shared_media.cpp b/Telegram/SourceFiles/data/data_shared_media.cpp index e1dc70e52..1db3ab294 100644 --- a/Telegram/SourceFiles/data/data_shared_media.cpp +++ b/Telegram/SourceFiles/data/data_shared_media.cpp @@ -269,7 +269,7 @@ std::optional SharedMediaWithLastSlice::indexOf(Value value) const { ? QString::number(*_ending->skippedAfter()) : QString("-")); if (const auto msgId = std::get_if(&value)) { - info.push_back("value:" + QString::number(msgId->channel)); + info.push_back("value:" + QString::number(msgId->channel.bare)); info.push_back(QString::number(msgId->msg)); const auto index = _slice.indexOf(*std::get_if(&value)); info.push_back("index:" + (index diff --git a/Telegram/SourceFiles/data/data_shared_media.h b/Telegram/SourceFiles/data/data_shared_media.h index c73de5bb7..937a1a882 100644 --- a/Telegram/SourceFiles/data/data_shared_media.h +++ b/Telegram/SourceFiles/data/data_shared_media.h @@ -155,9 +155,7 @@ private: return (a && b) ? base::make_optional(*a + *b) : std::nullopt; } static Value ComputeId(PeerId peerId, MsgId msgId) { - return FullMsgId( - peerIsChannel(peerId) ? peerToBareInt(peerId) : 0, - msgId); + return FullMsgId(peerToChannel(peerId), msgId); } static Value ComputeId(const Key &key) { if (const auto messageId = std::get_if(&key.universalId)) { diff --git a/Telegram/SourceFiles/data/data_sparse_ids.h b/Telegram/SourceFiles/data/data_sparse_ids.h index a01b2fa1b..3f5382490 100644 --- a/Telegram/SourceFiles/data/data_sparse_ids.h +++ b/Telegram/SourceFiles/data/data_sparse_ids.h @@ -119,9 +119,7 @@ private: : !fullId.channel; } static FullMsgId ComputeId(PeerId peerId, MsgId msgId) { - return FullMsgId( - peerIsChannel(peerId) ? peerToBareInt(peerId) : 0, - msgId); + return FullMsgId(peerToChannel(peerId), msgId); } static FullMsgId ComputeId(const Key &key) { return (key.universalId >= 0) diff --git a/Telegram/SourceFiles/data/data_types.h b/Telegram/SourceFiles/data/data_types.h index 03bca3ea8..2009e1a6f 100644 --- a/Telegram/SourceFiles/data/data_types.h +++ b/Telegram/SourceFiles/data/data_types.h @@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/value_ordering.h" #include "ui/text/text.h" // For QFIXED_MAX +#include "data/data_peer_id.h" class HistoryItem; using HistoryItemsList = std::vector>; @@ -63,11 +64,11 @@ struct FileOrigin; } // namespace Data struct MessageGroupId { - uint64 peer = 0; + PeerId peer = 0; uint64 value = 0; MessageGroupId() = default; - static MessageGroupId FromRaw(uint64 peer, uint64 value) { + static MessageGroupId FromRaw(PeerId peer, uint64 value) { auto result = MessageGroupId(); result.peer = peer; result.value = value; @@ -85,7 +86,7 @@ struct MessageGroupId { } friend inline std::pair value_ordering_helper(MessageGroupId value) { - return std::make_pair(value.value, value.peer); + return std::make_pair(value.value, value.peer.value); } }; @@ -101,84 +102,8 @@ namespace Data { class Folder; } // namespace Data -using UserId = int32; -using ChatId = int32; -using ChannelId = int32; using FolderId = int32; using FilterId = int32; - -constexpr auto NoChannel = ChannelId(0); - -using PeerId = uint64; - -constexpr auto PeerIdMask = PeerId(0xFFFFFFFFULL); -constexpr auto PeerIdTypeMask = PeerId(0xF00000000ULL); -constexpr auto PeerIdUserShift = PeerId(0x000000000ULL); -constexpr auto PeerIdChatShift = PeerId(0x100000000ULL); -constexpr auto PeerIdChannelShift = PeerId(0x200000000ULL); -constexpr auto PeerIdFakeShift = PeerId(0xF00000000ULL); - -inline constexpr bool peerIsUser(const PeerId &id) { - return (id & PeerIdTypeMask) == PeerIdUserShift; -} -inline constexpr bool peerIsChat(const PeerId &id) { - return (id & PeerIdTypeMask) == PeerIdChatShift; -} -inline constexpr bool peerIsChannel(const PeerId &id) { - return (id & PeerIdTypeMask) == PeerIdChannelShift; -} -inline constexpr PeerId peerFromUser(UserId user_id) { - return PeerIdUserShift | uint64(uint32(user_id)); -} -inline constexpr PeerId peerFromChat(ChatId chat_id) { - return PeerIdChatShift | uint64(uint32(chat_id)); -} -inline constexpr PeerId peerFromChannel(ChannelId channel_id) { - return PeerIdChannelShift | uint64(uint32(channel_id)); -} -inline constexpr PeerId peerFromUser(const MTPint &user_id) { - return peerFromUser(user_id.v); -} -inline constexpr PeerId peerFromChat(const MTPint &chat_id) { - return peerFromChat(chat_id.v); -} -inline constexpr PeerId peerFromChannel(const MTPint &channel_id) { - return peerFromChannel(channel_id.v); -} -inline constexpr int32 peerToBareInt(const PeerId &id) { - return int32(uint32(id & PeerIdMask)); -} -inline constexpr UserId peerToUser(const PeerId &id) { - return peerIsUser(id) ? peerToBareInt(id) : 0; -} -inline constexpr ChatId peerToChat(const PeerId &id) { - return peerIsChat(id) ? peerToBareInt(id) : 0; -} -inline constexpr ChannelId peerToChannel(const PeerId &id) { - return peerIsChannel(id) ? peerToBareInt(id) : NoChannel; -} -inline MTPint peerToBareMTPInt(const PeerId &id) { - return MTP_int(peerToBareInt(id)); -} -inline PeerId peerFromMTP(const MTPPeer &peer) { - switch (peer.type()) { - case mtpc_peerUser: return peerFromUser(peer.c_peerUser().vuser_id()); - case mtpc_peerChat: return peerFromChat(peer.c_peerChat().vchat_id()); - case mtpc_peerChannel: return peerFromChannel(peer.c_peerChannel().vchannel_id()); - } - return 0; -} -inline MTPpeer peerToMTP(const PeerId &id) { - if (peerIsUser(id)) { - return MTP_peerUser(peerToBareMTPInt(id)); - } else if (peerIsChat(id)) { - return MTP_peerChat(peerToBareMTPInt(id)); - } else if (peerIsChannel(id)) { - return MTP_peerChannel(peerToBareMTPInt(id)); - } - return MTP_peerUser(MTP_int(0)); -} - using MsgId = int32; constexpr auto StartClientMsgId = MsgId(-0x7FFFFFFF); constexpr auto EndClientMsgId = MsgId(-0x40000000); @@ -214,7 +139,8 @@ inline bool operator!=(const MsgRange &a, const MsgRange &b) { struct FullMsgId { constexpr FullMsgId() = default; - constexpr FullMsgId(ChannelId channel, MsgId msg) : channel(channel), msg(msg) { + constexpr FullMsgId(ChannelId channel, MsgId msg) + : channel(channel), msg(msg) { } explicit operator bool() const { diff --git a/Telegram/SourceFiles/data/data_user.cpp b/Telegram/SourceFiles/data/data_user.cpp index 03fb7530e..a555781a9 100644 --- a/Telegram/SourceFiles/data/data_user.cpp +++ b/Telegram/SourceFiles/data/data_user.cpp @@ -72,10 +72,7 @@ void UserData::setIsContact(bool is) { // see Serialize::readPeer as well void UserData::setPhoto(const MTPUserProfilePhoto &photo) { photo.match([&](const MTPDuserProfilePhoto &data) { - updateUserpic( - data.vphoto_id().v, - data.vdc_id().v, - data.vphoto_small()); + updateUserpic(data.vphoto_id().v, data.vdc_id().v); }, [&](const MTPDuserProfilePhotoEmpty &) { clearUserpic(); }); diff --git a/Telegram/SourceFiles/data/data_wall_paper.cpp b/Telegram/SourceFiles/data/data_wall_paper.cpp index 691afcbf7..e5d228fc8 100644 --- a/Telegram/SourceFiles/data/data_wall_paper.cpp +++ b/Telegram/SourceFiles/data/data_wall_paper.cpp @@ -365,11 +365,14 @@ QByteArray WallPaper::serialize() const { + Serialize::stringSize(_slug) + sizeof(qint32) // _settings + sizeof(quint32) // _backgroundColor - + sizeof(qint32); // _intensity + + sizeof(qint32) // _intensity + + (2 * sizeof(qint32)); // ownerId auto result = QByteArray(); result.reserve(size); { + const auto field1 = qint32(uint32(_ownerId.bare & 0xFFFFFFFF)); + const auto field2 = qint32(uint32(_ownerId.bare >> 32)); auto stream = QDataStream(&result, QIODevice::WriteOnly); stream.setVersion(QDataStream::Qt_5_1); stream @@ -380,7 +383,8 @@ QByteArray WallPaper::serialize() const { << qint32(_settings) << SerializeMaybeColor(_backgroundColor) << qint32(_intensity) - << qint32(_ownerId); + << field1 + << field2; } return result; } @@ -393,7 +397,7 @@ std::optional WallPaper::FromSerialized( auto id = quint64(); auto accessHash = quint64(); - auto ownerId = qint32(); + auto ownerId = UserId(); auto flags = qint32(); auto slug = QString(); auto settings = qint32(); @@ -411,7 +415,14 @@ std::optional WallPaper::FromSerialized( >> backgroundColor >> intensity; if (!stream.atEnd()) { - stream >> ownerId; + auto field1 = qint32(); + auto field2 = qint32(); + stream >> field1; + if (!stream.atEnd()) { + stream >> field2; + } + ownerId = UserId( + BareId(uint32(field1)) | (BareId(uint32(field2)) << 32)); } if (stream.status() != QDataStream::Ok) { return std::nullopt; diff --git a/Telegram/SourceFiles/data/stickers/data_stickers.cpp b/Telegram/SourceFiles/data/stickers/data_stickers.cpp index d2a062206..f5212ca11 100644 --- a/Telegram/SourceFiles/data/stickers/data_stickers.cpp +++ b/Telegram/SourceFiles/data/stickers/data_stickers.cpp @@ -324,6 +324,7 @@ bool Stickers::applyArchivedResultFake() { MTP_string(raw->shortName), MTP_vector(), MTP_int(0), + MTP_int(0), MTP_int(raw->count), MTP_int(raw->hash)); sets.push_back(MTP_stickerSetCovered( diff --git a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp index b28617e9e..c8dd1e1f3 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp @@ -229,12 +229,6 @@ InnerWidget::InnerWidget( refresh(); }, lifetime()); - subscribe(Window::Theme::Background(), [=](const Window::Theme::BackgroundUpdate &data) { - if (data.paletteChanged()) { - Layout::clearUnreadBadgesCache(); - } - }); - session().changes().historyUpdates( Data::HistoryUpdate::Flag::IsPinned | Data::HistoryUpdate::Flag::ChatOccupied @@ -742,7 +736,7 @@ bool InnerWidget::isSearchResultActive( const auto peer = item->history()->peer; return (item->fullId() == entry.fullId) || (peer->migrateTo() - && (peer->migrateTo()->bareId() == entry.fullId.channel) + && (peerToChannel(peer->migrateTo()->id) == entry.fullId.channel) && (item->id == -entry.fullId.msg)) || (uniqueSearchResults() && peer == entry.key.peer()); } @@ -2111,7 +2105,8 @@ bool InnerWidget::searchReceived( _lastSearchPeer = peer; } } else { - LOG(("API Error: a search results with not loaded peer %1").arg(peerId)); + LOG(("API Error: a search results with not loaded peer %1" + ).arg(peerId.value)); } if (isMigratedSearch) { _lastSearchMigratedId = msgId; @@ -2173,7 +2168,7 @@ void InnerWidget::peerSearchReceived( } else { LOG(("API Error: " "user %1 was not loaded in InnerWidget::peopleReceived()" - ).arg(peer->id)); + ).arg(peer->id.value)); } } for (const auto &mtpPeer : result) { @@ -2188,7 +2183,7 @@ void InnerWidget::peerSearchReceived( } else { LOG(("API Error: " "user %1 was not loaded in InnerWidget::peopleReceived()" - ).arg(peer->id)); + ).arg(peer->id.value)); } } refresh(); diff --git a/Telegram/SourceFiles/dialogs/dialogs_layout.cpp b/Telegram/SourceFiles/dialogs/dialogs_layout.cpp index c716e401d..511f45659 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_layout.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_layout.cpp @@ -701,6 +701,8 @@ struct UnreadBadgeSizeData { }; class UnreadBadgeStyleData : public Data::AbstractStructure { public: + UnreadBadgeStyleData(); + UnreadBadgeSizeData sizes[UnreadBadgeSizesCount]; style::color bg[6] = { st::dialogsUnreadBg, @@ -710,9 +712,24 @@ public: st::dialogsUnreadBgMutedOver, st::dialogsUnreadBgMutedActive }; + rpl::lifetime lifetime; }; Data::GlobalStructurePointer unreadBadgeStyle; +UnreadBadgeStyleData::UnreadBadgeStyleData() { + style::PaletteChanged( + ) | rpl::start_with_next([=] { + for (auto &data : sizes) { + for (auto &left : data.left) { + left = QPixmap(); + } + for (auto &right : data.right) { + right = QPixmap(); + } + } + }, lifetime); +} + void createCircleMask(UnreadBadgeSizeData *data, int size) { if (!data->circle.isNull()) return; @@ -1246,18 +1263,5 @@ void PaintCollapsedRow( } } -void clearUnreadBadgesCache() { - if (unreadBadgeStyle) { - for (auto &data : unreadBadgeStyle->sizes) { - for (auto &left : data.left) { - left = QPixmap(); - } - for (auto &right : data.right) { - right = QPixmap(); - } - } - } -} - } // namespace Layout } // namespace Dialogs diff --git a/Telegram/SourceFiles/dialogs/dialogs_search_from_controllers.cpp b/Telegram/SourceFiles/dialogs/dialogs_search_from_controllers.cpp index 6ff20477c..adb071607 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_search_from_controllers.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_search_from_controllers.cpp @@ -61,7 +61,7 @@ void SearchFromController::prepare() { AddSpecialBoxController::prepare(); delegate()->peerListSetTitle(tr::lng_search_messages_from()); if (const auto megagroup = peer()->asMegagroup()) { - if (!delegate()->peerListFindRow(megagroup->id)) { + if (!delegate()->peerListFindRow(megagroup->id.value)) { delegate()->peerListAppendRow( std::make_unique(megagroup)); setDescriptionText({}); diff --git a/Telegram/SourceFiles/export/data/export_data_types.cpp b/Telegram/SourceFiles/export/data/export_data_types.cpp index 2caf79935..b61cc1820 100644 --- a/Telegram/SourceFiles/export/data/export_data_types.cpp +++ b/Telegram/SourceFiles/export/data/export_data_types.cpp @@ -49,26 +49,22 @@ QString PreparePhotoFileName(int index, TimeId date) { } // namespace -PeerId UserPeerId(int32 userId) { - return kUserPeerIdShift | uint32(userId); -} - -PeerId ChatPeerId(int32 chatId) { - return kChatPeerIdShift | uint32(chatId); -} - -int32 BarePeerId(PeerId peerId) { - return int32(peerId & 0xFFFFFFFFULL); -} - -int PeerColorIndex(int32 bareId) { - const auto index = std::abs(bareId) % 7; +int PeerColorIndex(BareId bareId) { + const auto index = bareId % 7; const int map[] = { 0, 7, 4, 1, 6, 3, 5 }; return map[index]; } -int StringBarePeerId(const Utf8String &data) { - auto result = 0xFF; +BareId PeerToBareId(PeerId peerId) { + return (peerId.value & PeerId::kChatTypeMask); +} + +int PeerColorIndex(PeerId peerId) { + return PeerColorIndex(PeerToBareId(peerId)); +} + +BareId StringBarePeerId(const Utf8String &data) { + auto result = BareId(0xFF); for (const auto ch : data) { result *= 239; result += ch; @@ -98,22 +94,8 @@ int DomainApplicationId(const Utf8String &data) { return 0x1000 + StringBarePeerId(data); } -bool IsChatPeerId(PeerId peerId) { - return (peerId & kChatPeerIdShift) == kChatPeerIdShift; -} - -bool IsUserPeerId(PeerId peerId) { - return (peerId & kUserPeerIdShift) == kUserPeerIdShift; -} - PeerId ParsePeerId(const MTPPeer &data) { - return data.match([](const MTPDpeerUser &data) { - return UserPeerId(data.vuser_id().v); - }, [](const MTPDpeerChat &data) { - return ChatPeerId(data.vchat_id().v); - }, [](const MTPDpeerChannel &data) { - return ChatPeerId(data.vchannel_id().v); - }); + return peerFromMTP(data); } Utf8String ParseString(const MTPstring &data) { @@ -516,7 +498,7 @@ Venue ParseVenue(const MTPDmessageMediaVenue &data) { return result; } -Game ParseGame(const MTPGame &data, int32 botId) { +Game ParseGame(const MTPGame &data, UserId botId) { return data.match([&](const MTPDgame &data) { auto result = Game(); result.id = data.vid().v; @@ -687,16 +669,20 @@ ContactInfo ParseContactInfo(const MTPUser &data) { int ContactColorIndex(const ContactInfo &data) { if (data.userId != 0) { - return PeerColorIndex(data.userId); + return PeerColorIndex(data.userId.bare); } return PeerColorIndex(StringBarePeerId(data.phoneNumber)); } +PeerId User::id() const { + return UserId(bareId); +} + User ParseUser(const MTPUser &data) { auto result = User(); result.info = ParseContactInfo(data); data.match([&](const MTPDuser &data) { - result.id = data.vid().v; + result.bareId = data.vid().v; if (const auto username = data.vusername()) { result.username = ParseString(*username); } @@ -715,8 +701,8 @@ User ParseUser(const MTPUser &data) { return result; } -std::map ParseUsersList(const MTPVector &data) { - auto result = std::map(); +std::map ParseUsersList(const MTPVector &data) { + auto result = std::map(); for (const auto &user : data.v) { auto parsed = ParseUser(user); result.emplace(parsed.info.userId, std::move(parsed)); @@ -724,12 +710,18 @@ std::map ParseUsersList(const MTPVector &data) { return result; } +PeerId Chat::id() const { + return (isBroadcast || isSupergroup) + ? PeerId(ChannelId(bareId)) + : ChatId(bareId); +} + Chat ParseChat(const MTPChat &data) { auto result = Chat(); data.match([&](const MTPDchat &data) { - result.id = data.vid().v; + result.bareId = data.vid().v; result.title = ParseString(data.vtitle()); - result.input = MTP_inputPeerChat(MTP_int(result.id)); + result.input = MTP_inputPeerChat(MTP_int(result.bareId)); // #TODO ids if (const auto migratedTo = data.vmigrated_to()) { result.migratedToChannelId = migratedTo->match( [](const MTPDinputChannel &data) { @@ -737,14 +729,14 @@ Chat ParseChat(const MTPChat &data) { }, [](auto&&) { return 0; }); } }, [&](const MTPDchatEmpty &data) { - result.id = data.vid().v; - result.input = MTP_inputPeerChat(MTP_int(result.id)); + result.bareId = data.vid().v; + result.input = MTP_inputPeerChat(MTP_int(result.bareId)); // #TODO ids }, [&](const MTPDchatForbidden &data) { - result.id = data.vid().v; + result.bareId = data.vid().v; result.title = ParseString(data.vtitle()); - result.input = MTP_inputPeerChat(MTP_int(result.id)); + result.input = MTP_inputPeerChat(MTP_int(result.bareId)); // #TODO ids }, [&](const MTPDchannel &data) { - result.id = data.vid().v; + result.bareId = data.vid().v; result.isBroadcast = data.is_broadcast(); result.isSupergroup = data.is_megagroup(); result.title = ParseString(data.vtitle()); @@ -752,25 +744,25 @@ Chat ParseChat(const MTPChat &data) { result.username = ParseString(*username); } result.input = MTP_inputPeerChannel( - MTP_int(result.id), + MTP_int(result.bareId), // #TODO ids MTP_long(data.vaccess_hash().value_or_empty())); }, [&](const MTPDchannelForbidden &data) { - result.id = data.vid().v; + result.bareId = data.vid().v; result.isBroadcast = data.is_broadcast(); result.isSupergroup = data.is_megagroup(); result.title = ParseString(data.vtitle()); - result.input = MTP_inputPeerChannel( - MTP_int(result.id), + result.input = MTP_inputPeerChannel( // #TODO ids + MTP_int(result.bareId), data.vaccess_hash()); }); return result; } -std::map ParseChatsList(const MTPVector &data) { - auto result = std::map(); +std::map ParseChatsList(const MTPVector &data) { + auto result = std::map(); for (const auto &chat : data.v) { auto parsed = ParseChat(chat); - result.emplace(parsed.id, std::move(parsed)); + result.emplace(parsed.id(), std::move(parsed)); } return result; } @@ -799,9 +791,9 @@ const Chat *Peer::chat() const { PeerId Peer::id() const { if (const auto user = this->user()) { - return UserPeerId(user->info.userId); + return peerFromUser(user->info.userId); } else if (const auto chat = this->chat()) { - return ChatPeerId(chat->id); + return chat->id(); } Unexpected("Variant in Peer::id."); } @@ -835,29 +827,31 @@ std::map ParsePeersLists( for (const auto &user : users.v) { auto parsed = ParseUser(user); result.emplace( - UserPeerId(parsed.info.userId), + PeerId(parsed.info.userId), Peer{ std::move(parsed) }); } for (const auto &chat : chats.v) { auto parsed = ParseChat(chat); - result.emplace(ChatPeerId(parsed.id), Peer{ std::move(parsed) }); + result.emplace(parsed.id(), Peer{ std::move(parsed) }); } return result; } -User EmptyUser(int32 userId) { - return ParseUser(MTP_userEmpty(MTP_int(userId))); +User EmptyUser(UserId userId) { + return ParseUser(MTP_userEmpty(MTP_int(userId.bare))); // #TODO ids } -Chat EmptyChat(int32 chatId) { - return ParseChat(MTP_chatEmpty(MTP_int(chatId))); +Chat EmptyChat(ChatId chatId) { + return ParseChat(MTP_chatEmpty(MTP_int(chatId.bare))); // #TODO ids } Peer EmptyPeer(PeerId peerId) { - if (IsUserPeerId(peerId)) { - return Peer{ EmptyUser(BarePeerId(peerId)) }; - } else if (IsChatPeerId(peerId)) { - return Peer{ EmptyChat(BarePeerId(peerId)) }; + if (peerIsUser(peerId)) { + return Peer{ EmptyUser(peerToUser(peerId)) }; + } else if (peerIsChat(peerId)) { + return Peer{ EmptyChat(peerToChat(peerId)) }; + } else if (peerIsChannel(peerId)) { + return Peer{ EmptyChat(peerToChat(peerId)) }; } Unexpected("PeerId in EmptyPeer."); } @@ -1129,22 +1123,28 @@ ServiceAction ParseServiceAction( } result.content = content; }, [&](const MTPDmessageActionSetMessagesTTL &data) { - // #TODO ttl + result.content = ActionSetMessagesTTL{ + .period = data.vperiod().v, + }; + }, [&](const MTPDmessageActionGroupCallScheduled &data) { + result.content = ActionGroupCallScheduled{ + .date = data.vschedule_date().v, + }; }, [](const MTPDmessageActionEmpty &data) {}); return result; } File &Message::file() { - const auto service = &action.content; - if (const auto photo = std::get_if(service)) { + const auto content = &action.content; + if (const auto photo = std::get_if(content)) { return photo->photo.image.file; } return media.file(); } const File &Message::file() const { - const auto service = &action.content; - if (const auto photo = std::get_if(service)) { + const auto content = &action.content; + if (const auto photo = std::get_if(content)) { return photo->photo.image.file; } return media.file(); @@ -1171,9 +1171,6 @@ Message ParseMessage( result.selfId = context.selfPeerId; result.peerId = ParsePeerId(data.vpeer_id()); const auto fromId = data.vfrom_id(); - if (IsChatPeerId(result.peerId)) { - result.chatId = BarePeerId(result.peerId); - } if (fromId) { result.fromId = ParsePeerId(*fromId); } else { @@ -1241,11 +1238,11 @@ Message ParseMessage( result.viaBotId = viaBotId->v; } if (const auto media = data.vmedia()) { - context.botId = (result.viaBotId + context.botId = result.viaBotId ? result.viaBotId - : IsUserPeerId(result.forwardedFromId) - ? BarePeerId(result.forwardedFromId) - : result.fromId); + : peerIsUser(result.forwardedFromId) + ? peerToUser(result.forwardedFromId) + : peerToUser(result.fromId); result.media = ParseMedia( context, *media, @@ -1272,16 +1269,17 @@ Message ParseMessage( return result; } -std::map ParseMessagesList( +std::map ParseMessagesList( PeerId selfId, const MTPVector &data, const QString &mediaFolder) { auto context = ParseMediaContext{ .selfPeerId = selfId }; - auto result = std::map(); + auto result = std::map(); for (const auto &message : data.v) { auto parsed = ParseMessage(context, message, mediaFolder); - const auto shift = uint64(uint32(parsed.chatId)) << 32; - result.emplace(shift | uint32(parsed.id), std::move(parsed)); + result.emplace( + MessageId{ peerToChannel(parsed.peerId), parsed.id }, + std::move(parsed)); } return result; } @@ -1433,7 +1431,7 @@ SessionsList ParseSessionsList(const MTPaccount_Authorizations &data) { WebSession ParseWebSession( const MTPWebAuthorization &data, - const std::map &users) { + const std::map &users) { return data.match([&](const MTPDwebAuthorization &data) { auto result = WebSession(); const auto i = users.find(data.vbot_id().v); @@ -1549,11 +1547,10 @@ DialogsInfo ParseDialogsInfo(const MTPmessages_Dialogs &data) { : 0; } info.topMessageId = fields.vtop_message().v; - const auto shift = IsChatPeerId(info.peerId) - ? (uint64(uint32(BarePeerId(info.peerId))) << 32) - : 0; - const auto messageIt = messages.find( - shift | uint32(info.topMessageId)); + const auto messageIt = messages.find(MessageId{ + peerToChannel(info.peerId), + info.topMessageId, + }); if (messageIt != end(messages)) { const auto &message = messageIt->second; info.topMessageDate = message.date; @@ -1569,7 +1566,7 @@ DialogInfo DialogInfoFromUser(const User &data) { result.input = (Peer{ data }).input(); result.name = data.info.firstName; result.lastName = data.info.lastName; - result.peerId = UserPeerId(data.info.userId); + result.peerId = data.id(); result.topMessageDate = 0; result.topMessageId = 0; result.type = DialogTypeFromUser(data); @@ -1581,7 +1578,7 @@ DialogInfo DialogInfoFromChat(const Chat &data) { auto result = DialogInfo(); result.input = data.input; result.name = data.title; - result.peerId = ChatPeerId(data.id); + result.peerId = data.id(); result.topMessageDate = 0; result.topMessageId = 0; result.type = DialogTypeFromChat(data); @@ -1607,17 +1604,17 @@ DialogsInfo ParseDialogsInfo( const MTPVector &data) { const auto singleId = singlePeer.match( [](const MTPDinputPeerUser &data) { - return data.vuser_id().v; + return UserId(data.vuser_id()); }, [](const MTPDinputPeerSelf &data) { - return 0; - }, [](const auto &data) -> int { + return UserId(); + }, [](const auto &data) -> UserId { Unexpected("Single peer type in ParseDialogsInfo(users)."); }); auto result = DialogsInfo(); result.chats.reserve(data.v.size()); for (const auto &single : data.v) { const auto userId = single.match([&](const auto &data) { - return data.vid().v; + return peerFromUser(data.vid()); }); if (userId != singleId && (singleId != 0 @@ -1636,20 +1633,24 @@ DialogsInfo ParseDialogsInfo( const MTPmessages_Chats &data) { const auto singleId = singlePeer.match( [](const MTPDinputPeerChat &data) { - return data.vchat_id().v; + return peerFromChat(data.vchat_id()); }, [](const MTPDinputPeerChannel &data) { - return data.vchannel_id().v; - }, [](const auto &data) -> int { + return peerFromChannel(data.vchannel_id()); + }, [](const auto &data) -> PeerId { Unexpected("Single peer type in ParseDialogsInfo(chats)."); }); auto result = DialogsInfo(); data.match([&](const auto &data) { //MTPDmessages_chats &data) { result.chats.reserve(data.vchats().v.size()); for (const auto &single : data.vchats().v) { - const auto chatId = single.match([&](const auto &data) { - return data.vid().v; + const auto peerId = single.match([](const MTPDchannel &data) { + return peerFromChannel(data.vid()); + }, [](const MTPDchannelForbidden &data) { + return peerFromChannel(data.vid()); + }, [](const auto &data) { + return peerFromChat(data.vid()); }); - if (chatId != singleId) { + if (peerId != singleId) { continue; } const auto chat = ParseChat(single); @@ -1671,8 +1672,8 @@ bool AddMigrateFromSlice( const auto good = to.migratedFromInput.match([]( const MTPDinputPeerEmpty &) { return true; - }, [&](const MTPDinputPeerChat & data) { - return (ChatPeerId(data.vchat_id().v) == from.peerId); + }, [&](const MTPDinputPeerChat &data) { + return (peerFromChat(data.vchat_id()) == from.peerId); }, [](auto&&) { return false; }); if (!good) { return false; @@ -1820,7 +1821,7 @@ Utf8String FormatDateTime( ).toUtf8(); } -Utf8String FormatMoneyAmount(uint64 amount, const Utf8String ¤cy) { +Utf8String FormatMoneyAmount(int64 amount, const Utf8String ¤cy) { return Ui::FillAmountAndCurrency( amount, QString::fromUtf8(currency)).toUtf8(); diff --git a/Telegram/SourceFiles/export/data/export_data_types.h b/Telegram/SourceFiles/export/data/export_data_types.h index ff169de80..575393507 100644 --- a/Telegram/SourceFiles/export/data/export_data_types.h +++ b/Telegram/SourceFiles/export/data/export_data_types.h @@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "scheme.h" #include "base/optional.h" #include "base/variant.h" +#include "data/data_peer_id.h" #include #include @@ -22,14 +23,10 @@ struct Settings; namespace Data { using Utf8String = QByteArray; -using PeerId = uint64; -PeerId UserPeerId(int32 userId); -PeerId ChatPeerId(int32 chatId); -int32 BarePeerId(PeerId peerId); -bool IsChatPeerId(PeerId peerId); -bool IsUserPeerId(PeerId peerId); -int PeerColorIndex(int32 bareId); +int PeerColorIndex(BareId bareId); +BareId PeerToBareId(PeerId peerId); +int PeerColorIndex(PeerId peerId); int ApplicationColorIndex(int applicationId); int DomainApplicationId(const Utf8String &data); @@ -102,7 +99,7 @@ QString WriteImageThumb( const QString &postfix = "_thumb"); struct ContactInfo { - int32 userId = 0; + UserId userId = 0; Utf8String firstName; Utf8String lastName; Utf8String phoneNumber; @@ -169,7 +166,7 @@ struct Game { Utf8String title; Utf8String description; - int32 botId = 0; + UserId botId = 0; }; struct Invoice { @@ -204,9 +201,11 @@ UserpicsSlice ParseUserpicsSlice( int baseIndex); struct User { + PeerId id() const; + + BareId bareId = 0; ContactInfo info; Utf8String username; - int32 id; bool isBot = false; bool isSelf = false; bool isReplies = false; @@ -217,11 +216,13 @@ struct User { }; User ParseUser(const MTPUser &data); -std::map ParseUsersList(const MTPVector &data); +std::map ParseUsersList(const MTPVector &data); struct Chat { - int32 id = 0; - int32 migratedToChannelId = 0; + PeerId id() const; + + BareId bareId = 0; + ChannelId migratedToChannelId = 0; Utf8String title; Utf8String username; bool isBroadcast = false; @@ -231,7 +232,7 @@ struct Chat { }; Chat ParseChat(const MTPChat &data); -std::map ParseChatsList(const MTPVector &data); +std::map ParseChatsList(const MTPVector &data); struct Peer { PeerId id() const; @@ -336,7 +337,7 @@ struct ParseMediaContext { int videos = 0; int files = 0; int contacts = 0; - int32 botId = 0; + UserId botId = 0; }; Media ParseMedia( @@ -347,7 +348,7 @@ Media ParseMedia( struct ActionChatCreate { Utf8String title; - std::vector userIds; + std::vector userIds; }; struct ActionChatEditTitle { @@ -362,15 +363,15 @@ struct ActionChatDeletePhoto { }; struct ActionChatAddUser { - std::vector userIds; + std::vector userIds; }; struct ActionChatDeleteUser { - int32 userId = 0; + UserId userId = 0; }; struct ActionChatJoinedByLink { - int32 inviterId = 0; + UserId inviterId = 0; }; struct ActionChannelCreate { @@ -378,12 +379,12 @@ struct ActionChannelCreate { }; struct ActionChatMigrateTo { - int32 channelId = 0; + ChannelId channelId = 0; }; struct ActionChannelMigrateFrom { Utf8String title; - int32 chatId = 0; + ChatId chatId = 0; }; struct ActionPinMessage { @@ -463,7 +464,15 @@ struct ActionGroupCall { }; struct ActionInviteToGroupCall { - std::vector userIds; + std::vector userIds; +}; + +struct ActionSetMessagesTTL { + TimeId period = 0; +}; + +struct ActionGroupCallScheduled { + TimeId date = 0; }; struct ServiceAction { @@ -492,7 +501,9 @@ struct ServiceAction { ActionPhoneNumberRequest, ActionGeoProximityReached, ActionGroupCall, - ActionInviteToGroupCall> content; + ActionInviteToGroupCall, + ActionSetMessagesTTL, + ActionGroupCallScheduled> content; }; ServiceAction ParseServiceAction( @@ -527,9 +538,33 @@ struct TextPart { Utf8String additional; }; +struct MessageId { + ChannelId channelId; + int32 msgId = 0; +}; + +inline bool operator==(MessageId a, MessageId b) { + return (a.channelId == b.channelId) && (a.msgId == b.msgId); +} +inline bool operator!=(MessageId a, MessageId b) { + return !(a == b); +} +inline bool operator<(MessageId a, MessageId b) { + return (a.channelId < b.channelId) + || (a.channelId == b.channelId && a.msgId < b.msgId); +} +inline bool operator>(MessageId a, MessageId b) { + return (b < a); +} +inline bool operator<=(MessageId a, MessageId b) { + return !(b < a); +} +inline bool operator>=(MessageId a, MessageId b) { + return !(a < b); +} + struct Message { int32 id = 0; - int32 chatId = 0; TimeId date = 0; TimeId edited = 0; PeerId fromId = 0; @@ -542,7 +577,7 @@ struct Message { bool showForwardedAsOriginal = false; PeerId savedFromChatId = 0; Utf8String signature; - int32 viaBotId = 0; + UserId viaBotId = 0; int32 replyToMsgId = 0; PeerId replyToPeerId = 0; std::vector text; @@ -566,7 +601,7 @@ Message ParseMessage( ParseMediaContext &context, const MTPMessage &data, const QString &mediaFolder); -std::map ParseMessagesList( +std::map ParseMessagesList( PeerId selfId, const MTPVector &data, const QString &mediaFolder); @@ -594,7 +629,7 @@ struct DialogInfo { PeerId peerId = 0; MTPInputPeer migratedFromInput = MTP_inputPeerEmpty(); - int32 migratedToChannelId = 0; + ChannelId migratedToChannelId = 0; // User messages splits which contained that dialog. std::vector splits; @@ -660,7 +695,7 @@ Utf8String FormatDateTime( QChar dateSeparator = QChar('.'), QChar timeSeparator = QChar(':'), QChar separator = QChar(' ')); -Utf8String FormatMoneyAmount(uint64 amount, const Utf8String ¤cy); +Utf8String FormatMoneyAmount(int64 amount, const Utf8String ¤cy); Utf8String FormatFileSize(int64 size); Utf8String FormatDuration(int64 seconds); diff --git a/Telegram/SourceFiles/export/export_api_wrap.cpp b/Telegram/SourceFiles/export/export_api_wrap.cpp index 1a89a9568..cf7c7a45e 100644 --- a/Telegram/SourceFiles/export/export_api_wrap.cpp +++ b/Telegram/SourceFiles/export/export_api_wrap.cpp @@ -49,25 +49,13 @@ std::tuple value_ordering_helper(const LocationK LocationKey ComputeLocationKey(const Data::FileLocation &value) { auto result = LocationKey(); result.type = value.dcId; - value.data.match([&](const MTPDinputFileLocation &data) { - result.type |= (1ULL << 24); - result.type |= (uint64(uint32(data.vlocal_id().v)) << 32); - result.id = data.vvolume_id().v; - }, [&](const MTPDinputDocumentFileLocation &data) { + value.data.match([&](const MTPDinputDocumentFileLocation &data) { const auto letter = data.vthumb_size().v.isEmpty() ? char(0) : data.vthumb_size().v[0]; result.type |= (2ULL << 24); result.type |= (uint64(uint32(letter)) << 16); result.id = data.vid().v; - }, [&](const MTPDinputSecureFileLocation &data) { - result.type |= (3ULL << 24); - result.id = data.vid().v; - }, [&](const MTPDinputEncryptedFileLocation &data) { - result.type |= (4ULL << 24); - result.id = data.vid().v; - }, [&](const MTPDinputTakeoutFileLocation &data) { - result.type |= (5ULL << 24); }, [&](const MTPDinputPhotoFileLocation &data) { const auto letter = data.vthumb_size().v.isEmpty() ? char(0) @@ -75,22 +63,10 @@ LocationKey ComputeLocationKey(const Data::FileLocation &value) { result.type |= (6ULL << 24); result.type |= (uint64(uint32(letter)) << 16); result.id = data.vid().v; - }, [&](const MTPDinputPeerPhotoFileLocation &data) { - const auto letter = data.is_big() ? char(1) : char(0); - result.type |= (7ULL << 24); - result.type |= (uint64(uint32(data.vlocal_id().v)) << 32); - result.type |= (uint64(uint32(letter)) << 16); - result.id = data.vvolume_id().v; - }, [&](const MTPDinputStickerSetThumb &data) { - result.type |= (8ULL << 24); - result.type |= (uint64(uint32(data.vlocal_id().v)) << 32); - result.id = data.vvolume_id().v; - }, [&](const MTPDinputPhotoLegacyFileLocation &data) { - result.type |= (9ULL << 24); - result.type |= (uint64(uint32(data.vlocal_id().v)) << 32); - result.id = data.vvolume_id().v; - }, [&](const MTPDinputGroupCallStream &data) { - result.type = (10ULL << 24); + }, [&](const MTPDinputTakeoutFileLocation &data) { + result.type |= (5ULL << 24); + }, [](const auto &data) { + Unexpected("File location type in Export::ComputeLocationKey."); }); return result; } @@ -208,7 +184,7 @@ struct ApiWrap::ChatsProcess { Data::DialogsInfo info; int processedCount = 0; - std::map indexByPeer; + std::map indexByPeer; }; struct ApiWrap::LeftChannelsProcess : ChatsProcess { @@ -675,7 +651,7 @@ void ApiWrap::startMainSession(FnMut done) { for (const auto &user : result.v) { user.match([&](const MTPDuser &data) { if (data.is_self()) { - _selfId = data.vid().v; + _selfId.emplace(data.vid()); } }, [&](const MTPDuserEmpty&) { }); @@ -974,7 +950,7 @@ void ApiWrap::requestMessages( Expects(_selfId.has_value()); _chatProcess = std::make_unique(); - _chatProcess->context.selfPeerId = Data::UserPeerId(*_selfId); + _chatProcess->context.selfPeerId = peerFromUser(*_selfId); _chatProcess->info = info; _chatProcess->start = std::move(start); _chatProcess->fileProgress = std::move(progress); @@ -1366,7 +1342,7 @@ void ApiWrap::appendChatsSlice( for (auto &info : filtered) { const auto nextIndex = to.size(); if (info.migratedToChannelId) { - const auto toPeerId = Data::ChatPeerId(info.migratedToChannelId); + const auto toPeerId = PeerId(info.migratedToChannelId); const auto i = process.indexByPeer.find(toPeerId); if (i != process.indexByPeer.end() && Data::AddMigrateFromSlice( @@ -1939,7 +1915,7 @@ void ApiWrap::filePartExtractReference( Expects(_selfId.has_value()); auto context = Data::ParseMediaContext(); - context.selfPeerId = Data::UserPeerId(*_selfId); + context.selfPeerId = peerFromUser(*_selfId); const auto messages = Data::ParseMessagesSlice( context, data.vmessages(), diff --git a/Telegram/SourceFiles/export/export_api_wrap.h b/Telegram/SourceFiles/export/export_api_wrap.h index 7310e7a5b..5f449951e 100644 --- a/Telegram/SourceFiles/export/export_api_wrap.h +++ b/Telegram/SourceFiles/export/export_api_wrap.h @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #pragma once #include "mtproto/mtproto_concurrent_sender.h" +#include "data/data_peer_id.h" namespace Export { namespace Data { @@ -211,7 +212,7 @@ private: MTP::ConcurrentSender _mtp; std::optional _takeoutId; - std::optional _selfId; + std::optional _selfId; Output::Stats *_stats = nullptr; std::unique_ptr _settings; diff --git a/Telegram/SourceFiles/export/output/export_output_abstract.cpp b/Telegram/SourceFiles/export/output/export_output_abstract.cpp index 258d4d3e8..b02c0e715 100644 --- a/Telegram/SourceFiles/export/output/export_output_abstract.cpp +++ b/Telegram/SourceFiles/export/output/export_output_abstract.cpp @@ -131,7 +131,7 @@ Stats AbstractWriter::produceTestExample( topUser.rating = 0.5; auto topChat = Data::TopPeer(); auto chat = Data::Chat(); - chat.id = counter(); + chat.bareId = counter(); chat.title = "Group chat"; auto peerChat = Data::Peer{ chat }; topChat.peer = peerChat; @@ -148,7 +148,7 @@ Stats AbstractWriter::produceTestExample( topBot.peer = peerBot; topBot.rating = 0.125; - auto peers = std::map(); + auto peers = std::map(); peers.emplace(peerUser.id(), peerUser); peers.emplace(peerBot.id(), peerBot); peers.emplace(peerChat.id(), peerChat); @@ -197,7 +197,7 @@ Stats AbstractWriter::produceTestExample( message.edited = date(); static auto count = 0; if (++count % 3 == 0) { - message.forwardedFromId = Data::UserPeerId(user.info.userId); + message.forwardedFromId = peerFromUser(user.info.userId); message.forwardedDate = date(); } else if (count % 3 == 2) { message.forwardedFromName = "Test hidden forward"; @@ -357,14 +357,14 @@ Stats AbstractWriter::produceTestExample( sliceChat1.list.push_back([&] { auto message = serviceMessage(); auto action = Data::ActionChatMigrateTo(); - action.channelId = chat.id; + action.channelId = ChannelId(chat.bareId); message.action.content = action; return message; }()); sliceChat1.list.push_back([&] { auto message = serviceMessage(); auto action = Data::ActionChannelMigrateFrom(); - action.chatId = chat.id; + action.chatId = ChatId(chat.bareId); action.title = "Supergroup now"; message.action.content = action; return message; diff --git a/Telegram/SourceFiles/export/output/export_output_html.cpp b/Telegram/SourceFiles/export/output/export_output_html.cpp index d112d9da0..f1b07d8d2 100644 --- a/Telegram/SourceFiles/export/output/export_output_html.cpp +++ b/Telegram/SourceFiles/export/output/export_output_html.cpp @@ -340,7 +340,6 @@ struct UserpicData { class PeersMap { public: - using PeerId = Data::PeerId; using Peer = Data::Peer; using User = Data::User; using Chat = Data::Chat; @@ -348,15 +347,14 @@ public: PeersMap(const std::map &data); const Peer &peer(PeerId peerId) const; - const User &user(int32 userId) const; - const Chat &chat(int32 chatId) const; + const User &user(UserId userId) const; QByteArray wrapPeerName(PeerId peerId) const; - QByteArray wrapUserName(int32 userId) const; - QByteArray wrapUserNames(const std::vector &data) const; + QByteArray wrapUserName(UserId userId) const; + QByteArray wrapUserNames(const std::vector &data) const; private: - const std::map &_data; + const std::map &_data; }; @@ -380,22 +378,14 @@ auto PeersMap::peer(PeerId peerId) const -> const Peer & { return empty; } -auto PeersMap::user(int32 userId) const -> const User & { - if (const auto result = peer(Data::UserPeerId(userId)).user()) { +auto PeersMap::user(UserId userId) const -> const User & { + if (const auto result = peer(peerFromUser(userId)).user()) { return *result; } static auto empty = User(); return empty; } -auto PeersMap::chat(int32 chatId) const -> const Chat & { - if (const auto result = peer(Data::ChatPeerId(chatId)).chat()) { - return *result; - } - static auto empty = Chat(); - return empty; -} - QByteArray PeersMap::wrapPeerName(PeerId peerId) const { const auto result = peer(peerId).name(); return result.isEmpty() @@ -403,14 +393,14 @@ QByteArray PeersMap::wrapPeerName(PeerId peerId) const { : SerializeString(result); } -QByteArray PeersMap::wrapUserName(int32 userId) const { +QByteArray PeersMap::wrapUserName(UserId userId) const { const auto result = user(userId).name(); return result.isEmpty() ? QByteArray("Deleted Account") : SerializeString(result); } -QByteArray PeersMap::wrapUserNames(const std::vector &data) const { +QByteArray PeersMap::wrapUserNames(const std::vector &data) const { auto list = std::vector(); for (const auto userId : data) { list.push_back(wrapUserName(userId)); @@ -469,12 +459,12 @@ struct HtmlWriter::MessageInfo { Service, Default, }; - int32 id = 0; + int id = 0; Type type = Type::Service; - Data::PeerId fromId = 0; - int32 viaBotId = 0; + PeerId fromId = 0; + UserId viaBotId = 0; TimeId date = 0; - Data::PeerId forwardedFromId = 0; + PeerId forwardedFromId = 0; QString forwardedFromName; bool forwarded = false; bool showForwardedAsOriginal = false; @@ -895,8 +885,7 @@ QByteArray HtmlWriter::Wrap::pushServiceMessage( result.append(popTag()); if (photo) { auto userpic = UserpicData(); - userpic.colorIndex = Data::PeerColorIndex( - Data::BarePeerId(dialog.peerId)); + userpic.colorIndex = Data::PeerColorIndex(dialog.peerId); userpic.firstName = dialog.name; userpic.lastName = dialog.lastName; userpic.pixelSize = kServiceMessagePhotoSize; @@ -1082,15 +1071,37 @@ auto HtmlWriter::Wrap::pushMessage( }, [&](const ActionPhoneNumberRequest &data) { return serviceFrom + " requested your phone number"; }, [&](const ActionGroupCall &data) { - return "Group call" - + (data.duration - ? (" (" + QString::number(data.duration) + " seconds)") - : QString()).toUtf8(); + const auto durationText = (data.duration + ? (" (" + QString::number(data.duration) + " seconds)") + : QString()).toUtf8(); + return isChannel + ? ("Voice chat" + durationText) + : (serviceFrom + " started voice chat" + durationText); }, [&](const ActionInviteToGroupCall &data) { return serviceFrom + " invited " + peers.wrapUserNames(data.userIds) + " to the voice chat"; + }, [&](const ActionSetMessagesTTL &data) { + const auto periodText = (data.period == 7 * 86400) + ? "7 days" + : (data.period == 86400) + ? "24 hours" + : QByteArray(); + return isChannel + ? (data.period + ? "New messages will auto-delete in " + periodText + : "New messages will not auto-delete") + : (data.period + ? (serviceFrom + + " has set messages to auto-delete in " + periodText) + : (serviceFrom + + " has set messages not to auto-delete")); + }, [&](const ActionGroupCallScheduled &data) { + const auto dateText = FormatDateTime(data.date); + return isChannel + ? "Voice chat scheduled for " + dateText + : (serviceFrom + " scheduled a voice chat for " + dateText); }, [](v::null_t) { return QByteArray(); }); if (!serviceText.isEmpty()) { @@ -1114,7 +1125,7 @@ auto HtmlWriter::Wrap::pushMessage( auto forwardedUserpic = UserpicData(); if (message.forwarded) { forwardedUserpic.colorIndex = message.forwardedFromId - ? PeerColorIndex(BarePeerId(message.forwardedFromId)) + ? PeerColorIndex(message.forwardedFromId) : PeerColorIndex(message.id); forwardedUserpic.pixelSize = kHistoryUserpicSize; if (message.forwardedFromId) { @@ -1129,7 +1140,7 @@ auto HtmlWriter::Wrap::pushMessage( if (message.showForwardedAsOriginal) { userpic = forwardedUserpic; } else { - userpic.colorIndex = PeerColorIndex(BarePeerId(fromPeerId)); + userpic.colorIndex = PeerColorIndex(fromPeerId); userpic.pixelSize = kHistoryUserpicSize; FillUserpicNames(userpic, peers.peer(fromPeerId)); } @@ -1783,7 +1794,7 @@ bool HtmlWriter::Wrap::forwardedNeedsWrap( } else if (!message.forwardedFromId || message.forwardedFromId != previous->forwardedFromId) { return true; - } else if (Data::IsChatPeerId(message.forwardedFromId)) { + } else if (!peerIsUser(message.forwardedFromId)) { return true; } else if (abs(message.forwardedDate - previous->forwardedDate) > kJoinWithinSeconds) { @@ -2181,7 +2192,7 @@ Result HtmlWriter::writeFrequentContacts(const Data::ContactsList &data) { return {}; }(); auto userpic = UserpicData{ - Data::PeerColorIndex(Data::BarePeerId(top.peer.id())), + Data::PeerColorIndex(top.peer.id()), kEntryUserpicSize }; userpic.firstName = name; @@ -2536,7 +2547,7 @@ Result HtmlWriter::writeDialogEnd() { auto userpic = UserpicData{ ((_dialog.type == Type::Self || _dialog.type == Type::Replies) ? kSavedMessagesColorIndex - : Data::PeerColorIndex(Data::BarePeerId(_dialog.peerId))), + : Data::PeerColorIndex(_dialog.peerId)), kEntryUserpicSize }; userpic.firstName = NameString(_dialog); diff --git a/Telegram/SourceFiles/export/output/export_output_json.cpp b/Telegram/SourceFiles/export/output/export_output_json.cpp index c0e9390ed..d9ac09164 100644 --- a/Telegram/SourceFiles/export/output/export_output_json.cpp +++ b/Telegram/SourceFiles/export/output/export_output_json.cpp @@ -217,7 +217,7 @@ QByteArray FormatFilePath(const Data::File &file) { QByteArray SerializeMessage( Context &context, const Data::Message &message, - const std::map &peers, + const std::map &peers, const QString &internalLinksDomain) { using namespace Data; @@ -235,20 +235,13 @@ QByteArray SerializeMessage( static auto empty = Peer{ User() }; return empty; }; - const auto user = [&](int32 userId) -> const User& { - if (const auto result = peer(UserPeerId(userId)).user()) { + const auto user = [&](UserId userId) -> const User& { + if (const auto result = peer(userId).user()) { return *result; } static auto empty = User(); return empty; }; - const auto chat = [&](int32 chatId) -> const Chat& { - if (const auto result = peer(ChatPeerId(chatId)).chat()) { - return *result; - } - static auto empty = Chat(); - return empty; - }; auto values = std::vector>{ { "id", NumberToString(message.id) }, @@ -280,6 +273,25 @@ QByteArray SerializeMessage( const auto push = [&](const QByteArray &key, const auto &value) { if constexpr (std::is_arithmetic_v>) { pushBare(key, Data::NumberToString(value)); + } else if constexpr (std::is_same_v< + std::decay_t, + PeerId>) { + if (const auto chat = peerToChat(value)) { + pushBare( + key, + SerializeString("chat" + + Data::NumberToString(chat.bare))); + } else if (const auto channel = peerToChannel(value)) { + pushBare( + key, + SerializeString("channel" + + Data::NumberToString(channel.bare))); + } else { + pushBare( + key, + SerializeString("user" + + Data::NumberToString(peerToUser(value).bare))); + } } else { const auto wrapped = QByteArray(value); if (!wrapped.isEmpty()) { @@ -290,7 +302,7 @@ QByteArray SerializeMessage( const auto wrapPeerName = [&](PeerId peerId) { return StringAllowNull(peer(peerId).name()); }; - const auto wrapUserName = [&](int32 userId) { + const auto wrapUserName = [&](UserId userId) { return StringAllowNull(user(userId).name()); }; const auto pushFrom = [&](const QByteArray &label = "from") { @@ -309,7 +321,7 @@ QByteArray SerializeMessage( } }; const auto pushUserNames = [&]( - const std::vector &data, + const std::vector &data, const QByteArray &label = "members") { auto list = std::vector(); for (const auto userId : data) { @@ -497,6 +509,14 @@ QByteArray SerializeMessage( pushActor(); pushAction("invite_to_group_call"); pushUserNames(data.userIds); + }, [&](const ActionSetMessagesTTL &data) { + pushActor(); + pushAction("set_messages_ttl"); + push("period", data.period); + }, [&](const ActionGroupCallScheduled &data) { + pushActor(); + pushAction("group_call_scheduled"); + push("schedule_date", data.date); }, [](v::null_t) {}); if (v::is_null(message.action.content)) { @@ -708,7 +728,7 @@ Result JsonWriter::writePersonal(const Data::PersonalInfo &data) { return _output->writeBlock( prepareObjectItemStart("personal_information") + SerializeObject(_context, { - { "user_id", Data::NumberToString(data.user.id) }, + { "user_id", Data::NumberToString(data.user.bareId) }, { "first_name", SerializeString(info.firstName) }, { "last_name", SerializeString(info.lastName) }, { @@ -814,7 +834,12 @@ Result JsonWriter::writeSavedContacts(const Data::ContactsList &data) { })); } else { block.append(SerializeObject(_context, { - { "user_id", Data::NumberToString(contact.userId) }, + { + "user_id", + (contact.userId + ? Data::NumberToString(contact.userId.bare) + : QByteArray()) + }, { "first_name", SerializeString(contact.firstName) }, { "last_name", SerializeString(contact.lastName) }, { @@ -859,7 +884,7 @@ Result JsonWriter::writeFrequentContacts(const Data::ContactsList &data) { }(); block.append(prepareArrayItemStart()); block.append(SerializeObject(_context, { - { "id", Data::NumberToString(top.peer.id()) }, + { "id", Data::NumberToString(Data::PeerToBareId(top.peer.id())) }, { "category", SerializeString(category) }, { "type", SerializeString(type) }, { "name", StringAllowNull(top.peer.name()) }, @@ -1058,7 +1083,7 @@ Result JsonWriter::writeDialogStart(const Data::DialogInfo &data) { block.append(prepareObjectItemStart("type") + StringAllowNull(TypeString(data.type))); block.append(prepareObjectItemStart("id") - + Data::NumberToString(data.peerId)); + + Data::NumberToString(Data::PeerToBareId(data.peerId))); block.append(prepareObjectItemStart("messages")); block.append(pushNesting(Context::kArray)); return _output->writeBlock(block); diff --git a/Telegram/SourceFiles/facades.cpp b/Telegram/SourceFiles/facades.cpp index 551488bb9..631dadb1b 100644 --- a/Telegram/SourceFiles/facades.cpp +++ b/Telegram/SourceFiles/facades.cpp @@ -31,6 +31,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/history.h" #include "history/history_item.h" #include "history/view/media/history_view_media.h" +#include "payments/payments_checkout_process.h" #include "data/data_session.h" #include "styles/style_chat.h" @@ -121,7 +122,10 @@ void activateBotCommand( } break; case ButtonType::Buy: { - Ui::show(Box(tr::ktg_payments_not_supported(tr::now))); + Payments::CheckoutProcess::Start( + msg, + Payments::Mode::Payment, + crl::guard(App::wnd(), [] { App::wnd()->activate(); })); } break; case ButtonType::Url: { @@ -261,7 +265,7 @@ void showChatsList(not_null session) { if (const auto m = CheckMainWidget(session)) { m->ui_showPeerHistory( 0, - Window::SectionShow::Way::ClearStack, + ::Window::SectionShow::Way::ClearStack, 0); } } @@ -278,7 +282,7 @@ void showPeerHistory(not_null peer, MsgId msgId) { if (const auto m = CheckMainWidget(&peer->session())) { m->ui_showPeerHistory( peer->id, - Window::SectionShow::Way::ClearStack, + ::Window::SectionShow::Way::ClearStack, msgId); } } diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp index 53df93eac..66ed5d6e9 100644 --- a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp +++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp @@ -633,6 +633,9 @@ void InnerWidget::saveState(not_null memento) { memento->setAdminsCanEdit(std::move(_adminsCanEdit)); memento->setSearchQuery(std::move(_searchQuery)); if (!_filterChanged) { + for (auto &item : _items) { + item.clearView(); + } memento->setItems( base::take(_items), base::take(_eventIds), diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp b/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp index a8f982cf9..1dac7fa77 100644 --- a/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp +++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp @@ -350,7 +350,7 @@ auto GenerateParticipantString( auto peer = session->data().peer(participantId); auto name = TextWithEntities { peer->name }; if (const auto user = peer->asUser()) { - auto entityData = QString::number(user->id) + auto entityData = QString::number(user->id.value) + '.' + QString::number(user->accessHash()); name.entities.push_back({ @@ -480,7 +480,7 @@ OwnedItem &OwnedItem::operator=(OwnedItem &&other) { } OwnedItem::~OwnedItem() { - _view = nullptr; + clearView(); if (_data) { _data->destroy(); } @@ -491,6 +491,10 @@ void OwnedItem::refreshView( _view = _data->createView(delegate); } +void OwnedItem::clearView() { + _view = nullptr; +} + void GenerateItems( not_null delegate, not_null history, diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_item.h b/Telegram/SourceFiles/history/admin_log/history_admin_log_item.h index ceaa50bc3..c7a74b8fa 100644 --- a/Telegram/SourceFiles/history/admin_log/history_admin_log_item.h +++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_item.h @@ -37,17 +37,18 @@ public: OwnedItem &operator=(OwnedItem &&other); ~OwnedItem(); - HistoryView::Element *get() const { + [[nodiscard]] HistoryView::Element *get() const { return _view.get(); } - HistoryView::Element *operator->() const { + [[nodiscard]] HistoryView::Element *operator->() const { return get(); } - operator HistoryView::Element*() const { + [[nodiscard]] operator HistoryView::Element*() const { return get(); } void refreshView(not_null delegate); + void clearView(); private: HistoryItem *_data = nullptr; diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_section.cpp b/Telegram/SourceFiles/history/admin_log/history_admin_log_section.cpp index 57427a6ce..a1f0c7503 100644 --- a/Telegram/SourceFiles/history/admin_log/history_admin_log_section.cpp +++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_section.cpp @@ -321,7 +321,7 @@ not_null Widget::channel() const { Dialogs::RowDescriptor Widget::activeChat() const { return { channel()->owner().history(channel()), - FullMsgId(channel()->bareId(), ShowAtUnreadMsgId) + FullMsgId(peerToChannel(channel()->id), ShowAtUnreadMsgId) }; } diff --git a/Telegram/SourceFiles/history/history.cpp b/Telegram/SourceFiles/history/history.cpp index e92ec9d87..be952d364 100644 --- a/Telegram/SourceFiles/history/history.cpp +++ b/Telegram/SourceFiles/history/history.cpp @@ -42,6 +42,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "support/support_helper.h" #include "ui/image/image.h" #include "ui/text/text_options.h" +#include "ui/toasts/common_toasts.h" +#include "ui/text/text_utilities.h" +#include "payments/payments_checkout_process.h" #include "core/crash_reports.h" #include "core/application.h" #include "base/unixtime.h" @@ -856,14 +859,12 @@ void History::applyMessageChanges( void History::applyServiceChanges( not_null item, const MTPDmessageService &data) { - auto &action = data.vaction(); - switch (action.type()) { - case mtpc_messageActionChatAddUser: { - auto &d = action.c_messageActionChatAddUser(); + const auto replyTo = data.vreply_to(); + data.vaction().match([&](const MTPDmessageActionChatAddUser &data) { if (const auto megagroup = peer->asMegagroup()) { const auto mgInfo = megagroup->mgInfo.get(); Assert(mgInfo != nullptr); - for (const auto &userId : d.vusers().v) { + for (const auto &userId : data.vusers().v) { if (const auto user = owner().userLoaded(userId.v)) { if (!base::contains(mgInfo->lastParticipants, user)) { mgInfo->lastParticipants.push_front(user); @@ -874,21 +875,19 @@ void History::applyServiceChanges( } if (user->isBot()) { peer->asChannel()->mgInfo->bots.insert(user); - if (peer->asChannel()->mgInfo->botStatus != 0 && peer->asChannel()->mgInfo->botStatus < 2) { + if (peer->asChannel()->mgInfo->botStatus != 0 + && peer->asChannel()->mgInfo->botStatus < 2) { peer->asChannel()->mgInfo->botStatus = 2; } } } } } - } break; - - case mtpc_messageActionChatJoinedByLink: { - auto &d = action.c_messageActionChatJoinedByLink(); - if (auto megagroup = peer->asMegagroup()) { - auto mgInfo = megagroup->mgInfo.get(); + }, [&](const MTPDmessageActionChatJoinedByLink &data) { + if (const auto megagroup = peer->asMegagroup()) { + const auto mgInfo = megagroup->mgInfo.get(); Assert(mgInfo != nullptr); - if (auto user = item->from()->asUser()) { + if (const auto user = item->from()->asUser()) { if (!base::contains(mgInfo->lastParticipants, user)) { mgInfo->lastParticipants.push_front(user); session().changes().peerUpdated( @@ -904,25 +903,20 @@ void History::applyServiceChanges( } } } - } break; - - case mtpc_messageActionChatDeletePhoto: { + }, [&](const MTPDmessageActionChatDeletePhoto &data) { if (const auto chat = peer->asChat()) { chat->setPhoto(MTP_chatPhotoEmpty()); } - } break; - - case mtpc_messageActionChatDeleteUser: { - auto &d = action.c_messageActionChatDeleteUser(); - auto uid = d.vuser_id().v; + }, [&](const MTPDmessageActionChatDeleteUser &data) { + const auto uid = data.vuser_id().v; if (lastKeyboardFrom == peerFromUser(uid)) { clearLastKeyboard(); } - if (auto megagroup = peer->asMegagroup()) { - if (auto user = owner().userLoaded(uid)) { - auto mgInfo = megagroup->mgInfo.get(); + if (const auto megagroup = peer->asMegagroup()) { + if (const auto user = owner().userLoaded(uid)) { + const auto mgInfo = megagroup->mgInfo.get(); Assert(mgInfo != nullptr); - auto i = ranges::find( + const auto i = ranges::find( mgInfo->lastParticipants, user, [](not_null user) { return user.get(); }); @@ -934,15 +928,18 @@ void History::applyServiceChanges( } owner().removeMegagroupParticipant(megagroup, user); if (megagroup->membersCount() > 1) { - megagroup->setMembersCount(megagroup->membersCount() - 1); + megagroup->setMembersCount( + megagroup->membersCount() - 1); } else { - mgInfo->lastParticipantsStatus |= MegagroupInfo::LastParticipantsCountOutdated; + mgInfo->lastParticipantsStatus + |= MegagroupInfo::LastParticipantsCountOutdated; mgInfo->lastParticipantsCount = 0; } if (mgInfo->lastAdmins.contains(user)) { mgInfo->lastAdmins.remove(user); if (megagroup->adminsCount() > 1) { - megagroup->setAdminsCount(megagroup->adminsCount() - 1); + megagroup->setAdminsCount( + megagroup->adminsCount() - 1); } session().changes().peerUpdated( peer, @@ -955,43 +952,25 @@ void History::applyServiceChanges( } Data::ChannelAdminChanges(megagroup).remove(uid); } - } break; - - case mtpc_messageActionChatEditPhoto: { - auto &d = action.c_messageActionChatEditPhoto(); - d.vphoto().match([&](const MTPDphoto &data) { - const auto &sizes = data.vsizes().v; - if (!sizes.isEmpty()) { - auto photo = owner().processPhoto(data); - photo->peer = peer; - auto &smallSize = sizes.front(); - auto &bigSize = sizes.back(); - const MTPFileLocation *smallLoc = nullptr; - const MTPFileLocation *bigLoc = nullptr; - switch (smallSize.type()) { - case mtpc_photoSize: smallLoc = &smallSize.c_photoSize().vlocation(); break; - case mtpc_photoCachedSize: smallLoc = &smallSize.c_photoCachedSize().vlocation(); break; - } - switch (bigSize.type()) { - case mtpc_photoSize: bigLoc = &bigSize.c_photoSize().vlocation(); break; - case mtpc_photoCachedSize: bigLoc = &bigSize.c_photoCachedSize().vlocation(); break; - } - if (smallLoc && bigLoc) { - const auto chatPhoto = MTP_chatPhoto( - MTP_flags(photo->hasVideo() - ? MTPDchatPhoto::Flag::f_has_video - : MTPDchatPhoto::Flag(0)), - *smallLoc, - *bigLoc, - data.vdc_id()); - if (const auto chat = peer->asChat()) { - chat->setPhoto(photo->id, chatPhoto); - } else if (const auto channel = peer->asChannel()) { - channel->setPhoto(photo->id, chatPhoto); - } - peer->loadUserpic(); - } + }, [&](const MTPDmessageActionChatEditPhoto &data) { + data.vphoto().match([&](const MTPDphoto &data) { + using Flag = MTPDchatPhoto::Flag; + const auto photo = owner().processPhoto(data); + photo->peer = peer; + const auto chatPhoto = MTP_chatPhoto( + MTP_flags((photo->hasVideo() ? Flag::f_has_video : Flag(0)) + | (photo->inlineThumbnailBytes().isEmpty() + ? Flag(0) + : Flag::f_stripped_thumb)), + MTP_long(photo->id), + MTP_bytes(photo->inlineThumbnailBytes()), + data.vdc_id()); + if (const auto chat = peer->asChat()) { + chat->setPhoto(chatPhoto); + } else if (const auto channel = peer->asChannel()) { + channel->setPhoto(chatPhoto); } + peer->loadUserpic(); }, [&](const MTPDphotoEmpty &data) { if (const auto chat = peer->asChat()) { chat->setPhoto(MTP_chatPhotoEmpty()); @@ -999,37 +978,27 @@ void History::applyServiceChanges( channel->setPhoto(MTP_chatPhotoEmpty()); } }); - } break; - - case mtpc_messageActionChatEditTitle: { - auto &d = action.c_messageActionChatEditTitle(); - if (auto chat = peer->asChat()) { - chat->setName(qs(d.vtitle())); + }, [&](const MTPDmessageActionChatEditTitle &data) { + if (const auto chat = peer->asChat()) { + chat->setName(qs(data.vtitle())); } - } break; - - case mtpc_messageActionChatMigrateTo: { + }, [&](const MTPDmessageActionChatMigrateTo &data) { if (const auto chat = peer->asChat()) { chat->addFlags(MTPDchat::Flag::f_deactivated); - const auto &d = action.c_messageActionChatMigrateTo(); - if (const auto channel = owner().channelLoaded(d.vchannel_id().v)) { + if (const auto channel = owner().channelLoaded( + data.vchannel_id().v)) { Data::ApplyMigration(chat, channel); } } - } break; - - case mtpc_messageActionChannelMigrateFrom: { + }, [&](const MTPDmessageActionChannelMigrateFrom &data) { if (const auto channel = peer->asChannel()) { channel->addFlags(MTPDchannel::Flag::f_megagroup); - const auto &d = action.c_messageActionChannelMigrateFrom(); - if (const auto chat = owner().chatLoaded(d.vchat_id().v)) { + if (const auto chat = owner().chatLoaded(data.vchat_id().v)) { Data::ApplyMigration(chat, channel); } } - } break; - - case mtpc_messageActionPinMessage: { - if (const auto replyTo = data.vreply_to()) { + }, [&](const MTPDmessageActionPinMessage &data) { + if (replyTo) { replyTo->match([&](const MTPDmessageReplyHeader &data) { const auto id = data.vreply_to_msg_id().v; if (item) { @@ -1042,17 +1011,42 @@ void History::applyServiceChanges( } }); } - } break; - - case mtpc_messageActionGroupCall: { - const auto &d = action.c_messageActionGroupCall(); + }, [&](const MTPDmessageActionGroupCall &data) { if (const auto channel = peer->asChannel()) { - channel->setGroupCall(d.vcall()); + channel->setGroupCall(data.vcall()); } else if (const auto chat = peer->asChat()) { - chat->setGroupCall(d.vcall()); + chat->setGroupCall(data.vcall()); } - } break; - } + }, [&](const MTPDmessageActionGroupCallScheduled &data) { + if (const auto channel = peer->asChannel()) { + channel->setGroupCall(data.vcall(), data.vschedule_date().v); + } else if (const auto chat = peer->asChat()) { + chat->setGroupCall(data.vcall(), data.vschedule_date().v); + } + }, [&](const MTPDmessageActionPaymentSent &data) { + if (const auto payment = item->Get()) { + if (const auto message = payment->msg) { + if (const auto media = message->media()) { + if (const auto invoice = media->invoice()) { + using Payments::CheckoutProcess; + if (CheckoutProcess::TakePaymentStarted(message)) { + // Toast on a current active window. + Ui::ShowMultilineToast({ + .text = tr::lng_payments_success( + tr::now, + lt_amount, + Ui::Text::Bold(payment->amount), + lt_title, + Ui::Text::Bold(invoice->title), + Ui::Text::WithEntities), + }); + } + } + } + } + } + }, [](const auto &) { + }); } void History::mainViewRemoved( @@ -2515,7 +2509,7 @@ void History::dialogEntryApplied() { addOlderSlice(QVector()); if (const auto channel = peer->asChannel()) { const auto inviter = channel->inviter; - if (inviter > 0 && channel->amIn()) { + if (inviter && channel->amIn()) { if (const auto from = owner().userLoaded(inviter)) { insertJoinedMessage(); } @@ -2527,7 +2521,7 @@ void History::dialogEntryApplied() { if (chatListTimeId() != 0 && loadedAtBottom()) { if (const auto channel = peer->asChannel()) { const auto inviter = channel->inviter; - if (inviter > 0 + if (inviter && chatListTimeId() <= channel->inviteDate && channel->amIn()) { if (const auto from = owner().userLoaded(inviter)) { @@ -2782,7 +2776,7 @@ HistoryService *History::insertJoinedMessage() { return _joinedMessage; } - const auto inviter = (peer->asChannel()->inviter > 0) + const auto inviter = peer->asChannel()->inviter ? owner().userLoaded(peer->asChannel()->inviter) : nullptr; if (!inviter) { @@ -2853,7 +2847,7 @@ void History::checkLocalMessages() { } if (isChannel() && !_joinedMessage - && (peer->asChannel()->inviter > 0) + && peer->asChannel()->inviter && goodDate(peer->asChannel()->inviteDate)) { insertJoinedMessage(); } diff --git a/Telegram/SourceFiles/history/history_item.h b/Telegram/SourceFiles/history/history_item.h index 960bb0f1d..b9517f2ae 100644 --- a/Telegram/SourceFiles/history/history_item.h +++ b/Telegram/SourceFiles/history/history_item.h @@ -76,6 +76,8 @@ public: virtual MsgId dependencyMsgId() const { return 0; } + virtual void checkBuyButton() { + } [[nodiscard]] virtual bool notificationReady() const { return true; } diff --git a/Telegram/SourceFiles/history/history_message.cpp b/Telegram/SourceFiles/history/history_message.cpp index 6d09fe388..307d76564 100644 --- a/Telegram/SourceFiles/history/history_message.cpp +++ b/Telegram/SourceFiles/history/history_message.cpp @@ -809,7 +809,7 @@ bool HistoryMessage::checkCommentsLinkedChat(ChannelId id) const { if (channel->linkedChatKnown() || !(channel->flags() & MTPDchannel::Flag::f_has_link)) { const auto linked = channel->linkedChat(); - if (!linked || linked->bareId() != id) { + if (!linked || peerToChannel(linked->id) != id) { return false; } } @@ -875,7 +875,12 @@ MsgId HistoryMessage::computeRepliesInboxReadTillFull() const { ? history()->owner().historyLoaded( peerFromChannel(views->commentsMegagroupId)) : history().get(); - return group ? std::max(local, group->inboxReadTillId()) : local; + if (const auto megagroup = group->peer->asChannel()) { + if (megagroup->amIn()) { + return std::max(local, group->inboxReadTillId()); + } + } + return local; } MsgId HistoryMessage::repliesOutboxReadTill() const { @@ -909,7 +914,12 @@ MsgId HistoryMessage::computeRepliesOutboxReadTillFull() const { ? history()->owner().historyLoaded( peerFromChannel(views->commentsMegagroupId)) : history().get(); - return group ? std::max(local, group->outboxReadTillId()) : local; + if (const auto megagroup = group->peer->asChannel()) { + if (megagroup->amIn()) { + return std::max(local, group->outboxReadTillId()); + } + } + return local; } void HistoryMessage::setRepliesMaxId(MsgId maxId) { @@ -1116,7 +1126,7 @@ void HistoryMessage::createComponents(const CreateConfig &config) { MTP_int(0), MTP_int(0), MTPVector(), // recent_repliers - MTP_int(linked->bareId()), + MTP_int(peerToChannel(linked->id).bare), MTP_int(0), // max_id MTP_int(0))); // read_max_id } @@ -1223,6 +1233,10 @@ void HistoryMessage::returnSavedMedia() { void HistoryMessage::setMedia(const MTPMessageMedia &media) { _media = CreateMedia(this, media); + checkBuyButton(); +} + +void HistoryMessage::checkBuyButton() { if (const auto invoice = _media ? _media->invoice() : nullptr) { if (invoice->receiptMsgId) { replaceBuyWithReceiptInMarkup(); @@ -1349,11 +1363,18 @@ std::unique_ptr HistoryMessage::CreateMedia( } void HistoryMessage::replaceBuyWithReceiptInMarkup() { - if (auto markup = inlineReplyMarkup()) { + if (const auto markup = inlineReplyMarkup()) { for (auto &row : markup->rows) { for (auto &button : row) { if (button.type == HistoryMessageMarkupButton::Type::Buy) { - button.text = tr::lng_payments_receipt_button(tr::now); + const auto receipt = tr::lng_payments_receipt_button(tr::now); + if (button.text != receipt) { + button.text = receipt; + if (markup->inlineKeyboard) { + markup->inlineKeyboard = nullptr; + history()->owner().requestItemResize(this); + } + } } } } @@ -1742,10 +1763,11 @@ void HistoryMessage::setReplies(const MTPMessageReplies &data) { return result; }(); const auto count = data.vreplies().v; - const auto channelId = data.vchannel_id().value_or_empty(); + const auto channelId = ChannelId( + data.vchannel_id().value_or_empty()); const auto readTillId = data.vread_max_id() ? std::max( - { views->repliesInboxReadTillId, data.vread_max_id()->v, 1 }) + { views->repliesInboxReadTillId, data.vread_max_id()->v, 1 }) : views->repliesInboxReadTillId; const auto maxId = data.vmax_id().value_or(views->repliesMaxId); const auto countsChanged = (views->replies.count != count) diff --git a/Telegram/SourceFiles/history/history_message.h b/Telegram/SourceFiles/history/history_message.h index 4a2e9fa79..3a660aefb 100644 --- a/Telegram/SourceFiles/history/history_message.h +++ b/Telegram/SourceFiles/history/history_message.h @@ -120,6 +120,7 @@ public: void refreshSentMedia(const MTPMessageMedia *media); void returnSavedMedia() override; void setMedia(const MTPMessageMedia &media); + void checkBuyButton() override; [[nodiscard]] static std::unique_ptr CreateMedia( not_null item, const MTPMessageMedia &media); diff --git a/Telegram/SourceFiles/history/history_service.cpp b/Telegram/SourceFiles/history/history_service.cpp index 39d732b25..ba5b5e798 100644 --- a/Telegram/SourceFiles/history/history_service.cpp +++ b/Telegram/SourceFiles/history/history_service.cpp @@ -28,10 +28,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_changes.h" #include "data/data_group_call.h" // Data::GroupCall::id(). #include "core/application.h" +#include "core/click_handler_types.h" +#include "base/unixtime.h" +#include "base/timer_rpl.h" #include "calls/calls_instance.h" // Core::App().calls().joinGroupCall. #include "window/notifications_manager.h" +#include "window/window_controller.h" #include "window/window_session_controller.h" #include "storage/storage_shared_media.h" +#include "payments/payments_checkout_process.h" // CheckoutProcess::Start. #include "ui/text/format_values.h" #include "ui/text/text_options.h" #include "base/unixtime.h" @@ -78,17 +83,6 @@ QString GenerateServiceTime(TimeId date) { ); } -[[nodiscard]] std::optional PeerHasThisCall( - not_null peer, - uint64 id) { - const auto call = peer->groupCall(); - return call - ? std::make_optional(call->id() == id) - : PeerCallKnown(peer) - ? std::make_optional(false) - : std::nullopt; -} - [[nodiscard]] uint64 CallIdFromInput(const MTPInputGroupCall &data) { return data.match([&](const MTPDinputGroupCall &data) { return data.vid().v; @@ -355,14 +349,30 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) { auto prepareGroupCall = [this](const MTPDmessageActionGroupCall &action) { if (const auto duration = action.vduration()) { - return prepareDiscardedCallText(duration->v); + const auto seconds = duration->v; + const auto days = seconds / 86400; + const auto hours = seconds / 3600; + const auto minutes = seconds / 60; + auto text = (days > 1) + ? tr::lng_group_call_duration_days(tr::now, lt_count, days) + : (hours > 1) + ? tr::lng_group_call_duration_hours(tr::now, lt_count, hours) + : (minutes > 1) + ? tr::lng_group_call_duration_minutes(tr::now, lt_count, minutes) + : tr::lng_group_call_duration_seconds(tr::now, lt_count, seconds); + return PreparedText{ tr::lng_action_group_call_finished(tr::now, lt_duration, text) }; } - const auto callId = CallIdFromInput(action.vcall()); - const auto peer = history()->peer; - const auto linkCallId = PeerHasThisCall(peer, callId).value_or(false) - ? callId - : 0; - return prepareStartedCallText(linkCallId); + auto result = PreparedText{}; + if (history()->peer->isBroadcast()) { + result.text = tr::lng_action_group_call_started_channel(tr::now); + } else { + result.links.push_back(fromLink()); + result.text = tr::lng_action_group_call_started_group( + tr::now, + lt_from, + fromLinkText()); + } + return result; }; auto prepareInviteToGroupCall = [this](const MTPDmessageActionInviteToGroupCall &action) { @@ -467,6 +477,8 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) { return prepareInviteToGroupCall(data); }, [&](const MTPDmessageActionSetMessagesTTL &data) { return prepareSetMessagesTTL(data); + }, [&](const MTPDmessageActionGroupCallScheduled &data) { + return prepareCallScheduledText(data.vschedule_date().v); }, [](const MTPDmessageActionEmpty &) { return PreparedText{ tr::lng_message_empty(tr::now) }; }); @@ -482,7 +494,7 @@ void HistoryService::applyAction(const MTPMessageAction &action) { if (const auto channel = history()->peer->asMegagroup()) { const auto selfUserId = history()->session().userId(); for (const auto &item : data.vusers().v) { - if (item.v == selfUserId) { + if (peerFromUser(item) == selfUserId) { channel->mgInfo->joinedMessageFound = true; break; } @@ -532,18 +544,28 @@ bool HistoryService::updateDependent(bool force) { } if (!dependent->lnk) { - dependent->lnk = goToMessageClickHandler(history()->peer, dependent->msgId); + dependent->lnk = goToMessageClickHandler( + (dependent->peerId + ? history()->owner().peer(dependent->peerId) + : history()->peer), + dependent->msgId); } auto gotDependencyItem = false; if (!dependent->msg) { - dependent->msg = history()->owner().message(channelId(), dependent->msgId); + dependent->msg = history()->owner().message( + (dependent->peerId + ? peerToChannel(dependent->peerId) + : channelId()), + dependent->msgId); if (dependent->msg) { if (dependent->msg->isEmpty()) { // Really it is deleted. dependent->msg = nullptr; force = true; } else { - history()->owner().registerDependentMessage(this, dependent->msg); + history()->owner().registerDependentMessage( + this, + dependent->msg); gotDependencyItem = true; } } @@ -563,41 +585,6 @@ bool HistoryService::updateDependent(bool force) { return (dependent->msg || !dependent->msgId); } -HistoryService::PreparedText HistoryService::prepareDiscardedCallText( - int duration) { - const auto seconds = duration; - const auto days = seconds / 86400; - const auto hours = seconds / 3600; - const auto minutes = seconds / 60; - auto text = (days > 1) - ? tr::lng_group_call_duration_days(tr::now, lt_count, days) - : (hours > 1) - ? tr::lng_group_call_duration_hours(tr::now, lt_count, hours) - : (minutes > 1) - ? tr::lng_group_call_duration_minutes(tr::now, lt_count, minutes) - : tr::lng_group_call_duration_seconds(tr::now, lt_count, seconds); - return PreparedText{ tr::lng_action_group_call_finished(tr::now, lt_duration, text) }; -} - -HistoryService::PreparedText HistoryService::prepareStartedCallText( - uint64 linkCallId) { - auto result = PreparedText{}; - result.links.push_back(fromLink()); - auto chatText = tr::lng_action_group_call_started_chat(tr::now); - if (linkCallId) { - const auto peer = history()->peer; - result.links.push_back(GroupCallClickHandler(peer, linkCallId)); - chatText = textcmdLink(2, chatText); - } - result.text = tr::lng_action_group_call_started( - tr::now, - lt_from, - fromLinkText(), - lt_chat, - chatText); - return result; -} - HistoryService::PreparedText HistoryService::prepareInvitedToCallText( const QVector &users, uint64 linkCallId) { @@ -756,18 +743,16 @@ HistoryService::PreparedText HistoryService::prepareGameScoreText() { HistoryService::PreparedText HistoryService::preparePaymentSentText() { auto result = PreparedText {}; - auto payment = Get(); + const auto payment = Get(); + Assert(payment != nullptr); auto invoiceTitle = [&] { - if (payment && payment->msg) { + if (payment->msg) { if (const auto media = payment->msg->media()) { if (const auto invoice = media->invoice()) { - return invoice->title; + return textcmdLink(1, invoice->title); } } - return tr::lng_deleted_message(tr::now); - } else if (payment && payment->msgId) { - return tr::lng_contacts_loading(tr::now); } return QString(); }(); @@ -776,6 +761,71 @@ HistoryService::PreparedText HistoryService::preparePaymentSentText() { result.text = tr::lng_action_payment_done(tr::now, lt_amount, payment->amount, lt_user, history()->peer->name); } else { result.text = tr::lng_action_payment_done_for(tr::now, lt_amount, payment->amount, lt_user, history()->peer->name, lt_invoice, invoiceTitle); + if (payment->msg) { + result.links.push_back(payment->lnk); + } + } + return result; +} + +HistoryService::PreparedText HistoryService::prepareCallScheduledText( + TimeId scheduleDate) { + const auto call = Get(); + Assert(call != nullptr); + + const auto scheduled = base::unixtime::parse(scheduleDate); + const auto date = scheduled.date(); + const auto now = QDateTime::currentDateTime(); + const auto secsToDateAddDays = [&](int days) { + return now.secsTo(QDateTime(date.addDays(days), QTime(0, 0))); + }; + auto result = PreparedText(); + const auto prepareWithDate = [&](const QString &date) { + if (history()->peer->isBroadcast()) { + result.text = tr::lng_action_group_call_scheduled_channel( + tr::now, + lt_date, + date); + } else { + result.links.push_back(fromLink()); + result.text = tr::lng_action_group_call_scheduled_group( + tr::now, + lt_from, + fromLinkText(), + lt_date, + date); + } + }; + const auto time = scheduled.time().toString(cTimeFormat()); + const auto prepareGeneric = [&] { + prepareWithDate(tr::lng_group_call_starts_date( + tr::now, + lt_date, + langDayOfMonthFull(date), + lt_time, + time)); + }; + auto nextIn = TimeId(0); + if (now.date().addDays(1) < scheduled.date()) { + nextIn = secsToDateAddDays(-1); + prepareGeneric(); + } else if (now.date().addDays(1) == scheduled.date()) { + nextIn = secsToDateAddDays(0); + prepareWithDate( + tr::lng_group_call_starts_tomorrow(tr::now, lt_time, time)); + } else if (now.date() == scheduled.date()) { + nextIn = secsToDateAddDays(1); + prepareWithDate( + tr::lng_group_call_starts_today(tr::now, lt_time, time)); + } else { + prepareGeneric(); + } + if (nextIn) { + call->lifetime = base::timer_once( + (nextIn + 2) * crl::time(1000) + ) | rpl::start_with_next([=] { + updateText(prepareCallScheduledText(scheduleDate)); + }); } return result; } @@ -979,46 +1029,50 @@ void HistoryService::createFromMtp(const MTPDmessage &message) { } void HistoryService::createFromMtp(const MTPDmessageService &message) { - if (message.vaction().type() == mtpc_messageActionGameScore) { + const auto type = message.vaction().type(); + if (type == mtpc_messageActionGameScore) { const auto &data = message.vaction().c_messageActionGameScore(); UpdateComponents(HistoryServiceGameScore::Bit()); Get()->score = data.vscore().v; - } else if (message.vaction().type() == mtpc_messageActionPaymentSent) { + } else if (type == mtpc_messageActionPaymentSent) { const auto &data = message.vaction().c_messageActionPaymentSent(); UpdateComponents(HistoryServicePayment::Bit()); const auto amount = data.vtotal_amount().v; const auto currency = qs(data.vcurrency()); - Get()->amount = Ui::FillAmountAndCurrency(amount, currency); - } else if (message.vaction().type() == mtpc_messageActionGroupCall) { - const auto &data = message.vaction().c_messageActionGroupCall(); - if (data.vduration()) { + const auto payment = Get(); + const auto id = fullId(); + const auto owner = &history()->owner(); + payment->amount = Ui::FillAmountAndCurrency(amount, currency); + payment->invoiceLink = std::make_shared([=]( + ClickContext context) { + using namespace Payments; + const auto my = context.other.value(); + const auto weak = my.sessionWindow; + if (const auto item = owner->message(id)) { + CheckoutProcess::Start( + item, + Mode::Receipt, + crl::guard(weak, [=] { weak->window().activate(); })); + } + }); + } else if (type == mtpc_messageActionGroupCall + || type == mtpc_messageActionGroupCallScheduled) { + const auto started = (type == mtpc_messageActionGroupCall); + const auto &callData = started + ? message.vaction().c_messageActionGroupCall().vcall() + : message.vaction().c_messageActionGroupCallScheduled().vcall(); + const auto duration = started + ? message.vaction().c_messageActionGroupCall().vduration() + : tl::conditional(); + if (duration) { RemoveComponents(HistoryServiceOngoingCall::Bit()); } else { UpdateComponents(HistoryServiceOngoingCall::Bit()); const auto call = Get(); - const auto id = CallIdFromInput(data.vcall()); - call->lifetime.destroy(); - - const auto peer = history()->peer; - const auto has = PeerHasThisCall(peer, id); - if (!has.has_value()) { - PeerHasThisCallValue( - peer, - id - ) | rpl::start_with_next([=](bool has) { - updateText(prepareStartedCallText(has ? id : 0)); - }, call->lifetime); - } else if (*has) { - PeerHasThisCallValue( - peer, - id - ) | rpl::skip(1) | rpl::start_with_next([=](bool has) { - Assert(!has); - updateText(prepareStartedCallText(0)); - }, call->lifetime); - } + call->id = CallIdFromInput(callData); + call->link = GroupCallClickHandler(history()->peer, call->id); } - } else if (message.vaction().type() == mtpc_messageActionInviteToGroupCall) { + } else if (type == mtpc_messageActionInviteToGroupCall) { const auto &data = message.vaction().c_messageActionInviteToGroupCall(); const auto id = CallIdFromInput(data.vcall()); const auto peer = history()->peer; @@ -1035,6 +1089,7 @@ void HistoryService::createFromMtp(const MTPDmessageService &message) { } else { UpdateComponents(HistoryServiceOngoingCall::Bit()); const auto call = Get(); + call->id = id; call->lifetime.destroy(); const auto users = data.vusers().v; @@ -1048,21 +1103,25 @@ void HistoryService::createFromMtp(const MTPDmessageService &message) { } if (const auto replyTo = message.vreply_to()) { replyTo->match([&](const MTPDmessageReplyHeader &data) { - const auto peer = data.vreply_to_peer_id() + const auto peerId = data.vreply_to_peer_id() ? peerFromMTP(*data.vreply_to_peer_id()) : history()->peer->id; - if (!peer || peer == history()->peer->id) { - if (message.vaction().type() == mtpc_messageActionPinMessage) { - UpdateComponents(HistoryServicePinned::Bit()); - } - if (const auto dependent = GetDependentData()) { - dependent->msgId = data.vreply_to_msg_id().v; - if (!updateDependent()) { - history()->session().api().requestMessageData( - history()->peer->asChannel(), - dependent->msgId, - HistoryDependentItemCallback(this)); - } + if (message.vaction().type() == mtpc_messageActionPinMessage) { + UpdateComponents(HistoryServicePinned::Bit()); + } + if (const auto dependent = GetDependentData()) { + dependent->peerId = (peerId != history()->peer->id) + ? peerId + : 0; + dependent->msgId = data.vreply_to_msg_id().v; + if (!updateDependent()) { + history()->session().api().requestMessageData( + (peerIsChannel(dependent->peerId) + ? history()->owner().channel( + peerToChannel(dependent->peerId)).get() + : history()->peer->asChannel()), + dependent->msgId, + HistoryDependentItemCallback(this)); } } }); @@ -1137,11 +1196,20 @@ void HistoryService::updateText(PreparedText &&text) { void HistoryService::clearDependency() { if (const auto dependent = GetDependentData()) { if (dependent->msg) { - history()->owner().unregisterDependentMessage(this, dependent->msg); + history()->owner().unregisterDependentMessage( + this, + dependent->msg); + dependent->msg = nullptr; + dependent->msgId = 0; } } } +void HistoryService::dependencyItemRemoved(HistoryItem *dependency) { + clearDependency(); + updateDependentText(); +} + HistoryService::~HistoryService() { clearDependency(); _media.reset(); @@ -1185,3 +1253,14 @@ not_null GenerateJoinedMessage( GenerateJoinedText(history, inviter), flags); } + +std::optional PeerHasThisCall( + not_null peer, + uint64 id) { + const auto call = peer->groupCall(); + return call + ? std::make_optional(call->id() == id) + : PeerCallKnown(peer) + ? std::make_optional(false) + : std::nullopt; +} diff --git a/Telegram/SourceFiles/history/history_service.h b/Telegram/SourceFiles/history/history_service.h index 319f38126..9c27f038e 100644 --- a/Telegram/SourceFiles/history/history_service.h +++ b/Telegram/SourceFiles/history/history_service.h @@ -14,6 +14,7 @@ class Service; } // namespace HistoryView struct HistoryServiceDependentData { + PeerId peerId = 0; MsgId msgId = 0; HistoryItem *msg = nullptr; ClickHandlerPtr lnk; @@ -34,6 +35,7 @@ struct HistoryServicePayment : public RuntimeComponent , public HistoryServiceDependentData { QString amount; + ClickHandlerPtr invoiceLink; }; struct HistoryServiceSelfDestruct @@ -50,6 +52,7 @@ struct HistoryServiceSelfDestruct struct HistoryServiceOngoingCall : public RuntimeComponent { uint64 id = 0; + ClickHandlerPtr link; rpl::lifetime lifetime; }; @@ -102,6 +105,8 @@ public: Storage::SharedMediaTypesMask sharedMediaTypes() const override; + void dependencyItemRemoved(HistoryItem *dependency) override; + bool needCheck() const override; bool serviceMsg() const override { return true; @@ -167,11 +172,11 @@ private: PreparedText preparePinnedText(); PreparedText prepareGameScoreText(); PreparedText preparePaymentSentText(); - PreparedText prepareDiscardedCallText(int duration); - PreparedText prepareStartedCallText(uint64 linkCallId); PreparedText prepareInvitedToCallText( const QVector &users, uint64 linkCallId); + PreparedText prepareCallScheduledText( + TimeId scheduleDate); friend class HistoryView::Service; @@ -179,8 +184,11 @@ private: bool _needTime = true; }; -not_null GenerateJoinedMessage( +[[nodiscard]] not_null GenerateJoinedMessage( not_null history, TimeId inviteDate, not_null inviter, MTPDmessage::Flags flags); +[[nodiscard]] std::optional PeerHasThisCall( + not_null peer, + uint64 id); diff --git a/Telegram/SourceFiles/history/view/controls/history_view_voice_record_button.cpp b/Telegram/SourceFiles/history/view/controls/history_view_voice_record_button.cpp index fb9a4551c..b0789ec81 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_voice_record_button.cpp +++ b/Telegram/SourceFiles/history/view/controls/history_view_voice_record_button.cpp @@ -87,7 +87,7 @@ void VoiceRecordButton::init() { _blobs->setLevel(0.); } _blobsHideLastTime = hide ? crl::now() : 0; - if (!hide && !_animation.animating()) { + if (!hide && !_animation.animating() && isVisible()) { _animation.start(); } }, lifetime()); diff --git a/Telegram/SourceFiles/history/view/history_view_context_menu.cpp b/Telegram/SourceFiles/history/view/history_view_context_menu.cpp index 599507574..b640a696c 100644 --- a/Telegram/SourceFiles/history/view/history_view_context_menu.cpp +++ b/Telegram/SourceFiles/history/view/history_view_context_menu.cpp @@ -441,19 +441,20 @@ bool AddRescheduleAction( not_null list) { const auto owner = &request.navigation->session().data(); - const auto goodSingle = !(!HasEditMessageAction(request, list) - || !request.item->isScheduled()); + const auto goodSingle = HasEditMessageAction(request, list) + && request.item->isScheduled(); const auto goodMany = [&] { if (goodSingle) { return false; } - if (!request.overSelection || request.selectedItems.empty()) { + const auto &items = request.selectedItems; + if (!request.overSelection || items.empty()) { return false; } - if (request.selectedItems.size() > kRescheduleLimit) { + if (items.size() > kRescheduleLimit) { return false; } - return true; + return ranges::all_of(items, &SelectedItem::canSendNow); }(); if (!goodSingle && !goodMany) { return false; @@ -476,8 +477,8 @@ bool AddRescheduleAction( if (!firstItem) { return; } - list->cancelSelection(); const auto callback = [=](Api::SendOptions options) { + list->cancelSelection(); for (const auto &id : ids) { const auto item = owner->message(id); if (!item && !item->isScheduled()) { @@ -971,7 +972,7 @@ void CopyPostLink( Assert(channel != nullptr); if (const auto rootId = item->replyToTop()) { const auto root = item->history()->owner().message( - channel->bareId(), + peerToChannel(channel->id), rootId); const auto sender = root ? root->discussionPostOriginalSender() diff --git a/Telegram/SourceFiles/history/view/history_view_group_call_tracker.cpp b/Telegram/SourceFiles/history/view/history_view_group_call_tracker.cpp index 71c1e4dd2..f3fc904f4 100644 --- a/Telegram/SourceFiles/history/view/history_view_group_call_tracker.cpp +++ b/Telegram/SourceFiles/history/view/history_view_group_call_tracker.cpp @@ -171,7 +171,7 @@ rpl::producer GroupCallTracker::ContentByCall( state->current.users.push_back({ .userpic = pic.toImage(), .userpicKey = userpic.uniqueKey, - .id = userpic.peer->bareId(), + .id = userpic.peer->id.value, .speaking = userpic.speaking, }); if (userpic.peer->hasUserpic() @@ -352,10 +352,18 @@ rpl::producer GroupCallTracker::ContentByCall( RegenerateUserpics(state, call, userpicSize); - call->fullCountValue( - ) | rpl::start_with_next([=](int count) { + rpl::combine( + call->titleValue(), + call->scheduleDateValue(), + call->fullCountValue() + ) | rpl::start_with_next([=]( + const QString &title, + TimeId scheduleDate, + int count) { + state->current.title = title; + state->current.scheduleDate = scheduleDate; state->current.count = count; - state->current.shown = (count > 0); + state->current.shown = (count > 0) || (scheduleDate != 0); consumer.put_next_copy(state->current); }, lifetime); diff --git a/Telegram/SourceFiles/history/view/history_view_message.cpp b/Telegram/SourceFiles/history/view/history_view_message.cpp index 3a23a2f9f..dda5077e1 100644 --- a/Telegram/SourceFiles/history/view/history_view_message.cpp +++ b/Telegram/SourceFiles/history/view/history_view_message.cpp @@ -102,6 +102,7 @@ void KeyboardStyle::paintButtonIcon( switch (type) { case Type::Url: case Type::Auth: return &st::msgBotKbUrlIcon; + case Type::Buy: return &st::msgBotKbPaymentIcon; case Type::SwitchInlineSame: case Type::SwitchInline: return &st::msgBotKbSwitchPmIcon; } @@ -124,6 +125,7 @@ int KeyboardStyle::minButtonWidth( switch (type) { case Type::Url: case Type::Auth: iconWidth = st::msgBotKbUrlIcon.width(); break; + case Type::Buy: iconWidth = st::msgBotKbPaymentIcon.width(); break; case Type::SwitchInlineSame: case Type::SwitchInline: iconWidth = st::msgBotKbSwitchPmIcon.width(); break; case Type::Callback: diff --git a/Telegram/SourceFiles/history/view/history_view_schedule_box.cpp b/Telegram/SourceFiles/history/view/history_view_schedule_box.cpp index 5ddfe67c7..593c292a5 100644 --- a/Telegram/SourceFiles/history/view/history_view_schedule_box.cpp +++ b/Telegram/SourceFiles/history/view/history_view_schedule_box.cpp @@ -76,14 +76,14 @@ void ScheduleBox( box->closeBox(); copy(result); }; - auto descriptor = Ui::ChooseDateTimeBox( - box, - (type == SendMenu::Type::Reminder + auto descriptor = Ui::ChooseDateTimeBox(box, { + .title = (type == SendMenu::Type::Reminder ? tr::lng_remind_title() : tr::lng_schedule_title()), - tr::lng_schedule_button(), - [=](TimeId result) { save(false, result); }, - time); + .submit = tr::lng_schedule_button(), + .done = [=](TimeId result) { save(false, result); }, + .time = time, + }); SendMenu::SetupMenuAndShortcuts( descriptor.submit.data(), diff --git a/Telegram/SourceFiles/history/view/history_view_service_message.cpp b/Telegram/SourceFiles/history/view/history_view_service_message.cpp index a3e4d54e7..42a25d144 100644 --- a/Telegram/SourceFiles/history/view/history_view_service_message.cpp +++ b/Telegram/SourceFiles/history/view/history_view_service_message.cpp @@ -513,17 +513,18 @@ TextState Service::textState(QPoint point, StateRequest request) const { point - trect.topLeft(), trect.width(), textRequest)); - if (auto gamescore = item->Get()) { - if (!result.link - && result.cursor == CursorState::Text - && g.contains(point)) { + if (!result.link + && result.cursor == CursorState::Text + && g.contains(point)) { + if (const auto gamescore = item->Get()) { result.link = gamescore->lnk; - } - } else if (auto payment = item->Get()) { - if (!result.link - && result.cursor == CursorState::Text - && g.contains(point)) { - result.link = payment->lnk; + } else if (const auto payment = item->Get()) { + result.link = payment->invoiceLink; + } else if (const auto call = item->Get()) { + const auto peer = history()->peer; + if (PeerHasThisCall(peer, call->id).value_or(false)) { + result.link = call->link; + } } } } else if (media) { diff --git a/Telegram/SourceFiles/history/view/media/history_view_media.cpp b/Telegram/SourceFiles/history/view/media/history_view_media.cpp index b72638f3b..bcfc800f9 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_media.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_media.cpp @@ -47,7 +47,7 @@ QString DocumentTimestampLinkBase( FullMsgId context) { return QString( "doc%1_%2_%3" - ).arg(document->id).arg(context.channel).arg(context.msg); + ).arg(document->id).arg(context.channel.bare).arg(context.msg); } TextWithEntities AddTimestampLinks( diff --git a/Telegram/SourceFiles/info/common_groups/info_common_groups_inner_widget.cpp b/Telegram/SourceFiles/info/common_groups/info_common_groups_inner_widget.cpp index 7ddf3165e..60d189da0 100644 --- a/Telegram/SourceFiles/info/common_groups/info_common_groups_inner_widget.cpp +++ b/Telegram/SourceFiles/info/common_groups/info_common_groups_inner_widget.cpp @@ -50,7 +50,7 @@ private: std::unique_ptr createRow(not_null peer); struct SavedState : SavedStateBase { - int32 preloadGroupId = 0; + PeerId preloadGroupId = 0; bool allLoaded = false; bool wasLoading = false; }; @@ -59,7 +59,7 @@ private: not_null _user; mtpRequestId _preloadRequestId = 0; bool _allLoaded = false; - int32 _preloadGroupId = 0; + PeerId _preloadGroupId = 0; }; @@ -96,7 +96,9 @@ void ListController::loadMoreRows() { } _preloadRequestId = _api.request(MTPmessages_GetCommonChats( _user->inputUser, - MTP_int(_preloadGroupId), + MTP_int(peerIsChat(_preloadGroupId) + ? peerToChat(_preloadGroupId).bare + : peerToChannel(_preloadGroupId).bare), // #TODO ids MTP_int(kCommonGroupsPerPage) )).done([this](const MTPmessages_Chats &result) { _preloadRequestId = 0; @@ -112,7 +114,7 @@ void ListController::loadMoreRows() { delegate()->peerListAppendRow( createRow(peer)); } - _preloadGroupId = peer->bareId(); + _preloadGroupId = peer->id; _allLoaded = false; } } diff --git a/Telegram/SourceFiles/info/media/info_media_list_widget.cpp b/Telegram/SourceFiles/info/media/info_media_list_widget.cpp index bec2eff1c..0378461ce 100644 --- a/Telegram/SourceFiles/info/media/info_media_list_widget.cpp +++ b/Telegram/SourceFiles/info/media/info_media_list_widget.cpp @@ -695,11 +695,9 @@ void ListWidget::itemRemoved(not_null item) { FullMsgId ListWidget::computeFullId( UniversalMsgId universalId) const { Expects(universalId != 0); - auto peerChannel = [&] { - return _peer->isChannel() ? _peer->bareId() : NoChannel; - }; + return (universalId > 0) - ? FullMsgId(peerChannel(), universalId) + ? FullMsgId(peerToChannel(_peer->id), universalId) : FullMsgId(NoChannel, ServerMaxMsgId + universalId); } @@ -804,8 +802,8 @@ bool ListWidget::isMyItem(not_null item) const { } bool ListWidget::isPossiblyMyId(FullMsgId fullId) const { - return (fullId.channel != 0) - ? (_peer->isChannel() && _peer->bareId() == fullId.channel) + return fullId.channel + ? (_peer->isChannel() && peerToChannel(_peer->id) == fullId.channel) : (!_peer->isChannel() || _migrated); } diff --git a/Telegram/SourceFiles/info/polls/info_polls_results_inner_widget.cpp b/Telegram/SourceFiles/info/polls/info_polls_results_inner_widget.cpp index dd2ecad7d..2436b6d41 100644 --- a/Telegram/SourceFiles/info/polls/info_polls_results_inner_widget.cpp +++ b/Telegram/SourceFiles/info/polls/info_polls_results_inner_widget.cpp @@ -471,7 +471,7 @@ void ListController::rowClicked(not_null row) { } bool ListController::appendRow(not_null user) { - if (delegate()->peerListFindRow(user->id)) { + if (delegate()->peerListFindRow(user->id.value)) { return false; } delegate()->peerListAppendRow(createRow(user)); diff --git a/Telegram/SourceFiles/info/profile/info_profile_values.cpp b/Telegram/SourceFiles/info/profile/info_profile_values.cpp index 2fe322c98..30f21f49c 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_values.cpp +++ b/Telegram/SourceFiles/info/profile/info_profile_values.cpp @@ -71,13 +71,19 @@ void StripExternalLinks(TextWithEntities &text) { } // namespace QString IDString(not_null peer) { - auto resultId = QString::number(peer->bareId()); + auto resultId = QString::number(peerIsUser(peer->id) + ? peerToUser(peer->id).bare + : peerIsChat(peer->id) + ? peerToChat(peer->id).bare + : peerIsChannel(peer->id) + ? peerToChannel(peer->id).bare + : peer->id.value); if (cShowChatId() == 2) { if (peer->isChannel()) { - resultId = QString::number(kMaxChannelId - peer->bareId()); + resultId = QString::number(peerToChannel(peer->id).bare - kMaxChannelId).prepend("-"); } else if (peer->isChat()) { - resultId = QString::number(-peer->bareId()); + resultId = resultId.prepend("-"); } } diff --git a/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp b/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp index a88bb5dfe..ca2af7d37 100644 --- a/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp +++ b/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp @@ -696,24 +696,24 @@ void Video::initDimensions() { const auto withThumb = withThumbnail(); _maxw = st::emojiPanWidth - st::emojiScroll.width - st::inlineResultsLeft; - int32 textWidth = _maxw - (withThumb ? (st::inlineThumbSize + st::inlineThumbSkip) : 0); - TextParseOptions titleOpts = { 0, _maxw, 2 * st::semiboldFont->height, Qt::LayoutDirectionAuto }; + const auto textWidth = _maxw - (st::inlineThumbSize + st::inlineThumbSkip); + TextParseOptions titleOpts = { 0, textWidth, 2 * st::semiboldFont->height, Qt::LayoutDirectionAuto }; auto title = TextUtilities::SingleLine(_result->getLayoutTitle()); if (title.isEmpty()) { title = tr::lng_media_video(tr::now); } _title.setText(st::semiboldTextStyle, title, titleOpts); - int32 titleHeight = qMin(_title.countHeight(_maxw), 2 * st::semiboldFont->height); + int32 titleHeight = qMin(_title.countHeight(textWidth), 2 * st::semiboldFont->height); int32 descriptionLines = withThumb ? (titleHeight > st::semiboldFont->height ? 1 : 2) : 3; - TextParseOptions descriptionOpts = { TextParseMultiline, _maxw, descriptionLines * st::normalFont->height, Qt::LayoutDirectionAuto }; + TextParseOptions descriptionOpts = { TextParseMultiline, textWidth, descriptionLines * st::normalFont->height, Qt::LayoutDirectionAuto }; QString description = _result->getLayoutDescription(); if (description.isEmpty()) { description = _duration; } _description.setText(st::defaultTextStyle, description, descriptionOpts); - int32 descriptionHeight = qMin(_description.countHeight(_maxw), descriptionLines * st::normalFont->height); + int32 descriptionHeight = qMin(_description.countHeight(textWidth), descriptionLines * st::normalFont->height); _minh = st::inlineThumbSize; _minh += st::inlineRowMargin * 2 + st::inlineRowBorder; @@ -1073,13 +1073,13 @@ Contact::Contact(not_null context, not_null result) void Contact::initDimensions() { _maxw = st::emojiPanWidth - st::emojiScroll.width - st::inlineResultsLeft; int32 textWidth = _maxw - (st::inlineThumbSize + st::inlineThumbSkip); - TextParseOptions titleOpts = { 0, _maxw, st::semiboldFont->height, Qt::LayoutDirectionAuto }; + TextParseOptions titleOpts = { 0, textWidth, st::semiboldFont->height, Qt::LayoutDirectionAuto }; _title.setText(st::semiboldTextStyle, TextUtilities::SingleLine(_result->getLayoutTitle()), titleOpts); - int32 titleHeight = qMin(_title.countHeight(_maxw), st::semiboldFont->height); + int32 titleHeight = qMin(_title.countHeight(textWidth), st::semiboldFont->height); - TextParseOptions descriptionOpts = { TextParseMultiline, _maxw, st::normalFont->height, Qt::LayoutDirectionAuto }; + TextParseOptions descriptionOpts = { TextParseMultiline, textWidth, st::normalFont->height, Qt::LayoutDirectionAuto }; _description.setText(st::defaultTextStyle, _result->getLayoutDescription(), descriptionOpts); - int32 descriptionHeight = qMin(_description.countHeight(_maxw), st::normalFont->height); + int32 descriptionHeight = qMin(_description.countHeight(textWidth), st::normalFont->height); _minh = st::inlineFileSize; _minh += st::inlineRowMargin * 2 + st::inlineRowBorder; @@ -1170,7 +1170,7 @@ Article::Article( void Article::initDimensions() { _maxw = st::emojiPanWidth - st::emojiScroll.width - st::inlineResultsLeft; - int32 textWidth = _maxw - (_withThumb ? (st::inlineThumbSize + st::inlineThumbSkip) : 0); + int32 textWidth = _maxw - (_withThumb ? (st::inlineThumbSize + st::inlineThumbSkip) : (st::emojiPanHeaderLeft - st::inlineResultsLeft)); TextParseOptions titleOpts = { 0, textWidth, 2 * st::semiboldFont->height, Qt::LayoutDirectionAuto }; _title.setText(st::semiboldTextStyle, TextUtilities::SingleLine(_result->getLayoutTitle()), titleOpts); int32 titleHeight = qMin(_title.countHeight(textWidth), 2 * st::semiboldFont->height); @@ -1192,8 +1192,9 @@ int32 Article::resizeGetHeight(int32 width) { if (_url) { _urlText = getResultUrl(); _urlWidth = st::normalFont->width(_urlText); - if (_urlWidth > _width - st::inlineThumbSize - st::inlineThumbSkip) { - _urlText = st::normalFont->elided(_urlText, _width - st::inlineThumbSize - st::inlineThumbSkip); + int32 textWidth = _width - (_withThumb ? (st::inlineThumbSize + st::inlineThumbSkip) : (st::emojiPanHeaderLeft - st::inlineResultsLeft)); + if (_urlWidth > textWidth) { + _urlText = st::normalFont->elided(_urlText, textWidth); _urlWidth = st::normalFont->width(_urlText); } } diff --git a/Telegram/SourceFiles/inline_bots/inline_bot_layout_item.cpp b/Telegram/SourceFiles/inline_bots/inline_bot_layout_item.cpp index 248292865..30b656e3c 100644 --- a/Telegram/SourceFiles/inline_bots/inline_bot_layout_item.cpp +++ b/Telegram/SourceFiles/inline_bots/inline_bot_layout_item.cpp @@ -166,7 +166,7 @@ Image *ItemBase::getResultThumb(Data::FileOrigin origin) const { QPixmap ItemBase::getResultContactAvatar(int width, int height) const { if (_result->_type == Result::Type::Contact) { auto result = Ui::EmptyUserpic( - Data::PeerUserpicColor(qHash(_result->_id)), + Data::PeerUserpicColor(FakeChatId(BareId(qHash(_result->_id)))), _result->getLayoutTitle() ).generate(width); if (result.height() != height * cIntRetinaFactor()) { diff --git a/Telegram/SourceFiles/inline_bots/inline_bot_result.cpp b/Telegram/SourceFiles/inline_bots/inline_bot_result.cpp index e8e2e75e7..a605a7122 100644 --- a/Telegram/SourceFiles/inline_bots/inline_bot_result.cpp +++ b/Telegram/SourceFiles/inline_bots/inline_bot_result.cpp @@ -51,7 +51,7 @@ Result::Result(not_null session, const Creator &creator) std::unique_ptr Result::Create( not_null session, uint64 queryId, - const MTPBotInlineResult &mtpData) { + const MTPBotInlineResult &data) { using Type = Result::Type; const auto type = [&] { @@ -69,7 +69,7 @@ std::unique_ptr Result::Create( { u"geo"_q, Type::Geo }, { u"game"_q, Type::Game }, }; - const auto type = mtpData.match([](const auto &data) { + const auto type = data.match([](const auto &data) { return qs(data.vtype()); }); const auto i = kStringToTypeMap.find(type); @@ -82,16 +82,13 @@ std::unique_ptr Result::Create( auto result = std::make_unique( session, Creator{ queryId, type }); - const MTPBotInlineMessage *message = nullptr; - switch (mtpData.type()) { - case mtpc_botInlineResult: { - const auto &r = mtpData.c_botInlineResult(); - result->_id = qs(r.vid()); - result->_title = qs(r.vtitle().value_or_empty()); - result->_description = qs(r.vdescription().value_or_empty()); - result->_url = qs(r.vurl().value_or_empty()); + const auto message = data.match([&](const MTPDbotInlineResult &data) { + result->_id = qs(data.vid()); + result->_title = qs(data.vtitle().value_or_empty()); + result->_description = qs(data.vdescription().value_or_empty()); + result->_url = qs(data.vurl().value_or_empty()); const auto thumbMime = [&] { - if (const auto thumb = r.vthumb()) { + if (const auto thumb = data.vthumb()) { return thumb->match([&](const auto &data) { return data.vmime_type().v; }); @@ -99,7 +96,7 @@ std::unique_ptr Result::Create( return QByteArray(); }(); const auto contentMime = [&] { - if (const auto content = r.vcontent()) { + if (const auto content = data.vcontent()) { return content->match([&](const auto &data) { return data.vmime_type().v; }); @@ -109,50 +106,45 @@ std::unique_ptr Result::Create( const auto imageThumb = !thumbMime.isEmpty() && (thumbMime != kVideoThumbMime); const auto videoThumb = !thumbMime.isEmpty() && !imageThumb; - if (const auto content = r.vcontent()) { + if (const auto content = data.vcontent()) { result->_content_url = GetContentUrl(*content); if (result->_type == Type::Photo) { result->_photo = session->data().photoFromWeb( *content, (imageThumb - ? Images::FromWebDocument(*r.vthumb()) + ? Images::FromWebDocument(*data.vthumb()) : ImageLocation())); } else if (contentMime != "text/html"_q) { result->_document = session->data().documentFromWeb( result->adjustAttributes(*content), (imageThumb - ? Images::FromWebDocument(*r.vthumb()) + ? Images::FromWebDocument(*data.vthumb()) : ImageLocation()), (videoThumb - ? Images::FromWebDocument(*r.vthumb()) + ? Images::FromWebDocument(*data.vthumb()) : ImageLocation())); } } if (!result->_photo && !result->_document && imageThumb) { result->_thumbnail.update(result->_session, ImageWithLocation{ - .location = Images::FromWebDocument(*r.vthumb()) - }); + .location = Images::FromWebDocument(*data.vthumb()) + }); } - message = &r.vsend_message(); - } break; - case mtpc_botInlineMediaResult: { - const auto &r = mtpData.c_botInlineMediaResult(); - result->_id = qs(r.vid()); - result->_title = qs(r.vtitle().value_or_empty()); - result->_description = qs(r.vdescription().value_or_empty()); - if (const auto photo = r.vphoto()) { + return &data.vsend_message(); + }, [&](const MTPDbotInlineMediaResult &data) { + result->_id = qs(data.vid()); + result->_title = qs(data.vtitle().value_or_empty()); + result->_description = qs(data.vdescription().value_or_empty()); + if (const auto photo = data.vphoto()) { result->_photo = session->data().processPhoto(*photo); } - if (const auto document = r.vdocument()) { + if (const auto document = data.vdocument()) { result->_document = session->data().processDocument(*document); } - message = &r.vsend_message(); - } break; - } - auto badAttachment = (result->_photo && result->_photo->isNull()) - || (result->_document && result->_document->isNull()); - - if (!message) { + return &data.vsend_message(); + }); + if ((result->_photo && result->_photo->isNull()) + || (result->_document && result->_document->isNull())) { return nullptr; } @@ -170,108 +162,114 @@ std::unique_ptr Result::Create( } } - switch (message->type()) { - case mtpc_botInlineMessageMediaAuto: { - const auto &r = message->c_botInlineMessageMediaAuto(); - const auto message = qs(r.vmessage()); + message->match([&](const MTPDbotInlineMessageMediaAuto &data) { + const auto message = qs(data.vmessage()); const auto entities = Api::EntitiesFromMTP( session, - r.ventities().value_or_empty()); + data.ventities().value_or_empty()); if (result->_type == Type::Photo) { - if (!result->_photo) { - return nullptr; + if (result->_photo) { + result->sendData = std::make_unique( + session, + result->_photo, + message, + entities); + } else { + LOG(("Inline Error: No 'photo' in media-auto, type=photo.")); } - result->sendData = std::make_unique( - session, - result->_photo, - message, - entities); } else if (result->_type == Type::Game) { result->createGame(session); result->sendData = std::make_unique( session, result->_game); } else { - if (!result->_document) { - return nullptr; + if (result->_document) { + result->sendData = std::make_unique( + session, + result->_document, + message, + entities); + } else { + LOG(("Inline Error: No 'document' in media-auto, type=%1." + ).arg(int(result->_type))); } - result->sendData = std::make_unique( - session, - result->_document, - message, - entities); } - if (const auto markup = r.vreply_markup()) { - result->_mtpKeyboard = std::make_unique(*markup); - } - } break; - - case mtpc_botInlineMessageText: { - const auto &r = message->c_botInlineMessageText(); + }, [&](const MTPDbotInlineMessageText &data) { result->sendData = std::make_unique( session, - qs(r.vmessage()), - Api::EntitiesFromMTP(session, r.ventities().value_or_empty()), - r.is_no_webpage()); - if (const auto markup = r.vreply_markup()) { - result->_mtpKeyboard = std::make_unique(*markup); - } - } break; - - case mtpc_botInlineMessageMediaGeo: { - // #TODO layer 72 save period and send live location?.. - auto &r = message->c_botInlineMessageMediaGeo(); - if (r.vgeo().type() == mtpc_geoPoint) { - result->sendData = std::make_unique( - session, - r.vgeo().c_geoPoint()); - } else { - badAttachment = true; - } - if (const auto markup = r.vreply_markup()) { - result->_mtpKeyboard = std::make_unique(*markup); - } - } break; - - case mtpc_botInlineMessageMediaVenue: { - auto &r = message->c_botInlineMessageMediaVenue(); - if (r.vgeo().type() == mtpc_geoPoint) { + qs(data.vmessage()), + Api::EntitiesFromMTP(session, data.ventities().value_or_empty()), + data.is_no_webpage()); + }, [&](const MTPDbotInlineMessageMediaGeo &data) { + data.vgeo().match([&](const MTPDgeoPoint &geo) { + if (const auto period = data.vperiod()) { + result->sendData = std::make_unique( + session, + geo, + period->v, + (data.vheading() + ? std::make_optional(data.vheading()->v) + : std::nullopt), + (data.vproximity_notification_radius() + ? std::make_optional( + data.vproximity_notification_radius()->v) + : std::nullopt)); + } else { + result->sendData = std::make_unique( + session, + geo); + } + }, [&](const MTPDgeoPointEmpty &) { + LOG(("Inline Error: Empty 'geo' in media-geo.")); + }); + }, [&](const MTPDbotInlineMessageMediaVenue &data) { + data.vgeo().match([&](const MTPDgeoPoint &geo) { result->sendData = std::make_unique( session, - r.vgeo().c_geoPoint(), - qs(r.vvenue_id()), - qs(r.vprovider()), - qs(r.vtitle()), - qs(r.vaddress())); - } else { - badAttachment = true; - } - if (const auto markup = r.vreply_markup()) { - result->_mtpKeyboard = std::make_unique(*markup); - } - } break; - - case mtpc_botInlineMessageMediaContact: { - auto &r = message->c_botInlineMessageMediaContact(); + geo, + qs(data.vvenue_id()), + qs(data.vprovider()), + qs(data.vtitle()), + qs(data.vaddress())); + }, [&](const MTPDgeoPointEmpty &) { + LOG(("Inline Error: Empty 'geo' in media-venue.")); + }); + }, [&](const MTPDbotInlineMessageMediaContact &data) { result->sendData = std::make_unique( session, - qs(r.vfirst_name()), - qs(r.vlast_name()), - qs(r.vphone_number())); - if (const auto markup = r.vreply_markup()) { - result->_mtpKeyboard = std::make_unique(*markup); - } - } break; + qs(data.vfirst_name()), + qs(data.vlast_name()), + qs(data.vphone_number())); + }, [&](const MTPDbotInlineMessageMediaInvoice &data) { + using Flag = MTPDmessageMediaInvoice::Flag; + const auto media = MTP_messageMediaInvoice( + MTP_flags((data.is_shipping_address_requested() + ? Flag::f_shipping_address_requested + : Flag(0)) + | (data.is_test() ? Flag::f_test : Flag(0)) + | (data.vphoto() ? Flag::f_photo : Flag(0))), + data.vtitle(), + data.vdescription(), + data.vphoto() ? (*data.vphoto()) : MTPWebDocument(), + MTPint(), // receipt_msg_id + data.vcurrency(), + data.vtotal_amount(), + MTP_string(QString())); // start_param + result->sendData = std::make_unique( + session, + media); + }); - default: { - badAttachment = true; - } break; - } - - if (badAttachment || !result->sendData || !result->sendData->isValid()) { + if (!result->sendData || !result->sendData->isValid()) { return nullptr; } + message->match([&](const auto &data) { + if (const auto markup = data.vreply_markup()) { + result->_mtpKeyboard = std::make_unique(*markup); + } + }); + if (const auto point = result->getLocationPoint()) { const auto scale = 1 + (cScale() * cIntRetinaFactor()) / 200; const auto zoom = 15 + (scale - 1); diff --git a/Telegram/SourceFiles/inline_bots/inline_bot_send_data.cpp b/Telegram/SourceFiles/inline_bots/inline_bot_send_data.cpp index 2088615ee..4b9c2bd87 100644 --- a/Telegram/SourceFiles/inline_bots/inline_bot_send_data.cpp +++ b/Telegram/SourceFiles/inline_bots/inline_bot_send_data.cpp @@ -59,7 +59,7 @@ void SendDataCommon::addToHistory( peerToMTP(fromId), peerToMTP(history->peer->id), MTPMessageFwdHeader(), - MTP_int(viaBotId), + MTP_int(viaBotId.bare), // #TODO ids replyHeader, mtpDate, fields.text, @@ -97,7 +97,18 @@ SendDataCommon::SentMTPMessageFields SendText::getSentMessageFields() const { SendDataCommon::SentMTPMessageFields SendGeo::getSentMessageFields() const { SentMTPMessageFields result; - result.media = MTP_messageMediaGeo(_location.toMTP()); + if (_period) { + using Flag = MTPDmessageMediaGeoLive::Flag; + result.media = MTP_messageMediaGeoLive( + MTP_flags((_heading ? Flag::f_heading : Flag(0)) + | (_proximityNotificationRadius ? Flag::f_proximity_notification_radius : Flag(0))), + _location.toMTP(), + MTP_int(_heading.value_or(0)), + MTP_int(*_period), + MTP_int(_proximityNotificationRadius.value_or(0))); + } else { + result.media = MTP_messageMediaGeo(_location.toMTP()); + } return result; } @@ -253,5 +264,15 @@ QString SendGame::getErrorOnSend( return error.value_or(QString()); } +auto SendInvoice::getSentMessageFields() const -> SentMTPMessageFields { + SentMTPMessageFields result; + result.media = _media; + return result; +} + +QString SendInvoice::getLayoutDescription(const Result *owner) const { + return qs(_media.c_messageMediaInvoice().vdescription()); +} + } // namespace internal } // namespace InlineBots diff --git a/Telegram/SourceFiles/inline_bots/inline_bot_send_data.h b/Telegram/SourceFiles/inline_bots/inline_bot_send_data.h index 5d9780190..012d80554 100644 --- a/Telegram/SourceFiles/inline_bots/inline_bot_send_data.h +++ b/Telegram/SourceFiles/inline_bots/inline_bot_send_data.h @@ -135,6 +135,18 @@ public: : SendDataCommon(session) , _location(point) { } + SendGeo( + not_null session, + const MTPDgeoPoint &point, + int period, + std::optional heading, + std::optional proximityNotificationRadius) + : SendDataCommon(session) + , _location(point) + , _period(period) + , _heading(heading) + , _proximityNotificationRadius(proximityNotificationRadius){ + } bool isValid() const override { return true; @@ -151,6 +163,9 @@ public: private: Data::LocationPoint _location; + std::optional _period; + std::optional _heading; + std::optional _proximityNotificationRadius; }; @@ -336,5 +351,27 @@ private: }; +class SendInvoice : public SendDataCommon { +public: + SendInvoice( + not_null session, + MTPMessageMedia media) + : SendDataCommon(session) + , _media(media) { + } + + bool isValid() const override { + return true; + } + + SentMTPMessageFields getSentMessageFields() const override; + + QString getLayoutDescription(const Result *owner) const override; + +private: + MTPMessageMedia _media; + +}; + } // namespace internal } // namespace InlineBots diff --git a/Telegram/SourceFiles/intro/intro.style b/Telegram/SourceFiles/intro/intro.style index 9b462d5c2..5d671be9f 100644 --- a/Telegram/SourceFiles/intro/intro.style +++ b/Telegram/SourceFiles/intro/intro.style @@ -167,9 +167,11 @@ introBackButton: IconButton(defaultIconButton) { } } -introQrTop: -6px; +introQrTop: -18px; introQrPixel: 50px; // large enough introQrMaxSize: 180px; +introQrBackgroundSkip: 12px; +introQrBackgroundRadius: 8px; introQrLabelsWidth: 292px; introQrTitleWidth: 320px; introQrTitle: FlatLabel(defaultFlatLabel) { diff --git a/Telegram/SourceFiles/intro/intro_code.cpp b/Telegram/SourceFiles/intro/intro_code.cpp index 65d326807..a2d288e64 100644 --- a/Telegram/SourceFiles/intro/intro_code.cpp +++ b/Telegram/SourceFiles/intro/intro_code.cpp @@ -438,6 +438,9 @@ void CodeWidget::noTelegramCodeFail(const MTP::Error &error) { _noTelegramCodeRequestId = 0; showCodeError(tr::lng_flood_error()); return; + } else if (error.type() == u"SEND_CODE_UNAVAILABLE"_q) { + _noTelegramCodeRequestId = 0; + return; } _noTelegramCodeRequestId = 0; diff --git a/Telegram/SourceFiles/intro/intro_phone.cpp b/Telegram/SourceFiles/intro/intro_phone.cpp index 57cc33156..6eac34d2a 100644 --- a/Telegram/SourceFiles/intro/intro_phone.cpp +++ b/Telegram/SourceFiles/intro/intro_phone.cpp @@ -46,12 +46,24 @@ PhoneWidget::PhoneWidget( , _code(this, st::introCountryCode) , _phone(this, st::introPhone) , _checkRequestTimer([=] { checkRequest(); }) { - connect(_phone, SIGNAL(voidBackspace(QKeyEvent*)), _code, SLOT(startErasing(QKeyEvent*))); - connect(_country, SIGNAL(codeChanged(const QString &)), _code, SLOT(codeSelected(const QString &))); - connect(_code, SIGNAL(codeChanged(const QString &)), _country, SLOT(onChooseCode(const QString &))); - connect(_code, SIGNAL(codeChanged(const QString &)), _phone, SLOT(onChooseCode(const QString &))); + _phone->frontBackspaceEvent( + ) | rpl::start_with_next([=](not_null e) { + _code->startErasing(e); + }, _code->lifetime()); + + connect(_country, &CountryInput::codeChanged, [=](const QString &code) { + _code->codeSelected(code); + }); + _code->codeChanged( + ) | rpl::start_with_next([=](const QString &code) { + _country->onChooseCode(code); + _phone->chooseCode(code); + }, _code->lifetime()); connect(_country, SIGNAL(codeChanged(const QString &)), _phone, SLOT(onChooseCode(const QString &))); - connect(_code, SIGNAL(addedToNumber(const QString &)), _phone, SLOT(addedToNumber(const QString &))); + _code->addedToNumber( + ) | rpl::start_with_next([=](const QString &added) { + _phone->addedToNumber(added); + }, _phone->lifetime()); connect(_phone, &Ui::PhonePartInput::changed, [=] { phoneChanged(); }); connect(_code, &Ui::CountryCodeInput::changed, [=] { phoneChanged(); }); @@ -61,8 +73,8 @@ PhoneWidget::PhoneWidget( setErrorCentered(true); setupQrLogin(); - if (!_country->onChooseCountry(getData()->country)) { - _country->onChooseCountry(qsl("US")); + if (!_country->chooseCountry(getData()->country)) { + _country->chooseCountry(qsl("US")); } _changed = false; } @@ -251,7 +263,7 @@ QString PhoneWidget::fullNumber() const { } void PhoneWidget::selectCountry(const QString &country) { - _country->onChooseCountry(country); + _country->chooseCountry(country); } void PhoneWidget::setInnerFocus() { diff --git a/Telegram/SourceFiles/intro/intro_qr.cpp b/Telegram/SourceFiles/intro/intro_qr.cpp index 09818fb7b..55f930099 100644 --- a/Telegram/SourceFiles/intro/intro_qr.cpp +++ b/Telegram/SourceFiles/intro/intro_qr.cpp @@ -33,7 +33,7 @@ namespace details { namespace { [[nodiscard]] QImage TelegramQrExact(const Qr::Data &data, int pixel) { - return Qr::Generate(data, pixel, st::windowFg->c); + return Qr::Generate(data, pixel, Qt::black); } [[nodiscard]] QImage TelegramQr(const Qr::Data &data, int pixel, int max = 0) { @@ -44,7 +44,7 @@ namespace { } const auto qr = TelegramQrExact(data, pixel * style::DevicePixelRatio()); auto result = QImage(qr.size(), QImage::Format_ARGB32_Premultiplied); - result.fill(st::windowBg->c); + result.fill(Qt::white); { auto p = QPainter(&result); p.drawImage(QRect(QPoint(), qr.size()), qr); @@ -52,6 +52,10 @@ namespace { return result; } +[[nodiscard]] QColor QrActiveColor() { + return QColor(0x40, 0xA7, 0xE3); // Default windowBgActive. +} + [[nodiscard]] not_null PrepareQrWidget( not_null parent, rpl::producer codes) { @@ -80,7 +84,8 @@ namespace { const auto state = result->lifetime().make_state( [=] { result->update(); }); state->waiting.start(); - result->resize(st::introQrMaxSize, st::introQrMaxSize); + const auto size = st::introQrMaxSize + 2 * st::introQrBackgroundSkip; + result->resize(size, size); rpl::combine( std::move(qrs), rpl::duplicate(palettes) @@ -107,14 +112,31 @@ namespace { result->paintRequest( ) | rpl::start_with_next([=](QRect clip) { auto p = QPainter(result); - const auto shown = state->qr.isNull() ? 0. : state->shown.value(1.); + const auto has = !state->qr.isNull(); + const auto shown = has ? state->shown.value(1.) : 0.; + const auto usualSize = 41; + const auto pixel = std::clamp( + st::introQrMaxSize / usualSize, + 1, + st::introQrPixel); + const auto size = has + ? (state->qr.size() / cIntRetinaFactor()) + : QSize(usualSize * pixel, usualSize * pixel); + const auto qr = QRect( + (result->width() - size.width()) / 2, + (result->height() - size.height()) / 2, + size.width(), + size.height()); + const auto radius = st::introQrBackgroundRadius; + const auto skip = st::introQrBackgroundSkip; + auto hq = PainterHighQualityEnabler(p); + p.setPen(Qt::NoPen); + p.setBrush(Qt::white); + p.drawRoundedRect( + qr.marginsAdded({ skip, skip, skip, skip }), + radius, + radius); if (!state->qr.isNull()) { - const auto size = state->qr.size() / cIntRetinaFactor(); - const auto qr = QRect( - (result->width() - size.width()) / 2, - (result->height() - size.height()) / 2, - size.width(), - size.height()); if (shown == 1.) { state->previous = QImage(); } else if (!state->previous.isNull()) { @@ -134,7 +156,7 @@ namespace { auto hq = PainterHighQualityEnabler(p); const auto line = st::radialLine; const auto radial = state->waiting.computeState(); - auto pen = st::activeButtonBg->p; + auto pen = QPen(QrActiveColor()); pen.setWidth(line); pen.setCapStyle(Qt::RoundCap); p.setOpacity(radial.shown * (1. - shown)); @@ -422,7 +444,7 @@ QImage TelegramLogoImage() { { auto p = QPainter(&result); auto hq = PainterHighQualityEnabler(p); - p.setBrush(st::activeButtonBg); + p.setBrush(QrActiveColor()); p.setPen(Qt::NoPen); p.drawEllipse(QRect(QPoint(), size)); st::introQrPlane.paintInCenter(p, QRect(QPoint(), size)); diff --git a/Telegram/SourceFiles/intro/intro_step.cpp b/Telegram/SourceFiles/intro/intro_step.cpp index 4fdf5a7b6..787db231b 100644 --- a/Telegram/SourceFiles/intro/intro_step.cpp +++ b/Telegram/SourceFiles/intro/intro_step.cpp @@ -153,7 +153,7 @@ void Step::finish(const MTPUser &user, QImage &&photo) { const auto raw = existing.get(); if (const auto session = raw->maybeSession()) { if (raw->mtp().environment() == _account->mtp().environment() - && user.c_user().vid().v == session->userId()) { + && UserId(user.c_user().vid()) == session->userId()) { _account->logOut(); crl::on_main(raw, [=] { Core::App().domain().activate(raw); diff --git a/Telegram/SourceFiles/kotato/json_settings.cpp b/Telegram/SourceFiles/kotato/json_settings.cpp index 464264048..75b12dd86 100644 --- a/Telegram/SourceFiles/kotato/json_settings.cpp +++ b/Telegram/SourceFiles/kotato/json_settings.cpp @@ -11,6 +11,7 @@ https://github.com/kotatogram/kotatogram-desktop/blob/dev/LEGAL #include "mainwidget.h" #include "window/window_controller.h" #include "core/application.h" +#include "data/data_peer_id.h" #include "base/parse_helper.h" #include "facades.h" #include "ui/widgets/input_fields.h" @@ -90,7 +91,7 @@ bool ReadObjectOption(QJsonObject obj, QString key, std::function callback, std::function test) { +bool ReadAccountObjectOption(QJsonObject obj, QString key, std::function callback, std::function test) { auto readResult = false; auto readValueResult = ReadOption(obj, key, [&](QJsonValue v) { if (!v.isObject()) { @@ -111,14 +112,16 @@ bool ReadAccountObjectOption(QJsonObject obj, QString key, std::function::from_raw(folder.flags); - if (folder.ownerId < 0) { + if (folder.isTest) { accountId.prepend("test_"); } @@ -326,8 +331,8 @@ QByteArray GenerateSettingsJson(bool areDefault = false) { auto folderNever = QJsonArray(); for (auto peer : folder.never) { auto peerObj = QJsonObject(); - peerObj.insert(qsl("type"), peerTypeToStr(peer.type)); - peerObj.insert(qsl("id"), peer.id); + peerObj.insert(qsl("type"), peerToStr(peer)); + peerObj.insert(qsl("id"), peerToLocalBare(peer)); folderNever << peerObj; } folderObject.insert(qsl("never"), folderNever); @@ -335,8 +340,8 @@ QByteArray GenerateSettingsJson(bool areDefault = false) { auto folderPinned = QJsonArray(); for (auto peer : folder.pinned) { auto peerObj = QJsonObject(); - peerObj.insert(qsl("type"), peerTypeToStr(peer.type)); - peerObj.insert(qsl("id"), peer.id); + peerObj.insert(qsl("type"), peerToStr(peer)); + peerObj.insert(qsl("id"), peerToLocalBare(peer)); folderPinned << peerObj; } folderObject.insert(qsl("pinned"), folderPinned); @@ -344,8 +349,8 @@ QByteArray GenerateSettingsJson(bool areDefault = false) { auto folderAlways = QJsonArray(); for (auto peer : folder.always) { auto peerObj = QJsonObject(); - peerObj.insert(qsl("type"), peerTypeToStr(peer.type)); - peerObj.insert(qsl("id"), peer.id); + peerObj.insert(qsl("type"), peerToStr(peer)); + peerObj.insert(qsl("id"), peerToLocalBare(peer)); folderAlways << peerObj; } folderObject.insert(qsl("always"), folderAlways); @@ -646,12 +651,16 @@ bool Manager::readCustomFile() { ReadObjectOption(settings, "folders", [&](auto o) { auto isDefaultFilterRead = ReadIntOption(o, "default", [&](auto v) { - SetDefaultFilterId(0, v); + SetDefaultFilterId("0", v); }); if (!isDefaultFilterRead) { - ReadAccountObjectOption(o, "default", [&](auto account_id, auto value) { - SetDefaultFilterId(account_id, value.toInt(0)); + ReadAccountObjectOption(o, "default", [&](auto accountId, auto isTestAccount, auto value) { + auto account = QString::number(accountId); + if (isTestAccount) { + account = account.prepend("test_"); + } + SetDefaultFilterId(account, value.toInt(0)); }, [](auto v) { return v.toInt(0) != 0; }); @@ -673,7 +682,7 @@ bool Manager::readCustomFile() { cSetHideFilterAllChats(v); }); - ReadAccountObjectOption(o, "local", [&](auto account_id, auto value) { + ReadAccountObjectOption(o, "local", [&](auto accountId, auto isTestAccount, auto value) { auto v = value.toArray(); auto &folderOptionRef = cRefLocalFolders(); for (auto i = v.constBegin(), e = v.constEnd(); i != e; ++i) { @@ -683,7 +692,8 @@ bool Manager::readCustomFile() { const auto folderObject = (*i).toObject(); LocalFolder folderStruct; - folderStruct.ownerId = account_id; + folderStruct.ownerId = accountId; + folderStruct.isTest = isTestAccount; auto flags = base::flags(0); ReadIntOption(folderObject, "id", [&](auto id) { @@ -800,29 +810,35 @@ bool Manager::readCustomFile() { } auto peer = (*j).toObject(); - LocalFolder::Peer peerStruct; + BareId peerId = 0; auto isPeerIdRead = ReadIntOption(peer, "id", [&](auto id) { - peerStruct.id = id; + peerId = id; }); - if (peerStruct.id == 0 || !isPeerIdRead) { + if (!isPeerIdRead) { + isPeerIdRead = ReadStringOption(peer, "id", [&](auto id) { + peerId = static_cast(id.toLongLong()); + }); + } + + if (peerId == 0 || !isPeerIdRead) { continue; } auto isPeerTypeRead = ReadStringOption(peer, "type", [&](auto type) { - peerStruct.type = (QString::compare(type.toLower(), "channel") == 0) - ? LocalFolder::Peer::Type::Channel + peerId = (QString::compare(type.toLower(), "channel") == 0) + ? peerFromChannel(ChannelId(peerId)).value : (QString::compare(type.toLower(), "chat") == 0) - ? LocalFolder::Peer::Type::Chat - : LocalFolder::Peer::Type::User; + ? peerFromChat(ChatId(peerId)).value + : peerFromUser(UserId(peerId)).value; }); if (!isPeerTypeRead) { - peerStruct.type = LocalFolder::Peer::Type::User; + peerId = peerFromUser(UserId(peerId)).value; } - folderStruct.never.push_back(peerStruct); + folderStruct.never.push_back(peerId); } }); @@ -833,29 +849,35 @@ bool Manager::readCustomFile() { } auto peer = (*j).toObject(); - LocalFolder::Peer peerStruct; + BareId peerId = 0; auto isPeerIdRead = ReadIntOption(peer, "id", [&](auto id) { - peerStruct.id = id; + peerId = id; }); - if (peerStruct.id == 0 || !isPeerIdRead) { + if (!isPeerIdRead) { + isPeerIdRead = ReadStringOption(peer, "id", [&](auto id) { + peerId = static_cast(id.toLongLong()); + }); + } + + if (peerId == 0 || !isPeerIdRead) { continue; } auto isPeerTypeRead = ReadStringOption(peer, "type", [&](auto type) { - peerStruct.type = (QString::compare(type.toLower(), "channel") == 0) - ? LocalFolder::Peer::Type::Channel + peerId = (QString::compare(type.toLower(), "channel") == 0) + ? peerFromChannel(ChannelId(peerId)).value : (QString::compare(type.toLower(), "chat") == 0) - ? LocalFolder::Peer::Type::Chat - : LocalFolder::Peer::Type::User; + ? peerFromChat(ChatId(peerId)).value + : peerFromUser(UserId(peerId)).value; }); if (!isPeerTypeRead) { - peerStruct.type = LocalFolder::Peer::Type::User; + peerId = peerFromUser(UserId(peerId)).value; } - folderStruct.pinned.push_back(peerStruct); + folderStruct.pinned.push_back(peerId); } }); @@ -866,29 +888,35 @@ bool Manager::readCustomFile() { } auto peer = (*j).toObject(); - LocalFolder::Peer peerStruct; + BareId peerId = 0; auto isPeerIdRead = ReadIntOption(peer, "id", [&](auto id) { - peerStruct.id = id; + peerId = id; }); - if (peerStruct.id == 0 || !isPeerIdRead) { + if (!isPeerIdRead) { + isPeerIdRead = ReadStringOption(peer, "id", [&](auto id) { + peerId = static_cast(id.toLongLong()); + }); + } + + if (peerId == 0 || !isPeerIdRead) { continue; } auto isPeerTypeRead = ReadStringOption(peer, "type", [&](auto type) { - peerStruct.type = (QString::compare(type.toLower(), "channel") == 0) - ? LocalFolder::Peer::Type::Channel + peerId = (QString::compare(type.toLower(), "channel") == 0) + ? peerFromChannel(ChannelId(peerId)).value : (QString::compare(type.toLower(), "chat") == 0) - ? LocalFolder::Peer::Type::Chat - : LocalFolder::Peer::Type::User; + ? peerFromChat(ChatId(peerId)).value + : peerFromUser(UserId(peerId)).value; }); if (!isPeerTypeRead) { - peerStruct.type = LocalFolder::Peer::Type::User; + peerId = peerFromUser(UserId(peerId)).value; } - folderStruct.always.push_back(peerStruct); + folderStruct.always.push_back(peerId); } }); diff --git a/Telegram/SourceFiles/kotato/settings.cpp b/Telegram/SourceFiles/kotato/settings.cpp index 4614fc505..d2ae752fe 100644 --- a/Telegram/SourceFiles/kotato/settings.cpp +++ b/Telegram/SourceFiles/kotato/settings.cpp @@ -170,23 +170,23 @@ bool gUseTelegramPanelIcon = false; int gCustomAppIcon = 0; DefaultFilterMap gDefaultFilterId; -void SetDefaultFilterId(int account, int filter) { +void SetDefaultFilterId(QString account, int filter) { if (gDefaultFilterId.contains(account)) { gDefaultFilterId[account] = filter; } else { gDefaultFilterId.insert(account, filter); } } -int DefaultFilterId(int account) { +int DefaultFilterId(QString account) { if (gDefaultFilterId.contains(account)) { return gDefaultFilterId[account]; } return 0; } -bool HasDefaultFilterId(int account) { +bool HasDefaultFilterId(QString account) { return gDefaultFilterId.contains(account); } -bool ClearDefaultFilterId(int account) { +bool ClearDefaultFilterId(QString account) { return gDefaultFilterId.remove(account); } bool gUnmutedFilterCounterOnly = false; diff --git a/Telegram/SourceFiles/kotato/settings.h b/Telegram/SourceFiles/kotato/settings.h index 209c5133d..9ee1187bd 100644 --- a/Telegram/SourceFiles/kotato/settings.h +++ b/Telegram/SourceFiles/kotato/settings.h @@ -115,12 +115,12 @@ DeclareSetting(bool, DisableTrayCounter); DeclareSetting(bool, UseTelegramPanelIcon); DeclareSetting(int, CustomAppIcon); -using DefaultFilterMap = QMap; +using DefaultFilterMap = QMap; DeclareRefSetting(DefaultFilterMap, DefaultFilterId); -void SetDefaultFilterId(int account, int filter); -int DefaultFilterId(int account); -bool HasDefaultFilterId(int account); -bool ClearDefaultFilterId(int account); +void SetDefaultFilterId(QString account, int filter); +int DefaultFilterId(QString account); +bool HasDefaultFilterId(QString account); +bool ClearDefaultFilterId(QString account); DeclareSetting(bool, UnmutedFilterCounterOnly); DeclareSetting(bool, HideFilterEditButton); DeclareSetting(bool, HideFilterNames); @@ -146,30 +146,15 @@ DeclareSetting(bool, ForwardAlbumsAsIs); DeclareSetting(bool, ForwardGrouped); struct LocalFolder { - struct Peer { - enum Type { - User = 0, - Chat = 1, - Channel = 2, - }; - - Type type; - int32 id; - - inline bool operator==(const Peer& other) { - return type == other.type - && id == other.id; - } - }; - int id; - int ownerId; + uint64 ownerId; + bool isTest; int cloudOrder; QString name; QString emoticon; - std::vector always; - std::vector never; - std::vector pinned; + std::vector always; + std::vector never; + std::vector pinned; ushort flags; }; diff --git a/Telegram/SourceFiles/lang/lang_file_parser.cpp b/Telegram/SourceFiles/lang/lang_file_parser.cpp index 8aa860cf8..740586926 100644 --- a/Telegram/SourceFiles/lang/lang_file_parser.cpp +++ b/Telegram/SourceFiles/lang/lang_file_parser.cpp @@ -8,7 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "lang/lang_file_parser.h" #include "base/parse_helper.h" -#include "ui/integration.h" +#include "base/debug_log.h" #include #include @@ -155,18 +155,19 @@ bool FileParser::readKeyValue(const char *&from, const char *end) { QByteArray FileParser::ReadFile(const QString &absolutePath, const QString &relativePath) { QFile file(QFileInfo::exists(relativePath) ? relativePath : absolutePath); if (!file.open(QIODevice::ReadOnly)) { - Ui::Integration::Instance().writeLogEntry(u"Lang Error: Could not open file at '%1' ('%2')"_q.arg(relativePath, absolutePath)); + LOG(("Lang Error: Could not open file at '%1' ('%2')") + .arg(relativePath, absolutePath)); return QByteArray(); } if (file.size() > kLangFileLimit) { - Ui::Integration::Instance().writeLogEntry(u"Lang Error: File is too big: %1"_q.arg(file.size())); + LOG(("Lang Error: File is too big: %1").arg(file.size())); return QByteArray(); } constexpr auto kCodecMagicSize = 3; auto codecMagic = file.read(kCodecMagicSize); if (codecMagic.size() < kCodecMagicSize) { - Ui::Integration::Instance().writeLogEntry(u"Lang Error: Found bad file at '%1' ('%2')"_q.arg(relativePath, absolutePath)); + LOG(("Lang Error: Found bad file at '%1' ('%2')").arg(relativePath, absolutePath)); return QByteArray(); } file.seek(0); @@ -177,11 +178,11 @@ QByteArray FileParser::ReadFile(const QString &absolutePath, const QString &rela stream.setCodec("UTF-16"); auto string = stream.readAll(); if (stream.status() != QTextStream::Ok) { - Ui::Integration::Instance().writeLogEntry(u"Lang Error: Could not read UTF-16 data from '%1' ('%2')"_q.arg(relativePath, absolutePath)); + LOG(("Lang Error: Could not read UTF-16 data from '%1' ('%2')").arg(relativePath, absolutePath)); return QByteArray(); } if (string.isEmpty()) { - Ui::Integration::Instance().writeLogEntry(u"Lang Error: Empty UTF-16 content in '%1' ('%2')"_q.arg(relativePath, absolutePath)); + LOG(("Lang Error: Empty UTF-16 content in '%1' ('%2')").arg(relativePath, absolutePath)); return QByteArray(); } return string.toUtf8(); @@ -197,7 +198,7 @@ QByteArray FileParser::ReadFile(const QString &absolutePath, const QString &rela data = data.mid(3); // skip UTF-8 BOM } if (data.isEmpty()) { - Ui::Integration::Instance().writeLogEntry(u"Lang Error: Empty UTF-8 content in '%1' ('%2')"_q.arg(relativePath, absolutePath)); + LOG(("Lang Error: Empty UTF-8 content in '%1' ('%2')").arg(relativePath, absolutePath)); return QByteArray(); } return data; diff --git a/Telegram/SourceFiles/logs.cpp b/Telegram/SourceFiles/logs.cpp index 9941ab5ff..7ad8bbbda 100644 --- a/Telegram/SourceFiles/logs.cpp +++ b/Telegram/SourceFiles/logs.cpp @@ -323,11 +323,6 @@ bool DebugEnabled() { #endif } -QString ProfilePrefix() { - const auto now = crl::profile(); - return '[' + QString::number(now / 1000., 'f', 3) + "] "; -} - void start(not_null launcher) { Assert(LogsData == nullptr); @@ -527,29 +522,21 @@ void writeMain(const QString &v) { struct tm tm; mylocaltime(&tm, &t); - QString msg(QString("[%1.%2.%3 %4:%5:%6] %7\n").arg(tm.tm_year + 1900).arg(tm.tm_mon + 1, 2, 10, QChar('0')).arg(tm.tm_mday, 2, 10, QChar('0')).arg(tm.tm_hour, 2, 10, QChar('0')).arg(tm.tm_min, 2, 10, QChar('0')).arg(tm.tm_sec, 2, 10, QChar('0')).arg(v)); + const auto msg = QString("[%1.%2.%3 %4:%5:%6] %7\n" + ).arg(tm.tm_year + 1900 + ).arg(tm.tm_mon + 1, 2, 10, QChar('0') + ).arg(tm.tm_mday, 2, 10, QChar('0') + ).arg(tm.tm_hour, 2, 10, QChar('0') + ).arg(tm.tm_min, 2, 10, QChar('0') + ).arg(tm.tm_sec, 2, 10, QChar('0') + ).arg(v); _logsWrite(LogDataMain, msg); - QString debugmsg(QString("%1 %2\n").arg(_logsEntryStart(), v)); - _logsWrite(LogDataDebug, debugmsg); + writeDebug(v); } -void writeDebug(const char *file, int32 line, const QString &v) { - const char *last = strstr(file, "/"), *found = 0; - while (last) { - found = last; - last = strstr(last + 1, "/"); - } - last = strstr(file, "\\"); - while (last) { - found = last; - last = strstr(last + 1, "\\"); - } - if (found) { - file = found + 1; - } - - QString msg(QString("%1 %2 (%3 : %4)\n").arg(_logsEntryStart(), v, file, QString::number(line))); +void writeDebug(const QString &v) { + const auto msg = QString("%1 %2\n").arg(_logsEntryStart(), v); _logsWrite(LogDataDebug, msg); #ifdef Q_OS_WIN @@ -562,12 +549,15 @@ void writeDebug(const char *file, int32 line, const QString &v) { } void writeTcp(const QString &v) { - QString msg(QString("%1 %2\n").arg(_logsEntryStart(), v)); + const auto msg = QString("%1 %2\n").arg(_logsEntryStart(), v); _logsWrite(LogDataTcp, msg); } void writeMtp(int32 dc, const QString &v) { - QString msg(QString("%1 (dc:%2) %3\n").arg(_logsEntryStart()).arg(dc).arg(v)); + const auto msg = QString("%1 (dc:%2) %3\n").arg( + _logsEntryStart(), + QString::number(dc), + v); _logsWrite(LogDataMtp, msg); } diff --git a/Telegram/SourceFiles/logs.h b/Telegram/SourceFiles/logs.h index 14d2d216e..e42bf8b22 100644 --- a/Telegram/SourceFiles/logs.h +++ b/Telegram/SourceFiles/logs.h @@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/basic_types.h" #include "base/assertion.h" +#include "base/debug_log.h" namespace Core { class Launcher; @@ -19,8 +20,6 @@ namespace Logs { void SetDebugEnabled(bool enabled); bool DebugEnabled(); -QString ProfilePrefix(); - void start(not_null launcher); bool started(); void finish(); @@ -31,8 +30,7 @@ void multipleInstances(); void closeMain(); void writeMain(const QString &v); - -void writeDebug(const char *file, int32 line, const QString &v); +void writeDebug(const QString &v); void writeTcp(const QString &v); void writeMtp(int32 dc, const QString &v); @@ -70,18 +68,6 @@ inline MemoryBuffer mb(const void *ptr, uint32 size) { } // namespace Logs -#define LOG(msg) (Logs::writeMain(QString msg)) -//usage LOG(("log: %1 %2").arg(1).arg(2)) - -#define PROFILE_LOG(msg) (Logs::writeMain(Logs::ProfilePrefix() + QString msg)) - -#define DEBUG_LOG(msg) {\ - if (Logs::DebugEnabled() || !Logs::started()) {\ - Logs::writeDebug(SOURCE_FILE_BASENAME, __LINE__, QString msg);\ - }\ -} -//usage DEBUG_LOG(("log: %1 %2").arg(1).arg(2)) - #define TCP_LOG(msg) {\ if (Logs::DebugEnabled() || !Logs::started()) {\ Logs::writeTcp(QString msg);\ diff --git a/Telegram/SourceFiles/main/main_account.cpp b/Telegram/SourceFiles/main/main_account.cpp index 61325b21b..8f4569165 100644 --- a/Telegram/SourceFiles/main/main_account.cpp +++ b/Telegram/SourceFiles/main/main_account.cpp @@ -36,6 +36,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Main { namespace { +constexpr auto kWideIdsTag = ~uint64(0); + [[nodiscard]] QString ComposeDataString(const QString &dataName, int index) { auto result = dataName; result.replace('#', QString()); @@ -128,7 +130,7 @@ uint64 Account::willHaveSessionUniqueId(MTP::Config *config) const { if (!_sessionUserId) { return 0; } - return uint64(uint32(_sessionUserId)) + return _sessionUserId.bare | (config && config->isTestMode() ? 0x0100'0000'0000'0000ULL : 0ULL); } @@ -156,7 +158,7 @@ void Account::createSession( createSession( MTP_user( MTP_flags(flags), - MTP_int(base::take(_sessionUserId)), + MTP_int(base::take(_sessionUserId).bare), // #TODO ids MTPlong(), // access_hash MTPstring(), // first_name MTPstring(), // last_name @@ -190,13 +192,15 @@ void Account::createSession( Ensures(_session != nullptr); - const auto defaultFilterUserId = _mtp->isTestMode() - ? -session().userId() - : session().userId(); + auto defaultFilterUserId = QString::number(session().userId().bare); - if (HasDefaultFilterId(0)) { - const auto newDefaultFilterId = DefaultFilterId(0); - ClearDefaultFilterId(0); + if (_mtp->isTestMode()) { + defaultFilterUserId.prepend("test_"); + } + + if (HasDefaultFilterId("0")) { + const auto newDefaultFilterId = DefaultFilterId("0"); + ClearDefaultFilterId("0"); setDefaultFilterId(newDefaultFilterId); Kotato::JsonSettings::Write(); } else { @@ -290,7 +294,8 @@ QByteArray Account::serializeMtpAuthorization() const { }; auto result = QByteArray(); - auto size = sizeof(qint32) + sizeof(qint32); // userId + mainDcId + // wide tag + userId + mainDcId + auto size = 2 * sizeof(quint64) + sizeof(qint32); size += keysSize(keys) + keysSize(keysToDestroy); result.reserve(size); { @@ -299,12 +304,17 @@ QByteArray Account::serializeMtpAuthorization() const { const auto currentUserId = sessionExists() ? session().userId() - : 0; - stream << qint32(currentUserId) << qint32(mainDcId); + : UserId(); + stream + << quint64(kWideIdsTag) + << quint64(currentUserId.bare) + << qint32(mainDcId); writeKeys(stream, keys); writeKeys(stream, keysToDestroy); - DEBUG_LOG(("MTP Info: Keys written, userId: %1, dcId: %2").arg(currentUserId).arg(mainDcId)); + DEBUG_LOG(("MTP Info: Keys written, userId: %1, dcId: %2" + ).arg(currentUserId.bare + ).arg(mainDcId)); } return result; }; @@ -357,8 +367,18 @@ void Account::setMtpAuthorization(const QByteArray &serialized) { QDataStream stream(serialized); stream.setVersion(QDataStream::Qt_5_1); - auto userId = Serialize::read(stream); - auto mainDcId = Serialize::read(stream); + auto legacyUserId = Serialize::read(stream); + auto legacyMainDcId = Serialize::read(stream); + auto userId = quint64(); + auto mainDcId = qint32(); + if (((uint64(legacyUserId) << 32) | uint64(legacyMainDcId)) + == kWideIdsTag) { + userId = Serialize::read(stream); + mainDcId = Serialize::read(stream); + } else { + userId = legacyUserId; + mainDcId = legacyMainDcId; + } if (stream.status() != QDataStream::Ok) { LOG(("MTP Error: " "Could not read main fields from mtp authorization.")); @@ -589,14 +609,16 @@ void Account::destroyStaleAuthorizationKeys() { } } -void Account::setDefaultFilterId(int id) { +void Account::setDefaultFilterId(uint64 id) { Expects(_mtp != nullptr); Expects(_session != nullptr); _defaultFilterId = id; - const auto defaultFilterUserId = _mtp->isTestMode() - ? -session().userId() - : session().userId(); + auto defaultFilterUserId = QString::number(session().userId().bare); + + if (_mtp->isTestMode()) { + defaultFilterUserId.prepend("test_"); + } if (id == 0) { ClearDefaultFilterId(defaultFilterUserId); @@ -605,23 +627,22 @@ void Account::setDefaultFilterId(int id) { } } -bool Account::isCurrent(int id) { +bool Account::isCurrent(uint64 id, bool testMode) { Expects(_mtp != nullptr); Expects(_session != nullptr); - return id == (_mtp->isTestMode() - ? -session().userId() - : session().userId()); + return id == session().userId().bare + && _mtp->isTestMode() == testMode; } void Account::addToRecent(PeerId id) { - if (!_recent.contains(id)) { - _recent << id; + if (!_recent.contains(id.value)) { + _recent << id.value; } } bool Account::isRecent(PeerId id) { - return _recent.contains(id); + return _recent.contains(id.value); } void Account::resetAuthorizationKeys() { diff --git a/Telegram/SourceFiles/main/main_account.h b/Telegram/SourceFiles/main/main_account.h index 180534b90..bfa3f10d9 100644 --- a/Telegram/SourceFiles/main/main_account.h +++ b/Telegram/SourceFiles/main/main_account.h @@ -110,9 +110,9 @@ public: [[nodiscard]] int defaultFilterId() { return _defaultFilterId; } - void setDefaultFilterId(int id); + void setDefaultFilterId(uint64 id); - [[nodiscard]] bool isCurrent(int id); + [[nodiscard]] bool isCurrent(uint64 id, bool testMode); void addToRecent(PeerId id); [[nodiscard]] bool isRecent(PeerId id); @@ -164,7 +164,7 @@ private: bool _loggingOut = false; int _defaultFilterId = 0; - QSet _recent; + QSet _recent; rpl::lifetime _lifetime; diff --git a/Telegram/SourceFiles/main/main_session.cpp b/Telegram/SourceFiles/main/main_session.cpp index fcb1b1064..62e7584dc 100644 --- a/Telegram/SourceFiles/main/main_session.cpp +++ b/Telegram/SourceFiles/main/main_session.cpp @@ -31,6 +31,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "window/window_lock_widgets.h" #include "window/themes/window_theme.h" //#include "platform/platform_specific.h" +#include "base/unixtime.h" #include "calls/calls_instance.h" #include "support/support_helper.h" #include "facades.h" @@ -43,6 +44,7 @@ namespace Main { namespace { constexpr auto kLegacyCallsPeerToPeerNobody = 4; +constexpr auto kTmpPasswordReserveTime = TimeId(10); [[nodiscard]] QString ValidatedInternalLinksDomain( not_null session) { @@ -155,6 +157,20 @@ Session::Session( _api->requestNotifySettings(MTP_inputNotifyBroadcasts()); } +void Session::setTmpPassword(const QByteArray &password, TimeId validUntil) { + if (_tmpPassword.isEmpty() || validUntil > _tmpPasswordValidUntil) { + _tmpPassword = password; + _tmpPasswordValidUntil = validUntil; + } +} + +QByteArray Session::validTmpPassword() const { + return (_tmpPasswordValidUntil + >= base::unixtime::now() + kTmpPasswordReserveTime) + ? _tmpPassword + : QByteArray(); +} + // Can be called only right before ~Session. void Session::finishLogout() { updates().updateOnline(); @@ -196,12 +212,12 @@ rpl::producer<> Session::downloaderTaskFinished() const { uint64 Session::uniqueId() const { // See also Account::willHaveSessionUniqueId. - return uint64(uint32(userId())) + return userId().bare | (mtp().isTestMode() ? 0x0100'0000'0000'0000ULL : 0ULL); } UserId Session::userId() const { - return _user->bareId(); + return peerToUser(_user->id); } PeerId Session::userPeerId() const { @@ -212,7 +228,7 @@ bool Session::validateSelf(const MTPUser &user) { if (user.type() != mtpc_user || !user.c_user().is_self()) { LOG(("API Error: bad self user received.")); return false; - } else if (user.c_user().vid().v != userId()) { + } else if (UserId(user.c_user().vid()) != userId()) { LOG(("Auth Error: wrong self user received.")); crl::on_main(this, [=] { _account->logOut(); }); return false; diff --git a/Telegram/SourceFiles/main/main_session.h b/Telegram/SourceFiles/main/main_session.h index 712e86397..cd29bcb5b 100644 --- a/Telegram/SourceFiles/main/main_session.h +++ b/Telegram/SourceFiles/main/main_session.h @@ -145,6 +145,9 @@ public: [[nodiscard]] QString createInternalLink(const QString &query) const; [[nodiscard]] QString createInternalLinkFull(const QString &query) const; + void setTmpPassword(const QByteArray &password, TimeId validUntil); + [[nodiscard]] QByteArray validTmpPassword() const; + // Can be called only right before ~Session. void finishLogout(); @@ -190,6 +193,9 @@ private: base::flat_set> _windows; base::Timer _saveSettingsTimer; + QByteArray _tmpPassword; + TimeId _tmpPasswordValidUntil = 0; + rpl::lifetime _lifetime; }; diff --git a/Telegram/SourceFiles/main/main_session_settings.cpp b/Telegram/SourceFiles/main/main_session_settings.cpp index ba7e97919..44003707b 100644 --- a/Telegram/SourceFiles/main/main_session_settings.cpp +++ b/Telegram/SourceFiles/main/main_session_settings.cpp @@ -48,8 +48,8 @@ QByteArray SessionSettings::serialize() const { stream << qint32(kVersionTag) << qint32(kVersion); stream << static_cast(_selectorTab); stream << qint32(_groupStickersSectionHidden.size()); - for (auto peerId : _groupStickersSectionHidden) { - stream << quint64(peerId); + for (const auto peerId : _groupStickersSectionHidden) { + stream << SerializePeerId(peerId); } stream << qint32(_supportSwitch); stream << qint32(_supportFixChatsOrder ? 1 : 0); @@ -66,7 +66,7 @@ QByteArray SessionSettings::serialize() const { } stream << qint32(_hiddenPinnedMessages.size()); for (const auto &[key, value] : _hiddenPinnedMessages) { - stream << quint64(key) << qint32(value); + stream << SerializePeerId(key) << qint32(value); } stream << qint32(_dialogsFiltersEnabled ? 1 : 0); stream << qint32(_supportAllSilent ? 1 : 0); @@ -177,7 +177,8 @@ void SessionSettings::addFromSerialized(const QByteArray &serialized) { "Bad data for SessionSettings::addFromSerialized()")); return; } - groupStickersSectionHidden.insert(peerId); + groupStickersSectionHidden.emplace( + DeserializePeerId(peerId)); } } } @@ -316,7 +317,7 @@ void SessionSettings::addFromSerialized(const QByteArray &serialized) { "Bad data for SessionSettings::addFromSerialized()")); return; } - hiddenPinnedMessages.emplace(key, value); + hiddenPinnedMessages.emplace(DeserializePeerId(key), value); } } } diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 94f20ba0a..3e89b7cb6 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -999,6 +999,7 @@ void MainWidget::setCurrentGroupCall(Calls::GroupCall *call) { ) | rpl::start_with_next([=](Calls::GroupCall::State state) { using State = Calls::GroupCall::State; if (state != State::Creating + && state != State::Waiting && state != State::Joining && state != State::Joined && state != State::Connecting) { diff --git a/Telegram/SourceFiles/mainwindow.cpp b/Telegram/SourceFiles/mainwindow.cpp index 6049c026d..654bf650a 100644 --- a/Telegram/SourceFiles/mainwindow.cpp +++ b/Telegram/SourceFiles/mainwindow.cpp @@ -556,8 +556,13 @@ bool MainWindow::doWeMarkAsRead() { if (!_main || Ui::isLayerShown()) { return false; } - updateIsActive(); - return isActive() && _main->doWeMarkAsRead(); + // for tile grid in case other windows have shadows + // i've seen some windows with >70px shadow margins + const auto margin = style::ConvertScale(100); + return Ui::IsContentVisible( + this, + inner().marginsRemoved(QMargins(margin, margin, margin, margin))) + && _main->doWeMarkAsRead(); } void MainWindow::checkHistoryActivation() { diff --git a/Telegram/SourceFiles/media/view/media_view_group_thumbs.cpp b/Telegram/SourceFiles/media/view/media_view_group_thumbs.cpp index 8ec36ce2c..9b64a583d 100644 --- a/Telegram/SourceFiles/media/view/media_view_group_thumbs.cpp +++ b/Telegram/SourceFiles/media/view/media_view_group_thumbs.cpp @@ -39,15 +39,15 @@ using Context = GroupThumbs::Context; using Key = GroupThumbs::Key; [[nodiscard]] QString DebugSerializeMsgId(FullMsgId itemId) { - return QString("msg%1_%2").arg(itemId.channel).arg(itemId.msg); + return QString("msg%1_%2").arg(itemId.channel.bare).arg(itemId.msg); } [[nodiscard]] QString DebugSerializePeer(PeerId peerId) { return peerIsUser(peerId) - ? QString("user%1").arg(peerToUser(peerId)) + ? QString("user%1").arg(peerToUser(peerId).bare) : peerIsChat(peerId) - ? QString("chat%1").arg(peerToChat(peerId)) - : QString("channel%1").arg(peerToChannel(peerId)); + ? QString("chat%1").arg(peerToChat(peerId).bare) + : QString("channel%1").arg(peerToChannel(peerId).bare); } [[nodiscard]] QString DebugSerializeKey(const Key &key) { diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp index 1a8c61596..584572587 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp @@ -1779,7 +1779,7 @@ Data::FileOrigin OverlayWidget::fileOrigin() const { if (_msgid) { return _msgid; } else if (_photo && _user) { - return Data::FileOriginUserPhoto(_user->bareId(), _photo->id); + return Data::FileOriginUserPhoto(peerToUser(_user->id), _photo->id); } else if (_photo && _peer && _peer->userpicPhotoId() == _photo->id) { return Data::FileOriginPeerPhoto(_peer->id); } @@ -1794,7 +1794,7 @@ Data::FileOrigin OverlayWidget::fileOrigin(const Entity &entity) const { } const auto photo = v::get>(entity.data); if (_user) { - return Data::FileOriginUserPhoto(_user->bareId(), photo->id); + return Data::FileOriginUserPhoto(peerToUser(_user->id), photo->id); } else if (_peer && _peer->userpicPhotoId() == photo->id) { return Data::FileOriginPeerPhoto(_peer->id); } @@ -1875,10 +1875,7 @@ void OverlayWidget::handleSharedMediaUpdate(SharedMediaWithLastSlice &&update) { std::optional OverlayWidget::userPhotosKey() const { if (!_msgid && _user && _photo) { - return UserPhotosKey { - _user->bareId(), - _photo->id - }; + return UserPhotosKey{ peerToUser(_user->id), _photo->id }; } return std::nullopt; } @@ -2214,8 +2211,10 @@ void OverlayWidget::showDocument( _streamingStartPaused = false; displayDocument(document, context, cloud, continueStreaming); - preloadData(0); - activateControls(); + if (!isHidden()) { + preloadData(0); + activateControls(); + } } void OverlayWidget::displayPhoto(not_null photo, HistoryItem *item) { @@ -2412,7 +2411,11 @@ void OverlayWidget::displayDocument( contentSizeChanged(); refreshFromLabel(item); _blurred = false; - displayFinished(); + if (_showAsPip && _streamed && !videoIsGifOrUserpic()) { + switchToPip(); + } else { + displayFinished(); + } } void OverlayWidget::updateThemePreviewGeometry() { @@ -2987,8 +2990,10 @@ void OverlayWidget::switchToPip() { const auto document = _document; const auto msgId = _msgid; const auto closeAndContinue = [=] { + _showAsPip = false; showDocument(document, document->owner().message(msgId), {}, true); }; + _showAsPip = true; _pip = std::make_unique( this, document, diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.h b/Telegram/SourceFiles/media/view/media_view_overlay_widget.h index a840d4c2d..b76a9f261 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.h +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.h @@ -443,6 +443,7 @@ private: std::unique_ptr _streamed; std::unique_ptr _pip; + bool _showAsPip = false; const style::icon *_docIcon = nullptr; style::color _docIconColor; diff --git a/Telegram/SourceFiles/mtproto/details/mtproto_dc_key_creator.cpp b/Telegram/SourceFiles/mtproto/details/mtproto_dc_key_creator.cpp index 0c5514e71..8da3c22c9 100644 --- a/Telegram/SourceFiles/mtproto/details/mtproto_dc_key_creator.cpp +++ b/Telegram/SourceFiles/mtproto/details/mtproto_dc_key_creator.cpp @@ -502,8 +502,8 @@ void DcKeyCreator::dhClientParamsSend(not_null attempt) { AuthKey::FillData(attempt->authKey, computedAuthKey); auto auth_key_sha = openssl::Sha1(attempt->authKey); - memcpy(&attempt->data.auth_key_aux_hash, auth_key_sha.data(), 8); - memcpy(&attempt->data.auth_key_hash, auth_key_sha.data() + 12, 8); + memcpy(&attempt->data.auth_key_aux_hash.v, auth_key_sha.data(), 8); + memcpy(&attempt->data.auth_key_hash.v, auth_key_sha.data() + 12, 8); const auto client_dh_inner = MTP_client_DH_inner_data( attempt->data.nonce, diff --git a/Telegram/SourceFiles/mtproto/mtproto_response.h b/Telegram/SourceFiles/mtproto/mtproto_response.h index 784e9b5e2..247f1c7db 100644 --- a/Telegram/SourceFiles/mtproto/mtproto_response.h +++ b/Telegram/SourceFiles/mtproto/mtproto_response.h @@ -38,8 +38,12 @@ private: }; +inline bool IsFloodError(const QString &type) { + return type.startsWith(qstr("FLOOD_WAIT_")); +} + inline bool IsFloodError(const Error &error) { - return error.type().startsWith(qstr("FLOOD_WAIT_")); + return IsFloodError(error.type()); } inline bool IsTemporaryError(const Error &error) { diff --git a/Telegram/SourceFiles/mtproto/session_private.cpp b/Telegram/SourceFiles/mtproto/session_private.cpp index 4f525677f..98ecddc89 100644 --- a/Telegram/SourceFiles/mtproto/session_private.cpp +++ b/Telegram/SourceFiles/mtproto/session_private.cpp @@ -39,7 +39,6 @@ constexpr auto kPingSendAfterForce = 45 * crl::time(1000); constexpr auto kTemporaryExpiresIn = TimeId(86400); constexpr auto kBindKeyAdditionalExpiresTimeout = TimeId(30); constexpr auto kTestModeDcIdShift = 10000; -constexpr auto kCheckSentRequestsEach = 1 * crl::time(1000); constexpr auto kKeyOldEnoughForDestroy = 60 * crl::time(1000); constexpr auto kSentContainerLives = 600 * crl::time(1000); constexpr auto kFastRequestDuration = crl::time(500); @@ -163,13 +162,14 @@ SessionPrivate::SessionPrivate( , _waitForConnected(kMinConnectedTimeout) , _pingSender(thread, [=] { sendPingByTimer(); }) , _checkSentRequestsTimer(thread, [=] { checkSentRequests(); }) +, _clearOldContainersTimer(thread, [=] { clearOldContainers(); }) , _sessionData(std::move(data)) { Expects(_shiftedDcId != 0); moveToThread(thread); InvokeQueued(this, [=] { - _checkSentRequestsTimer.callEach(kCheckSentRequestsEach); + _clearOldContainersTimer.callEach(kSentContainerLives); connectToServer(); }); } @@ -246,41 +246,47 @@ int16 SessionPrivate::getProtocolDcId() const { } void SessionPrivate::checkSentRequests() { - clearOldContainers(); - const auto now = crl::now(); - if (_bindMsgId && _bindMessageSent + kCheckSentRequestTimeout < now) { + const auto checkTime = now - kCheckSentRequestTimeout; + if (_bindMsgId && _bindMessageSent < checkTime) { DEBUG_LOG(("MTP Info: " "Request state while key is not bound, restarting.")); restart(); + _checkSentRequestsTimer.callOnce(kCheckSentRequestTimeout); return; } auto requesting = false; + auto nextTimeout = kCheckSentRequestTimeout; { QReadLocker locker(_sessionData->haveSentMutex()); auto &haveSent = _sessionData->haveSentMap(); - const auto haveSentCount = haveSent.size(); - const auto checkAfter = kCheckSentRequestTimeout; for (const auto &[msgId, request] : haveSent) { - if (request->lastSentTime + checkAfter < now) { + if (request->lastSentTime <= checkTime) { // Need to check state. request->lastSentTime = now; if (_stateRequestData.emplace(msgId).second) { requesting = true; } + } else { + nextTimeout = std::min(request->lastSentTime - checkTime, nextTimeout); } } } if (requesting) { _sessionData->queueSendAnything(kSendStateRequestWaiting); } + if (nextTimeout < kCheckSentRequestTimeout) { + _checkSentRequestsTimer.callOnce(nextTimeout); + } } void SessionPrivate::clearOldContainers() { auto resent = false; + auto nextTimeout = kSentContainerLives; const auto now = crl::now(); + const auto checkTime = now - kSentContainerLives; for (auto i = _sentContainers.begin(); i != _sentContainers.end();) { - if (now > i->second.sent + kSentContainerLives) { + if (i->second.sent <= checkTime) { DEBUG_LOG(("MTP Info: Removing old container with resending %1, " "sent: %2, now: %3, current unixtime: %4" ).arg(i->first @@ -296,12 +302,18 @@ void SessionPrivate::clearOldContainers() { resend(innerMsgId, -1, true); } } else { + nextTimeout = std::min(i->second.sent - checkTime, nextTimeout); ++i; } } if (resent) { _sessionData->queueNeedToResumeAndSend(); } + if (nextTimeout < kSentContainerLives) { + _clearOldContainersTimer.callOnce(nextTimeout); + } else if (!_clearOldContainersTimer.isActive()) { + _clearOldContainersTimer.callEach(nextTimeout); + } } void SessionPrivate::destroyAllConnections() { @@ -683,6 +695,8 @@ void SessionPrivate::tryToSend() { { QWriteLocker locker1(_sessionData->toSendMutex()); + auto scheduleCheckSentRequests = false; + auto toSendDummy = base::flat_map(); auto &toSend = sendAll ? _sessionData->toSendMap() @@ -748,6 +762,7 @@ void SessionPrivate::tryToSend() { QWriteLocker locker2(_sessionData->haveSentMutex()); auto &haveSent = _sessionData->haveSentMap(); haveSent.emplace(msgId, toSendRequest); + scheduleCheckSentRequests = true; const auto wrapLayer = needsLayer && toSendRequest->needsLayer; if (toSendRequest->after) { @@ -871,6 +886,7 @@ void SessionPrivate::tryToSend() { //Assert(!haveSent.contains(msgId)); haveSent.emplace(msgId, request); sentIdsWrap.messages.push_back(msgId); + scheduleCheckSentRequests = true; needAnyResponse = true; } else { _ackedIds.emplace(msgId, request->requestId); @@ -922,6 +938,10 @@ void SessionPrivate::tryToSend() { bigMsgId, forceNewMsgId); _sentContainers.emplace(containerMsgId, std::move(sentIdsWrap)); + + if (scheduleCheckSentRequests && !_checkSentRequestsTimer.isActive()) { + _checkSentRequestsTimer.callOnce(kCheckSentRequestTimeout); + } } } sendSecureRequest(std::move(toSendRequest), needAnyResponse); @@ -1253,16 +1273,9 @@ void SessionPrivate::handleReceived() { auto msgId = *(uint64*)&decryptedInts[4]; auto seqNo = *(uint32*)&decryptedInts[6]; auto needAck = ((seqNo & 0x01) != 0); - auto messageLength = *(uint32*)&decryptedInts[7]; - if (messageLength > kMaxMessageLength) { - LOG(("TCP Error: bad messageLength %1").arg(messageLength)); - TCP_LOG(("TCP Error: bad message %1").arg(Logs::mb(ints, intsCount * kIntSize).str())); - - return restart(); - - } auto fullDataLength = kEncryptedHeaderIntsCount * kIntSize + messageLength; // Without padding. + auto badMessageLength = (messageLength > kMaxMessageLength); // Can underflow, but it is an unsigned type, so we just check the range later. auto paddingSize = static_cast(encryptedBytesCount) - static_cast(fullDataLength); @@ -1270,7 +1283,7 @@ void SessionPrivate::handleReceived() { #ifdef TDESKTOP_MTPROTO_OLD constexpr auto kMinPaddingSize_oldmtp = 0U; constexpr auto kMaxPaddingSize_oldmtp = 15U; - auto badMessageLength = (/*paddingSize < kMinPaddingSize_oldmtp || */paddingSize > kMaxPaddingSize_oldmtp); + badMessageLength |= (/*paddingSize < kMinPaddingSize_oldmtp || */paddingSize > kMaxPaddingSize_oldmtp); auto hashedDataLength = badMessageLength ? encryptedBytesCount : fullDataLength; auto sha1ForMsgKeyCheck = hashSha1(decryptedInts, hashedDataLength); @@ -1285,7 +1298,7 @@ void SessionPrivate::handleReceived() { #else // TDESKTOP_MTPROTO_OLD constexpr auto kMinPaddingSize = 12U; constexpr auto kMaxPaddingSize = 1024U; - auto badMessageLength = (paddingSize < kMinPaddingSize || paddingSize > kMaxPaddingSize); + badMessageLength |= (paddingSize < kMinPaddingSize || paddingSize > kMaxPaddingSize); std::array sha256Buffer = { { 0 } }; diff --git a/Telegram/SourceFiles/mtproto/session_private.h b/Telegram/SourceFiles/mtproto/session_private.h index 2c75c4511..b4638ba5f 100644 --- a/Telegram/SourceFiles/mtproto/session_private.h +++ b/Telegram/SourceFiles/mtproto/session_private.h @@ -218,6 +218,7 @@ private: mtpMsgId _pingMsgId = 0; base::Timer _pingSender; base::Timer _checkSentRequestsTimer; + base::Timer _clearOldContainersTimer; std::shared_ptr _sessionData; std::unique_ptr _options; diff --git a/Telegram/SourceFiles/passport/passport_form_controller.cpp b/Telegram/SourceFiles/passport/passport_form_controller.cpp index 1df040603..618da418a 100644 --- a/Telegram/SourceFiles/passport/passport_form_controller.cpp +++ b/Telegram/SourceFiles/passport/passport_form_controller.cpp @@ -751,7 +751,7 @@ std::vector> FormController::submitGetErrors() { bytes::make_span(_request.publicKey.toUtf8())); _submitRequestId = _api.request(MTPaccount_AcceptAuthorization( - MTP_int(_request.botId), + MTP_int(_request.botId.bare), // #TODO ids MTP_string(_request.scope), MTP_string(_request.publicKey), MTP_vector(prepared.hashes), @@ -2290,7 +2290,7 @@ void FormController::requestForm() { return; } _formRequestId = _api.request(MTPaccount_GetAuthorizationForm( - MTP_int(_request.botId), + MTP_int(_request.botId.bare), // #TODO ids MTP_string(_request.scope), MTP_string(_request.publicKey) )).done([=](const MTPaccount_AuthorizationForm &result) { diff --git a/Telegram/SourceFiles/passport/passport_panel.cpp b/Telegram/SourceFiles/passport/passport_panel.cpp index 0779886c7..da4df7ced 100644 --- a/Telegram/SourceFiles/passport/passport_panel.cpp +++ b/Telegram/SourceFiles/passport/passport_panel.cpp @@ -101,7 +101,7 @@ void Panel::showBox( } void Panel::showToast(const QString &text) { - _widget->showToast(text); + _widget->showToast({ text }); } Panel::~Panel() = default; diff --git a/Telegram/SourceFiles/passport/passport_panel_controller.cpp b/Telegram/SourceFiles/passport/passport_panel_controller.cpp index 8d41d54a9..cbb190739 100644 --- a/Telegram/SourceFiles/passport/passport_panel_controller.cpp +++ b/Telegram/SourceFiles/passport/passport_panel_controller.cpp @@ -9,10 +9,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "lang/lang_keys.h" #include "passport/passport_panel_edit_document.h" -#include "passport/passport_panel_details_row.h" #include "passport/passport_panel_edit_contact.h" #include "passport/passport_panel_edit_scans.h" #include "passport/passport_panel.h" +#include "passport/ui/passport_details_row.h" #include "base/openssl_help.h" #include "base/unixtime.h" #include "boxes/passcode_box.h" @@ -212,7 +212,7 @@ EditDocumentScheme GetDocumentScheme( result.rows = { { ValueClass::Fields, - PanelDetailsType::Text, + Ui::PanelDetailsType::Text, qsl("first_name"), tr::lng_passport_first_name(tr::now), NameValidate, @@ -221,7 +221,7 @@ EditDocumentScheme GetDocumentScheme( }, { ValueClass::Fields, - PanelDetailsType::Text, + Ui::PanelDetailsType::Text, qsl("middle_name"), tr::lng_passport_middle_name(tr::now), NameOrEmptyValidate, @@ -231,7 +231,7 @@ EditDocumentScheme GetDocumentScheme( }, { ValueClass::Fields, - PanelDetailsType::Text, + Ui::PanelDetailsType::Text, qsl("last_name"), tr::lng_passport_last_name(tr::now), NameValidate, @@ -241,7 +241,7 @@ EditDocumentScheme GetDocumentScheme( }, { ValueClass::Fields, - PanelDetailsType::Date, + Ui::PanelDetailsType::Date, qsl("birth_date"), tr::lng_passport_birth_date(tr::now), DateValidate, @@ -249,7 +249,7 @@ EditDocumentScheme GetDocumentScheme( }, { ValueClass::Fields, - PanelDetailsType::Gender, + Ui::PanelDetailsType::Gender, qsl("gender"), tr::lng_passport_gender(tr::now), GenderValidate, @@ -257,7 +257,7 @@ EditDocumentScheme GetDocumentScheme( }, { ValueClass::Fields, - PanelDetailsType::Country, + Ui::PanelDetailsType::Country, qsl("country_code"), tr::lng_passport_country(tr::now), CountryValidate, @@ -265,7 +265,7 @@ EditDocumentScheme GetDocumentScheme( }, { ValueClass::Fields, - PanelDetailsType::Country, + Ui::PanelDetailsType::Country, qsl("residence_country_code"), tr::lng_passport_residence_country(tr::now), CountryValidate, @@ -273,7 +273,7 @@ EditDocumentScheme GetDocumentScheme( }, { ValueClass::Scans, - PanelDetailsType::Text, + Ui::PanelDetailsType::Text, qsl("document_no"), tr::lng_passport_document_number(tr::now), DocumentValidate, @@ -282,7 +282,7 @@ EditDocumentScheme GetDocumentScheme( }, { ValueClass::Scans, - PanelDetailsType::Date, + Ui::PanelDetailsType::Date, qsl("expiry_date"), tr::lng_passport_expiry_date(tr::now), DateOrEmptyValidate, @@ -344,7 +344,7 @@ EditDocumentScheme GetDocumentScheme( auto additional = std::initializer_list{ { ValueClass::Additional, - PanelDetailsType::Text, + Ui::PanelDetailsType::Text, qsl("first_name_native"), tr::lng_passport_first_name(tr::now), NativeNameValidate, @@ -355,7 +355,7 @@ EditDocumentScheme GetDocumentScheme( }, { ValueClass::Additional, - PanelDetailsType::Text, + Ui::PanelDetailsType::Text, qsl("middle_name_native"), tr::lng_passport_middle_name(tr::now), NativeNameOrEmptyValidate, @@ -366,7 +366,7 @@ EditDocumentScheme GetDocumentScheme( }, { ValueClass::Additional, - PanelDetailsType::Text, + Ui::PanelDetailsType::Text, qsl("last_name_native"), tr::lng_passport_last_name(tr::now), NativeNameValidate, @@ -411,7 +411,7 @@ EditDocumentScheme GetDocumentScheme( result.rows = { { ValueClass::Fields, - PanelDetailsType::Text, + Ui::PanelDetailsType::Text, qsl("street_line1"), tr::lng_passport_street(tr::now), StreetValidate, @@ -420,7 +420,7 @@ EditDocumentScheme GetDocumentScheme( }, { ValueClass::Fields, - PanelDetailsType::Text, + Ui::PanelDetailsType::Text, qsl("street_line2"), tr::lng_passport_street(tr::now), DontValidate, @@ -429,7 +429,7 @@ EditDocumentScheme GetDocumentScheme( }, { ValueClass::Fields, - PanelDetailsType::Text, + Ui::PanelDetailsType::Text, qsl("city"), tr::lng_passport_city(tr::now), CityValidate, @@ -438,7 +438,7 @@ EditDocumentScheme GetDocumentScheme( }, { ValueClass::Fields, - PanelDetailsType::Text, + Ui::PanelDetailsType::Text, qsl("state"), tr::lng_passport_state(tr::now), DontValidate, @@ -447,7 +447,7 @@ EditDocumentScheme GetDocumentScheme( }, { ValueClass::Fields, - PanelDetailsType::Country, + Ui::PanelDetailsType::Country, qsl("country_code"), tr::lng_passport_country(tr::now), CountryValidate, @@ -455,7 +455,7 @@ EditDocumentScheme GetDocumentScheme( }, { ValueClass::Fields, - PanelDetailsType::Postcode, + Ui::PanelDetailsType::Postcode, qsl("post_code"), tr::lng_passport_postcode(tr::now), PostcodeValidate, diff --git a/Telegram/SourceFiles/passport/passport_panel_edit_contact.cpp b/Telegram/SourceFiles/passport/passport_panel_edit_contact.cpp index 578075696..59ca85cd9 100644 --- a/Telegram/SourceFiles/passport/passport_panel_edit_contact.cpp +++ b/Telegram/SourceFiles/passport/passport_panel_edit_contact.cpp @@ -8,7 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "passport/passport_panel_edit_contact.h" #include "passport/passport_panel_controller.h" -#include "passport/passport_panel_details_row.h" +#include "passport/ui/passport_details_row.h" #include "ui/widgets/input_fields.h" #include "ui/widgets/labels.h" #include "ui/widgets/buttons.h" @@ -278,7 +278,8 @@ void PanelEditContact::setupControls( wrap.data(), fieldStyle, std::move(fieldPlaceholder), - ExtractPhonePrefix(_controller->bot()->session().user()->phone()), + Ui::ExtractPhonePrefix( + _controller->bot()->session().user()->phone()), data); } else { _field = Ui::CreateChild( diff --git a/Telegram/SourceFiles/passport/passport_panel_edit_document.cpp b/Telegram/SourceFiles/passport/passport_panel_edit_document.cpp index fc018a42c..506f07fe8 100644 --- a/Telegram/SourceFiles/passport/passport_panel_edit_document.cpp +++ b/Telegram/SourceFiles/passport/passport_panel_edit_document.cpp @@ -8,8 +8,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "passport/passport_panel_edit_document.h" #include "passport/passport_panel_controller.h" -#include "passport/passport_panel_details_row.h" #include "passport/passport_panel_edit_scans.h" +#include "passport/ui/passport_details_row.h" #include "ui/widgets/input_fields.h" #include "ui/widgets/scroll_area.h" #include "ui/widgets/labels.h" @@ -19,6 +19,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/wrap/vertical_layout.h" #include "ui/wrap/fade_wrap.h" #include "ui/wrap/slide_wrap.h" +#include "data/data_countries.h" +#include "data/data_user.h" // ->bot()->session() +#include "main/main_session.h" // ->session().user() #include "ui/text/text_utilities.h" // Ui::Text::ToUpper #include "boxes/abstract_box.h" #include "boxes/confirm_box.h" @@ -363,7 +366,7 @@ not_null PanelEditDocument::setupContent( const ValueMap &fields) { accumulate_max( maxLabelWidth, - PanelDetailsRow::LabelWidth(row.label)); + Ui::PanelDetailsRow::LabelWidth(row.label)); }); if (maxLabelWidth > 0) { if (error && !error->isEmpty()) { @@ -513,12 +516,20 @@ void PanelEditDocument::createDetailsRow( }; const auto current = valueOrEmpty(fields, row.key); + const auto showBox = [controller = _controller]( + object_ptr box) { + controller->show(std::move(box)); + }; + const auto isoByPhone = Data::CountryISO2ByPhone( + _controller->bot()->session().user()->phone()); + const auto [it, ok] = _details.emplace( i, - container->add(PanelDetailsRow::Create( + container->add(Ui::PanelDetailsRow::Create( container, + showBox, + isoByPhone, row.inputType, - _controller, row.label, maxLabelWidth, current.text, @@ -537,7 +548,7 @@ void PanelEditDocument::createDetailsRow( }, it->second->lifetime()); } -not_null PanelEditDocument::findRow( +not_null PanelEditDocument::findRow( const QString &key) const { for (auto i = 0, count = int(_scheme.rows.size()); i != count; ++i) { const auto &row = _scheme.rows[i]; @@ -636,7 +647,7 @@ bool PanelEditDocument::validate() { _scroll->scrollToY(_scroll->scrollTop() + scrolldelta); error = firsttop.y(); } - auto first = QPointer(); + auto first = QPointer(); for (const auto &[i, field] : ranges::views::reverse(_details)) { const auto &row = _scheme.rows[i]; if (row.valueClass == Scheme::ValueClass::Additional diff --git a/Telegram/SourceFiles/passport/passport_panel_edit_document.h b/Telegram/SourceFiles/passport/passport_panel_edit_document.h index bf9dd9f96..aa13908ce 100644 --- a/Telegram/SourceFiles/passport/passport_panel_edit_document.h +++ b/Telegram/SourceFiles/passport/passport_panel_edit_document.h @@ -24,15 +24,19 @@ template class SlideWrap; } // namespace Ui +namespace Passport::Ui { +using namespace ::Ui; +enum class PanelDetailsType; +class PanelDetailsRow; +} // namespace Passport::Ui + namespace Passport { class PanelController; struct ValueMap; struct ScanInfo; class EditScans; -class PanelDetailsRow; enum class FileType; -enum class PanelDetailsType; struct ScanListData; struct EditDocumentScheme { @@ -50,7 +54,7 @@ struct EditDocumentScheme { using Validator = Fn(const QString &value)>; using Formatter = Fn; ValueClass valueClass = ValueClass::Fields; - PanelDetailsType inputType = PanelDetailsType(); + Ui::PanelDetailsType inputType = Ui::PanelDetailsType(); QString key; QString label; Validator error; @@ -140,7 +144,7 @@ private: const Scheme::Row &row, const ValueMap &fields, int maxLabelWidth); - not_null findRow(const QString &key) const; + not_null findRow(const QString &key) const; not_null _controller; Scheme _scheme; @@ -151,7 +155,7 @@ private: QPointer _editScans; QPointer> _commonError; - std::map> _details; + std::map> _details; bool _fieldsChanged = false; bool _additionalShown = false; diff --git a/Telegram/SourceFiles/passport/passport_panel_edit_scans.cpp b/Telegram/SourceFiles/passport/passport_panel_edit_scans.cpp index 9ef957d15..88c668408 100644 --- a/Telegram/SourceFiles/passport/passport_panel_edit_scans.cpp +++ b/Telegram/SourceFiles/passport/passport_panel_edit_scans.cpp @@ -8,7 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "passport/passport_panel_edit_scans.h" #include "passport/passport_panel_controller.h" -#include "passport/passport_panel_details_row.h" +#include "passport/ui/passport_details_row.h" #include "ui/widgets/buttons.h" #include "ui/widgets/labels.h" #include "ui/widgets/box_content_divider.h" diff --git a/Telegram/SourceFiles/passport/passport_panel_form.cpp b/Telegram/SourceFiles/passport/passport_panel_form.cpp index 75eeba4c3..5802e6c3a 100644 --- a/Telegram/SourceFiles/passport/passport_panel_form.cpp +++ b/Telegram/SourceFiles/passport/passport_panel_form.cpp @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "passport/passport_panel_form.h" #include "passport/passport_panel_controller.h" +#include "passport/ui/passport_form_row.h" #include "lang/lang_keys.h" #include "boxes/abstract_box.h" #include "core/click_handler_types.h" @@ -30,145 +31,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Passport { -class PanelForm::Row : public Ui::RippleButton { -public: - explicit Row(QWidget *parent); - - void updateContent( - const QString &title, - const QString &description, - bool ready, - bool error, - anim::type animated); - -protected: - int resizeGetHeight(int newWidth) override; - - void paintEvent(QPaintEvent *e) override; - -private: - int countAvailableWidth() const; - int countAvailableWidth(int newWidth) const; - - Ui::Text::String _title; - Ui::Text::String _description; - int _titleHeight = 0; - int _descriptionHeight = 0; - bool _ready = false; - bool _error = false; - Ui::Animations::Simple _errorAnimation; - -}; - -PanelForm::Row::Row(QWidget *parent) -: RippleButton(parent, st::passportRowRipple) -, _title(st::boxWideWidth / 2) -, _description(st::boxWideWidth / 2) { -} - -void PanelForm::Row::updateContent( - const QString &title, - const QString &description, - bool ready, - bool error, - anim::type animated) { - _title.setText( - st::semiboldTextStyle, - title, - Ui::NameTextOptions()); - _description.setText( - st::defaultTextStyle, - description, - TextParseOptions { - TextParseMultiline, - 0, - 0, - Qt::LayoutDirectionAuto - }); - _ready = ready && !error; - if (_error != error) { - _error = error; - if (animated == anim::type::instant) { - _errorAnimation.stop(); - } else { - _errorAnimation.start( - [=] { update(); }, - _error ? 0. : 1., - _error ? 1. : 0., - st::fadeWrapDuration); - } - } - resizeToWidth(width()); - update(); -} - -int PanelForm::Row::resizeGetHeight(int newWidth) { - const auto availableWidth = countAvailableWidth(newWidth); - _titleHeight = _title.countHeight(availableWidth); - _descriptionHeight = _description.countHeight(availableWidth); - const auto result = st::passportRowPadding.top() - + _titleHeight - + st::passportRowSkip - + _descriptionHeight - + st::passportRowPadding.bottom(); - return result; -} - -int PanelForm::Row::countAvailableWidth(int newWidth) const { - return newWidth - - st::passportRowPadding.left() - - st::passportRowPadding.right() - - (_ready - ? st::passportRowReadyIcon - : st::passportRowEmptyIcon).width() - - st::passportRowIconSkip; -} - -int PanelForm::Row::countAvailableWidth() const { - return countAvailableWidth(width()); -} - -void PanelForm::Row::paintEvent(QPaintEvent *e) { - Painter p(this); - - paintRipple(p, 0, 0); - - const auto left = st::passportRowPadding.left(); - const auto availableWidth = countAvailableWidth(); - auto top = st::passportRowPadding.top(); - - const auto error = _errorAnimation.value(_error ? 1. : 0.); - - p.setPen(st::passportRowTitleFg); - _title.drawLeft(p, left, top, availableWidth, width()); - top += _titleHeight + st::passportRowSkip; - - p.setPen(anim::pen( - st::passportRowDescriptionFg, - st::boxTextFgError, - error)); - _description.drawLeft(p, left, top, availableWidth, width()); - top += _descriptionHeight + st::passportRowPadding.bottom(); - - const auto &icon = _ready - ? st::passportRowReadyIcon - : st::passportRowEmptyIcon; - if (error > 0. && !_ready) { - icon.paint( - p, - width() - st::passportRowPadding.right() - icon.width(), - (height() - icon.height()) / 2, - width(), - anim::color(st::menuIconFgOver, st::boxTextFgError, error)); - } else { - icon.paint( - p, - width() - st::passportRowPadding.right() - icon.width(), - (height() - icon.height()) / 2, - width()); - } -} - PanelForm::PanelForm( QWidget *parent, not_null controller) diff --git a/Telegram/SourceFiles/passport/passport_panel_form.h b/Telegram/SourceFiles/passport/passport_panel_form.h index badc773f0..26b2dfa3b 100644 --- a/Telegram/SourceFiles/passport/passport_panel_form.h +++ b/Telegram/SourceFiles/passport/passport_panel_form.h @@ -19,6 +19,11 @@ class FlatLabel; class UserpicButton; } // namespace Ui +namespace Passport::Ui { +using namespace ::Ui; +class FormRow; +} // namespace Passport::Ui + namespace Passport { class PanelController; @@ -33,7 +38,7 @@ protected: void resizeEvent(QResizeEvent *e) override; private: - class Row; + using Row = Ui::FormRow; void setupControls(); not_null setupContent(); diff --git a/Telegram/SourceFiles/passport/passport_panel_details_row.cpp b/Telegram/SourceFiles/passport/ui/passport_details_row.cpp similarity index 90% rename from Telegram/SourceFiles/passport/passport_panel_details_row.cpp rename to Telegram/SourceFiles/passport/ui/passport_details_row.cpp index 20b7c9732..a93c30ebd 100644 --- a/Telegram/SourceFiles/passport/passport_panel_details_row.cpp +++ b/Telegram/SourceFiles/passport/ui/passport_details_row.cpp @@ -5,9 +5,8 @@ the official desktop application for the Telegram messaging service. For license and copyright information please follow this link: https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ -#include "passport/passport_panel_details_row.h" +#include "passport/ui/passport_details_row.h" -#include "passport/passport_panel_controller.h" #include "lang/lang_keys.h" #include "base/platform/base_platform_info.h" #include "ui/widgets/input_fields.h" @@ -15,17 +14,18 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/buttons.h" #include "ui/widgets/checkbox.h" #include "ui/wrap/slide_wrap.h" -#include "ui/countryinput.h" -#include "main/main_session.h" -#include "data/data_user.h" +#include "ui/layers/box_content.h" +#include "ui/boxes/country_select_box.h" #include "data/data_countries.h" #include "styles/style_layers.h" #include "styles/style_passport.h" -namespace Passport { +#include + +namespace Passport::Ui { namespace { -class PostcodeInput : public Ui::MaskedInputField { +class PostcodeInput : public MaskedInputField { public: PostcodeInput( QWidget *parent, @@ -103,7 +103,8 @@ class CountryRow : public PanelDetailsRow { public: CountryRow( QWidget *parent, - not_null controller, + Fn)> showBox, + const QString &defaultCountry, const QString &label, int maxLabelWidth, const QString &value); @@ -121,15 +122,16 @@ private: void toggleError(bool shown); void errorAnimationCallback(); - not_null _controller; - object_ptr _link; + QString _defaultCountry; + Fn)> _showBox; + object_ptr _link; rpl::variable _value; bool _errorShown = false; - Ui::Animations::Simple _errorAnimation; + Animations::Simple _errorAnimation; }; -class DateInput final : public Ui::MaskedInputField { +class DateInput final : public MaskedInputField { public: using MaskedInputField::MaskedInputField; @@ -191,21 +193,21 @@ private: int number(const object_ptr &field) const; object_ptr _day; - object_ptr> _separator1; + object_ptr> _separator1; object_ptr _month; - object_ptr> _separator2; + object_ptr> _separator2; object_ptr _year; rpl::variable _value; style::cursor _cursor = style::cur_default; - Ui::Animations::Simple _a_borderShown; + Animations::Simple _a_borderShown; int _borderAnimationStart = 0; - Ui::Animations::Simple _a_borderOpacity; + Animations::Simple _a_borderOpacity; bool _borderVisible = false; - Ui::Animations::Simple _a_error; + Animations::Simple _a_error; bool _error = false; - Ui::Animations::Simple _a_focused; + Animations::Simple _a_focused; bool _focused = false; }; @@ -238,18 +240,18 @@ private: void hideGenderError(); void errorAnimationCallback(); - std::unique_ptr createRadioView( - Ui::RadioView* &weak) const; + std::unique_ptr createRadioView( + RadioView* &weak) const; - std::shared_ptr> _group; - Ui::RadioView *_maleRadio = nullptr; - Ui::RadioView *_femaleRadio = nullptr; - object_ptr> _male; - object_ptr> _female; + std::shared_ptr> _group; + RadioView *_maleRadio = nullptr; + RadioView *_femaleRadio = nullptr; + object_ptr> _male; + object_ptr> _female; rpl::variable _value; bool _errorShown = false; - Ui::Animations::Simple _errorAnimation; + Animations::Simple _errorAnimation; }; @@ -308,12 +310,14 @@ QString CountryString(const QString &code) { CountryRow::CountryRow( QWidget *parent, - not_null controller, + Fn)> showBox, + const QString &defaultCountry, const QString &label, int maxLabelWidth, const QString &value) : PanelDetailsRow(parent, label, maxLabelWidth) -, _controller(controller) +, _defaultCountry(defaultCountry) +, _showBox(std::move(showBox)) , _link(this, CountryString(value), st::boxLinkButton) , _value(value) { _value.changes( @@ -380,20 +384,23 @@ void CountryRow::errorAnimationCallback() { void CountryRow::chooseCountry() { const auto top = _value.current(); const auto name = Data::CountryNameByISO2(top); - const auto isoByPhone = Data::CountryISO2ByPhone( - _controller->bot()->session().user()->phone()); - const auto box = _controller->show(Box(!name.isEmpty() + const auto country = !name.isEmpty() ? top - : !isoByPhone.isEmpty() - ? isoByPhone - : Platform::SystemCountry(), - CountrySelectBox::Type::Countries)); - connect(box, &CountrySelectBox::countryChosen, this, [=](QString iso) { + : !_defaultCountry.isEmpty() + ? _defaultCountry + : Platform::SystemCountry(); + auto box = Box( + country, + CountrySelectBox::Type::Countries); + const auto raw = box.data(); + raw->countryChosen( + ) | rpl::start_with_next([=](QString iso) { _value = iso; _link->setText(CountryString(iso)); hideCountryError(); - box->closeBox(); - }); + raw->closeBox(); + }, lifetime()); + _showBox(std::move(box)); } QDate ValidateDate(const QString &value) { @@ -528,7 +535,7 @@ DateRow::DateRow( GetDay(value)) , _separator1( this, - object_ptr( + object_ptr( this, QString(" / "), st::passportDetailsSeparator), @@ -540,7 +547,7 @@ DateRow::DateRow( GetMonth(value)) , _separator2( this, - object_ptr( + object_ptr( this, QString(" / "), st::passportDetailsSeparator), @@ -552,7 +559,7 @@ DateRow::DateRow( GetYear(value)) , _value(valueCurrent()) { const auto focused = [=](const object_ptr &field) { - return [this, pointer = Ui::MakeWeak(field.data())]{ + return [this, pointer = MakeWeak(field.data())]{ _borderAnimationStart = pointer->borderAnimationStart() + pointer->x() - _day->x(); @@ -565,15 +572,15 @@ DateRow::DateRow( const auto changed = [=] { _value = valueCurrent(); }; - connect(_day, &Ui::MaskedInputField::focused, focused(_day)); - connect(_month, &Ui::MaskedInputField::focused, focused(_month)); - connect(_year, &Ui::MaskedInputField::focused, focused(_year)); - connect(_day, &Ui::MaskedInputField::blurred, blurred); - connect(_month, &Ui::MaskedInputField::blurred, blurred); - connect(_year, &Ui::MaskedInputField::blurred, blurred); - connect(_day, &Ui::MaskedInputField::changed, changed); - connect(_month, &Ui::MaskedInputField::changed, changed); - connect(_year, &Ui::MaskedInputField::changed, changed); + connect(_day, &MaskedInputField::focused, focused(_day)); + connect(_month, &MaskedInputField::focused, focused(_month)); + connect(_year, &MaskedInputField::focused, focused(_year)); + connect(_day, &MaskedInputField::blurred, blurred); + connect(_month, &MaskedInputField::blurred, blurred); + connect(_year, &MaskedInputField::blurred, blurred); + connect(_day, &MaskedInputField::changed, changed); + connect(_month, &MaskedInputField::changed, changed); + connect(_year, &MaskedInputField::changed, changed); _day->setMaxValue(31); _day->putNext() | rpl::start_with_next([=](QChar ch) { putNext(_month, ch); @@ -845,8 +852,8 @@ GenderRow::GenderRow( const QString &value) : PanelDetailsRow(parent, label, maxLabelWidth) , _group(StringToGender(value).has_value() - ? std::make_shared>(*StringToGender(value)) - : std::make_shared>()) + ? std::make_shared>(*StringToGender(value)) + : std::make_shared>()) , _male( this, _group, @@ -868,9 +875,9 @@ GenderRow::GenderRow( }); } -std::unique_ptr GenderRow::createRadioView( - Ui::RadioView* &weak) const { - auto result = std::make_unique(st::defaultRadio, false); +std::unique_ptr GenderRow::createRadioView( + RadioView* &weak) const { + auto result = std::make_unique(st::defaultRadio, false); weak = result.get(); return result; } @@ -959,8 +966,9 @@ PanelDetailsRow::PanelDetailsRow( object_ptr PanelDetailsRow::Create( QWidget *parent, + Fn)> showBox, + const QString &defaultCountry, Type type, - not_null controller, const QString &label, int maxLabelWidth, const QString &value, @@ -969,7 +977,7 @@ object_ptr PanelDetailsRow::Create( auto result = [&]() -> object_ptr { switch (type) { case Type::Text: - return object_ptr>( + return object_ptr>( parent, label, maxLabelWidth, @@ -985,7 +993,8 @@ object_ptr PanelDetailsRow::Create( case Type::Country: return object_ptr( parent, - controller, + showBox, + defaultCountry, label, maxLabelWidth, value); @@ -1062,7 +1071,7 @@ void PanelDetailsRow::showError(std::optional error) { if (!_error) { _error.create( this, - object_ptr( + object_ptr( this, *error, st::passportVerifyErrorLabel)); @@ -1122,4 +1131,4 @@ void PanelDetailsRow::paintEvent(QPaintEvent *e) { p.drawTextLeft(padding.left(), padding.top(), width(), _label); } -} // namespace Passport +} // namespace Passport::Ui diff --git a/Telegram/SourceFiles/passport/passport_panel_details_row.h b/Telegram/SourceFiles/passport/ui/passport_details_row.h similarity index 83% rename from Telegram/SourceFiles/passport/passport_panel_details_row.h rename to Telegram/SourceFiles/passport/ui/passport_details_row.h index fc0204967..d93708fe4 100644 --- a/Telegram/SourceFiles/passport/passport_panel_details_row.h +++ b/Telegram/SourceFiles/passport/ui/passport_details_row.h @@ -11,18 +11,18 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/effects/animations.h" #include "ui/wrap/padding_wrap.h" #include "ui/widgets/labels.h" -#include "boxes/abstract_box.h" namespace Ui { +class BoxContent; class InputField; class FlatLabel; template class SlideWrap; } // namespace Ui -namespace Passport { +namespace Passport::Ui { -class PanelController; +using namespace ::Ui; enum class PanelDetailsType { Text, @@ -32,7 +32,7 @@ enum class PanelDetailsType { Gender, }; -class PanelDetailsRow : public Ui::RpWidget { +class PanelDetailsRow : public RpWidget { public: using Type = PanelDetailsType; @@ -43,8 +43,9 @@ public: static object_ptr Create( QWidget *parent, + Fn)> showBox, + const QString &defaultCountry, Type type, - not_null controller, const QString &label, int maxLabelWidth, const QString &value, @@ -74,11 +75,11 @@ private: QString _label; int _maxLabelWidth = 0; - object_ptr> _error = { nullptr }; + object_ptr> _error = { nullptr }; bool _errorShown = false; bool _errorHideSubscription = false; - Ui::Animations::Simple _errorAnimation; + Animations::Simple _errorAnimation; }; -} // namespace Passport +} // namespace Passport::Ui diff --git a/Telegram/SourceFiles/passport/ui/passport_form_row.cpp b/Telegram/SourceFiles/passport/ui/passport_form_row.cpp new file mode 100644 index 000000000..6d3104408 --- /dev/null +++ b/Telegram/SourceFiles/passport/ui/passport_form_row.cpp @@ -0,0 +1,125 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "passport/ui/passport_form_row.h" + +#include "ui/text/text_options.h" +#include "styles/style_passport.h" +#include "styles/style_layers.h" + +namespace Passport::Ui { + +FormRow::FormRow(QWidget *parent) +: RippleButton(parent, st::passportRowRipple) +, _title(st::boxWideWidth / 2) +, _description(st::boxWideWidth / 2) { +} + +void FormRow::updateContent( + const QString &title, + const QString &description, + bool ready, + bool error, + anim::type animated) { + _title.setText( + st::semiboldTextStyle, + title, + NameTextOptions()); + _description.setText( + st::defaultTextStyle, + description, + TextParseOptions { + TextParseMultiline, + 0, + 0, + Qt::LayoutDirectionAuto + }); + _ready = ready && !error; + if (_error != error) { + _error = error; + if (animated == anim::type::instant) { + _errorAnimation.stop(); + } else { + _errorAnimation.start( + [=] { update(); }, + _error ? 0. : 1., + _error ? 1. : 0., + st::fadeWrapDuration); + } + } + resizeToWidth(width()); + update(); +} + +int FormRow::resizeGetHeight(int newWidth) { + const auto availableWidth = countAvailableWidth(newWidth); + _titleHeight = _title.countHeight(availableWidth); + _descriptionHeight = _description.countHeight(availableWidth); + const auto result = st::passportRowPadding.top() + + _titleHeight + + st::passportRowSkip + + _descriptionHeight + + st::passportRowPadding.bottom(); + return result; +} + +int FormRow::countAvailableWidth(int newWidth) const { + return newWidth + - st::passportRowPadding.left() + - st::passportRowPadding.right() + - (_ready + ? st::passportRowReadyIcon + : st::passportRowEmptyIcon).width() + - st::passportRowIconSkip; +} + +int FormRow::countAvailableWidth() const { + return countAvailableWidth(width()); +} + +void FormRow::paintEvent(QPaintEvent *e) { + Painter p(this); + + paintRipple(p, 0, 0); + + const auto left = st::passportRowPadding.left(); + const auto availableWidth = countAvailableWidth(); + auto top = st::passportRowPadding.top(); + + const auto error = _errorAnimation.value(_error ? 1. : 0.); + + p.setPen(st::passportRowTitleFg); + _title.drawLeft(p, left, top, availableWidth, width()); + top += _titleHeight + st::passportRowSkip; + + p.setPen(anim::pen( + st::passportRowDescriptionFg, + st::boxTextFgError, + error)); + _description.drawLeft(p, left, top, availableWidth, width()); + top += _descriptionHeight + st::passportRowPadding.bottom(); + + const auto &icon = _ready + ? st::passportRowReadyIcon + : st::passportRowEmptyIcon; + if (error > 0. && !_ready) { + icon.paint( + p, + width() - st::passportRowPadding.right() - icon.width(), + (height() - icon.height()) / 2, + width(), + anim::color(st::menuIconFgOver, st::boxTextFgError, error)); + } else { + icon.paint( + p, + width() - st::passportRowPadding.right() - icon.width(), + (height() - icon.height()) / 2, + width()); + } +} + +} // namespace Passport::Ui diff --git a/Telegram/SourceFiles/passport/ui/passport_form_row.h b/Telegram/SourceFiles/passport/ui/passport_form_row.h new file mode 100644 index 000000000..2778df204 --- /dev/null +++ b/Telegram/SourceFiles/passport/ui/passport_form_row.h @@ -0,0 +1,48 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +#include "ui/text/text.h" +#include "ui/effects/animations.h" +#include "ui/widgets/buttons.h" + +namespace Passport::Ui { + +using namespace ::Ui; + +class FormRow : public RippleButton { +public: + explicit FormRow(QWidget *parent); + + void updateContent( + const QString &title, + const QString &description, + bool ready, + bool error, + anim::type animated); + +protected: + int resizeGetHeight(int newWidth) override; + + void paintEvent(QPaintEvent *e) override; + +private: + int countAvailableWidth() const; + int countAvailableWidth(int newWidth) const; + + Text::String _title; + Text::String _description; + int _titleHeight = 0; + int _descriptionHeight = 0; + bool _ready = false; + bool _error = false; + Animations::Simple _errorAnimation; + +}; + +} // namespace Passport::Ui diff --git a/Telegram/SourceFiles/payments/payments_checkout_process.cpp b/Telegram/SourceFiles/payments/payments_checkout_process.cpp new file mode 100644 index 000000000..efbb02d2b --- /dev/null +++ b/Telegram/SourceFiles/payments/payments_checkout_process.cpp @@ -0,0 +1,712 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "payments/payments_checkout_process.h" + +#include "payments/payments_form.h" +#include "payments/ui/payments_panel.h" +#include "main/main_session.h" +#include "main/main_account.h" +#include "main/main_domain.h" +#include "storage/storage_domain.h" +#include "history/history_item.h" +#include "history/history.h" +#include "data/data_user.h" // UserData::isBot. +#include "boxes/passcode_box.h" +#include "core/local_url_handlers.h" // TryConvertUrlToLocal. +#include "core/file_utilities.h" // File::OpenUrl. +#include "core/core_cloud_password.h" // Core::CloudPasswordState +#include "lang/lang_keys.h" +#include "apiwrap.h" + +#include +#include +#include +#include + +namespace Payments { +namespace { + +struct SessionProcesses { + base::flat_map> map; + base::flat_set paymentStarted; + rpl::lifetime lifetime; +}; + +base::flat_map, SessionProcesses> Processes; + +[[nodiscard]] SessionProcesses &LookupSessionProcesses( + not_null item) { + const auto session = &item->history()->session(); + const auto i = Processes.find(session); + if (i != end(Processes)) { + return i->second; + } + const auto j = Processes.emplace(session).first; + auto &result = j->second; + session->account().sessionChanges( + ) | rpl::start_with_next([=] { + Processes.erase(session); + }, result.lifetime); + return result; +} + +} // namespace + +void CheckoutProcess::Start( + not_null item, + Mode mode, + Fn reactivate) { + auto &processes = LookupSessionProcesses(item); + const auto session = &item->history()->session(); + const auto media = item->media(); + const auto invoice = media ? media->invoice() : nullptr; + if (mode == Mode::Payment && !invoice) { + return; + } + const auto id = (invoice && invoice->receiptMsgId) + ? FullMsgId(item->history()->channelId(), invoice->receiptMsgId) + : item->fullId(); + if (invoice) { + mode = invoice->receiptMsgId ? Mode::Receipt : Mode::Payment; + } else if (mode == Mode::Payment) { + LOG(("API Error: CheckoutProcess Payment start without invoice.")); + return; + } + const auto i = processes.map.find(id); + if (i != end(processes.map)) { + i->second->setReactivateCallback(std::move(reactivate)); + i->second->requestActivate(); + return; + } + const auto j = processes.map.emplace( + id, + std::make_unique( + item->history()->peer, + id.msg, + mode, + std::move(reactivate), + PrivateTag{})).first; + j->second->requestActivate(); +} + +bool CheckoutProcess::TakePaymentStarted( + not_null item) { + const auto session = &item->history()->session(); + const auto itemId = item->fullId(); + const auto i = Processes.find(session); + if (i == end(Processes) || !i->second.paymentStarted.contains(itemId)) { + return false; + } + i->second.paymentStarted.erase(itemId); + const auto j = i->second.map.find(itemId); + if (j != end(i->second.map)) { + j->second->closeAndReactivate(); + } else if (i->second.paymentStarted.empty() && i->second.map.empty()) { + Processes.erase(i); + } + return true; +} + +void CheckoutProcess::ClearAll() { + Processes.clear(); +} + +void CheckoutProcess::RegisterPaymentStart( + not_null process) { + const auto i = Processes.find(process->_session); + Assert(i != end(Processes)); + for (const auto &[itemId, itemProcess] : i->second.map) { + if (itemProcess.get() == process) { + i->second.paymentStarted.emplace(itemId); + } + } +} + +void CheckoutProcess::UnregisterPaymentStart( + not_null process) { + const auto i = Processes.find(process->_session); + if (i != end(Processes)) { + for (const auto &[itemId, itemProcess] : i->second.map) { + if (itemProcess.get() == process) { + i->second.paymentStarted.emplace(itemId); + } + } + } +} + +CheckoutProcess::CheckoutProcess( + not_null peer, + MsgId itemId, + Mode mode, + Fn reactivate, + PrivateTag) +: _session(&peer->session()) +, _form(std::make_unique
(peer, itemId, (mode == Mode::Receipt))) +, _panel(std::make_unique(panelDelegate())) +, _reactivate(std::move(reactivate)) { + _form->updates( + ) | rpl::start_with_next([=](const FormUpdate &update) { + handleFormUpdate(update); + }, _lifetime); + + _panel->backRequests( + ) | rpl::start_with_next([=] { + panelCancelEdit(); + }, _panel->lifetime()); + showForm(); + _panel->toggleProgress(true); + + if (mode == Mode::Payment) { + _session->api().passwordState( + ) | rpl::start_with_next([=](const Core::CloudPasswordState &state) { + _form->setHasPassword(!!state.request); + }, _lifetime); + } +} + +CheckoutProcess::~CheckoutProcess() { +} + +void CheckoutProcess::setReactivateCallback(Fn reactivate) { + _reactivate = std::move(reactivate); +} + +void CheckoutProcess::requestActivate() { + _panel->requestActivate(); +} + +not_null CheckoutProcess::panelDelegate() { + return static_cast(this); +} + +void CheckoutProcess::handleFormUpdate(const FormUpdate &update) { + v::match(update, [&](const ToggleProgress &data) { + _panel->toggleProgress(data.shown); + }, [&](const FormReady &) { + performInitialSilentValidation(); + if (!_initialSilentValidation) { + showForm(); + } + if (_form->paymentMethod().savedCredentials) { + _session->api().reloadPasswordState(); + } + }, [&](const ThumbnailUpdated &data) { + _panel->updateFormThumbnail(data.thumbnail); + }, [&](const ValidateFinished &) { + if (_initialSilentValidation) { + _initialSilentValidation = false; + } + showForm(); + const auto submitted = (_submitState == SubmitState::Validating); + _submitState = SubmitState::Validated; + if (submitted) { + panelSubmit(); + } + }, [&](const PaymentMethodUpdate &data) { + showForm(); + if (data.requestNewPassword) { + requestSetPassword(); + } + }, [&](const TmpPasswordRequired &) { + UnregisterPaymentStart(this); + _submitState = SubmitState::Validated; + requestPassword(); + }, [&](const BotTrustRequired &data) { + UnregisterPaymentStart(this); + _submitState = SubmitState::Validated; + _panel->showWarning(data.bot->name, data.provider->name); + if (const auto box = _enterPasswordBox.data()) { + box->closeBox(); + } + }, [&](const VerificationNeeded &data) { + auto bottomText = tr::lng_payments_processed_by( + lt_provider, + rpl::single(_form->invoice().provider)); + if (!_panel->showWebview(data.url, false, std::move(bottomText))) { + File::OpenUrl(data.url); + close(); + } + }, [&](const PaymentFinished &data) { + const auto weak = base::make_weak(this); + _session->api().applyUpdates(data.updates); + if (weak) { + closeAndReactivate(); + } + }, [&](const Error &error) { + handleError(error); + }); +} + +void CheckoutProcess::handleError(const Error &error) { + const auto showToast = [&](const TextWithEntities &text) { + _panel->requestActivate(); + _panel->showToast(text); + }; + const auto &id = error.id; + switch (error.type) { + case Error::Type::Form: + if (id == u"INVOICE_ALREADY_PAID"_q) { + _panel->showCriticalError({ + tr::lng_payments_already_paid(tr::now) + }); + } else if (true + || id == u"PROVIDER_ACCOUNT_INVALID"_q + || id == u"PROVIDER_ACCOUNT_TIMEOUT"_q) { + _panel->showCriticalError({ "Error: " + id }); + } + break; + case Error::Type::Validate: { + if (_submitState == SubmitState::Validating + || _submitState == SubmitState::Validated) { + _submitState = SubmitState::None; + } + if (_initialSilentValidation) { + _initialSilentValidation = false; + showForm(); + return; + } + using InfoField = Ui::InformationField; + using CardField = Ui::CardField; + if (id == u"REQ_INFO_NAME_INVALID"_q) { + showInformationError(InfoField::Name); + } else if (id == u"REQ_INFO_EMAIL_INVALID"_q) { + showInformationError(InfoField::Email); + } else if (id == u"REQ_INFO_PHONE_INVALID"_q) { + showInformationError(InfoField::Phone); + } else if (id == u"ADDRESS_STREET_LINE1_INVALID"_q) { + showInformationError(InfoField::ShippingStreet); + } else if (id == u"ADDRESS_CITY_INVALID"_q) { + showInformationError(InfoField::ShippingCity); + } else if (id == u"ADDRESS_STATE_INVALID"_q) { + showInformationError(InfoField::ShippingState); + } else if (id == u"ADDRESS_COUNTRY_INVALID"_q) { + showInformationError(InfoField::ShippingCountry); + } else if (id == u"ADDRESS_POSTCODE_INVALID"_q) { + showInformationError(InfoField::ShippingPostcode); + } else if (id == u"LOCAL_CARD_NUMBER_INVALID"_q) { + showCardError(CardField::Number); + } else if (id == u"LOCAL_CARD_EXPIRE_DATE_INVALID"_q) { + showCardError(CardField::ExpireDate); + } else if (id == u"LOCAL_CARD_CVC_INVALID"_q) { + showCardError(CardField::Cvc); + } else if (id == u"LOCAL_CARD_HOLDER_NAME_INVALID"_q) { + showCardError(CardField::Name); + } else if (id == u"LOCAL_CARD_BILLING_COUNTRY_INVALID"_q) { + showCardError(CardField::AddressCountry); + } else if (id == u"LOCAL_CARD_BILLING_ZIP_INVALID"_q) { + showCardError(CardField::AddressZip); + } else if (id == u"SHIPPING_BOT_TIMEOUT"_q) { + showToast({ "Error: Bot Timeout!" }); + } else if (id == u"SHIPPING_NOT_AVAILABLE"_q) { + showToast({ tr::lng_payments_shipping_not_available(tr::now) }); + } else { + showToast({ "Error: " + id }); + } + } break; + case Error::Type::Stripe: { + using Field = Ui::CardField; + if (id == u"InvalidNumber"_q || id == u"IncorrectNumber"_q) { + showCardError(Field::Number); + } else if (id == u"InvalidCVC"_q || id == u"IncorrectCVC"_q) { + showCardError(Field::Cvc); + } else if (id == u"InvalidExpiryMonth"_q + || id == u"InvalidExpiryYear"_q + || id == u"ExpiredCard"_q) { + showCardError(Field::ExpireDate); + } else if (id == u"CardDeclined"_q) { + showToast({ tr::lng_payments_card_declined(tr::now) }); + } else if (id == u"ProcessingError"_q) { + showToast({ "Sorry, a processing error occurred." }); + } else { + showToast({ "Stripe Error: " + id }); + } + } break; + case Error::Type::SmartGlocal: { + showToast({ "SmartGlocal Error: " + id }); + } break; + case Error::Type::TmpPassword: + if (const auto box = _enterPasswordBox.data()) { + if (!box->handleCustomCheckError(id)) { + showToast({ "Error: Could not generate tmp password." }); + } + } + break; + case Error::Type::Send: + if (const auto box = _enterPasswordBox.data()) { + box->closeBox(); + } + if (_submitState == SubmitState::Finishing) { + UnregisterPaymentStart(this); + _submitState = SubmitState::Validated; + } + if (id == u"INVOICE_ALREADY_PAID"_q) { + showToast({ tr::lng_payments_already_paid(tr::now) }); + } else if (id == u"PAYMENT_FAILED"_q) { + showToast({ tr::lng_payments_payment_failed(tr::now) }); + } else if (id == u"BOT_PRECHECKOUT_FAILED"_q) { + showToast({ tr::lng_payments_precheckout_failed(tr::now) }); + } else if (id == u"REQUESTED_INFO_INVALID"_q + || id == u"SHIPPING_OPTION_INVALID"_q + || id == u"PAYMENT_CREDENTIALS_INVALID"_q + || id == u"PAYMENT_CREDENTIALS_ID_INVALID"_q) { + showToast({ tr::lng_payments_payment_failed(tr::now) }); + showToast({ "Error: " + id + ". Your card has not been billed." }); + } else if (id == u"TMP_PASSWORD_INVALID"_q) { + requestPassword(); + } else { + showToast({ "Error: " + id }); + } + break; + default: Unexpected("Error type in CheckoutProcess::handleError."); + } +} + +void CheckoutProcess::panelRequestClose() { + if (_form->hasChanges()) { + _panel->showCloseConfirm(); + } else { + panelCloseSure(); + } +} + +void CheckoutProcess::panelCloseSure() { + closeAndReactivate(); +} + +void CheckoutProcess::closeAndReactivate() { + const auto reactivate = std::move(_reactivate); + close(); + if (reactivate) { + reactivate(); + } +} + +void CheckoutProcess::close() { + const auto i = Processes.find(_session); + if (i == end(Processes)) { + return; + } + const auto j = ranges::find(i->second.map, this, [](const auto &pair) { + return pair.second.get(); + }); + if (j == end(i->second.map)) { + return; + } + i->second.map.erase(j); + if (i->second.map.empty() && i->second.paymentStarted.empty()) { + Processes.erase(i); + } +} + +void CheckoutProcess::panelSubmit() { + if (_form->invoice().receipt.paid) { + closeAndReactivate(); + return; + } else if (_submitState == SubmitState::Validating + || _submitState == SubmitState::Finishing) { + return; + } + const auto &method = _form->paymentMethod(); + const auto &invoice = _form->invoice(); + const auto &options = _form->shippingOptions(); + if (!options.list.empty() && options.selectedId.isEmpty()) { + chooseShippingOption(); + } else if (_submitState != SubmitState::Validated + && options.list.empty() + && (invoice.isShippingAddressRequested + || invoice.isNameRequested + || invoice.isEmailRequested + || invoice.isPhoneRequested)) { + _submitState = SubmitState::Validating; + _form->validateInformation(_form->information()); + } else if (!method.newCredentials && !method.savedCredentials) { + editPaymentMethod(); + } else { + RegisterPaymentStart(this); + _submitState = SubmitState::Finishing; + _form->submit(); + } +} + +void CheckoutProcess::panelTrustAndSubmit() { + _form->trustBot(); + panelSubmit(); +} + +void CheckoutProcess::panelWebviewMessage( + const QJsonDocument &message, + bool saveInformation) { + if (!message.isArray()) { + LOG(("Payments Error: " + "Not an array received in buy_callback arguments.")); + return; + } + const auto list = message.array(); + if (list.at(0).toString() != "payment_form_submit") { + return; + } else if (!list.at(1).isString()) { + LOG(("Payments Error: " + "Not a string received in buy_callback result.")); + return; + } + + auto error = QJsonParseError(); + const auto document = QJsonDocument::fromJson( + list.at(1).toString().toUtf8(), + &error); + if (error.error != QJsonParseError::NoError) { + LOG(("Payments Error: " + "Failed to parse buy_callback arguments, error: %1." + ).arg(error.errorString())); + return; + } else if (!document.isObject()) { + LOG(("Payments Error: " + "Not an object decoded in buy_callback result.")); + return; + } + const auto root = document.object(); + const auto title = root.value("title").toString(); + const auto credentials = root.value("credentials"); + if (!credentials.isObject()) { + LOG(("Payments Error: " + "Not an object received in payment credentials.")); + return; + } + crl::on_main(this, [=] { + _form->setPaymentCredentials(NewCredentials{ + .title = title, + .data = QJsonDocument( + credentials.toObject() + ).toJson(QJsonDocument::Compact), + .saveOnServer = saveInformation, + }); + }); +} + +bool CheckoutProcess::panelWebviewNavigationAttempt(const QString &uri) { + if (Core::TryConvertUrlToLocal(uri) == uri) { + return true; + } + crl::on_main(this, [=] { closeAndReactivate(); }); + return false; +} + +void CheckoutProcess::panelCancelEdit() { + if (_submitState != SubmitState::None + && _submitState != SubmitState::Validated) { + return; + } + showForm(); +} + +void CheckoutProcess::panelEditPaymentMethod() { + if (_submitState != SubmitState::None + && _submitState != SubmitState::Validated) { + return; + } + editPaymentMethod(); +} + +void CheckoutProcess::panelValidateCard( + Ui::UncheckedCardDetails data, + bool saveInformation) { + _form->validateCard(data, saveInformation); +} + +void CheckoutProcess::panelEditShippingInformation() { + showEditInformation(Ui::InformationField::ShippingStreet); +} + +void CheckoutProcess::panelEditName() { + showEditInformation(Ui::InformationField::Name); +} + +void CheckoutProcess::panelEditEmail() { + showEditInformation(Ui::InformationField::Email); +} + +void CheckoutProcess::panelEditPhone() { + showEditInformation(Ui::InformationField::Phone); +} + +void CheckoutProcess::showForm() { + _panel->showForm( + _form->invoice(), + _form->information(), + _form->paymentMethod().ui, + _form->shippingOptions()); +} + +void CheckoutProcess::showEditInformation(Ui::InformationField field) { + if (_submitState != SubmitState::None + && _submitState != SubmitState::Validated) { + return; + } + _panel->showEditInformation( + _form->invoice(), + _form->information(), + field); +} + +void CheckoutProcess::showInformationError(Ui::InformationField field) { + Expects(_submitState != SubmitState::Validated); + + if (_submitState != SubmitState::None) { + return; + } + _panel->showInformationError( + _form->invoice(), + _form->information(), + field); +} + +void CheckoutProcess::showCardError(Ui::CardField field) { + if (_submitState != SubmitState::None + && _submitState != SubmitState::Validated) { + return; + } + _panel->showCardError(_form->paymentMethod().ui.native, field); +} + +void CheckoutProcess::chooseShippingOption() { + _panel->chooseShippingOption(_form->shippingOptions()); +} + +void CheckoutProcess::chooseTips() { + _panel->chooseTips(_form->invoice()); +} + +void CheckoutProcess::editPaymentMethod() { + _panel->choosePaymentMethod(_form->paymentMethod().ui); +} + +void CheckoutProcess::requestSetPassword() { + _session->api().reloadPasswordState(); + _panel->askSetPassword(); +} + +void CheckoutProcess::requestPassword() { + getPasswordState([=](const Core::CloudPasswordState &state) { + auto fields = PasscodeBox::CloudFields::From(state); + fields.customTitle = tr::lng_payments_password_title(); + fields.customDescription = tr::lng_payments_password_description( + tr::now, + lt_card, + _form->paymentMethod().savedCredentials.title); + fields.customSubmitButton = tr::lng_payments_password_submit(); + fields.customCheckCallback = [=]( + const Core::CloudPasswordResult &result) { + _form->submit(result); + }; + auto owned = Box(_session, fields); + _enterPasswordBox = owned.data(); + _panel->showBox(std::move(owned)); + }); +} + +void CheckoutProcess::panelSetPassword() { + getPasswordState([=](const Core::CloudPasswordState &state) { + if (state.request) { + return; + } + auto owned = Box( + _session, + PasscodeBox::CloudFields::From(state)); + const auto box = owned.data(); + + rpl::merge( + box->newPasswordSet() | rpl::to_empty, + box->passwordReloadNeeded() + ) | rpl::start_with_next([=] { + _session->api().reloadPasswordState(); + }, box->lifetime()); + + box->clearUnconfirmedPassword( + ) | rpl::start_with_next([=] { + _session->api().clearUnconfirmedPassword(); + }, box->lifetime()); + + _panel->showBox(std::move(owned)); + }); +} + +void CheckoutProcess::panelOpenUrl(const QString &url) { + File::OpenUrl(url); +} + +void CheckoutProcess::getPasswordState( + Fn callback) { + Expects(callback != nullptr); + + if (_gettingPasswordState) { + return; + } + _session->api().passwordState( + ) | rpl::start_with_next([=](const Core::CloudPasswordState &state) { + _gettingPasswordState.destroy(); + callback(state); + }, _gettingPasswordState); +} + +void CheckoutProcess::panelChooseShippingOption() { + if (_submitState != SubmitState::None + && _submitState != SubmitState::Validated) { + return; + } + chooseShippingOption(); +} + +void CheckoutProcess::panelChangeShippingOption(const QString &id) { + _form->setShippingOption(id); + showForm(); +} + +void CheckoutProcess::panelChooseTips() { + if (_submitState != SubmitState::None + && _submitState != SubmitState::Validated) { + return; + } + chooseTips(); +} + +void CheckoutProcess::panelChangeTips(int64 value) { + _form->setTips(value); + showForm(); +} + +void CheckoutProcess::panelValidateInformation( + Ui::RequestedInformation data) { + if (_submitState == SubmitState::Validated) { + _submitState = SubmitState::None; + } + _form->validateInformation(data); +} + +void CheckoutProcess::panelShowBox(object_ptr box) { + _panel->showBox(std::move(box)); +} + +void CheckoutProcess::performInitialSilentValidation() { + const auto &invoice = _form->invoice(); + const auto &saved = _form->information(); + if (invoice.receipt + || (invoice.isNameRequested && saved.name.isEmpty()) + || (invoice.isEmailRequested && saved.email.isEmpty()) + || (invoice.isPhoneRequested && saved.phone.isEmpty()) + || (invoice.isShippingAddressRequested && !saved.shippingAddress)) { + return; + } + _initialSilentValidation = true; + _form->validateInformation(saved); +} + +QString CheckoutProcess::panelWebviewDataPath() { + return _session->domain().local().webviewDataPath(); +} + +} // namespace Payments diff --git a/Telegram/SourceFiles/payments/payments_checkout_process.h b/Telegram/SourceFiles/payments/payments_checkout_process.h new file mode 100644 index 000000000..8dceffd2c --- /dev/null +++ b/Telegram/SourceFiles/payments/payments_checkout_process.h @@ -0,0 +1,145 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +#include "payments/ui/payments_panel_delegate.h" +#include "base/weak_ptr.h" + +class HistoryItem; +class PasscodeBox; + +namespace Core { +struct CloudPasswordState; +} // namespace Core + +namespace Main { +class Session; +} // namespace Main + +namespace Ui { +class GenericBox; +} // namespace Ui + +namespace Payments::Ui { +class Panel; +enum class InformationField; +enum class CardField; +} // namespace Payments::Ui + +namespace Payments { + +class Form; +struct FormUpdate; +struct Error; + +enum class Mode { + Payment, + Receipt, +}; + +class CheckoutProcess final + : public base::has_weak_ptr + , private Ui::PanelDelegate { + struct PrivateTag {}; + +public: + static void Start( + not_null item, + Mode mode, + Fn reactivate); + [[nodiscard]] static bool TakePaymentStarted( + not_null item); + static void ClearAll(); + + CheckoutProcess( + not_null peer, + MsgId itemId, + Mode mode, + Fn reactivate, + PrivateTag); + ~CheckoutProcess(); + +private: + enum class SubmitState { + None, + Validating, + Validated, + Finishing, + }; + [[nodiscard]] not_null panelDelegate(); + + static void RegisterPaymentStart(not_null process); + static void UnregisterPaymentStart(not_null process); + + void setReactivateCallback(Fn reactivate); + void requestActivate(); + void closeAndReactivate(); + void close(); + + void handleFormUpdate(const FormUpdate &update); + void handleError(const Error &error); + + void showForm(); + void showEditInformation(Ui::InformationField field); + void showInformationError(Ui::InformationField field); + void showCardError(Ui::CardField field); + void chooseShippingOption(); + void chooseTips(); + void editPaymentMethod(); + + void requestSetPassword(); + void requestPassword(); + void getPasswordState( + Fn callback); + + void performInitialSilentValidation(); + + void panelRequestClose() override; + void panelCloseSure() override; + void panelSubmit() override; + void panelTrustAndSubmit() override; + void panelWebviewMessage( + const QJsonDocument &message, + bool saveInformation) override; + bool panelWebviewNavigationAttempt(const QString &uri) override; + void panelSetPassword() override; + void panelOpenUrl(const QString &url) override; + + void panelCancelEdit() override; + void panelEditPaymentMethod() override; + void panelEditShippingInformation() override; + void panelEditName() override; + void panelEditEmail() override; + void panelEditPhone() override; + void panelChooseShippingOption() override; + void panelChangeShippingOption(const QString &id) override; + void panelChooseTips() override; + void panelChangeTips(int64 value) override; + + void panelValidateInformation(Ui::RequestedInformation data) override; + void panelValidateCard( + Ui::UncheckedCardDetails data, + bool saveInformation) override; + void panelShowBox(object_ptr box) override; + + QString panelWebviewDataPath() override; + + const not_null _session; + const std::unique_ptr _form; + const std::unique_ptr _panel; + QPointer _enterPasswordBox; + Fn _reactivate; + SubmitState _submitState = SubmitState::None; + bool _initialSilentValidation = false; + + rpl::lifetime _gettingPasswordState; + rpl::lifetime _lifetime; + +}; + +} // namespace Payments diff --git a/Telegram/SourceFiles/payments/payments_form.cpp b/Telegram/SourceFiles/payments/payments_form.cpp new file mode 100644 index 000000000..4e7b407ba --- /dev/null +++ b/Telegram/SourceFiles/payments/payments_form.cpp @@ -0,0 +1,946 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "payments/payments_form.h" + +#include "main/main_session.h" +#include "data/data_session.h" +#include "data/data_media_types.h" +#include "data/data_user.h" +#include "data/data_photo.h" +#include "data/data_photo_media.h" +#include "data/data_file_origin.h" +#include "data/data_countries.h" +#include "history/history_item.h" +#include "history/history_service.h" // HistoryServicePayment. +#include "stripe/stripe_api_client.h" +#include "stripe/stripe_error.h" +#include "stripe/stripe_token.h" +#include "stripe/stripe_card_validator.h" +#include "smartglocal/smartglocal_api_client.h" +#include "smartglocal/smartglocal_error.h" +#include "smartglocal/smartglocal_token.h" +#include "storage/storage_account.h" +#include "ui/image/image.h" +#include "apiwrap.h" +#include "core/core_cloud_password.h" +#include "styles/style_payments.h" // paymentsThumbnailSize. + +#include +#include +#include + +namespace Payments { +namespace { + +constexpr auto kPasswordPeriod = 15 * TimeId(60); + +[[nodiscard]] Ui::Address ParseAddress(const MTPPostAddress &address) { + return address.match([](const MTPDpostAddress &data) { + return Ui::Address{ + .address1 = qs(data.vstreet_line1()), + .address2 = qs(data.vstreet_line2()), + .city = qs(data.vcity()), + .state = qs(data.vstate()), + .countryIso2 = qs(data.vcountry_iso2()), + .postcode = qs(data.vpost_code()), + }; + }); +} + +[[nodiscard]] int64 ParsePriceAmount(uint64 value) { + return *reinterpret_cast(&value); +} + +[[nodiscard]] std::vector ParsePrices( + const MTPVector &data) { + return ranges::views::all( + data.v + ) | ranges::views::transform([](const MTPLabeledPrice &price) { + return price.match([&](const MTPDlabeledPrice &data) { + return Ui::LabeledPrice{ + .label = qs(data.vlabel()), + .price = ParsePriceAmount(data.vamount().v), + }; + }); + }) | ranges::to_vector; +} + +[[nodiscard]] MTPPaymentRequestedInfo Serialize( + const Ui::RequestedInformation &information) { + using Flag = MTPDpaymentRequestedInfo::Flag; + return MTP_paymentRequestedInfo( + MTP_flags((information.name.isEmpty() ? Flag(0) : Flag::f_name) + | (information.email.isEmpty() ? Flag(0) : Flag::f_email) + | (information.phone.isEmpty() ? Flag(0) : Flag::f_phone) + | (information.shippingAddress + ? Flag::f_shipping_address + : Flag(0))), + MTP_string(information.name), + MTP_string(information.phone), + MTP_string(information.email), + MTP_postAddress( + MTP_string(information.shippingAddress.address1), + MTP_string(information.shippingAddress.address2), + MTP_string(information.shippingAddress.city), + MTP_string(information.shippingAddress.state), + MTP_string(information.shippingAddress.countryIso2), + MTP_string(information.shippingAddress.postcode))); +} + +[[nodiscard]] QString CardTitle(const Stripe::Card &card) { + // Like server stores saved_credentials title. + return Stripe::CardBrandToString(card.brand()).toLower() + + " *" + + card.last4(); +} + +[[nodiscard]] QString CardTitle(const SmartGlocal::Card &card) { + // Like server stores saved_credentials title. + return card.type().toLower() + + " *" + + SmartGlocal::Last4(card); +} + +[[nodiscard]] QByteArray ThemeParams() { + const auto colors = std::vector>{ + { "bg_color", st::windowBg }, + { "text_color", st::windowFg }, + { "hint_color", st::windowSubTextFg }, + { "link_color", st::windowActiveTextFg }, + { "button_color", st::windowBgActive }, + { "button_text_color", st::windowFgActive }, + }; + auto object = QJsonObject(); + for (const auto &[name, color] : colors) { + const auto value = uint32(0xFF000000U) + | (uint32(color->c.red()) << 16) + | (uint32(color->c.green()) << 8) + | (uint32(color->c.blue())); + const auto int32value = *reinterpret_cast(&value); + object.insert(name, int32value); + } + return QJsonDocument(object).toJson(QJsonDocument::Compact); +} + +} // namespace + +Form::Form(not_null peer, MsgId itemId, bool receipt) +: _session(&peer->session()) +, _api(&_session->mtp()) +, _peer(peer) +, _msgId(itemId) +, _receiptMode(receipt) { + fillInvoiceFromMessage(); + if (_receiptMode) { + _invoice.receipt.paid = true; + requestReceipt(); + } else { + requestForm(); + } +} + +Form::~Form() = default; + +void Form::fillInvoiceFromMessage() { + const auto id = FullMsgId(peerToChannel(_peer->id), _msgId); + if (const auto item = _session->data().message(id)) { + const auto media = [&] { + if (const auto payment = item->Get()) { + if (const auto invoice = payment->msg) { + return invoice->media(); + } + } + return item->media(); + }(); + if (const auto invoice = media ? media->invoice() : nullptr) { + _invoice.isTest = invoice->isTest; + _invoice.cover = Ui::Cover{ + .title = invoice->title, + .description = invoice->description, + }; + if (const auto photo = invoice->photo) { + loadThumbnail(photo); + } + } + } +} + +void Form::showProgress() { + _updates.fire(ToggleProgress{ true }); +} + +void Form::hideProgress() { + _updates.fire(ToggleProgress{ false }); +} + +void Form::loadThumbnail(not_null photo) { + Expects(!_thumbnailLoadProcess); + + auto view = photo->createMediaView(); + if (auto good = prepareGoodThumbnail(view); !good.isNull()) { + _invoice.cover.thumbnail = std::move(good); + return; + } + _thumbnailLoadProcess = std::make_unique(); + if (auto blurred = prepareBlurredThumbnail(view); !blurred.isNull()) { + _invoice.cover.thumbnail = std::move(blurred); + _thumbnailLoadProcess->blurredSet = true; + } else { + _invoice.cover.thumbnail = prepareEmptyThumbnail(); + } + _thumbnailLoadProcess->view = std::move(view); + photo->load( + Data::PhotoSize::Thumbnail, + FullMsgId(peerToChannel(_peer->id), _msgId)); + _session->downloaderTaskFinished( + ) | rpl::start_with_next([=] { + const auto &view = _thumbnailLoadProcess->view; + if (auto good = prepareGoodThumbnail(view); !good.isNull()) { + _invoice.cover.thumbnail = std::move(good); + _thumbnailLoadProcess = nullptr; + } else if (_thumbnailLoadProcess->blurredSet) { + return; + } else if (auto blurred = prepareBlurredThumbnail(view) + ; !blurred.isNull()) { + _invoice.cover.thumbnail = std::move(blurred); + _thumbnailLoadProcess->blurredSet = true; + } else { + return; + } + _updates.fire(ThumbnailUpdated{ _invoice.cover.thumbnail }); + }, _thumbnailLoadProcess->lifetime); +} + +QImage Form::prepareGoodThumbnail( + const std::shared_ptr &view) const { + using Size = Data::PhotoSize; + if (const auto large = view->image(Size::Large)) { + return prepareThumbnail(large); + } else if (const auto thumbnail = view->image(Size::Thumbnail)) { + return prepareThumbnail(thumbnail); + } + return QImage(); +} + +QImage Form::prepareBlurredThumbnail( + const std::shared_ptr &view) const { + if (const auto small = view->image(Data::PhotoSize::Small)) { + return prepareThumbnail(small, true); + } else if (const auto blurred = view->thumbnailInline()) { + return prepareThumbnail(blurred, true); + } + return QImage(); +} + +QImage Form::prepareThumbnail( + not_null image, + bool blurred) const { + auto result = image->original().scaled( + st::paymentsThumbnailSize * cIntRetinaFactor(), + Qt::KeepAspectRatio, + Qt::SmoothTransformation); + Images::prepareRound(result, ImageRoundRadius::Large); + result.setDevicePixelRatio(cRetinaFactor()); + return result; +} + +QImage Form::prepareEmptyThumbnail() const { + auto result = QImage( + st::paymentsThumbnailSize * cIntRetinaFactor(), + QImage::Format_ARGB32_Premultiplied); + result.setDevicePixelRatio(cRetinaFactor()); + result.fill(Qt::transparent); + return result; +} + +void Form::requestForm() { + showProgress(); + _api.request(MTPpayments_GetPaymentForm( + MTP_flags(MTPpayments_GetPaymentForm::Flag::f_theme_params), + _peer->input, + MTP_int(_msgId), + MTP_dataJSON(MTP_bytes(ThemeParams())) + )).done([=](const MTPpayments_PaymentForm &result) { + hideProgress(); + result.match([&](const auto &data) { + processForm(data); + }); + }).fail([=](const MTP::Error &error) { + hideProgress(); + _updates.fire(Error{ Error::Type::Form, error.type() }); + }).send(); +} + +void Form::requestReceipt() { + showProgress(); + _api.request(MTPpayments_GetPaymentReceipt( + _peer->input, + MTP_int(_msgId) + )).done([=](const MTPpayments_PaymentReceipt &result) { + hideProgress(); + result.match([&](const auto &data) { + processReceipt(data); + }); + }).fail([=](const MTP::Error &error) { + hideProgress(); + _updates.fire(Error{ Error::Type::Form, error.type() }); + }).send(); +} + +void Form::processForm(const MTPDpayments_paymentForm &data) { + _session->data().processUsers(data.vusers()); + + data.vinvoice().match([&](const auto &data) { + processInvoice(data); + }); + processDetails(data); + if (const auto info = data.vsaved_info()) { + info->match([&](const auto &data) { + processSavedInformation(data); + }); + } + if (const auto credentials = data.vsaved_credentials()) { + credentials->match([&](const auto &data) { + processSavedCredentials(data); + }); + } + fillPaymentMethodInformation(); + _updates.fire(FormReady{}); +} + +void Form::processReceipt(const MTPDpayments_paymentReceipt &data) { + _session->data().processUsers(data.vusers()); + + data.vinvoice().match([&](const auto &data) { + processInvoice(data); + }); + processDetails(data); + if (const auto info = data.vinfo()) { + info->match([&](const auto &data) { + processSavedInformation(data); + }); + } + if (const auto shipping = data.vshipping()) { + processShippingOptions({ *shipping }); + if (!_shippingOptions.list.empty()) { + _shippingOptions.selectedId = _shippingOptions.list.front().id; + } + } + _paymentMethod.savedCredentials = SavedCredentials{ + .id = "(used)", + .title = qs(data.vcredentials_title()), + }; + fillPaymentMethodInformation(); + _updates.fire(FormReady{}); +} + +void Form::processInvoice(const MTPDinvoice &data) { + const auto suggested = data.vsuggested_tip_amounts().value_or_empty(); + _invoice = Ui::Invoice{ + .cover = std::move(_invoice.cover), + + .prices = ParsePrices(data.vprices()), + .suggestedTips = ranges::views::all( + suggested + ) | ranges::views::transform( + &MTPlong::v + ) | ranges::views::transform( + ParsePriceAmount + ) | ranges::to_vector, + .tipsMax = ParsePriceAmount(data.vmax_tip_amount().value_or_empty()), + .currency = qs(data.vcurrency()), + + .isNameRequested = data.is_name_requested(), + .isPhoneRequested = data.is_phone_requested(), + .isEmailRequested = data.is_email_requested(), + .isShippingAddressRequested = data.is_shipping_address_requested(), + .isFlexible = data.is_flexible(), + .isTest = data.is_test(), + + .phoneSentToProvider = data.is_phone_to_provider(), + .emailSentToProvider = data.is_email_to_provider(), + }; +} + +void Form::processDetails(const MTPDpayments_paymentForm &data) { + const auto nativeParams = data.vnative_params(); + auto nativeParamsJson = nativeParams + ? nativeParams->match( + [&](const MTPDdataJSON &data) { return data.vdata().v; }) + : QByteArray(); + _details = FormDetails{ + .formId = data.vform_id().v, + .url = qs(data.vurl()), + .nativeProvider = qs(data.vnative_provider().value_or_empty()), + .nativeParamsJson = std::move(nativeParamsJson), + .botId = data.vbot_id().v, + .providerId = data.vprovider_id().v, + .canSaveCredentials = data.is_can_save_credentials(), + .passwordMissing = data.is_password_missing(), + }; + if (const auto botId = _details.botId) { + if (const auto bot = _session->data().userLoaded(botId)) { + _invoice.cover.seller = bot->name; + } + } + if (const auto providerId = _details.providerId) { + if (const auto bot = _session->data().userLoaded(providerId)) { + _invoice.provider = bot->name; + } + } +} + +void Form::processDetails(const MTPDpayments_paymentReceipt &data) { + _invoice.receipt = Ui::Receipt{ + .date = data.vdate().v, + .totalAmount = ParsePriceAmount(data.vtotal_amount().v), + .currency = qs(data.vcurrency()), + .paid = true, + }; + _details = FormDetails{ + .botId = data.vbot_id().v, + .providerId = data.vprovider_id().v, + }; + if (_invoice.cover.title.isEmpty() + && _invoice.cover.description.isEmpty() + && _invoice.cover.thumbnail.isNull() + && !_thumbnailLoadProcess) { + _invoice.cover = Ui::Cover{ + .title = qs(data.vtitle()), + .description = qs(data.vdescription()), + }; + if (const auto web = data.vphoto()) { + if (const auto photo = _session->data().photoFromWeb(*web, {})) { + loadThumbnail(photo); + } + } + } + if (_details.botId) { + if (const auto bot = _session->data().userLoaded(_details.botId)) { + _invoice.cover.seller = bot->name; + } + } +} + +void Form::processSavedInformation(const MTPDpaymentRequestedInfo &data) { + const auto address = data.vshipping_address(); + _savedInformation = _information = Ui::RequestedInformation{ + .defaultPhone = defaultPhone(), + .defaultCountry = defaultCountry(), + .name = qs(data.vname().value_or_empty()), + .phone = qs(data.vphone().value_or_empty()), + .email = qs(data.vemail().value_or_empty()), + .shippingAddress = address ? ParseAddress(*address) : Ui::Address(), + }; +} + +void Form::processSavedCredentials( + const MTPDpaymentSavedCredentialsCard &data) { + _paymentMethod.savedCredentials = SavedCredentials{ + .id = qs(data.vid()), + .title = qs(data.vtitle()), + }; + refreshPaymentMethodDetails(); +} + +void Form::refreshPaymentMethodDetails() { + const auto &saved = _paymentMethod.savedCredentials; + const auto &entered = _paymentMethod.newCredentials; + _paymentMethod.ui.title = entered ? entered.title : saved.title; + _paymentMethod.ui.provider = _invoice.provider; + _paymentMethod.ui.ready = entered || saved; + _paymentMethod.ui.native.defaultCountry = defaultCountry(); + _paymentMethod.ui.canSaveInformation + = _paymentMethod.ui.native.canSaveInformation + = _details.canSaveCredentials || _details.passwordMissing; +} + +QString Form::defaultPhone() const { + return _session->user()->phone(); +} + +QString Form::defaultCountry() const { + return Data::CountryISO2ByPhone(defaultPhone()); +} + +void Form::fillPaymentMethodInformation() { + _paymentMethod.native = NativePaymentMethod(); + _paymentMethod.ui.native = Ui::NativeMethodDetails(); + _paymentMethod.ui.url = _details.url; + + //AssertIsDebug(); + //static auto counter = 0; // #TODO payments test both native and webview. + if (!_details.nativeProvider.isEmpty()/* && ((++counter) % 2)*/) { + auto error = QJsonParseError(); + auto document = QJsonDocument::fromJson( + _details.nativeParamsJson, + &error); + if (error.error != QJsonParseError::NoError) { + LOG(("Payment Error: Could not decode native_params, error %1: %2" + ).arg(error.error + ).arg(error.errorString())); + } else if (!document.isObject()) { + LOG(("Payment Error: Not an object in native_params.")); + } else { + const auto object = document.object(); + if (_details.nativeProvider == "stripe") { + fillStripeNativeMethod(object); + } else if (_details.nativeProvider == "smartglocal") { + fillSmartGlocalNativeMethod(object); + } else { + LOG(("Payment Error: Unknown native provider '%1'." + ).arg(_details.nativeProvider)); + } + } + } + refreshPaymentMethodDetails(); +} + +void Form::fillStripeNativeMethod(QJsonObject object) { + const auto value = [&](QStringView key) { + return object.value(key); + }; + const auto key = value(u"publishable_key").toString(); + if (key.isEmpty()) { + LOG(("Payment Error: No publishable_key in stripe native_params.")); + return; + } + _paymentMethod.native = NativePaymentMethod{ + .data = StripePaymentMethod{ + .publishableKey = key, + }, + }; + _paymentMethod.ui.native = Ui::NativeMethodDetails{ + .supported = true, + .needCountry = value(u"need_country").toBool(), + .needZip = value(u"need_zip").toBool(), + .needCardholderName = value(u"need_cardholder_name").toBool(), + }; +} + +void Form::fillSmartGlocalNativeMethod(QJsonObject object) { + const auto value = [&](QStringView key) { + return object.value(key); + }; + const auto key = value(u"public_token").toString(); + if (key.isEmpty()) { + LOG(("Payment Error: " + "No public_token in smartglocal native_params.")); + return; + } + _paymentMethod.native = NativePaymentMethod{ + .data = SmartGlocalPaymentMethod{ + .publicToken = key, + }, + }; + _paymentMethod.ui.native = Ui::NativeMethodDetails{ + .supported = true, + .needCountry = false, + .needZip = false, + .needCardholderName = false, + }; +} + +void Form::submit() { + Expects(_paymentMethod.newCredentials + || _paymentMethod.savedCredentials); + + const auto password = _paymentMethod.newCredentials + ? QByteArray() + : _session->validTmpPassword(); + if (!_paymentMethod.newCredentials && password.isEmpty()) { + _updates.fire(TmpPasswordRequired{}); + return; + } else if (!_session->local().isBotTrustedPayment(_details.botId)) { + _updates.fire(BotTrustRequired{ + .bot = _session->data().user(_details.botId), + .provider = _session->data().user(_details.providerId), + }); + return; + } + + using Flag = MTPpayments_SendPaymentForm::Flag; + showProgress(); + _api.request(MTPpayments_SendPaymentForm( + MTP_flags((_requestedInformationId.isEmpty() + ? Flag(0) + : Flag::f_requested_info_id) + | (_shippingOptions.selectedId.isEmpty() + ? Flag(0) + : Flag::f_shipping_option_id) + | (_invoice.tipsMax > 0 ? Flag::f_tip_amount : Flag(0))), + MTP_long(_details.formId), + _peer->input, + MTP_int(_msgId), + MTP_string(_requestedInformationId), + MTP_string(_shippingOptions.selectedId), + (_paymentMethod.newCredentials + ? MTP_inputPaymentCredentials( + MTP_flags((_paymentMethod.newCredentials.saveOnServer + && _details.canSaveCredentials) + ? MTPDinputPaymentCredentials::Flag::f_save + : MTPDinputPaymentCredentials::Flag(0)), + MTP_dataJSON(MTP_bytes(_paymentMethod.newCredentials.data))) + : MTP_inputPaymentCredentialsSaved( + MTP_string(_paymentMethod.savedCredentials.id), + MTP_bytes(password))), + MTP_long(_invoice.tipsSelected) + )).done([=](const MTPpayments_PaymentResult &result) { + hideProgress(); + result.match([&](const MTPDpayments_paymentResult &data) { + _updates.fire(PaymentFinished{ data.vupdates() }); + }, [&](const MTPDpayments_paymentVerificationNeeded &data) { + _updates.fire(VerificationNeeded{ qs(data.vurl()) }); + }); + }).fail([=](const MTP::Error &error) { + hideProgress(); + _updates.fire(Error{ Error::Type::Send, error.type() }); + }).send(); +} + +void Form::submit(const Core::CloudPasswordResult &result) { + if (_passwordRequestId) { + return; + } + _passwordRequestId = _api.request(MTPaccount_GetTmpPassword( + result.result, + MTP_int(kPasswordPeriod) + )).done([=](const MTPaccount_TmpPassword &result) { + _passwordRequestId = 0; + result.match([&](const MTPDaccount_tmpPassword &data) { + _session->setTmpPassword( + data.vtmp_password().v, + data.vvalid_until().v); + submit(); + }); + }).fail([=](const MTP::Error &error) { + _passwordRequestId = 0; + _updates.fire(Error{ Error::Type::TmpPassword, error.type() }); + }).send(); +} + +void Form::validateInformation(const Ui::RequestedInformation &information) { + if (_validateRequestId) { + if (_validatedInformation == information) { + return; + } + hideProgress(); + _api.request(base::take(_validateRequestId)).cancel(); + } + _validatedInformation = information; + if (!validateInformationLocal(information)) { + return; + } + + Assert(!_invoice.isShippingAddressRequested + || information.shippingAddress); + Assert(!_invoice.isNameRequested || !information.name.isEmpty()); + Assert(!_invoice.isEmailRequested || !information.email.isEmpty()); + Assert(!_invoice.isPhoneRequested || !information.phone.isEmpty()); + + showProgress(); + using Flag = MTPpayments_ValidateRequestedInfo::Flag; + _validateRequestId = _api.request(MTPpayments_ValidateRequestedInfo( + MTP_flags(information.save ? Flag::f_save : Flag(0)), + _peer->input, + MTP_int(_msgId), + Serialize(information) + )).done([=](const MTPpayments_ValidatedRequestedInfo &result) { + hideProgress(); + _validateRequestId = 0; + const auto oldSelectedId = _shippingOptions.selectedId; + result.match([&](const MTPDpayments_validatedRequestedInfo &data) { + _requestedInformationId = data.vid().value_or_empty(); + processShippingOptions( + data.vshipping_options().value_or_empty()); + }); + _shippingOptions.selectedId = ranges::contains( + _shippingOptions.list, + oldSelectedId, + &Ui::ShippingOption::id + ) ? oldSelectedId : QString(); + if (_shippingOptions.selectedId.isEmpty() + && _shippingOptions.list.size() == 1) { + _shippingOptions.selectedId = _shippingOptions.list.front().id; + } + _information = _validatedInformation; + if (_information.save) { + _savedInformation = _information; + } + _updates.fire(ValidateFinished{}); + }).fail([=](const MTP::Error &error) { + hideProgress(); + _validateRequestId = 0; + _updates.fire(Error{ Error::Type::Validate, error.type() }); + }).send(); +} + +bool Form::hasChanges() const { + const auto &information = _validateRequestId + ? _validatedInformation + : _information; + return (information != _savedInformation) + || (_stripe != nullptr) + || (_smartglocal != nullptr) + || !_paymentMethod.newCredentials.empty(); +} + +bool Form::validateInformationLocal( + const Ui::RequestedInformation &information) const { + if (const auto error = informationErrorLocal(information)) { + _updates.fire_copy(error); + return false; + } + return true; +} + +Error Form::informationErrorLocal( + const Ui::RequestedInformation &information) const { + auto errors = QStringList(); + const auto push = [&](const QString &id) { + errors.push_back(id); + }; + if (_invoice.isShippingAddressRequested) { + if (information.shippingAddress.address1.isEmpty()) { + push(u"ADDRESS_STREET_LINE1_INVALID"_q); + } + if (information.shippingAddress.city.isEmpty()) { + push(u"ADDRESS_CITY_INVALID"_q); + } + if (information.shippingAddress.countryIso2.isEmpty()) { + push(u"ADDRESS_COUNTRY_INVALID"_q); + } + } + if (_invoice.isNameRequested && information.name.isEmpty()) { + push(u"REQ_INFO_NAME_INVALID"_q); + } + if (_invoice.isEmailRequested && information.email.isEmpty()) { + push(u"REQ_INFO_EMAIL_INVALID"_q); + } + if (_invoice.isPhoneRequested && information.phone.isEmpty()) { + push(u"REQ_INFO_PHONE_INVALID"_q); + } + if (!errors.isEmpty()) { + return Error{ Error::Type::Validate, errors.front() }; + } + return Error(); +} + +void Form::validateCard( + const Ui::UncheckedCardDetails &details, + bool saveInformation) { + Expects(!v::is_null(_paymentMethod.native.data)); + + if (!validateCardLocal(details)) { + return; + } + const auto &native = _paymentMethod.native.data; + if (const auto smartglocal = std::get_if( + &native)) { + validateCard(*smartglocal, details, saveInformation); + } else if (const auto stripe = std::get_if(&native)) { + validateCard(*stripe, details, saveInformation); + } else { + Unexpected("Native payment provider in Form::validateCard."); + } +} + +bool Form::validateCardLocal(const Ui::UncheckedCardDetails &details) const { + if (auto error = cardErrorLocal(details)) { + _updates.fire(std::move(error)); + return false; + } + return true; +} + +Error Form::cardErrorLocal(const Ui::UncheckedCardDetails &details) const { + using namespace Stripe; + + auto errors = QStringList(); + const auto push = [&](const QString &id) { + errors.push_back(id); + }; + const auto kValid = ValidationState::Valid; + if (ValidateCard(details.number).state != kValid) { + push(u"LOCAL_CARD_NUMBER_INVALID"_q); + } + if (ValidateParsedExpireDate( + details.expireMonth, + details.expireYear + ) != kValid) { + push(u"LOCAL_CARD_EXPIRE_DATE_INVALID"_q); + } + if (ValidateCvc(details.number, details.cvc).state != kValid) { + push(u"LOCAL_CARD_CVC_INVALID"_q); + } + if (_paymentMethod.ui.native.needCardholderName + && details.cardholderName.isEmpty()) { + push(u"LOCAL_CARD_HOLDER_NAME_INVALID"_q); + } + if (_paymentMethod.ui.native.needCountry + && details.addressCountry.isEmpty()) { + push(u"LOCAL_CARD_BILLING_COUNTRY_INVALID"_q); + } + if (_paymentMethod.ui.native.needZip + && details.addressZip.isEmpty()) { + push(u"LOCAL_CARD_BILLING_ZIP_INVALID"_q); + } + if (!errors.isEmpty()) { + return Error{ Error::Type::Validate, errors.front() }; + } + return Error(); +} + +void Form::validateCard( + const StripePaymentMethod &method, + const Ui::UncheckedCardDetails &details, + bool saveInformation) { + Expects(!method.publishableKey.isEmpty()); + + if (_stripe) { + return; + } + auto configuration = Stripe::PaymentConfiguration{ + .publishableKey = method.publishableKey, + .companyName = "Telegram", + }; + _stripe = std::make_unique(std::move(configuration)); + auto card = Stripe::CardParams{ + .number = details.number, + .expMonth = details.expireMonth, + .expYear = details.expireYear, + .cvc = details.cvc, + .name = details.cardholderName, + .addressZip = details.addressZip, + .addressCountry = details.addressCountry, + }; + showProgress(); + _stripe->createTokenWithCard(std::move(card), crl::guard(this, [=]( + Stripe::Token token, + Stripe::Error error) { + hideProgress(); + _stripe = nullptr; + + if (error) { + LOG(("Stripe Error %1: %2 (%3)" + ).arg(int(error.code()) + ).arg(error.description() + ).arg(error.message())); + _updates.fire(Error{ Error::Type::Stripe, error.description() }); + } else { + setPaymentCredentials({ + .title = CardTitle(token.card()), + .data = QJsonDocument(QJsonObject{ + { "type", "card" }, + { "id", token.tokenId() }, + }).toJson(QJsonDocument::Compact), + .saveOnServer = saveInformation, + }); + } + })); +} + +void Form::validateCard( + const SmartGlocalPaymentMethod &method, + const Ui::UncheckedCardDetails &details, + bool saveInformation) { + Expects(!method.publicToken.isEmpty()); + + if (_smartglocal) { + return; + } + auto configuration = SmartGlocal::PaymentConfiguration{ + .publicToken = method.publicToken, + .isTest = _invoice.isTest, + }; + _smartglocal = std::make_unique( + std::move(configuration)); + auto card = Stripe::CardParams{ + .number = details.number, + .expMonth = details.expireMonth, + .expYear = details.expireYear, + .cvc = details.cvc, + .name = details.cardholderName, + .addressZip = details.addressZip, + .addressCountry = details.addressCountry, + }; + showProgress(); + _smartglocal->createTokenWithCard(std::move(card), crl::guard(this, [=]( + SmartGlocal::Token token, + SmartGlocal::Error error) { + hideProgress(); + _smartglocal = nullptr; + + if (error) { + LOG(("SmartGlocal Error %1: %2 (%3)" + ).arg(int(error.code()) + ).arg(error.description() + ).arg(error.message())); + _updates.fire(Error{ + Error::Type::SmartGlocal, + error.description(), + }); + } else { + setPaymentCredentials({ + .title = CardTitle(token.card()), + .data = QJsonDocument(QJsonObject{ + { "token", token.tokenId() }, + { "type", "card" }, + }).toJson(QJsonDocument::Compact), + .saveOnServer = saveInformation, + }); + } + })); +} + +void Form::setPaymentCredentials(const NewCredentials &credentials) { + Expects(!credentials.empty()); + + _paymentMethod.newCredentials = credentials; + const auto requestNewPassword = credentials.saveOnServer + && !_details.canSaveCredentials + && _details.passwordMissing; + refreshPaymentMethodDetails(); + _updates.fire(PaymentMethodUpdate{ requestNewPassword }); +} + +void Form::setHasPassword(bool has) { + if (_details.passwordMissing) { + _details.canSaveCredentials = has; + } +} + +void Form::setShippingOption(const QString &id) { + _shippingOptions.selectedId = id; +} + +void Form::setTips(int64 value) { + _invoice.tipsSelected = std::min(value, _invoice.tipsMax); +} + +void Form::trustBot() { + _session->local().markBotTrustedPayment(_details.botId); +} + +void Form::processShippingOptions(const QVector &data) { + const auto currency = _invoice.currency; + _shippingOptions = Ui::ShippingOptions{ currency, ranges::views::all( + data + ) | ranges::views::transform([](const MTPShippingOption &option) { + return option.match([](const MTPDshippingOption &data) { + return Ui::ShippingOption{ + .id = qs(data.vid()), + .title = qs(data.vtitle()), + .prices = ParsePrices(data.vprices()), + }; + }); + }) | ranges::to_vector }; + _shippingOptions.currency = _invoice.currency; +} + +} // namespace Payments diff --git a/Telegram/SourceFiles/payments/payments_form.h b/Telegram/SourceFiles/payments/payments_form.h new file mode 100644 index 000000000..ffddf7839 --- /dev/null +++ b/Telegram/SourceFiles/payments/payments_form.h @@ -0,0 +1,293 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +#include "payments/ui/payments_panel_data.h" +#include "base/weak_ptr.h" +#include "mtproto/sender.h" + +class Image; +class QJsonObject; + +namespace Core { +struct CloudPasswordResult; +} // namespace Core + +namespace Stripe { +class APIClient; +} // namespace Stripe + +namespace SmartGlocal { +class APIClient; +} // namespace SmartGlocal + +namespace Main { +class Session; +} // namespace Main + +namespace Data { +class PhotoMedia; +} // namespace Data + +namespace Payments { + +enum class Mode; + +struct FormDetails { + uint64 formId = 0; + QString url; + QString nativeProvider; + QByteArray nativeParamsJson; + UserId botId = 0; + UserId providerId = 0; + bool canSaveCredentials = false; + bool passwordMissing = false; + + [[nodiscard]] bool valid() const { + return !url.isEmpty(); + } + [[nodiscard]] explicit operator bool() const { + return valid(); + } +}; + +struct ThumbnailLoadProcess { + std::shared_ptr view; + bool blurredSet = false; + rpl::lifetime lifetime; +}; + +struct SavedCredentials { + QString id; + QString title; + + [[nodiscard]] bool valid() const { + return !id.isEmpty(); + } + [[nodiscard]] explicit operator bool() const { + return valid(); + } +}; + +struct NewCredentials { + QString title; + QByteArray data; + bool saveOnServer = false; + + [[nodiscard]] bool empty() const { + return data.isEmpty(); + } + [[nodiscard]] explicit operator bool() const { + return !empty(); + } +}; + +struct StripePaymentMethod { + QString publishableKey; +}; + +struct SmartGlocalPaymentMethod { + QString publicToken; +}; + +struct NativePaymentMethod { + std::variant< + v::null_t, + StripePaymentMethod, + SmartGlocalPaymentMethod> data; + + [[nodiscard]] bool valid() const { + return !v::is_null(data); + } + [[nodiscard]] explicit operator bool() const { + return valid(); + } +}; + +struct PaymentMethod { + NativePaymentMethod native; + SavedCredentials savedCredentials; + NewCredentials newCredentials; + Ui::PaymentMethodDetails ui; +}; + +struct ToggleProgress { + bool shown = true; +}; +struct FormReady {}; +struct ThumbnailUpdated { + QImage thumbnail; +}; +struct ValidateFinished {}; +struct PaymentMethodUpdate { + bool requestNewPassword = false; +}; +struct VerificationNeeded { + QString url; +}; +struct TmpPasswordRequired {}; +struct BotTrustRequired { + not_null bot; + not_null provider; +}; +struct PaymentFinished { + MTPUpdates updates; +}; +struct Error { + enum class Type { + None, + Form, + Validate, + Stripe, + SmartGlocal, + TmpPassword, + Send, + }; + Type type = Type::None; + QString id; + + [[nodiscard]] bool empty() const { + return (type == Type::None); + } + [[nodiscard]] explicit operator bool() const { + return !empty(); + } +}; + +struct FormUpdate : std::variant< + ToggleProgress, + FormReady, + ThumbnailUpdated, + ValidateFinished, + PaymentMethodUpdate, + VerificationNeeded, + TmpPasswordRequired, + BotTrustRequired, + PaymentFinished, + Error> { + using variant::variant; +}; + +class Form final : public base::has_weak_ptr { +public: + Form(not_null peer, MsgId itemId, bool receipt); + ~Form(); + + [[nodiscard]] const Ui::Invoice &invoice() const { + return _invoice; + } + [[nodiscard]] const FormDetails &details() const { + return _details; + } + [[nodiscard]] const Ui::RequestedInformation &information() const { + return _information; + } + [[nodiscard]] const PaymentMethod &paymentMethod() const { + return _paymentMethod; + } + [[nodiscard]] const Ui::ShippingOptions &shippingOptions() const { + return _shippingOptions; + } + [[nodiscard]] bool hasChanges() const; + + [[nodiscard]] rpl::producer updates() const { + return _updates.events(); + } + + void validateInformation(const Ui::RequestedInformation &information); + void validateCard( + const Ui::UncheckedCardDetails &details, + bool saveInformation); + void setPaymentCredentials(const NewCredentials &credentials); + void setHasPassword(bool has); + void setShippingOption(const QString &id); + void setTips(int64 value); + void trustBot(); + void submit(); + void submit(const Core::CloudPasswordResult &result); + +private: + void fillInvoiceFromMessage(); + void showProgress(); + void hideProgress(); + + void loadThumbnail(not_null photo); + [[nodiscard]] QImage prepareGoodThumbnail( + const std::shared_ptr &view) const; + [[nodiscard]] QImage prepareBlurredThumbnail( + const std::shared_ptr &view) const; + [[nodiscard]] QImage prepareThumbnail( + not_null image, + bool blurred = false) const; + [[nodiscard]] QImage prepareEmptyThumbnail() const; + + void requestForm(); + void requestReceipt(); + void processForm(const MTPDpayments_paymentForm &data); + void processReceipt(const MTPDpayments_paymentReceipt &data); + void processInvoice(const MTPDinvoice &data); + void processDetails(const MTPDpayments_paymentForm &data); + void processDetails(const MTPDpayments_paymentReceipt &data); + void processSavedInformation(const MTPDpaymentRequestedInfo &data); + void processSavedCredentials( + const MTPDpaymentSavedCredentialsCard &data); + void processShippingOptions(const QVector &data); + void fillPaymentMethodInformation(); + void fillStripeNativeMethod(QJsonObject object); + void fillSmartGlocalNativeMethod(QJsonObject object); + void refreshPaymentMethodDetails(); + [[nodiscard]] QString defaultPhone() const; + [[nodiscard]] QString defaultCountry() const; + + void validateCard( + const StripePaymentMethod &method, + const Ui::UncheckedCardDetails &details, + bool saveInformation); + void validateCard( + const SmartGlocalPaymentMethod &method, + const Ui::UncheckedCardDetails &details, + bool saveInformation); + + bool validateInformationLocal( + const Ui::RequestedInformation &information) const; + [[nodiscard]] Error informationErrorLocal( + const Ui::RequestedInformation &information) const; + + bool validateCardLocal( + const Ui::UncheckedCardDetails &details) const; + [[nodiscard]] Error cardErrorLocal( + const Ui::UncheckedCardDetails &details) const; + + const not_null _session; + MTP::Sender _api; + not_null _peer; + MsgId _msgId = 0; + bool _receiptMode = false; + + Ui::Invoice _invoice; + std::unique_ptr _thumbnailLoadProcess; + FormDetails _details; + Ui::RequestedInformation _savedInformation; + Ui::RequestedInformation _information; + PaymentMethod _paymentMethod; + + Ui::RequestedInformation _validatedInformation; + mtpRequestId _validateRequestId = 0; + mtpRequestId _passwordRequestId = 0; + + std::unique_ptr _stripe; + std::unique_ptr _smartglocal; + + Ui::ShippingOptions _shippingOptions; + QString _requestedInformationId; + + rpl::event_stream _updates; + +}; + +} // namespace Payments diff --git a/Telegram/SourceFiles/payments/smartglocal/smartglocal_api_client.cpp b/Telegram/SourceFiles/payments/smartglocal/smartglocal_api_client.cpp new file mode 100644 index 000000000..0fca1b7f0 --- /dev/null +++ b/Telegram/SourceFiles/payments/smartglocal/smartglocal_api_client.cpp @@ -0,0 +1,166 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "smartglocal/smartglocal_api_client.h" + +#include "smartglocal/smartglocal_error.h" +#include "smartglocal/smartglocal_token.h" + +#include +#include +#include +#include +#include + +namespace SmartGlocal { +namespace { + +[[nodiscard]] QString APIURLBase(bool isTest) { + return isTest + ? "tgb-playground.smart-glocal.com/cds/v1" + : "tgb.smart-glocal.com/cds/v1"; +} + +[[nodiscard]] QString TokenEndpoint() { + return "tokenize/card"; +} + +[[nodiscard]] QByteArray ToJson(const Stripe::CardParams &card) { + const auto zero = QChar('0'); + const auto month = QString("%1").arg(card.expMonth, 2, 10, zero); + const auto year = QString("%1").arg(card.expYear % 100, 2, 10, zero); + + return QJsonDocument(QJsonObject{ + { "card", QJsonObject{ + { "number", card.number }, + { "expiration_month", month }, + { "expiration_year", year }, + { "security_code", card.cvc }, + } }, + }).toJson(QJsonDocument::Compact); +} + +} // namespace + +APIClient::APIClient(PaymentConfiguration configuration) +: _apiUrl("https://" + APIURLBase(configuration.isTest)) +, _configuration(configuration) { + _additionalHttpHeaders = { + { "X-PUBLIC-TOKEN", _configuration.publicToken }, + }; +} + +APIClient::~APIClient() { + const auto destroy = std::move(_old); +} + +void APIClient::createTokenWithCard( + Stripe::CardParams card, + TokenCompletionCallback completion) { + createTokenWithData(ToJson(card), std::move(completion)); +} + +void APIClient::createTokenWithData( + QByteArray data, + TokenCompletionCallback completion) { + const auto url = QUrl(_apiUrl + '/' + TokenEndpoint()); + auto request = QNetworkRequest(url); + request.setHeader( + QNetworkRequest::ContentTypeHeader, + "application/json"); + for (const auto &[name, value] : _additionalHttpHeaders) { + request.setRawHeader(name.toUtf8(), value.toUtf8()); + } + destroyReplyDelayed(std::move(_reply)); + _reply.reset(_manager.post(request, data)); + const auto finish = [=](Token token, Error error) { + crl::on_main([ + completion, + token = std::move(token), + error = std::move(error) + ] { + completion(std::move(token), std::move(error)); + }); + }; + const auto finishWithError = [=](Error error) { + finish(Token::Empty(), std::move(error)); + }; + const auto finishWithToken = [=](Token token) { + finish(std::move(token), Error::None()); + }; + QObject::connect(_reply.get(), &QNetworkReply::finished, [=] { + const auto replyError = int(_reply->error()); + const auto replyErrorString = _reply->errorString(); + const auto bytes = _reply->readAll(); + destroyReplyDelayed(std::move(_reply)); + + auto parseError = QJsonParseError(); + const auto document = QJsonDocument::fromJson(bytes, &parseError); + if (!bytes.isEmpty()) { + if (parseError.error != QJsonParseError::NoError) { + const auto code = int(parseError.error); + finishWithError({ + Error::Code::JsonParse, + QString("InvalidJson%1").arg(code), + parseError.errorString(), + }); + return; + } else if (!document.isObject()) { + finishWithError({ + Error::Code::JsonFormat, + "InvalidJsonRoot", + "Not an object in JSON reply.", + }); + return; + } + const auto object = document.object(); + if (auto error = Error::DecodedObjectFromResponse(object)) { + finishWithError(std::move(error)); + return; + } + } + if (replyError != QNetworkReply::NoError) { + finishWithError({ + Error::Code::Network, + QString("RequestError%1").arg(replyError), + replyErrorString, + }); + return; + } + auto token = Token::DecodedObjectFromAPIResponse( + document.object().value("data").toObject()); + if (!token) { + finishWithError({ + Error::Code::JsonFormat, + "InvalidTokenJson", + "Could not parse token.", + }); + } + finishWithToken(std::move(token)); + }); +} + +void APIClient::destroyReplyDelayed(std::unique_ptr reply) { + if (!reply) { + return; + } + const auto raw = reply.get(); + _old.push_back(std::move(reply)); + QObject::disconnect(raw, &QNetworkReply::finished, nullptr, nullptr); + raw->deleteLater(); + QObject::connect(raw, &QObject::destroyed, [=] { + for (auto i = begin(_old); i != end(_old); ++i) { + if (i->get() == raw) { + i->release(); + _old.erase(i); + break; + } + } + }); +} + +} // namespace SmartGlocal diff --git a/Telegram/SourceFiles/payments/smartglocal/smartglocal_api_client.h b/Telegram/SourceFiles/payments/smartglocal/smartglocal_api_client.h new file mode 100644 index 000000000..6bf866d49 --- /dev/null +++ b/Telegram/SourceFiles/payments/smartglocal/smartglocal_api_client.h @@ -0,0 +1,48 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +#include "stripe/stripe_card_params.h" +#include "smartglocal/smartglocal_callbacks.h" + +#include +#include +#include + +namespace SmartGlocal { + +struct PaymentConfiguration { + QString publicToken; + bool isTest = false; +}; + +class APIClient final { +public: + explicit APIClient(PaymentConfiguration configuration); + ~APIClient(); + + void createTokenWithCard( + Stripe::CardParams card, + TokenCompletionCallback completion); + void createTokenWithData( + QByteArray data, + TokenCompletionCallback completion); + +private: + void destroyReplyDelayed(std::unique_ptr reply); + + QString _apiUrl; + PaymentConfiguration _configuration; + std::map _additionalHttpHeaders; + QNetworkAccessManager _manager; + std::unique_ptr _reply; + std::vector> _old; + +}; + +} // namespace SmartGlocal diff --git a/Telegram/SourceFiles/payments/smartglocal/smartglocal_callbacks.h b/Telegram/SourceFiles/payments/smartglocal/smartglocal_callbacks.h new file mode 100644 index 000000000..e200aec3b --- /dev/null +++ b/Telegram/SourceFiles/payments/smartglocal/smartglocal_callbacks.h @@ -0,0 +1,17 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +namespace SmartGlocal { + +class Token; +class Error; + +using TokenCompletionCallback = std::function; + +} // namespace SmartGlocal diff --git a/Telegram/SourceFiles/payments/smartglocal/smartglocal_card.cpp b/Telegram/SourceFiles/payments/smartglocal/smartglocal_card.cpp new file mode 100644 index 000000000..f0f4b95a9 --- /dev/null +++ b/Telegram/SourceFiles/payments/smartglocal/smartglocal_card.cpp @@ -0,0 +1,60 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "smartglocal/smartglocal_card.h" + +namespace SmartGlocal { + +Card::Card( + QString type, + QString network, + QString maskedNumber) +: _type(type) +, _network(network) +, _maskedNumber(maskedNumber) { +} + +Card Card::Empty() { + return Card(QString(), QString(), QString()); +} + +Card Card::DecodedObjectFromAPIResponse(QJsonObject object) { + const auto string = [&](QStringView key) { + return object.value(key).toString(); + }; + const auto type = string(u"card_type"); + const auto network = string(u"card_network"); + const auto maskedNumber = string(u"masked_card_number"); + if (type.isEmpty() || maskedNumber.isEmpty()) { + return Card::Empty(); + } + return Card(type, network, maskedNumber); +} + +QString Card::type() const { + return _type; +} + +QString Card::network() const { + return _network; +} + +QString Card::maskedNumber() const { + return _maskedNumber; +} + +bool Card::empty() const { + return _type.isEmpty() || _maskedNumber.isEmpty(); +} + +QString Last4(const Card &card) { + const auto masked = card.maskedNumber(); + const auto m = QRegularExpression("[^\\d]\\d*(\\d{4})$").match(masked); + return m.hasMatch() ? m.captured(1) : QString(); +} + +} // namespace SmartGlocal diff --git a/Telegram/SourceFiles/payments/smartglocal/smartglocal_card.h b/Telegram/SourceFiles/payments/smartglocal/smartglocal_card.h new file mode 100644 index 000000000..6ebd8bc0c --- /dev/null +++ b/Telegram/SourceFiles/payments/smartglocal/smartglocal_card.h @@ -0,0 +1,51 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +#include + +class QJsonObject; + +namespace SmartGlocal { + +class Card final { +public: + Card(const Card &other) = default; + Card &operator=(const Card &other) = default; + Card(Card &&other) = default; + Card &operator=(Card &&other) = default; + ~Card() = default; + + [[nodiscard]] static Card Empty(); + [[nodiscard]] static Card DecodedObjectFromAPIResponse( + QJsonObject object); + + [[nodiscard]] QString type() const; + [[nodiscard]] QString network() const; + [[nodiscard]] QString maskedNumber() const; + + [[nodiscard]] bool empty() const; + [[nodiscard]] explicit operator bool() const { + return !empty(); + } + +private: + Card( + QString type, + QString network, + QString maskedNumber); + + QString _type; + QString _network; + QString _maskedNumber; + +}; + +[[nodiscard]] QString Last4(const Card &card); + +} // namespace SmartGlocal diff --git a/Telegram/SourceFiles/payments/smartglocal/smartglocal_error.cpp b/Telegram/SourceFiles/payments/smartglocal/smartglocal_error.cpp new file mode 100644 index 000000000..cc764a07f --- /dev/null +++ b/Telegram/SourceFiles/payments/smartglocal/smartglocal_error.cpp @@ -0,0 +1,69 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "smartglocal/smartglocal_error.h" + +namespace SmartGlocal { + +Error::Code Error::code() const { + return _code; +} + +QString Error::description() const { + return _description; +} + +QString Error::message() const { + return _message; +} + +QString Error::parameter() const { + return _parameter; +} + +Error Error::None() { + return Error(Code::None, {}, {}, {}); +} + +Error Error::DecodedObjectFromResponse(QJsonObject object) { + if (object.value("status").toString() == "ok") { + return Error::None(); + } + const auto entry = object.value("error"); + if (!entry.isObject()) { + return { + Code::Unknown, + "GenericError", + "Could not read the error response " + "that was returned from SmartGlocal." + }; + } + const auto error = entry.toObject(); + const auto string = [&](QStringView key) { + return error.value(key).toString(); + }; + const auto code = string(u"code"); + const auto description = string(u"description"); + + // There should always be a message and type for the error + if (code.isEmpty() || description.isEmpty()) { + return { + Code::Unknown, + "GenericError", + "Could not interpret the error response " + "that was returned from SmartGlocal." + }; + } + + return { Code::Unknown, code, description }; +} + +bool Error::empty() const { + return (_code == Code::None); +} + +} // namespace SmartGlocal diff --git a/Telegram/SourceFiles/payments/smartglocal/smartglocal_error.h b/Telegram/SourceFiles/payments/smartglocal/smartglocal_error.h new file mode 100644 index 000000000..e3829ff45 --- /dev/null +++ b/Telegram/SourceFiles/payments/smartglocal/smartglocal_error.h @@ -0,0 +1,59 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +#include + +class QJsonObject; + +namespace SmartGlocal { + +class Error { +public: + enum class Code { + None = 0, // Non-SmartGlocal errors. + JsonParse = -1, + JsonFormat = -2, + Network = -3, + + Unknown = 8, + }; + + Error( + Code code, + const QString &description, + const QString &message, + const QString ¶meter = QString()) + : _code(code) + , _description(description) + , _message(message) + , _parameter(parameter) { + } + + [[nodiscard]] Code code() const; + [[nodiscard]] QString description() const; + [[nodiscard]] QString message() const; + [[nodiscard]] QString parameter() const; + + [[nodiscard]] static Error None(); + [[nodiscard]] static Error DecodedObjectFromResponse(QJsonObject object); + + [[nodiscard]] bool empty() const; + [[nodiscard]] explicit operator bool() const { + return !empty(); + } + +private: + Code _code = Code::None; + QString _description; + QString _message; + QString _parameter; + +}; + +} // namespace SmartGlocal diff --git a/Telegram/SourceFiles/payments/smartglocal/smartglocal_token.cpp b/Telegram/SourceFiles/payments/smartglocal/smartglocal_token.cpp new file mode 100644 index 000000000..ee8725ca0 --- /dev/null +++ b/Telegram/SourceFiles/payments/smartglocal/smartglocal_token.cpp @@ -0,0 +1,46 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "smartglocal/smartglocal_token.h" + +namespace SmartGlocal { + +QString Token::tokenId() const { + return _tokenId; +} + +Card Token::card() const { + return _card; +} + +Token Token::Empty() { + return Token(QString()); +} + +Token Token::DecodedObjectFromAPIResponse(QJsonObject object) { + const auto tokenId = object.value("token").toString(); + if (tokenId.isEmpty()) { + return Token::Empty(); + } + auto result = Token(tokenId); + const auto card = object.value("info"); + if (card.isObject()) { + result._card = Card::DecodedObjectFromAPIResponse(card.toObject()); + } + return result; +} + +bool Token::empty() const { + return _tokenId.isEmpty(); +} + +Token::Token(QString tokenId) +: _tokenId(std::move(tokenId)) { +} + +} // namespace SmartGlocal + diff --git a/Telegram/SourceFiles/payments/smartglocal/smartglocal_token.h b/Telegram/SourceFiles/payments/smartglocal/smartglocal_token.h new file mode 100644 index 000000000..43f822da0 --- /dev/null +++ b/Telegram/SourceFiles/payments/smartglocal/smartglocal_token.h @@ -0,0 +1,47 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +#include "smartglocal/smartglocal_card.h" + +#include + +class QJsonObject; + +namespace SmartGlocal { + +class Token { +public: + Token(const Token &other) = default; + Token &operator=(const Token &other) = default; + Token(Token &&other) = default; + Token &operator=(Token &&other) = default; + ~Token() = default; + + [[nodiscard]] QString tokenId() const; + [[nodiscard]] bool livemode() const; + [[nodiscard]] Card card() const; + + [[nodiscard]] static Token Empty(); + [[nodiscard]] static Token DecodedObjectFromAPIResponse( + QJsonObject object); + + [[nodiscard]] bool empty() const; + [[nodiscard]] explicit operator bool() const { + return !empty(); + } + +private: + explicit Token(QString tokenId); + + QString _tokenId; + Card _card = Card::Empty(); + +}; + +} // namespace SmartGlocal diff --git a/Telegram/SourceFiles/payments/stripe/stripe_address.h b/Telegram/SourceFiles/payments/stripe/stripe_address.h new file mode 100644 index 000000000..5be06f6cc --- /dev/null +++ b/Telegram/SourceFiles/payments/stripe/stripe_address.h @@ -0,0 +1,18 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +namespace Stripe { + +enum class BillingAddressFields { + None, + Zip, + Full, +}; + +} // namespace Stripe diff --git a/Telegram/SourceFiles/payments/stripe/stripe_api_client.cpp b/Telegram/SourceFiles/payments/stripe/stripe_api_client.cpp new file mode 100644 index 000000000..853dc1498 --- /dev/null +++ b/Telegram/SourceFiles/payments/stripe/stripe_api_client.cpp @@ -0,0 +1,167 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "stripe/stripe_api_client.h" + +#include "stripe/stripe_error.h" +#include "stripe/stripe_token.h" +#include "stripe/stripe_form_encoder.h" + +#include +#include +#include +#include +#include + +namespace Stripe { +namespace { + +[[nodiscard]] QString APIURLBase() { + return "api.stripe.com/v1"; +} + +[[nodiscard]] QString TokenEndpoint() { + return "tokens"; +} + +[[nodiscard]] QString StripeAPIVersion() { + return "2015-10-12"; +} + +[[nodiscard]] QString SDKVersion() { + return "9.1.0"; +} + +[[nodiscard]] QString StripeUserAgentDetails() { + const auto details = QJsonObject{ + { "lang", "objective-c" }, + { "bindings_version", SDKVersion() }, + }; + return QString::fromUtf8( + QJsonDocument(details).toJson(QJsonDocument::Compact)); +} + +} // namespace + +APIClient::APIClient(PaymentConfiguration configuration) +: _apiUrl("https://" + APIURLBase()) +, _configuration(configuration) { + _additionalHttpHeaders = { + { "X-Stripe-User-Agent", StripeUserAgentDetails() }, + { "Stripe-Version", StripeAPIVersion() }, + { "Authorization", "Bearer " + _configuration.publishableKey }, + }; +} + +APIClient::~APIClient() { + const auto destroy = std::move(_old); +} + +void APIClient::createTokenWithCard( + CardParams card, + TokenCompletionCallback completion) { + createTokenWithData( + FormEncoder::formEncodedDataForObject(MakeEncodable(card)), + std::move(completion)); +} + +void APIClient::createTokenWithData( + QByteArray data, + TokenCompletionCallback completion) { + const auto url = QUrl(_apiUrl + '/' + TokenEndpoint()); + auto request = QNetworkRequest(url); + for (const auto &[name, value] : _additionalHttpHeaders) { + request.setRawHeader(name.toUtf8(), value.toUtf8()); + } + destroyReplyDelayed(std::move(_reply)); + _reply.reset(_manager.post(request, data)); + const auto finish = [=](Token token, Error error) { + crl::on_main([ + completion, + token = std::move(token), + error = std::move(error) + ] { + completion(std::move(token), std::move(error)); + }); + }; + const auto finishWithError = [=](Error error) { + finish(Token::Empty(), std::move(error)); + }; + const auto finishWithToken = [=](Token token) { + finish(std::move(token), Error::None()); + }; + QObject::connect(_reply.get(), &QNetworkReply::finished, [=] { + const auto replyError = int(_reply->error()); + const auto replyErrorString = _reply->errorString(); + const auto bytes = _reply->readAll(); + destroyReplyDelayed(std::move(_reply)); + + auto parseError = QJsonParseError(); + const auto document = QJsonDocument::fromJson(bytes, &parseError); + if (!bytes.isEmpty()) { + if (parseError.error != QJsonParseError::NoError) { + const auto code = int(parseError.error); + finishWithError({ + Error::Code::JsonParse, + QString("InvalidJson%1").arg(code), + parseError.errorString(), + }); + return; + } else if (!document.isObject()) { + finishWithError({ + Error::Code::JsonFormat, + "InvalidJsonRoot", + "Not an object in JSON reply.", + }); + return; + } + const auto object = document.object(); + if (auto error = Error::DecodedObjectFromResponse(object)) { + finishWithError(std::move(error)); + return; + } + } + if (replyError != QNetworkReply::NoError) { + finishWithError({ + Error::Code::Network, + QString("RequestError%1").arg(replyError), + replyErrorString, + }); + return; + } + auto token = Token::DecodedObjectFromAPIResponse(document.object()); + if (!token) { + finishWithError({ + Error::Code::JsonFormat, + "InvalidTokenJson", + "Could not parse token.", + }); + } + finishWithToken(std::move(token)); + }); +} + +void APIClient::destroyReplyDelayed(std::unique_ptr reply) { + if (!reply) { + return; + } + const auto raw = reply.get(); + _old.push_back(std::move(reply)); + QObject::disconnect(raw, &QNetworkReply::finished, nullptr, nullptr); + raw->deleteLater(); + QObject::connect(raw, &QObject::destroyed, [=] { + for (auto i = begin(_old); i != end(_old); ++i) { + if (i->get() == raw) { + i->release(); + _old.erase(i); + break; + } + } + }); +} + +} // namespace Stripe diff --git a/Telegram/SourceFiles/payments/stripe/stripe_api_client.h b/Telegram/SourceFiles/payments/stripe/stripe_api_client.h new file mode 100644 index 000000000..020df992d --- /dev/null +++ b/Telegram/SourceFiles/payments/stripe/stripe_api_client.h @@ -0,0 +1,44 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +#include "stripe/stripe_payment_configuration.h" +#include "stripe/stripe_card_params.h" +#include "stripe/stripe_callbacks.h" + +#include +#include +#include + +namespace Stripe { + +class APIClient final { +public: + explicit APIClient(PaymentConfiguration configuration); + ~APIClient(); + + void createTokenWithCard( + CardParams card, + TokenCompletionCallback completion); + void createTokenWithData( + QByteArray data, + TokenCompletionCallback completion); + +private: + void destroyReplyDelayed(std::unique_ptr reply); + + QString _apiUrl; + PaymentConfiguration _configuration; + std::map _additionalHttpHeaders; + QNetworkAccessManager _manager; + std::unique_ptr _reply; + std::vector> _old; + +}; + +} // namespace Stripe diff --git a/Telegram/SourceFiles/payments/stripe/stripe_callbacks.h b/Telegram/SourceFiles/payments/stripe/stripe_callbacks.h new file mode 100644 index 000000000..8e0d0a2a5 --- /dev/null +++ b/Telegram/SourceFiles/payments/stripe/stripe_callbacks.h @@ -0,0 +1,19 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +#include + +namespace Stripe { + +class Error; +class Token; + +using TokenCompletionCallback = std::function; + +} // namespace Stripe diff --git a/Telegram/SourceFiles/payments/stripe/stripe_card.cpp b/Telegram/SourceFiles/payments/stripe/stripe_card.cpp new file mode 100644 index 000000000..ca2c864a9 --- /dev/null +++ b/Telegram/SourceFiles/payments/stripe/stripe_card.cpp @@ -0,0 +1,188 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "stripe/stripe_card.h" + +#include "stripe/stripe_decode.h" + +namespace Stripe { +namespace { + +CardBrand BrandFromString(const QString &brand) { + if (brand == "visa") { + return CardBrand::Visa; + } else if (brand == "american express") { + return CardBrand::Amex; + } else if (brand == "mastercard") { + return CardBrand::MasterCard; + } else if (brand == "discover") { + return CardBrand::Discover; + } else if (brand == "jcb") { + return CardBrand::JCB; + } else if (brand == "diners club") { + return CardBrand::DinersClub; + } else { + return CardBrand::Unknown; + } +} + +CardFundingType FundingFromString(const QString &funding) { + if (funding == "credit") { + return CardFundingType::Credit; + } else if (funding == "debit") { + return CardFundingType::Debit; + } else if (funding == "prepaid") { + return CardFundingType::Prepaid; + } else { + return CardFundingType::Other; + } +} + +} // namespace + +Card::Card( + QString id, + QString last4, + CardBrand brand, + quint32 expMonth, + quint32 expYear) +: _cardId(id) +, _last4(last4) +, _brand(brand) +, _expMonth(expMonth) +, _expYear(expYear) { +} + +Card Card::Empty() { + return Card(QString(), QString(), CardBrand::Unknown, 0, 0); +} + +Card Card::DecodedObjectFromAPIResponse(QJsonObject object) { + if (!ContainsFields(object, { + u"id", + u"last4", + u"brand", + u"exp_month", + u"exp_year" + })) { + return Card::Empty(); + } + + const auto string = [&](QStringView key) { + return object.value(key).toString(); + }; + const auto cardId = string(u"id"); + const auto last4 = string(u"last4"); + const auto brand = BrandFromString(string(u"brand").toLower()); + const auto expMonth = object.value("exp_month").toInt(); + const auto expYear = object.value("exp_year").toInt(); + auto result = Card(cardId, last4, brand, expMonth, expYear); + result._name = string(u"name"); + result._dynamicLast4 = string(u"dynamic_last4"); + result._funding = FundingFromString(string(u"funding").toLower()); + result._fingerprint = string(u"fingerprint"); + result._country = string(u"country"); + result._currency = string(u"currency"); + result._addressLine1 = string(u"address_line1"); + result._addressLine2 = string(u"address_line2"); + result._addressCity = string(u"address_city"); + result._addressState = string(u"address_state"); + result._addressZip = string(u"address_zip"); + result._addressCountry = string(u"address_country"); + + // TODO incomplete, not used. + //result._allResponseFields = object; + + return result; +} + +QString Card::cardId() const { + return _cardId; +} + +QString Card::name() const { + return _name; +} + +QString Card::last4() const { + return _last4; +} + +QString Card::dynamicLast4() const { + return _dynamicLast4; +} + +CardBrand Card::brand() const { + return _brand; +} + +CardFundingType Card::funding() const { + return _funding; +} + +QString Card::fingerprint() const { + return _fingerprint; +} + +QString Card::country() const { + return _country; +} + +QString Card::currency() const { + return _currency; +} + +quint32 Card::expMonth() const { + return _expMonth; +} + +quint32 Card::expYear() const { + return _expYear; +} + +QString Card::addressLine1() const { + return _addressLine1; +} + +QString Card::addressLine2() const { + return _addressLine2; +} + +QString Card::addressCity() const { + return _addressCity; +} + +QString Card::addressState() const { + return _addressState; +} + +QString Card::addressZip() const { + return _addressZip; +} + +QString Card::addressCountry() const { + return _addressCountry; +} + +bool Card::empty() const { + return _cardId.isEmpty(); +} + +QString CardBrandToString(CardBrand brand) { + switch (brand) { + case CardBrand::Amex: return "American Express"; + case CardBrand::DinersClub: return "Diners Club"; + case CardBrand::Discover: return "Discover"; + case CardBrand::JCB: return "JCB"; + case CardBrand::MasterCard: return "MasterCard"; + case CardBrand::Unknown: return "Unknown"; + case CardBrand::Visa: return "Visa"; + } + std::abort(); +} + +} // namespace Stripe diff --git a/Telegram/SourceFiles/payments/stripe/stripe_card.h b/Telegram/SourceFiles/payments/stripe/stripe_card.h new file mode 100644 index 000000000..30ff47b64 --- /dev/null +++ b/Telegram/SourceFiles/payments/stripe/stripe_card.h @@ -0,0 +1,99 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +#include + +class QJsonObject; + +namespace Stripe { + +enum class CardBrand { + Visa, + Amex, + MasterCard, + Discover, + JCB, + DinersClub, + UnionPay, + Unknown, +}; + +enum class CardFundingType { + Debit, + Credit, + Prepaid, + Other, +}; + +class Card final { +public: + Card(const Card &other) = default; + Card &operator=(const Card &other) = default; + Card(Card &&other) = default; + Card &operator=(Card &&other) = default; + ~Card() = default; + + [[nodiscard]] static Card Empty(); + [[nodiscard]] static Card DecodedObjectFromAPIResponse( + QJsonObject object); + + [[nodiscard]] QString cardId() const; + [[nodiscard]] QString name() const; + [[nodiscard]] QString last4() const; + [[nodiscard]] QString dynamicLast4() const; + [[nodiscard]] CardBrand brand() const; + [[nodiscard]] CardFundingType funding() const; + [[nodiscard]] QString fingerprint() const; + [[nodiscard]] QString country() const; + [[nodiscard]] QString currency() const; + [[nodiscard]] quint32 expMonth() const; + [[nodiscard]] quint32 expYear() const; + [[nodiscard]] QString addressLine1() const; + [[nodiscard]] QString addressLine2() const; + [[nodiscard]] QString addressCity() const; + [[nodiscard]] QString addressState() const; + [[nodiscard]] QString addressZip() const; + [[nodiscard]] QString addressCountry() const; + + [[nodiscard]] bool empty() const; + [[nodiscard]] explicit operator bool() const { + return !empty(); + } + +private: + Card( + QString id, + QString last4, + CardBrand brand, + quint32 expMonth, + quint32 expYear); + + QString _cardId; + QString _name; + QString _last4; + QString _dynamicLast4; + CardBrand _brand = CardBrand::Unknown; + CardFundingType _funding = CardFundingType::Other; + QString _fingerprint; + QString _country; + QString _currency; + quint32 _expMonth = 0; + quint32 _expYear = 0; + QString _addressLine1; + QString _addressLine2; + QString _addressCity; + QString _addressState; + QString _addressZip; + QString _addressCountry; + +}; + +[[nodiscard]] QString CardBrandToString(CardBrand brand); + +} // namespace Stripe diff --git a/Telegram/SourceFiles/payments/stripe/stripe_card_params.cpp b/Telegram/SourceFiles/payments/stripe/stripe_card_params.cpp new file mode 100644 index 000000000..81b72c4e0 --- /dev/null +++ b/Telegram/SourceFiles/payments/stripe/stripe_card_params.cpp @@ -0,0 +1,33 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "stripe/stripe_card_params.h" + +namespace Stripe { + +QString CardParams::rootObjectName() { + return "card"; +} + +std::map CardParams::formFieldValues() const { + return { + { "number", number }, + { "cvc", cvc }, + { "name", name }, + { "address_line1", addressLine1 }, + { "address_line2", addressLine2 }, + { "address_city", addressCity }, + { "address_state", addressState }, + { "address_zip", addressZip }, + { "address_country", addressCountry }, + { "exp_month", QString::number(expMonth) }, + { "exp_year", QString::number(expYear) }, + { "currency", currency }, + }; +} + +} // namespace Stripe diff --git a/Telegram/SourceFiles/payments/stripe/stripe_card_params.h b/Telegram/SourceFiles/payments/stripe/stripe_card_params.h new file mode 100644 index 000000000..d107dc57f --- /dev/null +++ b/Telegram/SourceFiles/payments/stripe/stripe_card_params.h @@ -0,0 +1,32 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +#include "stripe/stripe_form_encodable.h" + +namespace Stripe { + +struct CardParams { + QString number; + quint32 expMonth = 0; + quint32 expYear = 0; + QString cvc; + QString name; + QString addressLine1; + QString addressLine2; + QString addressCity; + QString addressState; + QString addressZip; + QString addressCountry; + QString currency; + + [[nodiscard]] static QString rootObjectName(); + [[nodiscard]] std::map formFieldValues() const; +}; + +} // namespace Stripe diff --git a/Telegram/SourceFiles/payments/stripe/stripe_card_validator.cpp b/Telegram/SourceFiles/payments/stripe/stripe_card_validator.cpp new file mode 100644 index 000000000..aa2482c09 --- /dev/null +++ b/Telegram/SourceFiles/payments/stripe/stripe_card_validator.cpp @@ -0,0 +1,279 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "stripe/stripe_card_validator.h" + +#include + +namespace Stripe { +namespace { + +constexpr auto kMinCvcLength = 3; + +struct BinRange { + QString low; + QString high; + int length = 0; + CardBrand brand = CardBrand::Unknown; +}; + +[[nodiscard]] const std::vector &AllRanges() { + static auto kResult = std::vector{ + // Unknown + { "", "", 19, CardBrand::Unknown }, + // American Express + { "34", "34", 15, CardBrand::Amex }, + { "37", "37", 15, CardBrand::Amex }, + // Diners Club + { "30", "30", 16, CardBrand::DinersClub }, + { "36", "36", 14, CardBrand::DinersClub }, + { "38", "39", 16, CardBrand::DinersClub }, + // Discover + { "60", "60", 16, CardBrand::Discover }, + { "64", "65", 16, CardBrand::Discover }, + // JCB + { "35", "35", 16, CardBrand::JCB }, + // Mastercard + { "50", "59", 16, CardBrand::MasterCard }, + { "22", "27", 16, CardBrand::MasterCard }, + { "67", "67", 16, CardBrand::MasterCard }, // Maestro + // UnionPay + { "62", "62", 16, CardBrand::UnionPay }, + { "81", "81", 16, CardBrand::UnionPay }, + // Visa + { "40", "49", 16, CardBrand::Visa }, + { "413600", "413600", 13, CardBrand::Visa }, + { "444509", "444509", 13, CardBrand::Visa }, + { "444509", "444509", 13, CardBrand::Visa }, + { "444550", "444550", 13, CardBrand::Visa }, + { "450603", "450603", 13, CardBrand::Visa }, + { "450617", "450617", 13, CardBrand::Visa }, + { "450628", "450629", 13, CardBrand::Visa }, + { "450636", "450636", 13, CardBrand::Visa }, + { "450640", "450641", 13, CardBrand::Visa }, + { "450662", "450662", 13, CardBrand::Visa }, + { "463100", "463100", 13, CardBrand::Visa }, + { "476142", "476142", 13, CardBrand::Visa }, + { "476143", "476143", 13, CardBrand::Visa }, + { "492901", "492902", 13, CardBrand::Visa }, + { "492920", "492920", 13, CardBrand::Visa }, + { "492923", "492923", 13, CardBrand::Visa }, + { "492928", "492930", 13, CardBrand::Visa }, + { "492937", "492937", 13, CardBrand::Visa }, + { "492939", "492939", 13, CardBrand::Visa }, + { "492960", "492960", 13, CardBrand::Visa }, + }; + return kResult; +} + +[[nodiscard]] bool BinRangeMatchesNumber( + const BinRange &range, + const QString &sanitized) { + const auto minWithLow = std::min(sanitized.size(), range.low.size()); + if (sanitized.midRef(0, minWithLow).toInt() + < range.low.midRef(0, minWithLow).toInt()) { + return false; + } + const auto minWithHigh = std::min(sanitized.size(), range.high.size()); + if (sanitized.midRef(0, minWithHigh).toInt() + > range.high.midRef(0, minWithHigh).toInt()) { + return false; + } + return true; +} + +[[nodiscard]] bool IsNumeric(const QString &value) { + return QRegularExpression("^[0-9]*$").match(value).hasMatch(); +} + +[[nodiscard]] QString RemoveWhitespaces(QString value) { + return value.replace(QRegularExpression("\\s"), QString()); +} + +[[nodiscard]] std::vector BinRangesForNumber( + const QString &sanitized) { + const auto &all = AllRanges(); + auto result = std::vector(); + result.reserve(all.size()); + for (const auto &range : all) { + if (BinRangeMatchesNumber(range, sanitized)) { + result.push_back(range); + } + } + return result; +} + +[[nodiscard]] BinRange MostSpecificBinRangeForNumber( + const QString &sanitized) { + auto possible = BinRangesForNumber(sanitized); + const auto compare = [&](const BinRange &a, const BinRange &b) { + if (sanitized.isEmpty()) { + const auto aUnknown = (a.brand == CardBrand::Unknown); + const auto bUnknown = (b.brand == CardBrand::Unknown); + if (aUnknown && !bUnknown) { + return true; + } else if (!aUnknown && bUnknown) { + return false; + } + } + return a.low.size() < b.low.size(); + }; + std::sort(begin(possible), end(possible), compare); + return possible.back(); +} + +[[nodiscard]] int MaxCvcLengthForBranch(CardBrand brand) { + switch (brand) { + case CardBrand::Amex: + case CardBrand::Unknown: + return 4; + default: + return 3; + } +} + +[[nodiscard]] std::vector PossibleBrandsForNumber( + const QString &sanitized) { + const auto ranges = BinRangesForNumber(sanitized); + auto result = std::vector(); + for (const auto &range : ranges) { + const auto brand = range.brand; + if (brand == CardBrand::Unknown + || (std::find(begin(result), end(result), brand) + != end(result))) { + continue; + } + result.push_back(brand); + } + return result; +} + +[[nodiscard]] CardBrand BrandForNumber(const QString &number) { + const auto sanitized = RemoveWhitespaces(number); + if (!IsNumeric(sanitized)) { + return CardBrand::Unknown; + } + const auto possible = PossibleBrandsForNumber(sanitized); + return (possible.size() == 1) ? possible.front() : CardBrand::Unknown; +} + +[[nodiscard]] bool IsValidLuhn(const QString &sanitized) { + auto odd = true; + auto sum = 0; + for (auto i = sanitized.end(); i != sanitized.begin();) { + --i; + auto digit = int(i->unicode() - '0'); + odd = !odd; + if (odd) { + digit *= 2; + } + if (digit > 9) { + digit -= 9; + } + sum += digit; + } + return (sum % 10) == 0; +} + +} // namespace + +CardValidationResult ValidateCard(const QString &number) { + const auto sanitized = RemoveWhitespaces(number); + if (!IsNumeric(sanitized)) { + return { .state = ValidationState::Invalid }; + } else if (sanitized.isEmpty()) { + return { .state = ValidationState::Incomplete }; + } + const auto range = MostSpecificBinRangeForNumber(sanitized); + const auto brand = range.brand; + if (sanitized.size() > range.length) { + return { .state = ValidationState::Invalid, .brand = brand }; + } else if (sanitized.size() < range.length) { + return { .state = ValidationState::Incomplete, .brand = brand }; + } else if (!IsValidLuhn(sanitized)) { + return { .state = ValidationState::Invalid, .brand = brand }; + } + return { + .state = ValidationState::Valid, + .brand = brand, + .finished = true, + }; +} + +ExpireDateValidationResult ValidateExpireDate(const QString &date) { + const auto sanitized = RemoveWhitespaces(date).replace('/', QString()); + if (!IsNumeric(sanitized)) { + return { ValidationState::Invalid }; + } else if (sanitized.size() < 2) { + return { ValidationState::Incomplete }; + } + const auto normalized = (sanitized[0] > '1' ? "0" : "") + sanitized; + const auto month = normalized.mid(0, 2).toInt(); + if (month < 1 || month > 12) { + return { ValidationState::Invalid }; + } else if (normalized.size() < 4) { + return { ValidationState::Incomplete }; + } else if (normalized.size() > 4) { + return { ValidationState::Invalid }; + } + const auto year = 2000 + normalized.mid(2).toInt(); + + const auto currentDate = QDate::currentDate(); + const auto currentMonth = currentDate.month(); + const auto currentYear = currentDate.year(); + if (year < currentYear) { + return { ValidationState::Invalid }; + } else if (year == currentYear && month < currentMonth) { + return { ValidationState::Invalid }; + } + return { ValidationState::Valid, true }; +} + +ValidationState ValidateParsedExpireDate( + quint32 month, + quint32 year) { + if ((year / 100) != 20) { + return ValidationState::Invalid; + } + return ValidateExpireDate( + QString("%1%2" + ).arg(month, 2, 10, QChar('0') + ).arg(year % 100, 2, 10, QChar('0')) + ).state; +} + +CvcValidationResult ValidateCvc( + const QString &number, + const QString &cvc) { + if (!IsNumeric(cvc)) { + return { ValidationState::Invalid }; + } else if (cvc.size() < kMinCvcLength) { + return { ValidationState::Incomplete }; + } + const auto maxLength = MaxCvcLengthForBranch(BrandForNumber(number)); + if (cvc.size() > maxLength) { + return { ValidationState::Invalid }; + } + return { ValidationState::Valid, (cvc.size() == maxLength) }; +} + +std::vector CardNumberFormat(const QString &number) { + static const auto kDefault = std::vector{ 4, 4, 4, 4 }; + const auto sanitized = RemoveWhitespaces(number); + if (!IsNumeric(sanitized)) { + return kDefault; + } + const auto range = MostSpecificBinRangeForNumber(sanitized); + if (range.brand == CardBrand::DinersClub && range.length == 14) { + return { 4, 6, 4 }; + } else if (range.brand == CardBrand::Amex) { + return { 4, 6, 5 }; + } + return kDefault; +} + +} // namespace Stripe diff --git a/Telegram/SourceFiles/payments/stripe/stripe_card_validator.h b/Telegram/SourceFiles/payments/stripe/stripe_card_validator.h new file mode 100644 index 000000000..417d4d958 --- /dev/null +++ b/Telegram/SourceFiles/payments/stripe/stripe_card_validator.h @@ -0,0 +1,51 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +#include "stripe/stripe_card.h" + +namespace Stripe { + +enum class ValidationState { + Invalid, + Incomplete, + Valid, +}; + +struct CardValidationResult { + ValidationState state = ValidationState::Invalid; + CardBrand brand = CardBrand::Unknown; + bool finished = false; +}; + +[[nodiscard]] CardValidationResult ValidateCard(const QString &number); + +struct ExpireDateValidationResult { + ValidationState state = ValidationState::Invalid; + bool finished = false; +}; + +[[nodiscard]] ExpireDateValidationResult ValidateExpireDate( + const QString &date); + +[[nodiscard]] ValidationState ValidateParsedExpireDate( + quint32 month, + quint32 year); + +struct CvcValidationResult { + ValidationState state = ValidationState::Invalid; + bool finished = false; +}; + +[[nodiscard]] CvcValidationResult ValidateCvc( + const QString &number, + const QString &cvc); + +[[nodiscard]] std::vector CardNumberFormat(const QString &number); + +} // namespace Stripe diff --git a/Telegram/SourceFiles/payments/stripe/stripe_decode.cpp b/Telegram/SourceFiles/payments/stripe/stripe_decode.cpp new file mode 100644 index 000000000..acd2dac0b --- /dev/null +++ b/Telegram/SourceFiles/payments/stripe/stripe_decode.cpp @@ -0,0 +1,23 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "stripe/stripe_decode.h" + +namespace Stripe { + +[[nodiscard]] bool ContainsFields( + const QJsonObject &object, + std::vector keys) { + for (const auto &key : keys) { + if (object.value(key).isUndefined()) { + return false; + } + } + return true; +} + +} // namespace Stripe diff --git a/Telegram/SourceFiles/payments/stripe/stripe_decode.h b/Telegram/SourceFiles/payments/stripe/stripe_decode.h new file mode 100644 index 000000000..41edb3445 --- /dev/null +++ b/Telegram/SourceFiles/payments/stripe/stripe_decode.h @@ -0,0 +1,18 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +#include + +namespace Stripe { + +[[nodiscard]] bool ContainsFields( + const QJsonObject &object, + std::vector keys); + +} // namespace Stripe diff --git a/Telegram/SourceFiles/payments/stripe/stripe_error.cpp b/Telegram/SourceFiles/payments/stripe/stripe_error.cpp new file mode 100644 index 000000000..72f843252 --- /dev/null +++ b/Telegram/SourceFiles/payments/stripe/stripe_error.cpp @@ -0,0 +1,107 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "stripe/stripe_error.h" + +#include "stripe/stripe_decode.h" + +namespace Stripe { + +Error::Code Error::code() const { + return _code; +} + +QString Error::description() const { + return _description; +} + +QString Error::message() const { + return _message; +} + +QString Error::parameter() const { + return _parameter; +} + +Error Error::None() { + return Error(Code::None, {}, {}, {}); +} + +Error Error::DecodedObjectFromResponse(QJsonObject object) { + const auto entry = object.value("error"); + if (!entry.isObject()) { + return Error::None(); + } + const auto error = entry.toObject(); + const auto string = [&](QStringView key) { + return error.value(key).toString(); + }; + const auto type = string(u"type"); + const auto message = string(u"message"); + const auto parameterSnakeCase = string(u"param"); + + // There should always be a message and type for the error + if (message.isEmpty() || type.isEmpty()) { + return { + Code::API, + "GenericError", + "Could not interpret the error response " + "that was returned from Stripe." + }; + } + + auto parameterWords = parameterSnakeCase.isEmpty() + ? QStringList() + : parameterSnakeCase.split('_', Qt::SkipEmptyParts); + auto first = true; + for (auto &word : parameterWords) { + if (first) { + first = false; + } else { + word = word[0].toUpper() + word.midRef(1); + } + } + const auto parameter = parameterWords.join(QString()); + if (type == "api_error") { + return { Code::API, "GenericError", message, parameter }; + } else if (type == "invalid_request_error") { + return { Code::InvalidRequest, "GenericError", message, parameter }; + } else if (type != "card_error") { + return { Code::Unknown, type, message, parameter }; + } + const auto code = string(u"code"); + const auto cardError = [&](const QString &description) { + return Error{ Code::Card, description, message, parameter }; + }; + if (code == "incorrect_number") { + return cardError("IncorrectNumber"); + } else if (code == "invalid_number") { + return cardError("InvalidNumber"); + } else if (code == "invalid_expiry_month") { + return cardError("InvalidExpiryMonth"); + } else if (code == "invalid_expiry_year") { + return cardError("InvalidExpiryYear"); + } else if (code == "invalid_cvc") { + return cardError("InvalidCVC"); + } else if (code == "expired_card") { + return cardError("ExpiredCard"); + } else if (code == "incorrect_cvc") { + return cardError("IncorrectCVC"); + } else if (code == "card_declined") { + return cardError("CardDeclined"); + } else if (code == "processing_error") { + return cardError("ProcessingError"); + } else { + return cardError(code); + } +} + +bool Error::empty() const { + return (_code == Code::None); +} + +} // namespace Stripe diff --git a/Telegram/SourceFiles/payments/stripe/stripe_error.h b/Telegram/SourceFiles/payments/stripe/stripe_error.h new file mode 100644 index 000000000..36247d387 --- /dev/null +++ b/Telegram/SourceFiles/payments/stripe/stripe_error.h @@ -0,0 +1,66 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +#include + +class QJsonObject; + +namespace Stripe { + +class Error { +public: + enum class Code { + None = 0, // Non-Stripe errors. + JsonParse = -1, + JsonFormat = -2, + Network = -3, + + Unknown = 8, + Connection = 40, // Trouble connecting to Stripe. + InvalidRequest = 50, // Your request had invalid parameters. + API = 60, // General-purpose API error (should be rare). + Card = 70, // Something was wrong with the given card (most common). + Cancellation = 80, // The operation was cancelled. + CheckoutUnknown = 5000, // Checkout failed + CheckoutTooManyAttempts = 5001, // Too many incorrect code attempts + }; + + Error( + Code code, + const QString &description, + const QString &message, + const QString ¶meter = QString()) + : _code(code) + , _description(description) + , _message(message) + , _parameter(parameter) { + } + + [[nodiscard]] Code code() const; + [[nodiscard]] QString description() const; + [[nodiscard]] QString message() const; + [[nodiscard]] QString parameter() const; + + [[nodiscard]] static Error None(); + [[nodiscard]] static Error DecodedObjectFromResponse(QJsonObject object); + + [[nodiscard]] bool empty() const; + [[nodiscard]] explicit operator bool() const { + return !empty(); + } + +private: + Code _code = Code::None; + QString _description; + QString _message; + QString _parameter; + +}; + +} // namespace Stripe diff --git a/Telegram/SourceFiles/payments/stripe/stripe_form_encodable.h b/Telegram/SourceFiles/payments/stripe/stripe_form_encodable.h new file mode 100644 index 000000000..7cae5e250 --- /dev/null +++ b/Telegram/SourceFiles/payments/stripe/stripe_form_encodable.h @@ -0,0 +1,39 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +#include +#include + +namespace Stripe { + +class FormEncodable { +public: + [[nodiscard]] virtual QString rootObjectName() = 0; + [[nodiscard]] virtual std::map formFieldValues() = 0; +}; + +template +struct MakeEncodable final : FormEncodable { +public: + MakeEncodable(const T &value) : _value(value) { + } + + QString rootObjectName() override { + return _value.rootObjectName(); + } + std::map formFieldValues() override { + return _value.formFieldValues(); + } + +private: + const T &_value; + +}; + +} // namespace Stripe diff --git a/Telegram/SourceFiles/payments/stripe/stripe_form_encoder.cpp b/Telegram/SourceFiles/payments/stripe/stripe_form_encoder.cpp new file mode 100644 index 000000000..a60425076 --- /dev/null +++ b/Telegram/SourceFiles/payments/stripe/stripe_form_encoder.cpp @@ -0,0 +1,40 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "stripe/stripe_form_encoder.h" + +#include +#include + +namespace Stripe { + +QByteArray FormEncoder::formEncodedDataForObject( + FormEncodable &&object) { + const auto root = object.rootObjectName(); + const auto values = object.formFieldValues(); + auto result = QByteArray(); + auto keys = std::vector(); + for (const auto &[key, value] : values) { + if (!value.isEmpty()) { + keys.push_back(key); + } + } + std::sort(begin(keys), end(keys)); + const auto encode = [](const QString &value) { + return QUrl::toPercentEncoding(value); + }; + for (const auto &key : keys) { + const auto fullKey = root.isEmpty() ? key : (root + '[' + key + ']'); + if (!result.isEmpty()) { + result += '&'; + } + result += encode(fullKey) + '=' + encode(values.at(key)); + } + return result; +} + +} // namespace Stripe diff --git a/Telegram/SourceFiles/payments/stripe/stripe_form_encoder.h b/Telegram/SourceFiles/payments/stripe/stripe_form_encoder.h new file mode 100644 index 000000000..79e28471d --- /dev/null +++ b/Telegram/SourceFiles/payments/stripe/stripe_form_encoder.h @@ -0,0 +1,21 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +#include "stripe/stripe_form_encodable.h" + +namespace Stripe { + +class FormEncoder { +public: + [[nodiscard]] static QByteArray formEncodedDataForObject( + FormEncodable &&object); + +}; + +} // namespace Stripe diff --git a/Telegram/SourceFiles/payments/stripe/stripe_payment_configuration.h b/Telegram/SourceFiles/payments/stripe/stripe_payment_configuration.h new file mode 100644 index 000000000..a42e7921a --- /dev/null +++ b/Telegram/SourceFiles/payments/stripe/stripe_payment_configuration.h @@ -0,0 +1,29 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +#include "stripe/stripe_address.h" + +#include + +namespace Stripe { + +struct PaymentConfiguration { + QString publishableKey; + // PaymentMethodType additionalPaymentMethods; // Apply Pay + + // TODO incomplete, not used. + //BillingAddressFields requiredBillingAddressFields + // = BillingAddressFields::None; + + QString companyName; + // QString appleMerchantIdentifier; // Apple Pay + // bool smsAutofillDisabled = true; // Mobile only +}; + +} // namespace Stripe diff --git a/Telegram/SourceFiles/payments/stripe/stripe_pch.h b/Telegram/SourceFiles/payments/stripe/stripe_pch.h new file mode 100644 index 000000000..b7c2e9fee --- /dev/null +++ b/Telegram/SourceFiles/payments/stripe/stripe_pch.h @@ -0,0 +1,13 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ + +#include +#include +#include +#include +#include diff --git a/Telegram/SourceFiles/payments/stripe/stripe_token.cpp b/Telegram/SourceFiles/payments/stripe/stripe_token.cpp new file mode 100644 index 000000000..66a131a36 --- /dev/null +++ b/Telegram/SourceFiles/payments/stripe/stripe_token.cpp @@ -0,0 +1,65 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "stripe/stripe_token.h" + +#include "stripe/stripe_decode.h" + +namespace Stripe { + +QString Token::tokenId() const { + return _tokenId; +} + +bool Token::livemode() const { + return _livemode; +} + +Card Token::card() const { + return _card; +} + +Token Token::Empty() { + return Token(QString(), false, QDateTime()); +} + +Token Token::DecodedObjectFromAPIResponse(QJsonObject object) { + if (!ContainsFields(object, { u"id", u"livemode", u"created" })) { + return Token::Empty(); + } + const auto tokenId = object.value("id").toString(); + const auto livemode = object.value("livemode").toBool(); + const auto created = QDateTime::fromTime_t( + object.value("created").toDouble()); + auto result = Token(tokenId, livemode, created); + const auto card = object.value("card"); + if (card.isObject()) { + result._card = Card::DecodedObjectFromAPIResponse(card.toObject()); + } + + // TODO incomplete, not used. + //const auto bankAccount = object.value("bank_account"); + //if (bankAccount.isObject()) { + // result._bankAccount = bankAccount::DecodedObjectFromAPIResponse( + // bankAccount.toObject()); + //} + //result._allResponseFields = object; + + return result; +} + +bool Token::empty() const { + return _tokenId.isEmpty(); +} + +Token::Token(QString tokenId, bool livemode, QDateTime created) +: _tokenId(std::move(tokenId)) +, _livemode(livemode) +, _created(std::move(created)) { +} + +} // namespace Stripe diff --git a/Telegram/SourceFiles/payments/stripe/stripe_token.h b/Telegram/SourceFiles/payments/stripe/stripe_token.h new file mode 100644 index 000000000..f22aefe60 --- /dev/null +++ b/Telegram/SourceFiles/payments/stripe/stripe_token.h @@ -0,0 +1,49 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +#include "stripe/stripe_card.h" + +#include + +class QJsonObject; + +namespace Stripe { + +class Token { +public: + Token(const Token &other) = default; + Token &operator=(const Token &other) = default; + Token(Token &&other) = default; + Token &operator=(Token &&other) = default; + ~Token() = default; + + [[nodiscard]] QString tokenId() const; + [[nodiscard]] bool livemode() const; + [[nodiscard]] Card card() const; + + [[nodiscard]] static Token Empty(); + [[nodiscard]] static Token DecodedObjectFromAPIResponse( + QJsonObject object); + + [[nodiscard]] bool empty() const; + [[nodiscard]] explicit operator bool() const { + return !empty(); + } + +private: + Token(QString tokenId, bool livemode, QDateTime created); + + QString _tokenId; + bool _livemode = false; + QDateTime _created; + Card _card = Card::Empty(); + +}; + +} // namespace Stripe diff --git a/Telegram/SourceFiles/payments/ui/payments.style b/Telegram/SourceFiles/payments/ui/payments.style new file mode 100644 index 000000000..03247628e --- /dev/null +++ b/Telegram/SourceFiles/payments/ui/payments.style @@ -0,0 +1,133 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +using "ui/basic.style"; + +using "info/info.style"; + +paymentsPanelSize: size(392px, 600px); + +paymentsPanelButton: defaultBoxButton; +paymentsPanelSubmit: RoundButton(defaultActiveButton) { + width: -36px; + height: 36px; + font: boxButtonFont; +} +paymentsPanelPadding: margins(8px, 12px, 15px, 12px); + +paymentsCoverPadding: margins(26px, 0px, 26px, 13px); +paymentsDescription: FlatLabel(defaultFlatLabel) { + minWidth: 160px; + textFg: windowFg; +} +paymentsTitle: FlatLabel(paymentsDescription) { + style: semiboldTextStyle; +} +paymentsSeller: FlatLabel(paymentsDescription) { + textFg: windowSubTextFg; +} +paymentsWebviewBottom: FlatLabel(defaultFlatLabel) { + textFg: windowSubTextFg; +} +paymentsPriceLabel: paymentsDescription; +paymentsPriceAmount: defaultFlatLabel; +paymentsFullPriceLabel: paymentsTitle; +paymentsFullPriceAmount: FlatLabel(defaultFlatLabel) { + style: semiboldTextStyle; +} + +paymentsTitleTop: 0px; +paymentsDescriptionTop: 3px; +paymentsSellerTop: 4px; + +paymentsThumbnailSize: size(80px, 80px); +paymentsThumbnailSkip: 18px; + +paymentsPricesTopSkip: 12px; +paymentsPricesBottomSkip: 13px; +paymentsPricePadding: margins(28px, 6px, 28px, 5px); + +paymentsTipSkip: 8px; +paymentsTipButton: RoundButton(defaultLightButton) { + textFg: paymentsTipActive; + textFgOver: paymentsTipActive; + textBgOver: transparent; + + width: -16px; + height: 28px; + textTop: 5px; +} +paymentsTipChosen: RoundButton(paymentsTipButton) { + textFg: windowFgActive; + textFgOver: windowFgActive; + textBgOver: transparent; +} +paymentsTipButtonsPadding: margins(26px, 6px, 26px, 6px); + +paymentsSectionsTopSkip: 11px; +paymentsSectionButton: SettingsButton(infoProfileButton) { + padding: margins(68px, 11px, 14px, 9px); +} + +paymentsIconPaymentMethod: icon {{ "payments/payment_card", menuIconFg }}; +paymentsIconShippingAddress: icon {{ "payments/payment_address", menuIconFg }}; +paymentsIconName: icon {{ "payments/payment_name", menuIconFg }}; +paymentsIconEmail: icon {{ "payments/payment_email", menuIconFg }}; +paymentsIconPhone: icon {{ "payments/payment_phone", menuIconFg }}; +paymentsIconShippingMethod: icon {{ "payments/payment_shipping", menuIconFg }}; + +paymentsField: defaultInputField; +paymentsMoneyField: InputField(paymentsField) { + textMargins: margins(0px, 4px, 0px, 4px); + heightMin: 30px; +} +paymentsFieldAdditional: FlatLabel(defaultFlatLabel) { + style: boxTextStyle; +} + +paymentsFieldPadding: margins(28px, 0px, 28px, 2px); +paymentsSaveCheckboxPadding: margins(28px, 20px, 28px, 8px); +paymentsExpireCvcSkip: 34px; + +paymentsBillingInformationTitle: FlatLabel(defaultFlatLabel) { + style: semiboldTextStyle; + textFg: windowActiveTextFg; + minWidth: 240px; +} +paymentsBillingInformationTitlePadding: margins(28px, 26px, 28px, 1px); + +paymentsShippingMargin: margins(27px, 11px, 27px, 20px); +paymentsShippingLabel: FlatLabel(defaultFlatLabel) { + style: boxTextStyle; +} +paymentsShippingPrice: FlatLabel(defaultFlatLabel) { + textFg: windowSubTextFg; +} +paymentsShippingLabelPosition: point(43px, 8px); +paymentsShippingPricePosition: point(43px, 29px); + +paymentTipsErrorLabel: FlatLabel(defaultFlatLabel) { + minWidth: 275px; + textFg: boxTextFgError; +} +paymentTipsErrorPadding: margins(22px, 6px, 22px, 0px); + +paymentsToProviderLabel: paymentsShippingPrice; +paymentsToProviderPadding: margins(28px, 6px, 28px, 6px); + +paymentsCriticalError: FlatLabel(boxLabel) { + minWidth: 370px; + align: align(top); + textFg: windowSubTextFg; +} +paymentsCriticalErrorPadding: margins(10px, 40px, 10px, 0px); + +paymentsLoading: InfiniteRadialAnimation(defaultInfiniteRadialAnimation) { + size: size(24px, 24px); + color: windowSubTextFg; + thickness: 4px; +} diff --git a/Telegram/SourceFiles/payments/ui/payments_edit_card.cpp b/Telegram/SourceFiles/payments/ui/payments_edit_card.cpp new file mode 100644 index 000000000..95200b504 --- /dev/null +++ b/Telegram/SourceFiles/payments/ui/payments_edit_card.cpp @@ -0,0 +1,432 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "payments/ui/payments_edit_card.h" + +#include "payments/ui/payments_panel_delegate.h" +#include "payments/ui/payments_field.h" +#include "stripe/stripe_card_validator.h" +#include "ui/widgets/scroll_area.h" +#include "ui/widgets/buttons.h" +#include "ui/widgets/labels.h" +#include "ui/widgets/checkbox.h" +#include "ui/wrap/vertical_layout.h" +#include "ui/wrap/fade_wrap.h" +#include "lang/lang_keys.h" +#include "styles/style_payments.h" +#include "styles/style_passport.h" + +#include + +namespace Payments::Ui { +namespace { + +struct SimpleFieldState { + QString value; + int position = 0; +}; + +[[nodiscard]] uint32 ExtractYear(const QString &value) { + return value.split('/').value(1).toInt() + 2000; +} + +[[nodiscard]] uint32 ExtractMonth(const QString &value) { + return value.split('/').value(0).toInt(); +} + +[[nodiscard]] QString RemoveNonNumbers(QString value) { + return value.replace(QRegularExpression("[^0-9]"), QString()); +} + +[[nodiscard]] SimpleFieldState NumbersOnlyState(SimpleFieldState state) { + return { + .value = RemoveNonNumbers(state.value), + .position = RemoveNonNumbers( + state.value.mid(0, state.position)).size(), + }; +} + +[[nodiscard]] SimpleFieldState PostprocessCardValidateResult( + SimpleFieldState result) { + const auto groups = Stripe::CardNumberFormat(result.value); + auto position = 0; + for (const auto length : groups) { + position += length; + if (position >= result.value.size()) { + break; + } + result.value.insert(position, QChar(' ')); + if (result.position >= position) { + ++result.position; + } + ++position; + } + return result; +} + +[[nodiscard]] SimpleFieldState PostprocessExpireDateValidateResult( + SimpleFieldState result) { + if (result.value.isEmpty()) { + return result; + } else if (result.value[0] == '1' && result.value[1] > '2') { + result.value = result.value.mid(0, 2); + return result; + } else if (result.value[0] > '1') { + result.value = '0' + result.value; + ++result.position; + } + if (result.value.size() > 1) { + if (result.value.size() > 4) { + result.value = result.value.mid(0, 4); + } + result.value.insert(2, '/'); + if (result.position >= 2) { + ++result.position; + } + } + return result; +} + +[[nodiscard]] bool IsBackspace(const FieldValidateRequest &request) { + return (request.wasAnchor == request.wasPosition) + && (request.wasPosition == request.nowPosition + 1) + && (request.wasValue.midRef(0, request.wasPosition - 1) + == request.nowValue.midRef(0, request.nowPosition)) + && (request.wasValue.midRef(request.wasPosition) + == request.nowValue.midRef(request.nowPosition)); +} + +[[nodiscard]] bool IsDelete(const FieldValidateRequest &request) { + return (request.wasAnchor == request.wasPosition) + && (request.wasPosition == request.nowPosition) + && (request.wasValue.midRef(0, request.wasPosition) + == request.nowValue.midRef(0, request.nowPosition)) + && (request.wasValue.midRef(request.wasPosition + 1) + == request.nowValue.midRef(request.nowPosition)); +} + +template < + typename ValueValidator, + typename ValueValidateResult = decltype( + std::declval()(QString()))> +[[nodiscard]] auto ComplexNumberValidator( + ValueValidator valueValidator, + Fn postprocess) { + using namespace Stripe; + return [=](FieldValidateRequest request) { + const auto realNowState = [&] { + const auto backspaced = IsBackspace(request); + const auto deleted = IsDelete(request); + if (!backspaced && !deleted) { + return NumbersOnlyState({ + .value = request.nowValue, + .position = request.nowPosition, + }); + } + const auto realWasState = NumbersOnlyState({ + .value = request.wasValue, + .position = request.wasPosition, + }); + const auto changedValue = deleted + ? (realWasState.value.mid(0, realWasState.position) + + realWasState.value.mid(realWasState.position + 1)) + : (realWasState.position > 1) + ? (realWasState.value.mid(0, realWasState.position - 1) + + realWasState.value.mid(realWasState.position)) + : realWasState.value.mid(realWasState.position); + return SimpleFieldState{ + .value = changedValue, + .position = (deleted + ? realWasState.position + : std::max(realWasState.position - 1, 0)) + }; + }(); + const auto result = valueValidator(realNowState.value); + const auto postprocessed = postprocess(realNowState); + return FieldValidateResult{ + .value = postprocessed.value, + .position = postprocessed.position, + .invalid = (result.state == ValidationState::Invalid), + .finished = result.finished, + }; + }; + +} + +[[nodiscard]] auto CardNumberValidator() { + return ComplexNumberValidator( + Stripe::ValidateCard, + PostprocessCardValidateResult); +} + +[[nodiscard]] auto ExpireDateValidator() { + return ComplexNumberValidator( + Stripe::ValidateExpireDate, + PostprocessExpireDateValidateResult); +} + +[[nodiscard]] auto CvcValidator(Fn number) { + using namespace Stripe; + return [=](FieldValidateRequest request) { + const auto realNowState = NumbersOnlyState({ + .value = request.nowValue, + .position = request.nowPosition, + }); + const auto result = ValidateCvc(number(), realNowState.value); + + return FieldValidateResult{ + .value = realNowState.value, + .position = realNowState.position, + .invalid = (result.state == ValidationState::Invalid), + .finished = result.finished, + }; + }; +} + +[[nodiscard]] auto CardHolderNameValidator() { + return [=](FieldValidateRequest request) { + return FieldValidateResult{ + .value = request.nowValue.toUpper(), + .position = request.nowPosition, + .invalid = request.nowValue.isEmpty(), + }; + }; +} + +} // namespace + +EditCard::EditCard( + QWidget *parent, + const NativeMethodDetails &native, + CardField field, + not_null delegate) +: _delegate(delegate) +, _native(native) +, _scroll(this, st::passportPanelScroll) +, _topShadow(this) +, _bottomShadow(this) +, _submit( + this, + tr::lng_about_done(), + st::paymentsPanelButton) +, _cancel( + this, + tr::lng_cancel(), + st::paymentsPanelButton) { + setupControls(); +} + +void EditCard::setFocus(CardField field) { + _focusField = field; + if (const auto control = lookupField(field)) { + _scroll->ensureWidgetVisible(control->widget()); + control->setFocus(); + } +} + +void EditCard::setFocusFast(CardField field) { + _focusField = field; + if (const auto control = lookupField(field)) { + _scroll->ensureWidgetVisible(control->widget()); + control->setFocusFast(); + } +} + +void EditCard::showError(CardField field) { + if (const auto control = lookupField(field)) { + _scroll->ensureWidgetVisible(control->widget()); + control->showError(); + } +} + +void EditCard::setupControls() { + const auto inner = setupContent(); + + _submit->addClickHandler([=] { + _delegate->panelValidateCard(collect(), (_save && _save->checked())); + }); + _cancel->addClickHandler([=] { + _delegate->panelCancelEdit(); + }); + + using namespace rpl::mappers; + + _topShadow->toggleOn( + _scroll->scrollTopValue() | rpl::map(_1 > 0)); + _bottomShadow->toggleOn(rpl::combine( + _scroll->scrollTopValue(), + _scroll->heightValue(), + inner->heightValue(), + _1 + _2 < _3)); +} + +not_null EditCard::setupContent() { + const auto inner = _scroll->setOwnedWidget( + object_ptr(this)); + + _scroll->widthValue( + ) | rpl::start_with_next([=](int width) { + inner->resizeToWidth(width); + }, inner->lifetime()); + + const auto showBox = [=](object_ptr box) { + _delegate->panelShowBox(std::move(box)); + }; + auto last = (Field*)nullptr; + const auto make = [&](QWidget *parent, FieldConfig &&config) { + auto result = std::make_unique(parent, std::move(config)); + if (last) { + last->setNextField(result.get()); + result->setPreviousField(last); + } + last = result.get(); + return result; + }; + const auto add = [&](FieldConfig &&config) { + auto result = make(inner, std::move(config)); + inner->add(result->ownedWidget(), st::paymentsFieldPadding); + return result; + }; + _number = add({ + .type = FieldType::CardNumber, + .placeholder = tr::lng_payments_card_number(), + .validator = CardNumberValidator(), + }); + auto container = inner->add( + object_ptr( + inner, + _number->widget()->height()), + st::paymentsFieldPadding); + _expire = make(container, { + .type = FieldType::CardExpireDate, + .placeholder = rpl::single(u"MM / YY"_q), + .validator = ExpireDateValidator(), + }); + _cvc = make(container, { + .type = FieldType::CardCVC, + .placeholder = rpl::single(u"CVC"_q), + .validator = CvcValidator([=] { return _number->value(); }), + }); + container->widthValue( + ) | rpl::start_with_next([=](int width) { + const auto left = (width - st::paymentsExpireCvcSkip) / 2; + const auto right = width - st::paymentsExpireCvcSkip - left; + _expire->widget()->resizeToWidth(left); + _cvc->widget()->resizeToWidth(right); + _expire->widget()->moveToLeft(0, 0, width); + _cvc->widget()->moveToRight(0, 0, width); + }, container->lifetime()); + + if (_native.needCardholderName) { + _name = add({ + .type = FieldType::Text, + .placeholder = tr::lng_payments_card_holder(), + .validator = CardHolderNameValidator(), + }); + } + + if (_native.needCountry || _native.needZip) { + inner->add( + object_ptr( + inner, + tr::lng_payments_billing_address(), + st::paymentsBillingInformationTitle), + st::paymentsBillingInformationTitlePadding); + } + if (_native.needCountry) { + _country = add({ + .type = FieldType::Country, + .placeholder = tr::lng_payments_billing_country(), + .validator = RequiredFinishedValidator(), + .showBox = showBox, + .defaultCountry = _native.defaultCountry, + }); + } + if (_native.needZip) { + _zip = add({ + .type = FieldType::Text, + .placeholder = tr::lng_payments_billing_zip_code(), + .validator = RequiredValidator(), + }); + if (_country) { + _country->finished( + ) | rpl::start_with_next([=] { + _zip->setFocus(); + }, lifetime()); + } + } + if (_native.canSaveInformation) { + _save = inner->add( + object_ptr( + inner, + tr::lng_payments_save_information(tr::now), + false), + st::paymentsSaveCheckboxPadding); + } + + last->submitted( + ) | rpl::start_with_next([=] { + _delegate->panelValidateCard(collect(), _save && _save->checked()); + }, lifetime()); + + return inner; +} + +void EditCard::resizeEvent(QResizeEvent *e) { + updateControlsGeometry(); +} + +void EditCard::focusInEvent(QFocusEvent *e) { + if (const auto control = lookupField(_focusField)) { + control->setFocusFast(); + } +} + +void EditCard::updateControlsGeometry() { + const auto &padding = st::paymentsPanelPadding; + const auto buttonsHeight = padding.top() + + _cancel->height() + + padding.bottom(); + const auto buttonsTop = height() - buttonsHeight; + _scroll->setGeometry(0, 0, width(), buttonsTop); + _topShadow->resizeToWidth(width()); + _topShadow->moveToLeft(0, 0); + _bottomShadow->resizeToWidth(width()); + _bottomShadow->moveToLeft(0, buttonsTop - st::lineWidth); + auto right = padding.right(); + _submit->moveToRight(right, buttonsTop + padding.top()); + right += _submit->width() + padding.left(); + _cancel->moveToRight(right, buttonsTop + padding.top()); + + _scroll->updateBars(); +} + +auto EditCard::lookupField(CardField field) const -> Field* { + switch (field) { + case CardField::Number: return _number.get(); + case CardField::Cvc: return _cvc.get(); + case CardField::ExpireDate: return _expire.get(); + case CardField::Name: return _name.get(); + case CardField::AddressCountry: return _country.get(); + case CardField::AddressZip: return _zip.get(); + } + Unexpected("Unknown field in EditCard::controlForField."); +} + +UncheckedCardDetails EditCard::collect() const { + return { + .number = _number ? _number->value() : QString(), + .cvc = _cvc ? _cvc->value() : QString(), + .expireYear = _expire ? ExtractYear(_expire->value()) : 0, + .expireMonth = _expire ? ExtractMonth(_expire->value()) : 0, + .cardholderName = _name ? _name->value() : QString(), + .addressCountry = _country ? _country->value() : QString(), + .addressZip = _zip ? _zip->value() : QString(), + }; +} + +} // namespace Payments::Ui diff --git a/Telegram/SourceFiles/payments/ui/payments_edit_card.h b/Telegram/SourceFiles/payments/ui/payments_edit_card.h new file mode 100644 index 000000000..ae4cce54b --- /dev/null +++ b/Telegram/SourceFiles/payments/ui/payments_edit_card.h @@ -0,0 +1,72 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +#include "ui/rp_widget.h" +#include "payments/ui/payments_panel_data.h" +#include "base/object_ptr.h" + +namespace Ui { +class ScrollArea; +class FadeShadow; +class RoundButton; +class Checkbox; +} // namespace Ui + +namespace Payments::Ui { + +using namespace ::Ui; + +class PanelDelegate; +class Field; + +class EditCard final : public RpWidget { +public: + EditCard( + QWidget *parent, + const NativeMethodDetails &native, + CardField field, + not_null delegate); + + void setFocus(CardField field); + void setFocusFast(CardField field); + void showError(CardField field); + +private: + void resizeEvent(QResizeEvent *e) override; + void focusInEvent(QFocusEvent *e) override; + + void setupControls(); + [[nodiscard]] not_null setupContent(); + void updateControlsGeometry(); + [[nodiscard]] Field *lookupField(CardField field) const; + + [[nodiscard]] UncheckedCardDetails collect() const; + + const not_null _delegate; + NativeMethodDetails _native; + + object_ptr _scroll; + object_ptr _topShadow; + object_ptr _bottomShadow; + object_ptr _submit; + object_ptr _cancel; + + std::unique_ptr _number; + std::unique_ptr _cvc; + std::unique_ptr _expire; + std::unique_ptr _name; + std::unique_ptr _country; + std::unique_ptr _zip; + Checkbox *_save = nullptr; + + CardField _focusField = CardField::Number; + +}; + +} // namespace Payments::Ui diff --git a/Telegram/SourceFiles/payments/ui/payments_edit_information.cpp b/Telegram/SourceFiles/payments/ui/payments_edit_information.cpp new file mode 100644 index 000000000..ab96a8a9e --- /dev/null +++ b/Telegram/SourceFiles/payments/ui/payments_edit_information.cpp @@ -0,0 +1,282 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "payments/ui/payments_edit_information.h" + +#include "payments/ui/payments_panel_delegate.h" +#include "payments/ui/payments_field.h" +#include "ui/widgets/scroll_area.h" +#include "ui/widgets/buttons.h" +#include "ui/widgets/labels.h" +#include "ui/widgets/checkbox.h" +#include "ui/wrap/vertical_layout.h" +#include "ui/wrap/fade_wrap.h" +#include "lang/lang_keys.h" +#include "styles/style_payments.h" +#include "styles/style_passport.h" + +namespace Payments::Ui { +namespace { + +constexpr auto kMaxStreetSize = 64; +constexpr auto kMaxPostcodeSize = 10; +constexpr auto kMaxNameSize = 64; +constexpr auto kMaxEmailSize = 128; +constexpr auto kMaxPhoneSize = 16; +constexpr auto kMinCitySize = 2; +constexpr auto kMaxCitySize = 64; + +} // namespace + +EditInformation::EditInformation( + QWidget *parent, + const Invoice &invoice, + const RequestedInformation ¤t, + InformationField field, + not_null delegate) +: _delegate(delegate) +, _invoice(invoice) +, _information(current) +, _scroll(this, st::passportPanelScroll) +, _topShadow(this) +, _bottomShadow(this) +, _submit( + this, + tr::lng_settings_save(), + st::paymentsPanelButton) +, _cancel( + this, + tr::lng_cancel(), + st::paymentsPanelButton) { + setupControls(); +} + +EditInformation::~EditInformation() = default; + +void EditInformation::setFocus(InformationField field) { + _focusField = field; + if (const auto control = lookupField(field)) { + _scroll->ensureWidgetVisible(control->widget()); + control->setFocus(); + } +} + +void EditInformation::setFocusFast(InformationField field) { + _focusField = field; + if (const auto control = lookupField(field)) { + _scroll->ensureWidgetVisible(control->widget()); + control->setFocusFast(); + } +} + +void EditInformation::showError(InformationField field) { + if (const auto control = lookupField(field)) { + _scroll->ensureWidgetVisible(control->widget()); + control->showError(); + } +} + +void EditInformation::setupControls() { + const auto inner = setupContent(); + + _submit->addClickHandler([=] { + _delegate->panelValidateInformation(collect()); + }); + _cancel->addClickHandler([=] { + _delegate->panelCancelEdit(); + }); + + using namespace rpl::mappers; + + _topShadow->toggleOn( + _scroll->scrollTopValue() | rpl::map(_1 > 0)); + _bottomShadow->toggleOn(rpl::combine( + _scroll->scrollTopValue(), + _scroll->heightValue(), + inner->heightValue(), + _1 + _2 < _3)); +} + +not_null EditInformation::setupContent() { + const auto inner = _scroll->setOwnedWidget( + object_ptr(this)); + + _scroll->widthValue( + ) | rpl::start_with_next([=](int width) { + inner->resizeToWidth(width); + }, inner->lifetime()); + + const auto showBox = [=](object_ptr box) { + _delegate->panelShowBox(std::move(box)); + }; + auto last = (Field*)nullptr; + const auto add = [&](FieldConfig &&config) { + auto result = std::make_unique(inner, std::move(config)); + inner->add(result->ownedWidget(), st::paymentsFieldPadding); + if (last) { + last->setNextField(result.get()); + result->setPreviousField(last); + } + last = result.get(); + return result; + }; + if (_invoice.isShippingAddressRequested) { + _street1 = add({ + .placeholder = tr::lng_payments_address_street1(), + .value = _information.shippingAddress.address1, + .validator = RangeLengthValidator(1, kMaxStreetSize), + }); + _street2 = add({ + .placeholder = tr::lng_payments_address_street2(), + .value = _information.shippingAddress.address2, + .validator = MaxLengthValidator(kMaxStreetSize), + }); + _city = add({ + .placeholder = tr::lng_payments_address_city(), + .value = _information.shippingAddress.city, + .validator = RangeLengthValidator(kMinCitySize, kMaxCitySize), + }); + _state = add({ + .placeholder = tr::lng_payments_address_state(), + .value = _information.shippingAddress.state, + }); + _country = add({ + .type = FieldType::Country, + .placeholder = tr::lng_payments_address_country(), + .value = _information.shippingAddress.countryIso2, + .validator = RequiredFinishedValidator(), + .showBox = showBox, + .defaultCountry = _information.defaultCountry, + }); + _postcode = add({ + .placeholder = tr::lng_payments_address_postcode(), + .value = _information.shippingAddress.postcode, + .validator = RangeLengthValidator(1, kMaxPostcodeSize), + }); + } + if (_invoice.isNameRequested) { + _name = add({ + .placeholder = tr::lng_payments_info_name(), + .value = _information.name, + .validator = RangeLengthValidator(1, kMaxNameSize), + }); + } + if (_invoice.isEmailRequested) { + _email = add({ + .type = FieldType::Email, + .placeholder = tr::lng_payments_info_email(), + .value = _information.email, + .validator = RangeLengthValidator(1, kMaxEmailSize), + }); + } + if (_invoice.isPhoneRequested) { + _phone = add({ + .type = FieldType::Phone, + .placeholder = tr::lng_payments_info_phone(), + .value = _information.phone, + .validator = RangeLengthValidator(1, kMaxPhoneSize), + .defaultPhone = _information.defaultPhone, + }); + } + const auto emailToProvider = _invoice.isEmailRequested + && _invoice.emailSentToProvider; + const auto phoneToProvider = _invoice.isPhoneRequested + && _invoice.phoneSentToProvider; + if (emailToProvider || phoneToProvider) { + inner->add( + object_ptr( + inner, + ((emailToProvider && phoneToProvider) + ? tr::lng_payments_to_provider_phone_email + : emailToProvider + ? tr::lng_payments_to_provider_email + : tr::lng_payments_to_provider_phone)( + lt_provider, + rpl::single(_invoice.provider)), + st::paymentsToProviderLabel), + st::paymentsToProviderPadding); + } + _save = inner->add( + object_ptr( + inner, + tr::lng_payments_save_information(tr::now), + true), + st::paymentsSaveCheckboxPadding); + + if (last) { + last->submitted( + ) | rpl::start_with_next([=] { + _delegate->panelValidateInformation(collect()); + }, lifetime()); + } + + return inner; +} + +void EditInformation::resizeEvent(QResizeEvent *e) { + updateControlsGeometry(); +} + +void EditInformation::focusInEvent(QFocusEvent *e) { + if (const auto control = lookupField(_focusField)) { + control->setFocus(); + } +} + +void EditInformation::updateControlsGeometry() { + const auto &padding = st::paymentsPanelPadding; + const auto buttonsHeight = padding.top() + + _cancel->height() + + padding.bottom(); + const auto buttonsTop = height() - buttonsHeight; + _scroll->setGeometry(0, 0, width(), buttonsTop); + _topShadow->resizeToWidth(width()); + _topShadow->moveToLeft(0, 0); + _bottomShadow->resizeToWidth(width()); + _bottomShadow->moveToLeft(0, buttonsTop - st::lineWidth); + auto right = padding.right(); + _submit->moveToRight(right, buttonsTop + padding.top()); + right += _submit->width() + padding.left(); + _cancel->moveToRight(right, buttonsTop + padding.top()); + + _scroll->updateBars(); +} + +auto EditInformation::lookupField(InformationField field) const -> Field* { + switch (field) { + case InformationField::ShippingStreet: return _street1.get(); + case InformationField::ShippingCity: return _city.get(); + case InformationField::ShippingState: return _state.get(); + case InformationField::ShippingCountry: return _country.get(); + case InformationField::ShippingPostcode: return _postcode.get(); + case InformationField::Name: return _name.get(); + case InformationField::Email: return _email.get(); + case InformationField::Phone: return _phone.get(); + } + Unexpected("Unknown field in EditInformation::lookupField."); +} + +RequestedInformation EditInformation::collect() const { + return { + .defaultPhone = _information.defaultPhone, + .defaultCountry = _information.defaultCountry, + .save = _save->checked(), + .name = _name ? _name->value() : QString(), + .phone = _phone ? _phone->value() : QString(), + .email = _email ? _email->value() : QString(), + .shippingAddress = { + .address1 = _street1 ? _street1->value() : QString(), + .address2 = _street2 ? _street2->value() : QString(), + .city = _city ? _city->value() : QString(), + .state = _state ? _state->value() : QString(), + .countryIso2 = _country ? _country->value() : QString(), + .postcode = _postcode ? _postcode->value() : QString(), + }, + }; +} + +} // namespace Payments::Ui diff --git a/Telegram/SourceFiles/payments/ui/payments_edit_information.h b/Telegram/SourceFiles/payments/ui/payments_edit_information.h new file mode 100644 index 000000000..6755939a0 --- /dev/null +++ b/Telegram/SourceFiles/payments/ui/payments_edit_information.h @@ -0,0 +1,80 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +#include "ui/rp_widget.h" +#include "payments/ui/payments_panel_data.h" +#include "base/object_ptr.h" + +namespace Ui { +class ScrollArea; +class FadeShadow; +class RoundButton; +class InputField; +class MaskedInputField; +class Checkbox; +} // namespace Ui + +namespace Payments::Ui { + +using namespace ::Ui; + +class PanelDelegate; +class Field; + +class EditInformation final : public RpWidget { +public: + EditInformation( + QWidget *parent, + const Invoice &invoice, + const RequestedInformation ¤t, + InformationField field, + not_null delegate); + ~EditInformation(); + + void setFocus(InformationField field); + void setFocusFast(InformationField field); + void showError(InformationField field); + +private: + void resizeEvent(QResizeEvent *e) override; + void focusInEvent(QFocusEvent *e) override; + + void setupControls(); + [[nodiscard]] not_null setupContent(); + void updateControlsGeometry(); + [[nodiscard]] Field *lookupField(InformationField field) const; + + [[nodiscard]] RequestedInformation collect() const; + + const not_null _delegate; + Invoice _invoice; + RequestedInformation _information; + + object_ptr _scroll; + object_ptr _topShadow; + object_ptr _bottomShadow; + object_ptr _submit; + object_ptr _cancel; + + std::unique_ptr _street1; + std::unique_ptr _street2; + std::unique_ptr _city; + std::unique_ptr _state; + std::unique_ptr _country; + std::unique_ptr _postcode; + std::unique_ptr _name; + std::unique_ptr _email; + std::unique_ptr _phone; + Checkbox *_save = nullptr; + + InformationField _focusField = InformationField::ShippingStreet; + +}; + +} // namespace Payments::Ui diff --git a/Telegram/SourceFiles/payments/ui/payments_field.cpp b/Telegram/SourceFiles/payments/ui/payments_field.cpp new file mode 100644 index 000000000..97b8244e7 --- /dev/null +++ b/Telegram/SourceFiles/payments/ui/payments_field.cpp @@ -0,0 +1,713 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "payments/ui/payments_field.h" + +#include "ui/widgets/input_fields.h" +#include "ui/boxes/country_select_box.h" +#include "ui/text/format_values.h" +#include "ui/ui_utility.h" +#include "ui/special_fields.h" +#include "data/data_countries.h" +#include "base/platform/base_platform_info.h" +#include "base/event_filter.h" +#include "styles/style_payments.h" + +#include + +namespace Payments::Ui { +namespace { + +struct SimpleFieldState { + QString value; + int position = 0; +}; + +[[nodiscard]] char FieldThousandsSeparator(const CurrencyRule &rule) { + return (rule.thousands == '.' || rule.thousands == ',') + ? ' ' + : rule.thousands; +} + +[[nodiscard]] QString RemoveNonNumbers(QString value) { + return value.replace(QRegularExpression("[^0-9]"), QString()); +} + +[[nodiscard]] SimpleFieldState CleanMoneyState( + const CurrencyRule &rule, + SimpleFieldState state) { + const auto withDecimal = state.value.replace( + QChar('.'), + rule.decimal + ).replace( + QChar(','), + rule.decimal + ); + const auto digitsLimit = 16 - rule.exponent; + const auto beforePosition = state.value.mid(0, state.position); + auto decimalPosition = withDecimal.lastIndexOf(rule.decimal); + if (decimalPosition < 0) { + state = { + .value = RemoveNonNumbers(state.value), + .position = RemoveNonNumbers(beforePosition).size(), + }; + } else { + const auto onlyNumbersBeforeDecimal = RemoveNonNumbers( + state.value.mid(0, decimalPosition)); + state = { + .value = (onlyNumbersBeforeDecimal + + QChar(rule.decimal) + + RemoveNonNumbers(state.value.mid(decimalPosition + 1))), + .position = (RemoveNonNumbers(beforePosition).size() + + (state.position > decimalPosition ? 1 : 0)), + }; + decimalPosition = onlyNumbersBeforeDecimal.size(); + const auto maxLength = decimalPosition + 1 + rule.exponent; + if (state.value.size() > maxLength) { + state = { + .value = state.value.mid(0, maxLength), + .position = std::min(state.position, maxLength), + }; + } + } + if (!state.value.isEmpty() && state.value[0] == QChar(rule.decimal)) { + state = { + .value = QChar('0') + state.value, + .position = state.position + 1, + }; + if (decimalPosition >= 0) { + ++decimalPosition; + } + } + auto skip = 0; + while (state.value.size() > skip + 1 + && state.value[skip] == QChar('0') + && state.value[skip + 1] != QChar(rule.decimal)) { + ++skip; + } + state = { + .value = state.value.mid(skip), + .position = std::max(state.position - skip, 0), + }; + if (decimalPosition >= 0) { + Assert(decimalPosition >= skip); + decimalPosition -= skip; + if (decimalPosition > digitsLimit) { + state = { + .value = (state.value.mid(0, digitsLimit) + + state.value.mid(decimalPosition)), + .position = (state.position > digitsLimit + ? std::max( + state.position - (decimalPosition - digitsLimit), + digitsLimit) + : state.position), + }; + } + } else if (state.value.size() > digitsLimit) { + state = { + .value = state.value.mid(0, digitsLimit), + .position = std::min(state.position, digitsLimit), + }; + } + return state; +} + +[[nodiscard]] SimpleFieldState PostprocessMoneyResult( + const CurrencyRule &rule, + SimpleFieldState result) { + const auto position = result.value.indexOf(rule.decimal); + const auto from = (position >= 0) ? position : result.value.size(); + for (auto insertAt = from - 3; insertAt > 0; insertAt -= 3) { + result.value.insert(insertAt, QChar(FieldThousandsSeparator(rule))); + if (result.position >= insertAt) { + ++result.position; + } + } + return result; +} + +[[nodiscard]] bool IsBackspace(const FieldValidateRequest &request) { + return (request.wasAnchor == request.wasPosition) + && (request.wasPosition == request.nowPosition + 1) + && (request.wasValue.midRef(0, request.wasPosition - 1) + == request.nowValue.midRef(0, request.nowPosition)) + && (request.wasValue.midRef(request.wasPosition) + == request.nowValue.midRef(request.nowPosition)); +} + +[[nodiscard]] bool IsDelete(const FieldValidateRequest &request) { + return (request.wasAnchor == request.wasPosition) + && (request.wasPosition == request.nowPosition) + && (request.wasValue.midRef(0, request.wasPosition) + == request.nowValue.midRef(0, request.nowPosition)) + && (request.wasValue.midRef(request.wasPosition + 1) + == request.nowValue.midRef(request.nowPosition)); +} + +[[nodiscard]] auto MoneyValidator(const CurrencyRule &rule) { + return [=](FieldValidateRequest request) { + const auto realNowState = [&] { + const auto backspaced = IsBackspace(request); + const auto deleted = IsDelete(request); + if (!backspaced && !deleted) { + return CleanMoneyState(rule, { + .value = request.nowValue, + .position = request.nowPosition, + }); + } + const auto realWasState = CleanMoneyState(rule, { + .value = request.wasValue, + .position = request.wasPosition, + }); + const auto changedValue = deleted + ? (realWasState.value.mid(0, realWasState.position) + + realWasState.value.mid(realWasState.position + 1)) + : (realWasState.position > 1) + ? (realWasState.value.mid(0, realWasState.position - 1) + + realWasState.value.mid(realWasState.position)) + : realWasState.value.mid(realWasState.position); + return SimpleFieldState{ + .value = changedValue, + .position = (deleted + ? realWasState.position + : std::max(realWasState.position - 1, 0)) + }; + }(); + const auto postprocessed = PostprocessMoneyResult( + rule, + realNowState); + return FieldValidateResult{ + .value = postprocessed.value, + .position = postprocessed.position, + }; + }; +} + +[[nodiscard]] QString Parse(const FieldConfig &config) { + if (config.type == FieldType::Country) { + return Data::CountryNameByISO2(config.value); + } else if (config.type == FieldType::Money) { + const auto amount = config.value.toLongLong(); + if (!amount) { + return QString(); + } + const auto rule = LookupCurrencyRule(config.currency); + const auto value = std::abs(amount) / std::pow(10., rule.exponent); + const auto precision = (!rule.stripDotZero + || std::floor(value) != value) + ? rule.exponent + : 0; + return FormatWithSeparators( + value, + precision, + rule.decimal, + FieldThousandsSeparator(rule)); + } + return config.value; +} + +[[nodiscard]] QString Format( + const FieldConfig &config, + const QString &parsed, + const QString &countryIso2) { + if (config.type == FieldType::Country) { + return countryIso2; + } else if (config.type == FieldType::Money) { + const auto rule = LookupCurrencyRule(config.currency); + const auto real = QString(parsed).replace( + QChar(rule.decimal), + QChar('.') + ).replace( + QChar(','), + QChar('.') + ).replace( + QRegularExpression("[^0-9\\.]"), + QString() + ).toDouble(); + return QString::number( + int64(std::round(real * std::pow(10., rule.exponent)))); + } else if (config.type == FieldType::CardNumber + || config.type == FieldType::CardCVC) { + return QString(parsed).replace( + QRegularExpression("[^0-9\\.]"), + QString()); + } + return parsed; +} + +[[nodiscard]] bool UseMaskedField(FieldType type) { + switch (type) { + case FieldType::Text: + case FieldType::Email: + return false; + case FieldType::CardNumber: + case FieldType::CardExpireDate: + case FieldType::CardCVC: + case FieldType::Country: + case FieldType::Phone: + case FieldType::Money: + return true; + } + Unexpected("FieldType in Payments::Ui::UseMaskedField."); +} + +[[nodiscard]] base::unique_qptr CreateWrap( + QWidget *parent, + FieldConfig &config) { + switch (config.type) { + case FieldType::Text: + case FieldType::Email: + return base::make_unique_q( + parent, + st::paymentsField, + std::move(config.placeholder), + Parse(config)); + case FieldType::CardNumber: + case FieldType::CardExpireDate: + case FieldType::CardCVC: + case FieldType::Country: + case FieldType::Phone: + case FieldType::Money: + return base::make_unique_q(parent); + } + Unexpected("FieldType in Payments::Ui::CreateWrap."); +} + +[[nodiscard]] InputField *LookupInputField( + not_null wrap, + FieldConfig &config) { + return UseMaskedField(config.type) + ? nullptr + : static_cast(wrap.get()); +} + +[[nodiscard]] MaskedInputField *CreateMoneyField( + not_null wrap, + FieldConfig &config, + rpl::producer<> textPossiblyChanged) { + struct State { + CurrencyRule rule; + style::InputField st; + QString currencyText; + int currencySkip = 0; + FlatLabel *left = nullptr; + FlatLabel *right = nullptr; + }; + const auto state = wrap->lifetime().make_state(State{ + .rule = LookupCurrencyRule(config.currency), + .st = st::paymentsMoneyField, + }); + const auto &rule = state->rule; + state->currencySkip = rule.space ? state->st.font->spacew : 0; + state->currencyText = ((!rule.left && rule.space) + ? QString(QChar(' ')) + : QString()) + (*rule.international + ? QString(rule.international) + : config.currency) + ((rule.left && rule.space) + ? QString(QChar(' ')) + : QString()); + if (rule.left) { + state->left = CreateChild( + wrap.get(), + state->currencyText, + st::paymentsFieldAdditional); + } + state->right = CreateChild( + wrap.get(), + QString(), + st::paymentsFieldAdditional); + const auto leftSkip = state->left + ? (state->left->naturalWidth() + state->currencySkip) + : 0; + const auto rightSkip = st::paymentsFieldAdditional.style.font->width( + QString(QChar(rule.decimal)) + + QString(QChar('0')).repeated(rule.exponent) + + (rule.left ? QString() : state->currencyText)); + state->st.textMargins += QMargins(leftSkip, 0, rightSkip, 0); + state->st.placeholderMargins -= QMargins(leftSkip, 0, rightSkip, 0); + const auto result = CreateChild( + wrap.get(), + state->st, + std::move(config.placeholder), + Parse(config)); + result->setPlaceholderHidden(true); + if (state->left) { + state->left->move(0, state->st.textMargins.top()); + } + const auto updateRight = [=] { + const auto text = result->getLastText(); + const auto width = state->st.font->width(text); + const auto rect = result->getTextRect(); + const auto &rule = state->rule; + const auto symbol = QChar(rule.decimal); + const auto decimal = text.indexOf(symbol); + const auto zeros = (decimal >= 0) + ? std::max(rule.exponent - (text.size() - decimal - 1), 0) + : rule.stripDotZero + ? 0 + : rule.exponent; + const auto valueDecimalSeparator = (decimal >= 0 || !zeros) + ? QString() + : QString(symbol); + const auto zeroString = QString(QChar('0')); + const auto valueRightPart = (text.isEmpty() ? zeroString : QString()) + + valueDecimalSeparator + + zeroString.repeated(zeros); + const auto right = valueRightPart + + (rule.left ? QString() : state->currencyText); + state->right->setText(right); + state->right->setTextColorOverride(valueRightPart.isEmpty() + ? std::nullopt + : std::make_optional(st::windowSubTextFg->c)); + state->right->move( + (state->st.textMargins.left() + + width + + ((rule.left || !valueRightPart.isEmpty()) + ? 0 + : state->currencySkip)), + state->st.textMargins.top()); + }; + std::move( + textPossiblyChanged + ) | rpl::start_with_next(updateRight, result->lifetime()); + if (state->left) { + state->left->raise(); + } + state->right->raise(); + return result; +} + +[[nodiscard]] MaskedInputField *LookupMaskedField( + not_null wrap, + FieldConfig &config, + rpl::producer<> textPossiblyChanged) { + if (!UseMaskedField(config.type)) { + return nullptr; + } + switch (config.type) { + case FieldType::Text: + case FieldType::Email: + return nullptr; + case FieldType::CardNumber: + case FieldType::CardExpireDate: + case FieldType::CardCVC: + case FieldType::Country: + return CreateChild( + wrap.get(), + st::paymentsField, + std::move(config.placeholder), + Parse(config)); + case FieldType::Phone: + return CreateChild( + wrap.get(), + st::paymentsField, + std::move(config.placeholder), + ExtractPhonePrefix(config.defaultPhone), + Parse(config)); + case FieldType::Money: + return CreateMoneyField( + wrap, + config, + std::move(textPossiblyChanged)); + } + Unexpected("FieldType in Payments::Ui::LookupMaskedField."); +} + +} // namespace + +Field::Field(QWidget *parent, FieldConfig &&config) +: _config(config) +, _wrap(CreateWrap(parent, config)) +, _input(LookupInputField(_wrap.get(), config)) +, _masked(LookupMaskedField( + _wrap.get(), + config, + _textPossiblyChanged.events_starting_with({}))) +, _countryIso2(config.value) { + if (_masked) { + setupMaskedGeometry(); + } + if (_config.type == FieldType::Country) { + setupCountry(); + } + if (const auto &validator = config.validator) { + setupValidator(validator); + } else if (config.type == FieldType::Money) { + setupValidator(MoneyValidator(LookupCurrencyRule(config.currency))); + } + setupFrontBackspace(); + setupSubmit(); +} + +RpWidget *Field::widget() const { + return _wrap.get(); +} + +object_ptr Field::ownedWidget() const { + return object_ptr::fromRaw(_wrap.get()); +} + +QString Field::value() const { + return Format( + _config, + _input ? _input->getLastText() : _masked->getLastText(), + _countryIso2); +} + +rpl::producer<> Field::frontBackspace() const { + return _frontBackspace.events(); +} + +rpl::producer<> Field::finished() const { + return _finished.events(); +} + +rpl::producer<> Field::submitted() const { + return _submitted.events(); +} + +void Field::setupMaskedGeometry() { + Expects(_masked != nullptr); + + _wrap->resize(_masked->size()); + _wrap->widthValue( + ) | rpl::start_with_next([=](int width) { + _masked->resize(width, _masked->height()); + }, _masked->lifetime()); + _masked->heightValue( + ) | rpl::start_with_next([=](int height) { + _wrap->resize(_wrap->width(), height); + }, _masked->lifetime()); +} + +void Field::setupCountry() { + Expects(_config.type == FieldType::Country); + Expects(_masked != nullptr); + + QObject::connect(_masked, &MaskedInputField::focused, [=] { + setFocus(); + + const auto name = Data::CountryNameByISO2(_countryIso2); + const auto country = !name.isEmpty() + ? _countryIso2 + : !_config.defaultCountry.isEmpty() + ? _config.defaultCountry + : Platform::SystemCountry(); + auto box = Box( + country, + CountrySelectBox::Type::Countries); + const auto raw = box.data(); + raw->countryChosen( + ) | rpl::start_with_next([=](QString iso2) { + _countryIso2 = iso2; + _masked->setText(Data::CountryNameByISO2(iso2)); + _masked->hideError(); + raw->closeBox(); + if (!iso2.isEmpty()) { + if (_nextField) { + _nextField->activate(); + } else { + _submitted.fire({}); + } + } + }, _masked->lifetime()); + raw->boxClosing() | rpl::start_with_next([=] { + setFocus(); + }, _masked->lifetime()); + _config.showBox(std::move(box)); + }); +} + +void Field::setupValidator(Fn validator) { + Expects(validator != nullptr); + + const auto state = [=]() -> State { + if (_masked) { + const auto position = _masked->cursorPosition(); + const auto selectionStart = _masked->selectionStart(); + const auto selectionEnd = _masked->selectionEnd(); + return { + .value = _masked->getLastText(), + .position = position, + .anchor = (selectionStart == selectionEnd + ? position + : (selectionStart == position) + ? selectionEnd + : selectionStart), + }; + } + const auto cursor = _input->textCursor(); + return { + .value = _input->getLastText(), + .position = cursor.position(), + .anchor = cursor.anchor(), + }; + }; + const auto save = [=] { + _was = state(); + }; + const auto setText = [=](const QString &text) { + if (_masked) { + _masked->setText(text); + } else { + _input->setText(text); + } + }; + const auto setPosition = [=](int position) { + if (_masked) { + _masked->setCursorPosition(position); + } else { + auto cursor = _input->textCursor(); + cursor.setPosition(position); + _input->setTextCursor(cursor); + } + }; + const auto validate = [=] { + if (_validating) { + return; + } + _validating = true; + const auto guard = gsl::finally([&] { + _validating = false; + save(); + _textPossiblyChanged.fire({}); + }); + + const auto now = state(); + const auto result = validator(ValidateRequest{ + .wasValue = _was.value, + .wasPosition = _was.position, + .wasAnchor = _was.anchor, + .nowValue = now.value, + .nowPosition = now.position, + }); + _valid = result.finished || !result.invalid; + + const auto changed = (result.value != now.value); + if (changed) { + setText(result.value); + } + if (changed || result.position != now.position) { + setPosition(result.position); + } + if (result.finished) { + _finished.fire({}); + } else if (result.invalid) { + Ui::PostponeCall( + _masked ? (QWidget*)_masked : _input, + [=] { showErrorNoFocus(); }); + } + }; + if (_masked) { + QObject::connect(_masked, &QLineEdit::cursorPositionChanged, save); + QObject::connect(_masked, &MaskedInputField::changed, validate); + } else { + const auto raw = _input->rawTextEdit(); + QObject::connect(raw, &QTextEdit::cursorPositionChanged, save); + QObject::connect(_input, &InputField::changed, validate); + } +} + +void Field::setupFrontBackspace() { + const auto filter = [=](not_null e) { + const auto frontBackspace = (e->type() == QEvent::KeyPress) + && (static_cast(e.get())->key() == Qt::Key_Backspace) + && (_masked + ? (_masked->cursorPosition() == 0 + && _masked->selectionLength() == 0) + : (_input->textCursor().position() == 0 + && _input->textCursor().anchor() == 0)); + if (frontBackspace) { + _frontBackspace.fire({}); + } + return base::EventFilterResult::Continue; + }; + if (_masked) { + base::install_event_filter(_masked, filter); + } else { + base::install_event_filter(_input->rawTextEdit(), filter); + } +} + +void Field::setupSubmit() { + const auto submitted = [=] { + if (!_valid) { + showError(); + } else if (_nextField) { + _nextField->activate(); + } else { + _submitted.fire({}); + } + }; + if (_masked) { + QObject::connect(_masked, &MaskedInputField::submitted, submitted); + } else { + QObject::connect(_input, &InputField::submitted, submitted); + } +} + +void Field::setNextField(not_null field) { + _nextField = field; + + finished() | rpl::start_with_next([=] { + field->setFocus(); + }, _masked ? _masked->lifetime() : _input->lifetime()); +} + +void Field::setPreviousField(not_null field) { + frontBackspace( + ) | rpl::start_with_next([=] { + field->setFocus(); + }, _masked ? _masked->lifetime() : _input->lifetime()); +} + +void Field::activate() { + if (_input) { + _input->setFocus(); + } else { + _masked->setFocus(); + } +} + +void Field::setFocus() { + if (_config.type == FieldType::Country) { + _wrap->setFocus(); + } else { + activate(); + } +} + +void Field::setFocusFast() { + if (_config.type == FieldType::Country) { + setFocus(); + } else if (_input) { + _input->setFocusFast(); + } else { + _masked->setFocusFast(); + } +} + +void Field::showError() { + if (_config.type == FieldType::Country) { + setFocus(); + _masked->showErrorNoFocus(); + } else if (_input) { + _input->showError(); + } else { + _masked->showError(); + } +} + +void Field::showErrorNoFocus() { + if (_input) { + _input->showErrorNoFocus(); + } else { + _masked->showErrorNoFocus(); + } +} + +} // namespace Payments::Ui diff --git a/Telegram/SourceFiles/payments/ui/payments_field.h b/Telegram/SourceFiles/payments/ui/payments_field.h new file mode 100644 index 000000000..26711ddd9 --- /dev/null +++ b/Telegram/SourceFiles/payments/ui/payments_field.h @@ -0,0 +1,143 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +#include "base/object_ptr.h" +#include "base/unique_qptr.h" + +namespace Ui { +class RpWidget; +class InputField; +class MaskedInputField; +class BoxContent; +} // namespace Ui + +namespace Payments::Ui { + +using namespace ::Ui; + +enum class FieldType { + Text, + CardNumber, + CardExpireDate, + CardCVC, + Country, + Phone, + Email, + Money, +}; + +struct FieldValidateRequest { + QString wasValue; + int wasPosition = 0; + int wasAnchor = 0; + QString nowValue; + int nowPosition = 0; +}; + +struct FieldValidateResult { + QString value; + int position = 0; + bool invalid = false; + bool finished = false; +}; + +[[nodiscard]] inline auto RangeLengthValidator(int minLength, int maxLength) { + return [=](FieldValidateRequest request) { + return FieldValidateResult{ + .value = request.nowValue, + .position = request.nowPosition, + .invalid = (request.nowValue.size() < minLength + || request.nowValue.size() > maxLength), + }; + }; +} + +[[nodiscard]] inline auto MaxLengthValidator(int maxLength) { + return RangeLengthValidator(0, maxLength); +} + +[[nodiscard]] inline auto RequiredValidator() { + return RangeLengthValidator(1, std::numeric_limits::max()); +} + +[[nodiscard]] inline auto RequiredFinishedValidator() { + return [=](FieldValidateRequest request) { + return FieldValidateResult{ + .value = request.nowValue, + .position = request.nowPosition, + .invalid = request.nowValue.isEmpty(), + .finished = !request.nowValue.isEmpty(), + }; + }; +} + +struct FieldConfig { + FieldType type = FieldType::Text; + rpl::producer placeholder; + QString value; + Fn validator; + Fn)> showBox; + QString currency; + QString defaultPhone; + QString defaultCountry; +}; + +class Field final { +public: + Field(QWidget *parent, FieldConfig &&config); + + [[nodiscard]] RpWidget *widget() const; + [[nodiscard]] object_ptr ownedWidget() const; + + [[nodiscard]] QString value() const; + [[nodiscard]] rpl::producer<> frontBackspace() const; + [[nodiscard]] rpl::producer<> finished() const; + [[nodiscard]] rpl::producer<> submitted() const; + + void activate(); + void setFocus(); + void setFocusFast(); + void showError(); + void showErrorNoFocus(); + + void setNextField(not_null field); + void setPreviousField(not_null field); + +private: + struct State { + QString value; + int position = 0; + int anchor = 0; + }; + using ValidateRequest = FieldValidateRequest; + using ValidateResult = FieldValidateResult; + + void setupMaskedGeometry(); + void setupCountry(); + void setupValidator(Fn validator); + void setupFrontBackspace(); + void setupSubmit(); + + const FieldConfig _config; + const base::unique_qptr _wrap; + rpl::event_stream<> _frontBackspace; + rpl::event_stream<> _finished; + rpl::event_stream<> _submitted; + rpl::event_stream<> _textPossiblyChanged; // Must be above _masked. + InputField *_input = nullptr; + MaskedInputField *_masked = nullptr; + Field *_nextField = nullptr; + QString _countryIso2; + State _was; + bool _validating = false; + bool _valid = true; + +}; + +} // namespace Payments::Ui diff --git a/Telegram/SourceFiles/payments/ui/payments_form_summary.cpp b/Telegram/SourceFiles/payments/ui/payments_form_summary.cpp new file mode 100644 index 000000000..3dfba7782 --- /dev/null +++ b/Telegram/SourceFiles/payments/ui/payments_form_summary.cpp @@ -0,0 +1,609 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "payments/ui/payments_form_summary.h" + +#include "payments/ui/payments_panel_delegate.h" +#include "settings/settings_common.h" +#include "ui/widgets/scroll_area.h" +#include "ui/widgets/buttons.h" +#include "ui/widgets/labels.h" +#include "ui/wrap/vertical_layout.h" +#include "ui/wrap/fade_wrap.h" +#include "ui/text/format_values.h" +#include "ui/text/text_utilities.h" +#include "data/data_countries.h" +#include "lang/lang_keys.h" +#include "base/unixtime.h" +#include "styles/style_payments.h" +#include "styles/style_passport.h" + +namespace App { +QString formatPhone(QString phone); // #TODO +} // namespace App + +namespace Payments::Ui { +namespace { + +constexpr auto kLightOpacity = 0.1; +constexpr auto kLightRippleOpacity = 0.11; +constexpr auto kChosenOpacity = 0.8; +constexpr auto kChosenRippleOpacity = 0.5; + +[[nodiscard]] Fn TransparentColor( + const style::color &c, + float64 opacity) { + return [&c, opacity] { + return QColor( + c->c.red(), + c->c.green(), + c->c.blue(), + c->c.alpha() * opacity); + }; +} + +[[nodiscard]] style::RoundButton TipButtonStyle( + const style::RoundButton &original, + const style::color &light, + const style::color &ripple) { + auto result = original; + result.textBg = light; + result.ripple.color = ripple; + return result; +} + +} // namespace + +using namespace ::Ui; + +class PanelDelegate; + +FormSummary::FormSummary( + QWidget *parent, + const Invoice &invoice, + const RequestedInformation ¤t, + const PaymentMethodDetails &method, + const ShippingOptions &options, + not_null delegate, + int scrollTop) +: _delegate(delegate) +, _invoice(invoice) +, _method(method) +, _options(options) +, _information(current) +, _scroll(this, st::passportPanelScroll) +, _layout(_scroll->setOwnedWidget(object_ptr(this))) +, _topShadow(this) +, _bottomShadow(this) +, _submit(_invoice.receipt.paid + ? object_ptr(nullptr) + : object_ptr( + this, + tr::lng_payments_pay_amount( + lt_amount, + rpl::single(formatAmount(computeTotalAmount()))), + st::paymentsPanelSubmit)) +, _cancel( + this, + (_invoice.receipt.paid + ? tr::lng_about_done() + : tr::lng_cancel()), + st::paymentsPanelButton) +, _tipLightBg(TransparentColor(st::paymentsTipActive, kLightOpacity)) +, _tipLightRipple( + TransparentColor(st::paymentsTipActive, kLightRippleOpacity)) +, _tipChosenBg(TransparentColor(st::paymentsTipActive, kChosenOpacity)) +, _tipChosenRipple( + TransparentColor(st::paymentsTipActive, kChosenRippleOpacity)) +, _tipButton(TipButtonStyle( + st::paymentsTipButton, + _tipLightBg.color(), + _tipLightRipple.color())) +, _tipChosen(TipButtonStyle( + st::paymentsTipChosen, + _tipChosenBg.color(), + _tipChosenRipple.color())) +, _initialScrollTop(scrollTop) { + setupControls(); +} + +rpl::producer FormSummary::scrollTopValue() const { + return _scroll->scrollTopValue(); +} + +bool FormSummary::showCriticalError(const TextWithEntities &text) { + if (_invoice + || (_scroll->height() - _layout->height() + < st::paymentsPanelSize.height() / 2)) { + return false; + } + Settings::AddSkip(_layout.get(), st::paymentsPricesTopSkip); + _layout->add(object_ptr( + _layout.get(), + rpl::single(text), + st::paymentsCriticalError)); + return true; +} + +int FormSummary::contentHeight() const { + return _invoice ? _scroll->height() : _layout->height(); +} + +void FormSummary::updateThumbnail(const QImage &thumbnail) { + _invoice.cover.thumbnail = thumbnail; + _thumbnails.fire_copy(thumbnail); +} + +QString FormSummary::formatAmount( + int64 amount, + bool forceStripDotZero) const { + return FillAmountAndCurrency( + amount, + _invoice.currency, + forceStripDotZero); +} + +int64 FormSummary::computeTotalAmount() const { + const auto total = ranges::accumulate( + _invoice.prices, + int64(0), + std::plus<>(), + &LabeledPrice::price); + const auto selected = ranges::find( + _options.list, + _options.selectedId, + &ShippingOption::id); + const auto shipping = (selected != end(_options.list)) + ? ranges::accumulate( + selected->prices, + int64(0), + std::plus<>(), + &LabeledPrice::price) + : int64(0); + return total + shipping + _invoice.tipsSelected; +} + +void FormSummary::setupControls() { + setupContent(_layout.get()); + + if (_submit) { + _submit->addClickHandler([=] { + _delegate->panelSubmit(); + }); + } + _cancel->addClickHandler([=] { + _delegate->panelRequestClose(); + }); + if (!_invoice) { + if (_submit) { + _submit->hide(); + } + _cancel->hide(); + } + + using namespace rpl::mappers; + + _topShadow->toggleOn( + _scroll->scrollTopValue() | rpl::map(_1 > 0)); + _bottomShadow->toggleOn(rpl::combine( + _scroll->scrollTopValue(), + _scroll->heightValue(), + _layout->heightValue(), + _1 + _2 < _3)); + + rpl::merge( + _submit->widthValue(), + _cancel->widthValue() + ) | rpl::skip(2) | rpl::start_with_next([=] { + updateControlsGeometry(); + }, lifetime()); +} + +void FormSummary::setupCover(not_null layout) { + struct State { + QImage thumbnail; + FlatLabel *title = nullptr; + FlatLabel *description = nullptr; + FlatLabel *seller = nullptr; + }; + + const auto cover = layout->add(object_ptr(layout)); + const auto state = cover->lifetime().make_state(); + state->title = CreateChild( + cover, + _invoice.cover.title, + st::paymentsTitle); + state->description = CreateChild( + cover, + _invoice.cover.description, + st::paymentsDescription); + state->seller = CreateChild( + cover, + _invoice.cover.seller, + st::paymentsSeller); + cover->paintRequest( + ) | rpl::start_with_next([=](QRect clip) { + if (state->thumbnail.isNull()) { + return; + } + const auto &padding = st::paymentsCoverPadding; + const auto thumbnailSkip = st::paymentsThumbnailSize.width() + + st::paymentsThumbnailSkip; + const auto left = padding.left(); + const auto top = padding.top(); + const auto rect = QRect( + QPoint(left, top), + state->thumbnail.size() / state->thumbnail.devicePixelRatio()); + if (rect.intersects(clip)) { + QPainter(cover).drawImage(rect, state->thumbnail); + } + }, cover->lifetime()); + rpl::combine( + cover->widthValue(), + _thumbnails.events_starting_with_copy(_invoice.cover.thumbnail) + ) | rpl::start_with_next([=](int width, QImage &&thumbnail) { + const auto &padding = st::paymentsCoverPadding; + const auto thumbnailSkip = st::paymentsThumbnailSize.width() + + st::paymentsThumbnailSkip; + const auto left = padding.left() + + (thumbnail.isNull() ? 0 : thumbnailSkip); + const auto available = width + - padding.left() + - padding.right() + - (thumbnail.isNull() ? 0 : thumbnailSkip); + state->title->resizeToNaturalWidth(available); + state->title->moveToLeft( + left, + padding.top() + st::paymentsTitleTop); + state->description->resizeToNaturalWidth(available); + state->description->moveToLeft( + left, + (state->title->y() + + state->title->height() + + st::paymentsDescriptionTop)); + state->seller->resizeToNaturalWidth(available); + state->seller->moveToLeft( + left, + (state->description->y() + + state->description->height() + + st::paymentsSellerTop)); + const auto thumbnailHeight = padding.top() + + (thumbnail.isNull() + ? 0 + : int(thumbnail.height() / thumbnail.devicePixelRatio())) + + padding.bottom(); + const auto height = state->seller->y() + + state->seller->height() + + padding.bottom(); + cover->resize(width, std::max(thumbnailHeight, height)); + state->thumbnail = std::move(thumbnail); + cover->update(); + }, cover->lifetime()); +} + +void FormSummary::setupPrices(not_null layout) { + const auto addRow = [&]( + const QString &label, + const TextWithEntities &value, + bool full = false) { + const auto &st = full + ? st::paymentsFullPriceAmount + : st::paymentsPriceAmount; + const auto right = CreateChild( + layout.get(), + rpl::single(value), + st); + const auto &padding = st::paymentsPricePadding; + const auto left = layout->add( + object_ptr( + layout, + label, + (full + ? st::paymentsFullPriceLabel + : st::paymentsPriceLabel)), + style::margins( + padding.left(), + padding.top(), + (padding.right() + + right->naturalWidth() + + 2 * st.style.font->spacew), + padding.bottom())); + rpl::combine( + left->topValue(), + layout->widthValue() + ) | rpl::start_with_next([=](int top, int width) { + right->moveToRight(st::paymentsPricePadding.right(), top, width); + }, right->lifetime()); + return right; + }; + + Settings::AddSkip(layout, st::paymentsPricesTopSkip); + if (_invoice.receipt) { + addRow( + tr::lng_payments_date_label(tr::now), + { langDateTime(base::unixtime::parse(_invoice.receipt.date)) }, + true); + Settings::AddSkip(layout, st::paymentsPricesBottomSkip); + Settings::AddDivider(layout); + Settings::AddSkip(layout, st::paymentsPricesBottomSkip); + } + + const auto add = [&]( + const QString &label, + int64 amount, + bool full = false) { + addRow(label, { formatAmount(amount) }, full); + }; + for (const auto &price : _invoice.prices) { + add(price.label, price.price); + } + const auto selected = ranges::find( + _options.list, + _options.selectedId, + &ShippingOption::id); + if (selected != end(_options.list)) { + for (const auto &price : selected->prices) { + add(price.label, price.price); + } + } + + const auto computedTotal = computeTotalAmount(); + const auto total = _invoice.receipt.paid + ? _invoice.receipt.totalAmount + : computedTotal; + if (_invoice.receipt.paid) { + if (const auto tips = total - computedTotal) { + add(tr::lng_payments_tips_label(tr::now), tips); + } + } else if (_invoice.tipsMax > 0) { + const auto text = formatAmount(_invoice.tipsSelected); + const auto label = addRow( + tr::lng_payments_tips_label(tr::now), + Ui::Text::Link(text, "internal:edit_tips")); + label->setClickHandlerFilter([=](auto&&...) { + _delegate->panelChooseTips(); + return false; + }); + setupSuggestedTips(layout); + } + + add(tr::lng_payments_total_label(tr::now), total, true); + Settings::AddSkip(layout, st::paymentsPricesBottomSkip); +} + +void FormSummary::setupSuggestedTips(not_null layout) { + if (_invoice.suggestedTips.empty()) { + return; + } + struct Button { + RoundButton *widget = nullptr; + int minWidth = 0; + }; + struct State { + std::vector