Merge commit '172aa382701692eed41c1167e527af829814a13f'

This commit is contained in:
Ali 2021-02-12 00:41:21 +04:00
commit d366ea37d7
42 changed files with 37829 additions and 38149 deletions

15
.gitattributes vendored Normal file
View File

@ -0,0 +1,15 @@
# Linguist overrides for vendored code
submodules/AsyncDisplayKit/* linguist-vendored
submodules/ffmpeg/* linguist-vendored
submodules/HockeySDK-iOS/* linguist-vendored
submodules/libphonenumber/* linguist-vendored
submodules/libtgvoip/* linguist-vendored
submodules/lottie-ios/* linguist-vendored
submodules/Opus/* linguist-vendored
submodules/OpusBinding/* linguist-vendored
submodules/rlottie/rlottie/* linguist-vendored
submodules/sqlcipher/* linguist-vendored
submodules/Stripe/* linguist-vendored
submodules/ton/tonlib-src/* linguist-vendored
submodules/webp/include/* linguist-vendored
third-party/* linguist-vendored

View File

@ -5838,6 +5838,7 @@ Sorry for the inconvenience.";
"InviteLink.PeopleJoined_many" = "%@ people joined";
"InviteLink.PeopleJoined_any" = "%@ people joined";
"InviteLink.CreatePrivateLinkHelp" = "Anyone who has Telegram installed will be able to join your group by following this link.";
"InviteLink.CreatePrivateLinkHelpChannel" = "Anyone who has Telegram installed will be able to join your channel by following this link.";
"InviteLink.Manage" = "Manage Invite Links";
"InviteLink.PeopleJoinedShortNoneExpired" = "no one joined";
@ -5899,6 +5900,7 @@ Sorry for the inconvenience.";
"InviteLink.QRCode.Title" = "Invite by QR Code";
"InviteLink.QRCode.Info" = "Everyone on Telegram can scan this code to join your group.";
"InviteLink.QRCode.InfoChannel" = "Everyone on Telegram can scan this code to join your channel.";
"InviteLink.QRCode.Share" = "Share QR Code";
"InviteLink.InviteLink" = "Invite Link";
@ -6021,6 +6023,9 @@ Sorry for the inconvenience.";
"Channel.AdminLog.DeletedInviteLink" = "%1$@ deleted invite link %2$@";
"Channel.AdminLog.RevokedInviteLink" = "%1$@ revoked invite link %2$@";
"Channel.AdminLog.EditedInviteLink" = "%1$@ edited invite link %2$@";
"Channel.AdminLog.CreatedInviteLink" = "%1$@ created invite link %2$@";
"Channel.AdminLog.JoinedViaInviteLink" = "%1$@ joined via invite link %2$@";
"GroupInfo.Permissions.BroadcastTitle" = "Broadcast Channel";
"GroupInfo.Permissions.BroadcastConvert" = "Convert Group to Channel";
@ -6056,7 +6061,6 @@ Sorry for the inconvenience.";
"Report.Report" = "Report";
"Report.Succeed" = "Telegram moderators will study your report. Thank you!";
"Conversation.AutoremoveRemainingTime" = "auto-delete in %@";
"Conversation.AutoremoveRemainingDays_1" = "auto-delete in %@ day";
"Conversation.AutoremoveRemainingDays_any" = "auto-delete in %@ days";
@ -6066,3 +6070,7 @@ Sorry for the inconvenience.";
"Conversation.AutoremoveChanged" = "Auto-Delete timer set to %@";
"PeerInfo.ReportProfilePhoto" = "Report Profile Photo";
"PeerInfo.ReportProfileVideo" = "Report Profile Video";
"Channel.AdminLog.CanInviteUsersViaLink" = "Invite Users via Link";

View File

@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder.WatchKit.Storyboard" version="3.0" toolsVersion="17156" targetRuntime="watchKit" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="umr-Wa-jBL">
<document type="com.apple.InterfaceBuilder.WatchKit.Storyboard" version="3.0" toolsVersion="17701" targetRuntime="watchKit" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="umr-Wa-jBL">
<device id="watch38"/>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17125"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBWatchKitPlugin" version="17034"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17703"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBWatchKitPlugin" version="17500"/>
</dependencies>
<scenes>
<!--TGNeoConversationController-->
@ -1569,11 +1569,445 @@ contacts found.</string>
</objects>
<point key="canvasLocation" x="729" y="757.25"/>
</scene>
<!--Static M-->
<scene sceneID="uW0-5W-J6c">
<objects>
<notificationController backgroundImage="BubbleNotification" spacing="0.0" id="JcB-1W-jcv" userLabel="Static M">
<items>
<group width="1" alignment="left" radius="0.0" id="11B-ry-7nl">
<items>
<label alignment="left" text="Text" numberOfLines="0" id="RMV-rW-0qs">
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<fontDescription key="font" style="UICTFontTextStyleBody"/>
</label>
</items>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<edgeInsets key="margins" left="8" right="8" top="10" bottom="11"/>
</group>
</items>
<notificationCategory key="notificationCategory" identifier="m" id="MXx-dC-nsP">
<color key="titleColor" red="0.10051588710000001" green="0.10051287709999999" blue="0.1005146056" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color key="sashColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
</notificationCategory>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<edgeInsets key="margins" left="0.0" right="0.0" top="0.0" bottom="4"/>
<connections>
<outlet property="notificationAlertLabel" destination="RMV-rW-0qs" id="buR-qh-N93"/>
<segue destination="RQh-4n-Jyy" kind="relationship" relationship="dynamicNotificationInterface" id="wDy-DD-bRl"/>
<segue destination="RQh-4n-Jyy" kind="relationship" relationship="dynamicInteractiveNotificationInterface" id="MKi-5u-rhv"/>
</connections>
</notificationController>
</objects>
<point key="canvasLocation" x="263" y="875"/>
</scene>
<!--Dynamic M-->
<scene sceneID="aFy-up-fB6">
<objects>
<controller backgroundImage="BubbleNotification" id="RQh-4n-Jyy" userLabel="Dynamic M" customClass="TGNotificationController">
<items>
<group width="1" alignment="left" layout="vertical" radius="0.0" spacing="0.0" id="Dd4-YD-hsp">
<items>
<group width="1" alignment="left" layout="vertical" spacing="0.0" id="ZlG-DJ-Ctv">
<items>
<label alignment="left" hidden="YES" text="Chat Title" id="frP-KX-c0b">
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<fontDescription key="font" type="system" weight="medium" pointSize="12"/>
</label>
<label alignment="left" hidden="YES" text="Name" id="JUM-Bm-hxM">
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<fontDescription key="font" type="system" weight="medium" pointSize="16"/>
</label>
<group width="1" height="29" alignment="left" hidden="YES" layout="vertical" spacing="0.0" id="r84-Ll-prj">
<items>
<label alignment="left" verticalAlignment="center" text="Forwarded from" id="Br6-65-UTH" userLabel="ForwardTitle">
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<fontDescription key="font" type="system" pointSize="12"/>
</label>
<label alignment="left" verticalAlignment="center" text="Name" id="DI0-a4-1u0" userLabel="ForwardFrom">
<color key="textColor" red="0.1131299585" green="0.50641471149999995" blue="0.96399867530000005" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<fontDescription key="font" type="system" weight="medium" pointSize="12"/>
</label>
</items>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<edgeInsets key="margins" left="0.0" right="0.0" top="0.0" bottom="0.0"/>
</group>
<group width="1" height="29" alignment="left" hidden="YES" spacing="4" id="Th4-sR-kDF" userLabel="ReplyHeader">
<items>
<group width="2" height="26" alignment="left" verticalAlignment="center" radius="0.0" spacing="0.0" id="n46-ZY-9jJ" userLabel="ReplyLine">
<color key="backgroundColor" red="0.1131299585" green="0.50641471149999995" blue="0.96399867530000005" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<edgeInsets key="margins" left="0.0" right="0.0" top="0.0" bottom="0.0"/>
</group>
<group width="26" height="26" alignment="left" verticalAlignment="center" radius="2" id="FmK-ex-jTs" userLabel="ReplyImage">
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.089999999999999997" colorSpace="custom" customColorSpace="sRGB"/>
</group>
<group width="1" alignment="left" layout="vertical" spacing="0.0" id="tQQ-et-qfm" userLabel="ReplyMessage">
<items>
<label alignment="left" text="Name" id="ifF-tf-ens" userLabel="ReplyAuthor">
<color key="textColor" red="0.1131299585" green="0.50641471149999995" blue="0.96399867530000005" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<fontDescription key="font" type="system" weight="medium" pointSize="12"/>
</label>
<label alignment="left" text="Text" id="tWb-zc-3NN" userLabel="ReplyText">
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<fontDescription key="font" type="system" pointSize="12"/>
</label>
</items>
<edgeInsets key="margins" left="0.0" right="0.0" top="0.0" bottom="0.0"/>
</group>
</items>
<edgeInsets key="margins" left="0.0" right="0.0" top="0.0" bottom="0.0"/>
</group>
<label alignment="left" text="Text" numberOfLines="0" id="t9S-qg-lnm">
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<fontDescription key="font" style="UICTFontTextStyleBody"/>
</label>
</items>
<edgeInsets key="margins" left="8" right="8" top="0.0" bottom="5"/>
</group>
<group width="1" alignment="left" hidden="YES" layout="vertical" id="CMM-a2-p1K" userLabel="WrapperGroup">
<items>
<group width="1" alignment="left" layout="vertical" radius="10" spacing="0.0" id="r4K-n7-uKy" userLabel="LocationGroup">
<items>
<map height="92" alignment="left" id="PY6-r9-1nn"/>
</items>
<edgeInsets key="margins" left="0.0" right="0.0" top="0.0" bottom="0.0"/>
</group>
<group width="1" alignment="left" spacing="5" id="XJr-R0-HOA" userLabel="FileGroup">
<items>
<imageView width="26" height="26" alignment="left" verticalAlignment="center" hidden="YES" image="Location" contentMode="center" id="vZ3-mt-ICu" userLabel="VenueIcon">
<color key="tintColor" red="0.35566622019999999" green="0.68838506939999999" blue="0.91561108830000004" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</imageView>
<group width="26" height="26" alignment="left" verticalAlignment="center" hidden="YES" radius="13" spacing="0.0" id="IJt-Cp-BKm" userLabel="AudioGroup">
<items>
<imageView width="26" height="26" alignment="left" image="MediaAudioPlay" contentMode="center" id="PDu-Bz-gy5"/>
</items>
<color key="backgroundColor" red="0.35566622019999999" green="0.68838506939999999" blue="0.91561108830000004" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<edgeInsets key="margins" left="0.0" right="0.0" top="0.0" bottom="0.0"/>
</group>
<group width="26" height="26" alignment="left" verticalAlignment="center" radius="0.0" id="Gd3-ap-jO4" userLabel="FileIconGroup">
<items>
<imageView alignment="center" verticalAlignment="center" image="File.png" contentMode="center" id="dZu-99-pMR">
<color key="tintColor" red="0.14697439970000001" green="0.5607914329" blue="0.88162887099999998" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</imageView>
</items>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
</group>
<group alignment="left" verticalAlignment="center" layout="vertical" spacing="0.0" id="DHa-mY-ceB" userLabel="FileMetaGroup">
<items>
<label alignment="left" text="File Name" id="tul-U8-7fj">
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<fontDescription key="font" type="system" weight="medium" pointSize="12"/>
</label>
<label alignment="left" text="Size" id="m2I-fn-zCe">
<color key="textColor" red="0.41865724329999998" green="0.41825520989999998" blue="0.4306421876" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<fontDescription key="font" type="system" pointSize="12"/>
</label>
</items>
<edgeInsets key="margins" left="0.0" right="0.0" top="0.0" bottom="0.0"/>
</group>
</items>
<edgeInsets key="margins" left="6.5" right="6.5" top="2" bottom="1"/>
</group>
<group width="1" alignment="left" spacing="0.0" id="DLM-Wi-QFQ" userLabel="StickerWrapper">
<items>
<group width="0.5" height="64" alignment="left" contentMode="scaleAspectFit" id="7TZ-8f-EgD" userLabel="StickerGroup">
<variation key="device=watch42mm" height="72"/>
</group>
</items>
<edgeInsets key="margins" left="6.5" right="0.0" top="0.0" bottom="0.0"/>
</group>
<group width="1" alignment="left" radius="12" id="sxE-kX-fH5" userLabel="MediaGroup">
<items>
<group alignment="right" verticalAlignment="bottom" radius="10" id="GEf-6R-cw1" userLabel="DurationGroup">
<items>
<label alignment="left" text="2:34" id="wRk-Fm-UIl" userLabel="Duration">
<color key="textColor" red="0.2461894453" green="0.24618205430000001" blue="0.2461862564" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<fontDescription key="font" type="system" pointSize="12"/>
</label>
</items>
<color key="backgroundColor" red="0.89292949440000002" green="0.91148859260000004" blue="0.93112039570000005" alpha="0.80000000000000004" colorSpace="custom" customColorSpace="sRGB"/>
<edgeInsets key="margins" left="6" right="6" top="2" bottom="2"/>
</group>
</items>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.089999999999999997" colorSpace="custom" customColorSpace="sRGB"/>
<edgeInsets key="margins" left="4" right="4" top="4" bottom="4"/>
</group>
<group width="1" alignment="left" id="W49-eu-bIX" userLabel="CaptionGroup">
<items>
<label alignment="left" text="Caption" numberOfLines="0" id="3dY-xL-bio">
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<fontDescription key="font" type="system" pointSize="16"/>
</label>
</items>
<edgeInsets key="margins" left="8" right="8" top="4" bottom="4"/>
</group>
</items>
<edgeInsets key="margins" left="1.5" right="1.5" top="0.0" bottom="0.0"/>
</group>
</items>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<edgeInsets key="margins" left="0.0" right="0.0" top="10" bottom="5"/>
</group>
</items>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<edgeInsets key="margins" left="0.0" right="0.0" top="0.0" bottom="4"/>
<connections>
<outlet property="audioGroup" destination="IJt-Cp-BKm" id="UDG-gj-AJm"/>
<outlet property="captionGroup" destination="W49-eu-bIX" id="fLo-8u-qJ7"/>
<outlet property="captionLabel" destination="3dY-xL-bio" id="HPd-qv-6SR"/>
<outlet property="chatTitleLabel" destination="frP-KX-c0b" id="gQB-RH-eAC"/>
<outlet property="durationGroup" destination="GEf-6R-cw1" id="b6I-rw-1ma"/>
<outlet property="durationLabel" destination="wRk-Fm-UIl" id="OY4-mh-oGN"/>
<outlet property="fileGroup" destination="XJr-R0-HOA" id="jAA-6n-GcD"/>
<outlet property="fileIconGroup" destination="Gd3-ap-jO4" id="Gk0-pX-7xT"/>
<outlet property="forwardFromLabel" destination="DI0-a4-1u0" id="97d-FM-hvd"/>
<outlet property="forwardHeaderGroup" destination="r84-Ll-prj" id="eOS-0I-fH2"/>
<outlet property="forwardTitleLabel" destination="Br6-65-UTH" id="ZXg-hL-4Nt"/>
<outlet property="map" destination="PY6-r9-1nn" id="QtL-Ma-9i3"/>
<outlet property="mapGroup" destination="r4K-n7-uKy" id="QaM-0N-n60"/>
<outlet property="mediaGroup" destination="sxE-kX-fH5" id="oMl-K9-upS"/>
<outlet property="messageTextLabel" destination="t9S-qg-lnm" id="NTR-1N-c27"/>
<outlet property="nameLabel" destination="JUM-Bm-hxM" id="Eiz-Pp-a1C"/>
<outlet property="replyAuthorNameLabel" destination="ifF-tf-ens" id="NSF-eV-jEP"/>
<outlet property="replyHeaderGroup" destination="Th4-sR-kDF" id="SjQ-KI-BYD"/>
<outlet property="replyHeaderImageGroup" destination="FmK-ex-jTs" id="j7i-Sc-BUV"/>
<outlet property="replyMessageTextLabel" destination="tWb-zc-3NN" id="aSf-v0-kQf"/>
<outlet property="stickerGroup" destination="7TZ-8f-EgD" id="f6z-Rx-GYV"/>
<outlet property="stickerWrapperGroup" destination="DLM-Wi-QFQ" id="wXu-ff-inw"/>
<outlet property="subtitleLabel" destination="m2I-fn-zCe" id="WMT-u4-Tgp"/>
<outlet property="titleLabel" destination="tul-U8-7fj" id="VxJ-io-0DL"/>
<outlet property="venueIcon" destination="vZ3-mt-ICu" id="WvO-Db-hRw"/>
<outlet property="wrapperGroup" destination="CMM-a2-p1K" id="SSt-aA-79a"/>
</connections>
</controller>
</objects>
<point key="canvasLocation" x="503" y="874.75"/>
</scene>
<!--Static R-->
<scene sceneID="Aix-4r-fFK">
<objects>
<notificationController backgroundImage="BubbleNotification" spacing="0.0" id="crw-Qo-dti" userLabel="Static R">
<items>
<group width="1" alignment="left" layout="vertical" radius="0.0" id="RTo-Ed-NsD">
<items>
<label alignment="left" text="Text" numberOfLines="0" id="6f0-WK-Add">
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<fontDescription key="font" style="UICTFontTextStyleBody"/>
</label>
</items>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<edgeInsets key="margins" left="8" right="8" top="10" bottom="11"/>
</group>
</items>
<notificationCategory key="notificationCategory" id="pgn-NP-bt0">
<color key="titleColor" red="0.10051588710000001" green="0.10051287709999999" blue="0.1005146056" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color key="sashColor" red="1" green="0.99997437" blue="0.99999129769999995" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
</notificationCategory>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<edgeInsets key="margins" left="0.0" right="0.0" top="0.0" bottom="4"/>
<connections>
<outlet property="notificationAlertLabel" destination="6f0-WK-Add" id="ioT-Xv-KJp"/>
<segue destination="79a-X2-tmF" kind="relationship" relationship="dynamicNotificationInterface" id="dmG-n7-HwD"/>
<segue destination="79a-X2-tmF" kind="relationship" relationship="dynamicInteractiveNotificationInterface" id="UyA-Ra-UVy"/>
</connections>
</notificationController>
</objects>
<point key="canvasLocation" x="263" y="1281"/>
</scene>
<!--Dynamic R-->
<scene sceneID="K4P-nV-Yqh">
<objects>
<controller backgroundImage="BubbleNotification" spacing="0.0" id="79a-X2-tmF" userLabel="Dynamic R" customClass="TGNotificationController">
<items>
<group width="1" alignment="left" layout="vertical" radius="0.0" spacing="0.0" id="Go3-q1-pnJ">
<items>
<group width="1" alignment="left" layout="vertical" spacing="0.0" id="JAA-Ky-cyx">
<items>
<label alignment="left" hidden="YES" text="Chat Title" id="HtY-WB-aFI">
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<fontDescription key="font" type="system" weight="medium" pointSize="12"/>
</label>
<label alignment="left" hidden="YES" text="Name" id="Hqd-Pr-2zp">
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<fontDescription key="font" type="system" weight="medium" pointSize="16"/>
</label>
<group width="1" height="29" alignment="left" hidden="YES" layout="vertical" spacing="0.0" id="Kzq-HH-8Ev">
<items>
<label alignment="left" verticalAlignment="center" text="Forwarded from" id="NTL-lz-9dd" userLabel="ForwardTitle">
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<fontDescription key="font" type="system" pointSize="12"/>
</label>
<label alignment="left" verticalAlignment="center" text="Name" id="B2R-Qa-MeP" userLabel="ForwardFrom">
<color key="textColor" red="0.1131299585" green="0.50641471149999995" blue="0.96399867530000005" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<fontDescription key="font" type="system" weight="medium" pointSize="12"/>
</label>
</items>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<edgeInsets key="margins" left="0.0" right="0.0" top="0.0" bottom="0.0"/>
</group>
<group width="1" height="29" alignment="left" hidden="YES" spacing="4" id="KRV-O6-45y" userLabel="ReplyHeader">
<items>
<group width="2" height="26" alignment="left" verticalAlignment="center" radius="0.0" spacing="0.0" id="eW6-JQ-FAY" userLabel="ReplyLine">
<color key="backgroundColor" red="0.1131299585" green="0.50641471149999995" blue="0.96399867530000005" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<edgeInsets key="margins" left="0.0" right="0.0" top="0.0" bottom="0.0"/>
</group>
<group width="26" height="26" alignment="left" verticalAlignment="center" radius="2" id="swd-Nx-MDo" userLabel="ReplyImage">
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.089999999999999997" colorSpace="custom" customColorSpace="sRGB"/>
</group>
<group width="1" alignment="left" layout="vertical" spacing="0.0" id="h1Y-se-IEW" userLabel="ReplyMessage">
<items>
<label alignment="left" text="Name" id="g6r-Nm-SOQ" userLabel="ReplyAuthor">
<color key="textColor" red="0.1131299585" green="0.50641471149999995" blue="0.96399867530000005" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<fontDescription key="font" type="system" weight="medium" pointSize="12"/>
</label>
<label alignment="left" text="Text" id="dAz-x8-jbh" userLabel="ReplyText">
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<fontDescription key="font" type="system" pointSize="12"/>
</label>
</items>
<edgeInsets key="margins" left="0.0" right="0.0" top="0.0" bottom="0.0"/>
</group>
</items>
<edgeInsets key="margins" left="0.0" right="0.0" top="0.0" bottom="0.0"/>
</group>
<label alignment="left" text="Text" numberOfLines="0" id="wxg-r7-aSG">
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<fontDescription key="font" style="UICTFontTextStyleBody"/>
</label>
</items>
<edgeInsets key="margins" left="8" right="8" top="0.0" bottom="5"/>
</group>
<group width="1" alignment="left" hidden="YES" layout="vertical" id="0ch-zY-dJX" userLabel="WrapperGroup">
<items>
<group width="1" alignment="left" layout="vertical" radius="10" spacing="0.0" id="vel-8f-OFm" userLabel="LocationGroup">
<items>
<map height="92" alignment="left" id="k4c-6T-xVa"/>
</items>
<edgeInsets key="margins" left="0.0" right="0.0" top="0.0" bottom="0.0"/>
</group>
<group width="1" alignment="left" spacing="5" id="pRF-iD-Gt4" userLabel="FileGroup">
<items>
<imageView width="26" height="26" alignment="left" verticalAlignment="center" hidden="YES" image="Location" contentMode="center" id="uxs-Nx-we9" userLabel="VenueIcon">
<color key="tintColor" red="0.35566622019999999" green="0.68838506939999999" blue="0.91561108830000004" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</imageView>
<group width="26" height="26" alignment="left" verticalAlignment="center" hidden="YES" radius="13" spacing="0.0" id="Y04-zP-Wh2" userLabel="AudioGroup">
<items>
<imageView width="26" height="26" alignment="left" image="MediaAudioPlay" contentMode="center" id="pcd-Ly-8eO"/>
</items>
<color key="backgroundColor" red="0.35566622019999999" green="0.68838506939999999" blue="0.91561108830000004" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<edgeInsets key="margins" left="0.0" right="0.0" top="0.0" bottom="0.0"/>
</group>
<group width="26" height="26" alignment="left" verticalAlignment="center" radius="0.0" id="vs3-R3-hff" userLabel="FileIconGroup">
<items>
<imageView alignment="center" verticalAlignment="center" image="File.png" contentMode="center" id="XqZ-BE-Abt">
<color key="tintColor" red="0.14697439970000001" green="0.5607914329" blue="0.88162887099999998" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</imageView>
</items>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
</group>
<group alignment="left" verticalAlignment="center" layout="vertical" spacing="0.0" id="7Mg-3H-okj" userLabel="FileMetaGroup">
<items>
<label alignment="left" text="File Name" id="xUk-Hc-qsr">
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<fontDescription key="font" type="system" weight="medium" pointSize="12"/>
</label>
<label alignment="left" text="Size" id="d5k-bL-6BP">
<color key="textColor" red="0.41865724329999998" green="0.41825520989999998" blue="0.4306421876" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<fontDescription key="font" type="system" pointSize="12"/>
</label>
</items>
<edgeInsets key="margins" left="0.0" right="0.0" top="0.0" bottom="0.0"/>
</group>
</items>
<edgeInsets key="margins" left="6.5" right="6.5" top="2" bottom="1"/>
</group>
<group width="1" alignment="left" spacing="0.0" id="zqd-Tm-ZRg" userLabel="StickerWrapper">
<items>
<group width="84" height="84" alignment="left" contentMode="scaleAspectFit" id="CH0-jD-uni" userLabel="StickerGroup">
<variation key="device=watch38mm" height="72" width="72"/>
<variation key="device=watch40mm" height="88" width="88"/>
<variation key="device=watch44mm" height="100" width="100"/>
</group>
</items>
<edgeInsets key="margins" left="6.5" right="0.0" top="0.0" bottom="0.0"/>
</group>
<group width="1" alignment="left" radius="12" id="hTA-bS-Jf1" userLabel="MediaGroup">
<items>
<group alignment="right" verticalAlignment="bottom" radius="10" id="eEx-yh-cyr" userLabel="DurationGroup">
<items>
<label alignment="left" text="2:34" id="RiW-Br-zCj" userLabel="Duration">
<color key="textColor" red="0.2461894453" green="0.24618205430000001" blue="0.2461862564" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<fontDescription key="font" type="system" pointSize="12"/>
</label>
</items>
<color key="backgroundColor" red="0.89292949440000002" green="0.91148859260000004" blue="0.93112039570000005" alpha="0.80000000000000004" colorSpace="custom" customColorSpace="sRGB"/>
<edgeInsets key="margins" left="6" right="6" top="2" bottom="2"/>
</group>
</items>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.089999999999999997" colorSpace="custom" customColorSpace="sRGB"/>
<edgeInsets key="margins" left="4" right="4" top="4" bottom="4"/>
</group>
<group width="1" alignment="left" id="0B5-0H-Py3" userLabel="CaptionGroup">
<items>
<label alignment="left" text="Caption" numberOfLines="0" id="isV-kh-Mdr">
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<fontDescription key="font" type="system" pointSize="16"/>
</label>
</items>
<edgeInsets key="margins" left="8" right="8" top="4" bottom="4"/>
</group>
</items>
<edgeInsets key="margins" left="1.5" right="1.5" top="0.0" bottom="0.0"/>
</group>
</items>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<edgeInsets key="margins" left="0.0" right="0.0" top="10" bottom="5"/>
</group>
</items>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<edgeInsets key="margins" left="0.0" right="0.0" top="0.0" bottom="4"/>
<connections>
<outlet property="audioGroup" destination="Y04-zP-Wh2" id="qTp-zF-Epj"/>
<outlet property="captionGroup" destination="0B5-0H-Py3" id="jwq-FC-JCS"/>
<outlet property="captionLabel" destination="isV-kh-Mdr" id="egK-Dy-gRD"/>
<outlet property="chatTitleLabel" destination="HtY-WB-aFI" id="sSF-mw-4JJ"/>
<outlet property="durationGroup" destination="eEx-yh-cyr" id="8Qk-ml-oO8"/>
<outlet property="durationLabel" destination="RiW-Br-zCj" id="19H-az-0O6"/>
<outlet property="fileGroup" destination="pRF-iD-Gt4" id="I5Q-Fx-UjF"/>
<outlet property="fileIconGroup" destination="vs3-R3-hff" id="MGI-5h-Lv8"/>
<outlet property="forwardFromLabel" destination="B2R-Qa-MeP" id="kPS-wB-hNR"/>
<outlet property="forwardHeaderGroup" destination="Kzq-HH-8Ev" id="bhU-ZI-H0w"/>
<outlet property="forwardTitleLabel" destination="NTL-lz-9dd" id="CCG-3i-IZ9"/>
<outlet property="map" destination="k4c-6T-xVa" id="2aI-Tb-Oqf"/>
<outlet property="mapGroup" destination="vel-8f-OFm" id="mDw-hR-bxF"/>
<outlet property="mediaGroup" destination="hTA-bS-Jf1" id="nQw-W2-W4B"/>
<outlet property="messageTextLabel" destination="wxg-r7-aSG" id="yS9-nx-01R"/>
<outlet property="nameLabel" destination="Hqd-Pr-2zp" id="ija-IM-5QH"/>
<outlet property="replyAuthorNameLabel" destination="g6r-Nm-SOQ" id="dK1-3l-hWN"/>
<outlet property="replyHeaderGroup" destination="KRV-O6-45y" id="RZE-Vt-aui"/>
<outlet property="replyHeaderImageGroup" destination="swd-Nx-MDo" id="aYu-g3-upQ"/>
<outlet property="replyMessageTextLabel" destination="dAz-x8-jbh" id="6Cn-zA-Kcs"/>
<outlet property="stickerGroup" destination="CH0-jD-uni" id="CcD-rg-vXX"/>
<outlet property="stickerWrapperGroup" destination="zqd-Tm-ZRg" id="kps-Rx-vsc"/>
<outlet property="subtitleLabel" destination="d5k-bL-6BP" id="7va-0d-xfX"/>
<outlet property="titleLabel" destination="xUk-Hc-qsr" id="ck7-ks-w9e"/>
<outlet property="venueIcon" destination="uxs-Nx-we9" id="ab7-gA-XrO"/>
<outlet property="wrapperGroup" destination="0ch-zY-dJX" id="YqT-J5-azh"/>
</connections>
</controller>
</objects>
<point key="canvasLocation" x="503" y="1283"/>
</scene>
</scenes>
<inferredMetricsTieBreakers>
<segue reference="UyA-Ra-UVy"/>
<segue reference="wDy-DD-bRl"/>
</inferredMetricsTieBreakers>
<color key="tintColor" red="0.15550534427165985" green="0.57037848234176636" blue="0.8720671534538269" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<resources>
<image name="BotCommandIcon" width="128" height="128"/>
<image name="BubbleNotification" width="128" height="128"/>
<image name="File.png" width="128" height="128"/>
<image name="Location" width="128" height="128"/>
<image name="LocationIcon" width="128" height="128"/>
<image name="LoginIcon" width="128" height="128"/>
<image name="MediaAudioPlay" width="128" height="128"/>

View File

@ -10,15 +10,15 @@ public func transformedWithFitzModifier(data: Data, fitzModifier: EmojiFitzModif
let replacementColors: [UIColor]
switch fitzModifier {
case .type12:
replacementColors = [0xca907a, 0xedc5a5, 0xf7e3c3, 0xfbefd6].map { UIColor(rgb: $0) }
replacementColors = [0xcb7b55, 0xf6b689, 0xffcda7, 0xffdfc5].map { UIColor(rgb: $0) }
case .type3:
replacementColors = [0xaa7c60, 0xc8a987, 0xddc89f, 0xe6d6b2].map { UIColor(rgb: $0) }
replacementColors = [0xa45a38, 0xdf986b, 0xedb183, 0xf4c3a0].map { UIColor(rgb: $0) }
case .type4:
replacementColors = [0x8c6148, 0xad8562, 0xc49e76, 0xd4b188].map { UIColor(rgb: $0) }
replacementColors = [0x703a17, 0xab673d, 0xc37f4e, 0xd89667].map { UIColor(rgb: $0) }
case .type5:
replacementColors = [0x6e3c2c, 0x925a34, 0xa16e46, 0xac7a52].map { UIColor(rgb: $0) }
replacementColors = [0x4a2409, 0x7d3e0e, 0x965529, 0xa96337].map { UIColor(rgb: $0) }
case .type6:
replacementColors = [0x291c12, 0x472a22, 0x573b30, 0x68493c].map { UIColor(rgb: $0) }
replacementColors = [0x200f0a, 0x412924, 0x593d37, 0x63453f].map { UIColor(rgb: $0) }
}
func colorToString(_ color: UIColor) -> String {

View File

@ -835,7 +835,6 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
items.append(ActionSheetButtonItem(title: globalTitle, color: .destructive, action: { [weak actionSheet] in
actionSheet?.dismissAnimated()
if let strongSelf = self {
// strongSelf.headerNode.navigationButtonContainer.performAction?(.selectionDone)
let _ = deleteMessagesInteractively(account: strongSelf.context.account, messageIds: Array(messageIds), type: .forEveryone).start()
strongSelf.updateState { state in
@ -859,7 +858,6 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
items.append(ActionSheetButtonItem(title: localOptionText, color: .destructive, action: { [weak actionSheet] in
actionSheet?.dismissAnimated()
if let strongSelf = self {
// strongSelf.headerNode.navigationButtonContainer.performAction?(.selectionDone)
let _ = deleteMessagesInteractively(account: strongSelf.context.account, messageIds: Array(messageIds), type: .forLocalPeer).start()
strongSelf.updateState { state in

View File

@ -307,23 +307,6 @@ public final class DatePickerNode: ASDisplayNode {
public var valueUpdated: ((Date) -> Void)?
public var maximumDate: Date {
get {
return self.state.maxDate
}
set {
guard newValue != self.maximumDate else {
return
}
let updatedState = State(minDate: self.state.minDate, maxDate: newValue, date: self.state.date, displayingMonthSelection: self.state.displayingMonthSelection, selectedMonth: self.state.selectedMonth)
self.updateState(updatedState, animated: false)
if let size = self.validLayout {
let _ = self.updateLayout(size: size, transition: .immediate)
}
}
}
public var minimumDate: Date {
get {
return self.state.minDate
@ -336,11 +319,34 @@ public final class DatePickerNode: ASDisplayNode {
let updatedState = State(minDate: newValue, maxDate: self.state.maxDate, date: self.state.date, displayingMonthSelection: self.state.displayingMonthSelection, selectedMonth: self.state.selectedMonth)
self.updateState(updatedState, animated: false)
self.pickerNode.minimumDate = newValue
if let size = self.validLayout {
let _ = self.updateLayout(size: size, transition: .immediate)
}
}
}
public var maximumDate: Date {
get {
return self.state.maxDate
}
set {
guard newValue != self.maximumDate else {
return
}
let updatedState = State(minDate: self.state.minDate, maxDate: newValue, date: self.state.date, displayingMonthSelection: self.state.displayingMonthSelection, selectedMonth: self.state.selectedMonth)
self.updateState(updatedState, animated: false)
self.pickerNode.maximumDate = newValue
if let size = self.validLayout {
let _ = self.updateLayout(size: size, transition: .immediate)
}
}
}
public var date: Date {
get {
return self.state.date
@ -385,6 +391,8 @@ public final class DatePickerNode: ASDisplayNode {
self.pickerNode = MonthPickerNode(theme: theme, strings: strings, date: self.state.date, yearRange: yearRange(for: self.state), valueChanged: { date in
monthChangedImpl?(date)
})
self.pickerNode.minimumDate = self.state.minDate
self.pickerNode.maximumDate = self.state.maxDate
self.monthButtonNode = HighlightTrackingButtonNode()
self.monthTextNode = ImmediateTextNode()
@ -768,8 +776,8 @@ private final class MonthPickerNode: ASDisplayNode, UIPickerViewDelegate, UIPick
}
}
var minDate: Date?
var maxDate: Date?
var minimumDate: Date?
var maximumDate: Date?
private let valueChanged: (Date) -> Void
private let pickerView: UIPickerView
@ -847,7 +855,26 @@ private final class MonthPickerNode: ASDisplayNode, UIPickerViewDelegate, UIPick
let numberOfDays = calendar.range(of: .day, in: .month, for: tempDate)!.count
components.day = min(day, numberOfDays)
let date = calendar.date(from: components)!
var date = calendar.date(from: components)!
if let minimumDate = self.minimumDate, let maximumDate = self.maximumDate {
var changed = false
if date < minimumDate {
date = minimumDate
changed = true
}
if date > maximumDate {
date = maximumDate
changed = true
}
if changed {
let month = calendar.component(.month, from: date)
let year = calendar.component(.year, from: date)
self.pickerView.selectRow(month - 1, inComponent: 0, animated: true)
self.pickerView.selectRow(year - yearRange.startIndex, inComponent: 1, animated: true)
}
}
self.date = date
self.valueChanged(date)
@ -924,7 +951,7 @@ private final class TimePickerNode: ASDisplayNode {
let minutes = Int32(calendar.component(.hour, from: self.date))
let string = stringForShortTimestamp(hours: hours, minutes: minutes, dateTimeFormat: self.dateTimeFormat).replacingOccurrences(of: " AM", with: "").replacingOccurrences(of: " PM", with: "")
self.textNode.attributedText = NSAttributedString(string: string, font: Font.with(size: 21.0, design: .monospace, weight: .regular, traits: []), textColor: self.theme.textColor)
self.textNode.attributedText = NSAttributedString(string: string, font: Font.with(size: 21.0, design: .regular, weight: .regular, traits: [.monospacedNumbers]), textColor: self.theme.textColor)
let textSize = self.textNode.updateLayout(size)
self.textNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((self.backgroundNode.frame.width - textSize.width) / 2.0), y: floorToScreenPixels((self.backgroundNode.frame.height - textSize.height) / 2.0)), size: textSize)

View File

@ -48,14 +48,12 @@ private struct InviteLinkInviteTransaction {
private enum InviteLinkInviteEntryId: Hashable {
case header
case mainLink
case links(Int32)
case manage
}
private enum InviteLinkInviteEntry: Comparable, Identifiable {
case header(PresentationTheme, String, String)
case mainLink(PresentationTheme, ExportedInvitation)
case links(Int32, PresentationTheme, [ExportedInvitation])
case manage(PresentationTheme, String, Bool)
var stableId: InviteLinkInviteEntryId {
@ -64,8 +62,6 @@ private enum InviteLinkInviteEntry: Comparable, Identifiable {
return .header
case .mainLink:
return .mainLink
case let .links(index, _, _):
return .links(index)
case .manage:
return .manage
}
@ -85,12 +81,6 @@ private enum InviteLinkInviteEntry: Comparable, Identifiable {
} else {
return false
}
case let .links(lhsIndex, lhsTheme, lhsInvitations):
if case let .links(rhsIndex, rhsTheme, rhsInvitations) = rhs, lhsIndex == rhsIndex, lhsTheme === rhsTheme, lhsInvitations == rhsInvitations {
return true
} else {
return false
}
case let .manage(lhsTheme, lhsText, lhsStandalone):
if case let .manage(rhsTheme, rhsText, rhsStandalone) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsStandalone == rhsStandalone {
return true
@ -106,28 +96,19 @@ private enum InviteLinkInviteEntry: Comparable, Identifiable {
switch rhs {
case .header:
return false
case .mainLink, .links, .manage:
case .mainLink, .manage:
return true
}
case .mainLink:
switch rhs {
case .header, .mainLink:
return false
case .links, .manage:
return true
}
case let .links(lhsIndex, _, _):
switch rhs {
case .header, .mainLink:
return false
case let .links(rhsIndex, _, _):
return lhsIndex < rhsIndex
case .manage:
return true
}
case .manage:
switch rhs {
case .header, .mainLink, .links:
case .header, .mainLink:
return false
case .manage:
return true
@ -148,12 +129,6 @@ private enum InviteLinkInviteEntry: Comparable, Identifiable {
interaction.mainLinkContextAction(invite, node, nil)
}, viewAction: {
})
case let .links(_, _, invites):
return ItemListInviteLinkGridItem(presentationData: ItemListPresentationData(presentationData), invites: invites, count: 0, share: true, sectionId: 1, style: .plain, tapAction: { invite in
interaction.copyLink(invite)
}, contextAction: { invite, _ in
interaction.shareLink(invite)
})
case let .manage(theme, text, standalone):
return InviteLinkInviteManageItem(theme: theme, text: text, standalone: standalone, action: {
interaction.manageLinks()
@ -372,8 +347,17 @@ public final class InviteLinkInviteController: ViewController {
f(.dismissWithoutContent)
if let invite = invite {
let controller = InviteLinkQRCodeController(context: context, invite: invite)
self?.controller?.present(controller, in: .window(.root))
let _ = (context.account.postbox.loadedPeerWithId(peerId)
|> deliverOnMainQueue).start(next: { [weak self] peer in
let isGroup: Bool
if let peer = peer as? TelegramChannel, case .broadcast = peer.info {
isGroup = false
} else {
isGroup = true
}
let controller = InviteLinkQRCodeController(context: context, invite: invite, isGroup: isGroup)
self?.controller?.present(controller, in: .window(.root))
})
}
})))
@ -392,10 +376,6 @@ public final class InviteLinkInviteController: ViewController {
ActionSheetButtonItem(title: presentationData.strings.GroupInfo_InviteLink_RevokeLink, color: .destructive, action: {
dismissAction()
// revokePeerExportedInvitation(account: <#T##Account#>, peerId: <#T##PeerId#>, link: <#T##String#>)
// self?.revokeDisposable.set((revokePersistentPeerExportedInvitation(account: context.account, peerId: peerId) |> deliverOnMainQueue).start(completed: {
//
// }))
})
]),
ActionSheetItemGroup(items: [ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, action: { dismissAction() })])
@ -430,7 +410,13 @@ public final class InviteLinkInviteController: ViewController {
if let strongSelf = self {
var entries: [InviteLinkInviteEntry] = []
entries.append(.header(presentationData.theme, presentationData.strings.InviteLink_InviteLink, presentationData.strings.InviteLink_CreatePrivateLinkHelp))
let helpText: String
if let peer = peerViewMainPeer(view) as? TelegramChannel, case .broadcast = peer.info {
helpText = presentationData.strings.InviteLink_CreatePrivateLinkHelpChannel
} else {
helpText = presentationData.strings.InviteLink_CreatePrivateLinkHelp
}
entries.append(.header(presentationData.theme, presentationData.strings.InviteLink_InviteLink, helpText))
let mainInvite: ExportedInvitation?
if let cachedData = view.cachedData as? CachedGroupData, let invite = cachedData.exportedInvitation {
@ -444,19 +430,7 @@ public final class InviteLinkInviteController: ViewController {
entries.append(.mainLink(presentationData.theme, mainInvite))
}
let additionalInvites = invites.invitations.filter { $0.link != mainInvite?.link }
var index: Int32 = 0
for i in stride(from: 0, to: additionalInvites.endIndex, by: 2) {
var invitesPair: [ExportedInvitation] = []
invitesPair.append(additionalInvites[i])
if i + 1 < additionalInvites.count {
invitesPair.append(additionalInvites[i + 1])
}
entries.append(.links(index, presentationData.theme, invitesPair))
index += 1
}
entries.append(.manage(presentationData.theme, presentationData.strings.InviteLink_Manage, additionalInvites.isEmpty))
entries.append(.manage(presentationData.theme, presentationData.strings.InviteLink_Manage, true))
let previousEntries = previousEntries.swap(entries)

View File

@ -70,7 +70,7 @@ private enum InviteLinksListEntry: ItemListNodeEntry {
case revokedLinksHeader(PresentationTheme, String)
case revokedLinksDeleteAll(PresentationTheme, String)
case revokedLinks(Int32, PresentationTheme, ExportedInvitation?)
case revokedLink(Int32, PresentationTheme, ExportedInvitation?)
case adminsHeader(PresentationTheme, String)
case admin(Int32, PresentationTheme, ExportedInvitationCreator)
@ -83,7 +83,7 @@ private enum InviteLinksListEntry: ItemListNodeEntry {
return InviteLinksListSection.mainLink.rawValue
case .linksHeader, .linksCreate, .link, .linksInfo:
return InviteLinksListSection.links.rawValue
case .revokedLinksHeader, .revokedLinksDeleteAll, .revokedLinks:
case .revokedLinksHeader, .revokedLinksDeleteAll, .revokedLink:
return InviteLinksListSection.revokedLinks.rawValue
case .adminsHeader, .admin:
return InviteLinksListSection.admins.rawValue
@ -112,7 +112,7 @@ private enum InviteLinksListEntry: ItemListNodeEntry {
return 10001
case .revokedLinksDeleteAll:
return 10002
case let .revokedLinks(index, _, _):
case let .revokedLink(index, _, _):
return 10003 + index
case .adminsHeader:
return 20001
@ -183,8 +183,8 @@ private enum InviteLinksListEntry: ItemListNodeEntry {
} else {
return false
}
case let .revokedLinks(lhsIndex, lhsTheme, lhsLink):
if case let .revokedLinks(rhsIndex, rhsTheme, rhsLink) = rhs, lhsIndex == rhsIndex, lhsTheme === rhsTheme, lhsLink == rhsLink {
case let .revokedLink(lhsIndex, lhsTheme, lhsLink):
if case let .revokedLink(rhsIndex, rhsTheme, rhsLink) = rhs, lhsIndex == rhsIndex, lhsTheme === rhsTheme, lhsLink == rhsLink {
return true
} else {
return false
@ -253,7 +253,7 @@ private enum InviteLinksListEntry: ItemListNodeEntry {
return ItemListPeerActionItem(presentationData: presentationData, icon: PresentationResourcesItemList.deleteIconImage(theme), title: text, sectionId: self.section, color: .destructive, editing: false, action: {
arguments.deleteAllRevokedLinks()
})
case let .revokedLinks(_, _, invite):
case let .revokedLink(_, _, invite):
return ItemListInviteLinkItem(presentationData: presentationData, invite: invite, share: false, sectionId: self.section, style: .blocks) { invite in
arguments.openLink(invite)
} contextAction: { invite, node, gesture in
@ -273,7 +273,13 @@ private func inviteLinkListControllerEntries(presentationData: PresentationData,
var entries: [InviteLinksListEntry] = []
if admin == nil {
entries.append(.header(presentationData.theme, presentationData.strings.InviteLink_CreatePrivateLinkHelp))
let helpText: String
if let peer = peerViewMainPeer(view) as? TelegramChannel, case .broadcast = peer.info {
helpText = presentationData.strings.InviteLink_CreatePrivateLinkHelpChannel
} else {
helpText = presentationData.strings.InviteLink_CreatePrivateLinkHelp
}
entries.append(.header(presentationData.theme, helpText))
}
let mainInvite: ExportedInvitation?
@ -308,21 +314,32 @@ private func inviteLinkListControllerEntries(presentationData: PresentationData,
entries.append(.mainLinkOtherInfo(presentationData.theme, string.0))
}
entries.append(.linksHeader(presentationData.theme, presentationData.strings.InviteLink_AdditionalLinks.uppercased()))
if admin == nil {
entries.append(.linksCreate(presentationData.theme, presentationData.strings.InviteLink_Create))
}
var additionalInvites: [ExportedInvitation]?
if let invites = invites {
additionalInvites = invites.filter { $0.link != mainInvite?.link }
}
var hasLinks = false
if let additionalInvites = additionalInvites, !additionalInvites.isEmpty {
hasLinks = true
} else if let admin = admin, admin.count > 1 {
hasLinks = true
}
if hasLinks {
entries.append(.linksHeader(presentationData.theme, presentationData.strings.InviteLink_AdditionalLinks.uppercased()))
}
if admin == nil {
entries.append(.linksCreate(presentationData.theme, presentationData.strings.InviteLink_Create))
}
if let additionalInvites = additionalInvites {
var index: Int32 = 0
for invite in additionalInvites {
entries.append(.link(index, presentationData.theme, invite, invite.expireDate != nil ? tick : nil))
index += 1
}
} else if let admin = admin, admin.count > 0 {
} else if let admin = admin, admin.count > 1 {
var index: Int32 = 0
for _ in 0 ..< admin.count - 1 {
entries.append(.link(index, presentationData.theme, nil, nil))
@ -340,7 +357,13 @@ private func inviteLinkListControllerEntries(presentationData: PresentationData,
}
var index: Int32 = 0
for invite in revokedInvites {
entries.append(.revokedLinks(index, presentationData.theme, invite))
entries.append(.revokedLink(index, presentationData.theme, invite))
index += 1
}
} else if let admin = admin, admin.revokedCount > 0 {
var index: Int32 = 0
for _ in 0 ..< admin.revokedCount {
entries.append(.revokedLink(index, presentationData.theme, nil))
index += 1
}
}
@ -387,7 +410,7 @@ public func inviteLinkListController(context: AccountContext, peerId: PeerId, ad
var getControllerImpl: (() -> ViewController?)?
let adminId = admin?.peer.peer?.id
let invitesContext = PeerExportedInvitationsContext(account: context.account, peerId: peerId, adminId: adminId, revoked: false, forceUpdate: false)
let invitesContext = PeerExportedInvitationsContext(account: context.account, peerId: peerId, adminId: adminId, revoked: false, forceUpdate: true)
let revokedInvitesContext = PeerExportedInvitationsContext(account: context.account, peerId: peerId, adminId: adminId, revoked: true, forceUpdate: true)
let creators: Signal<[ExportedInvitationCreator], NoError>
@ -435,8 +458,17 @@ public func inviteLinkListController(context: AccountContext, peerId: PeerId, ad
}, action: { _, f in
f(.dismissWithoutContent)
let controller = InviteLinkQRCodeController(context: context, invite: invite)
presentControllerImpl?(controller, nil)
let _ = (context.account.postbox.loadedPeerWithId(peerId)
|> deliverOnMainQueue).start(next: { peer in
let isGroup: Bool
if let peer = peer as? TelegramChannel, case .broadcast = peer.info {
isGroup = false
} else {
isGroup = true
}
let controller = InviteLinkQRCodeController(context: context, invite: invite, isGroup: isGroup)
presentControllerImpl?(controller, nil)
})
})))
if invite.adminId.toInt64() != 0 {
@ -545,8 +577,17 @@ public func inviteLinkListController(context: AccountContext, peerId: PeerId, ad
}, action: { _, f in
f(.default)
let controller = InviteLinkQRCodeController(context: context, invite: invite)
presentControllerImpl?(controller, nil)
let _ = (context.account.postbox.loadedPeerWithId(peerId)
|> deliverOnMainQueue).start(next: { peer in
let isGroup: Bool
if let peer = peer as? TelegramChannel, case .broadcast = peer.info {
isGroup = false
} else {
isGroup = true
}
let controller = InviteLinkQRCodeController(context: context, invite: invite, isGroup: isGroup)
presentControllerImpl?(controller, nil)
})
})))
items.append(.action(ContextMenuActionItem(text: presentationData.strings.InviteLink_ContextEdit, icon: { theme in

View File

@ -37,14 +37,16 @@ public final class InviteLinkQRCodeController: ViewController {
private let context: AccountContext
private let invite: ExportedInvitation
private let isGroup: Bool
private var presentationDataDisposable: Disposable?
private let idleTimerExtensionDisposable = MetaDisposable()
public init(context: AccountContext, invite: ExportedInvitation) {
public init(context: AccountContext, invite: ExportedInvitation, isGroup: Bool) {
self.context = context
self.invite = invite
self.isGroup = isGroup
super.init(navigationBarPresentationData: nil)
@ -74,7 +76,7 @@ public final class InviteLinkQRCodeController: ViewController {
}
override public func loadDisplayNode() {
self.displayNode = Node(context: self.context, invite: self.invite)
self.displayNode = Node(context: self.context, invite: self.invite, isGroup: self.isGroup)
self.controllerNode.dismiss = { [weak self] in
self?.presentingViewController?.dismiss(animated: false, completion: nil)
}
@ -133,7 +135,7 @@ public final class InviteLinkQRCodeController: ViewController {
var dismiss: (() -> Void)?
var cancel: (() -> Void)?
init(context: AccountContext, invite: ExportedInvitation) {
init(context: AccountContext, invite: ExportedInvitation, isGroup: Bool) {
self.context = context
self.invite = invite
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
@ -211,7 +213,7 @@ public final class InviteLinkQRCodeController: ViewController {
let textFont = Font.regular(13.0)
self.textNode.attributedText = NSAttributedString(string: self.presentationData.strings.InviteLink_QRCode_Info, font: textFont, textColor: secondaryTextColor)
self.textNode.attributedText = NSAttributedString(string: isGroup ? self.presentationData.strings.InviteLink_QRCode_Info : self.presentationData.strings.InviteLink_QRCode_InfoChannel, font: textFont, textColor: secondaryTextColor)
self.buttonNode.title = self.presentationData.strings.InviteLink_QRCode_Share
self.cancelButton.addTarget(self, action: #selector(self.cancelButtonPressed), forControlEvents: .touchUpInside)

View File

@ -343,7 +343,6 @@ public final class InviteLinkViewController: ViewController {
private var presentationDataDisposable: Disposable?
private var disposable: Disposable?
private let actionDisposable = MetaDisposable()
private let dimNode: ASDisplayNode
private let contentNode: ASDisplayNode
@ -473,9 +472,10 @@ public final class InviteLinkViewController: ViewController {
ActionSheetTextItem(title: presentationData.strings.InviteLink_DeleteLinkAlert_Text),
ActionSheetButtonItem(title: presentationData.strings.InviteLink_DeleteLinkAlert_Action, color: .destructive, action: {
dismissAction()
self?.actionDisposable.set((deletePeerExportedInvitation(account: context.account, peerId: peerId, link: invite.link) |> deliverOnMainQueue).start(completed: {
}))
self?.controller?.dismiss()
let _ = (deletePeerExportedInvitation(account: context.account, peerId: peerId, link: invite.link) |> deliverOnMainQueue).start(completed: {
})
self?.controller?.revokedInvitationsContext?.remove(invite)
})
@ -490,7 +490,42 @@ public final class InviteLinkViewController: ViewController {
}, action: { [weak self] _, f in
f(.dismissWithoutContent)
let controller = InviteLinkQRCodeController(context: context, invite: invite)
let _ = (context.account.postbox.loadedPeerWithId(peerId)
|> deliverOnMainQueue).start(next: { [weak self] peer in
let isGroup: Bool
if let peer = peer as? TelegramChannel, case .broadcast = peer.info {
isGroup = false
} else {
isGroup = true
}
let controller = InviteLinkQRCodeController(context: context, invite: invite, isGroup: isGroup)
self?.controller?.present(controller, in: .window(.root))
})
})))
items.append(.action(ContextMenuActionItem(text: presentationData.strings.InviteLink_ContextRevoke, textColor: .destructive, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.actionSheet.destructiveActionTextColor)
}, action: { [weak self] _, f in
f(.dismissWithoutContent)
let controller = ActionSheetController(presentationData: presentationData)
let dismissAction: () -> Void = { [weak controller] in
controller?.dismissAnimated()
}
controller.setItemGroups([
ActionSheetItemGroup(items: [
ActionSheetTextItem(title: presentationData.strings.GroupInfo_InviteLink_RevokeAlert_Text),
ActionSheetButtonItem(title: presentationData.strings.GroupInfo_InviteLink_RevokeLink, color: .destructive, action: {
dismissAction()
self?.controller?.dismiss()
let _ = (revokePeerExportedInvitation(account: context.account, peerId: peerId, link: invite.link) |> deliverOnMainQueue).start(completed: {
})
self?.controller?.revokedInvitationsContext?.remove(invite)
})
]),
ActionSheetItemGroup(items: [ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, action: { dismissAction() })])
])
self?.controller?.present(controller, in: .window(.root))
})))
}
@ -739,6 +774,7 @@ public final class InviteLinkViewController: ViewController {
subtitleText = self.presentationData.strings.InviteLink_Revoked
} else if let usageLimit = self.invite.usageLimit, let count = self.invite.count, count >= usageLimit {
subtitleText = self.presentationData.strings.InviteLink_UsageLimitReached
subtitleColor = self.presentationData.theme.list.itemDestructiveColor
} else if let expireDate = self.invite.expireDate {
let currentTime = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)
if currentTime >= expireDate {

View File

@ -1,670 +0,0 @@
import Foundation
import UIKit
import Display
import AsyncDisplayKit
import SwiftSignalKit
import SyncCore
import TelegramPresentationData
import ItemListUI
import SolidRoundedButtonNode
import RadialStatusNode
private let itemSpacing: CGFloat = 10.0
private let titleFont = Font.semibold(17.0)
private let subtitleFont = Font.regular(12.0)
private enum ItemBackgroundColor: Equatable {
case blue
case green
case yellow
case red
case gray
var colors: (top: UIColor, bottom: UIColor, text: UIColor) {
switch self {
case .blue:
return (UIColor(rgb: 0x00b5f7), UIColor(rgb: 0x00b2f6), UIColor(rgb: 0xa7f4ff))
case .green:
return (UIColor(rgb: 0x4aca62), UIColor(rgb: 0x43c85c), UIColor(rgb: 0xc5ffe6))
case .yellow:
return (UIColor(rgb: 0xf8a953), UIColor(rgb: 0xf7a64e), UIColor(rgb: 0xfeffd7))
case .red:
return (UIColor(rgb: 0xf2656a), UIColor(rgb: 0xf25f65), UIColor(rgb: 0xffd3de))
case .gray:
return (UIColor(rgb: 0xa8b2bb), UIColor(rgb: 0xa2abb4), UIColor(rgb: 0xe3e6e8))
}
}
}
private let moreIcon = generateImage(CGSize(width: 26.0, height: 26.0), contextGenerator: { size, context in
context.clear(CGRect(origin: CGPoint(), size: size))
context.setFillColor(UIColor.white.cgColor)
context.fillEllipse(in: CGRect(origin: CGPoint(), size: size))
context.setBlendMode(.clear)
context.fillEllipse(in: CGRect(origin: CGPoint(x: 4.0, y: 11.0), size: CGSize(width: 4.0, height: 4.0)))
context.fillEllipse(in: CGRect(origin: CGPoint(x: 11.0, y: 11.0), size: CGSize(width: 4.0, height: 4.0)))
context.fillEllipse(in: CGRect(origin: CGPoint(x: 18.0, y: 11.0), size: CGSize(width: 4.0, height: 4.0)))
})
private let shareIcon = generateImage(CGSize(width: 26.0, height: 26.0), contextGenerator: { size, context in
context.clear(CGRect(origin: CGPoint(), size: size))
context.setFillColor(UIColor.white.cgColor)
context.fillEllipse(in: CGRect(origin: CGPoint(), size: size))
if let maskImage = UIImage(bundleImageName: "Chat/Links/Share") {
context.clip(to: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - maskImage.size.width) / 2.0), y: floorToScreenPixels((size.height - maskImage.size.height) / 2.0)), size: maskImage.size), mask: maskImage.cgImage!)
context.setBlendMode(.clear)
context.fillEllipse(in: CGRect(origin: CGPoint(), size: size))
}
})
private class ItemNode: ASDisplayNode {
private let selectionNode: HighlightTrackingButtonNode
private let wrapperNode: ASDisplayNode
private let backgroundNode: ASDisplayNode
private let backgroundGradientLayer: CAGradientLayer
private let iconNode: ASImageNode
private var timerNode: TimerNode?
private let extractedContainerNode: ContextExtractedContentContainingNode
private let containerNode: ContextControllerSourceNode
private let buttonNode: HighlightTrackingButtonNode
private let buttonIconNode: ASImageNode
private let titleNode: ImmediateTextNode
private let subtitleNode: ImmediateTextNode
private var updateTimer: SwiftSignalKit.Timer?
private var params: (size: CGSize, wide: Bool, invite: ExportedInvitation?, color: ItemBackgroundColor, presentationData: ItemListPresentationData)?
var action: (() -> Void)?
var contextAction: ((ASDisplayNode) -> Void)?
private let hapticFeedback = HapticFeedback()
override init() {
self.selectionNode = HighlightTrackingButtonNode()
self.wrapperNode = ASDisplayNode()
self.backgroundNode = ASDisplayNode()
self.backgroundNode.clipsToBounds = true
self.backgroundNode.cornerRadius = 15.0
if #available(iOS 13.0, *) {
self.backgroundNode.layer.cornerCurve = .continuous
}
self.backgroundNode.isUserInteractionEnabled = false
self.backgroundGradientLayer = CAGradientLayer()
self.backgroundGradientLayer.startPoint = CGPoint(x: 0.5, y: 0.0)
self.backgroundGradientLayer.endPoint = CGPoint(x: 0.5, y: 1.0)
self.backgroundNode.layer.addSublayer(self.backgroundGradientLayer)
self.iconNode = ASImageNode()
self.iconNode.displaysAsynchronously = false
self.iconNode.displayWithoutProcessing = true
self.iconNode.isUserInteractionEnabled = false
self.buttonNode = HighlightTrackingButtonNode()
self.extractedContainerNode = ContextExtractedContentContainingNode()
self.containerNode = ContextControllerSourceNode()
self.containerNode.isGestureEnabled = false
self.buttonIconNode = ASImageNode()
self.buttonIconNode.displaysAsynchronously = false
self.buttonIconNode.displayWithoutProcessing = true
self.titleNode = ImmediateTextNode()
self.titleNode.maximumNumberOfLines = 2
self.titleNode.isUserInteractionEnabled = false
self.subtitleNode = ImmediateTextNode()
self.subtitleNode.maximumNumberOfLines = 1
self.subtitleNode.isUserInteractionEnabled = false
super.init()
self.addSubnode(self.wrapperNode)
self.wrapperNode.addSubnode(self.backgroundNode)
self.wrapperNode.addSubnode(self.iconNode)
self.containerNode.addSubnode(self.extractedContainerNode)
self.extractedContainerNode.contentNode.addSubnode(self.buttonIconNode)
self.containerNode.targetNodeForActivationProgress = self.extractedContainerNode.contentNode
self.buttonNode.addSubnode(self.containerNode)
self.wrapperNode.addSubnode(self.selectionNode)
self.wrapperNode.addSubnode(self.buttonNode)
self.wrapperNode.addSubnode(self.titleNode)
self.wrapperNode.addSubnode(self.subtitleNode)
self.selectionNode.addTarget(self, action: #selector(self.tapped), forControlEvents: .touchUpInside)
self.selectionNode.highligthedChanged = { [weak self] highlighted in
if let strongSelf = self {
if highlighted {
let transition: ContainedViewLayoutTransition = .animated(duration: 0.18, curve: .linear)
transition.updateSublayerTransformScale(node: strongSelf, scale: 0.95)
} else {
let transition: ContainedViewLayoutTransition = .animated(duration: 0.25, curve: .linear)
transition.updateSublayerTransformScale(node: strongSelf, scale: 1.0)
}
}
}
self.buttonNode.addTarget(self, action: #selector(self.buttonPressed), forControlEvents: .touchUpInside)
self.buttonNode.highligthedChanged = { [weak self] highlighted in
if let strongSelf = self {
if highlighted {
strongSelf.buttonIconNode.layer.removeAnimation(forKey: "opacity")
strongSelf.buttonIconNode.alpha = 0.4
} else {
strongSelf.buttonIconNode.alpha = 1.0
strongSelf.buttonIconNode.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2)
}
}
}
}
deinit {
self.updateTimer?.invalidate()
}
@objc private func tapped() {
self.hapticFeedback.impact(.light)
self.action?()
}
@objc private func buttonPressed() {
self.contextAction?(self.extractedContainerNode)
}
func update(size: CGSize, wide: Bool, share: Bool, invite: ExportedInvitation?, presentationData: ItemListPresentationData, transition: ContainedViewLayoutTransition) -> CGSize {
let currentTime = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)
let availability = invite.flatMap { invitationAvailability($0) } ?? 0.0
let transitionFraction: CGFloat
let color: ItemBackgroundColor
let nextColor: ItemBackgroundColor
if let invite = invite {
if invite.isRevoked {
color = .gray
nextColor = .gray
transitionFraction = 0.0
} else if invite.expireDate == nil && invite.usageLimit == nil {
color = .blue
nextColor = .blue
transitionFraction = 0.0
} else if availability >= 0.5 {
color = .green
nextColor = .yellow
transitionFraction = (availability - 0.5) / 0.5
} else if availability > 0.0 {
color = .yellow
nextColor = .red
transitionFraction = availability / 0.5
} else {
color = .red
nextColor = .red
transitionFraction = 0.0
}
} else {
color = .gray
nextColor = .gray
transitionFraction = 0.0
}
let previousParams = self.params
self.params = (size, wide, invite, color, presentationData)
let previousExpireDate = previousParams?.invite?.expireDate
if previousExpireDate != invite?.expireDate {
self.updateTimer?.invalidate()
self.updateTimer = nil
if let expireDate = invite?.expireDate, availability > 0.0 {
let timeout = min(2.0, max(0.001, Double(expireDate - currentTime)))
let updateTimer = SwiftSignalKit.Timer(timeout: timeout, repeat: true, completion: { [weak self] in
if let strongSelf = self {
if let (size, wide, invite, _, presentationData) = strongSelf.params {
let _ = strongSelf.update(size: size, wide: wide, share: share, invite: invite, presentationData: presentationData, transition: .animated(duration: 0.3, curve: .linear))
}
}
}, queue: Queue.mainQueue())
self.updateTimer = updateTimer
updateTimer.start()
}
} else if availability.isZero {
self.updateTimer?.invalidate()
self.updateTimer = nil
}
let topColor = color.colors.top
let bottomColor = color.colors.bottom
let nextTopColor = nextColor.colors.top
let nextBottomColor = nextColor.colors.bottom
let colors: NSArray
if let invite = invite {
colors = [nextTopColor.mixedWith(topColor, alpha: transitionFraction).cgColor, nextBottomColor.mixedWith(bottomColor, alpha: transitionFraction).cgColor]
} else {
colors = [UIColor(rgb: 0xf2f2f7).cgColor, UIColor(rgb: 0xf2f2f7).cgColor]
}
if let (_, _, previousInvite, previousColor, _) = previousParams, previousInvite == invite {
if previousColor != color && color == .red {
if let snapshotView = self.wrapperNode.view.snapshotContentTree() {
snapshotView.frame = self.wrapperNode.bounds
self.wrapperNode.view.addSubview(snapshotView)
snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { [weak snapshotView] _ in
snapshotView?.removeFromSuperview()
})
}
self.backgroundGradientLayer.colors = colors as? [Any]
} else if (color == .green && nextColor == .yellow) || (color == .yellow && nextColor == .red) {
let previousColors = self.backgroundGradientLayer.colors
if transition.isAnimated {
self.backgroundGradientLayer.animate(from: previousColors as AnyObject, to: self.backgroundGradientLayer.colors as AnyObject, keyPath: "colors", timingFunction: CAMediaTimingFunctionName.linear.rawValue, duration: 2.5)
}
self.backgroundGradientLayer.colors = colors as? [Any]
}
} else {
self.backgroundGradientLayer.colors = colors as? [Any]
}
let secondaryTextColor = nextColor.colors.text.mixedWith(color.colors.text, alpha: transitionFraction)
let itemWidth = wide ? size.width : floor((size.width - itemSpacing) / 2.0)
var inviteLink = invite?.link.replacingOccurrences(of: "https://", with: "") ?? ""
if !wide {
inviteLink = inviteLink.replacingOccurrences(of: "joinchat/", with: "joinchat/\n")
inviteLink = inviteLink.replacingOccurrences(of: "join/", with: "join/\n")
}
let title: NSMutableAttributedString = NSMutableAttributedString(string: inviteLink, font: titleFont, textColor: UIColor.white)
if inviteLink.hasPrefix("t.me/joinchat/") {
title.addAttribute(NSAttributedString.Key.foregroundColor, value: secondaryTextColor, range: NSMakeRange(0, "t.me/joinchat/".count))
} else if inviteLink.hasPrefix("t.me/join/") {
title.addAttribute(NSAttributedString.Key.foregroundColor, value: secondaryTextColor, range: NSMakeRange(0, "t.me/join/".count))
}
self.titleNode.attributedText = title
self.buttonIconNode.image = share ? shareIcon : moreIcon
var subtitleText: String = ""
if let invite = invite {
if let count = invite.count {
subtitleText = presentationData.strings.InviteLink_PeopleJoinedShort(count)
} else {
subtitleText = [.red, .gray].contains(color) ? presentationData.strings.InviteLink_PeopleJoinedShortNoneExpired : presentationData.strings.InviteLink_PeopleJoinedShortNone
}
if invite.isRevoked {
if !subtitleText.isEmpty {
subtitleText += ""
}
subtitleText += presentationData.strings.InviteLink_Revoked
self.iconNode.image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Links/Expired"), color: .white)
self.timerNode?.removeFromSupernode()
self.timerNode = nil
} else if let expireDate = invite.expireDate, currentTime >= expireDate {
if !subtitleText.isEmpty {
subtitleText += ""
}
if share {
subtitleText = presentationData.strings.InviteLink_Expired
} else {
subtitleText += presentationData.strings.InviteLink_Expired
}
self.iconNode.image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Links/Expired"), color: .white)
self.timerNode?.removeFromSupernode()
self.timerNode = nil
} else if let usageLimit = invite.usageLimit, let count = invite.count, count >= usageLimit {
if !subtitleText.isEmpty {
subtitleText += ""
}
if share {
subtitleText = presentationData.strings.InviteLink_UsageLimitReached
} else {
subtitleText += presentationData.strings.InviteLink_UsageLimitReached
}
self.iconNode.image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Links/Expired"), color: .white)
self.timerNode?.removeFromSupernode()
self.timerNode = nil
} else if let expireDate = invite.expireDate {
self.iconNode.image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Links/Flame"), color: .white)
let timerNode: TimerNode
if let current = self.timerNode {
timerNode = current
} else {
timerNode = TimerNode()
timerNode.isUserInteractionEnabled = false
self.timerNode = timerNode
self.addSubnode(timerNode)
}
timerNode.update(color: UIColor.white, creationTimestamp: invite.startDate ?? invite.date, deadlineTimestamp: expireDate)
if share {
subtitleText = presentationData.strings.InviteLink_TapToCopy
}
} else {
self.iconNode.image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Links/Link"), color: .white)
self.timerNode?.removeFromSupernode()
self.timerNode = nil
if share {
subtitleText = presentationData.strings.InviteLink_TapToCopy
}
}
self.iconNode.isHidden = false
self.buttonIconNode.isHidden = false
} else {
self.iconNode.isHidden = true
self.buttonIconNode.isHidden = true
}
self.iconNode.frame = CGRect(x: 10.0, y: 10.0, width: 30.0, height: 30.0)
self.timerNode?.frame = CGRect(x: 8.0, y: 8.0, width: 34.0, height: 34.0)
self.subtitleNode.attributedText = NSAttributedString(string: subtitleText, font: subtitleFont, textColor: secondaryTextColor)
let titleSize = self.titleNode.updateLayout(CGSize(width: itemWidth - 24.0, height: 100.0))
let subtitleSize = self.subtitleNode.updateLayout(CGSize(width: itemWidth - 24.0, height: 100.0))
self.titleNode.frame = CGRect(origin: CGPoint(x: 12.0, y: 52.0), size: titleSize)
self.subtitleNode.frame = CGRect(origin: CGPoint(x: 12.0, y: 52.0 + titleSize.height + 3.0), size: subtitleSize)
let itemSize = CGSize(width: itemWidth, height: wide ? 102.0 : 122.0)
let backgroundFrame = CGRect(origin: CGPoint(), size: itemSize)
transition.updateFrame(node: self.wrapperNode, frame: backgroundFrame)
transition.updateFrame(node: self.backgroundNode, frame: backgroundFrame)
transition.updateFrame(node: self.selectionNode, frame: backgroundFrame)
transition.updateFrame(layer: self.backgroundGradientLayer, frame: backgroundFrame)
let buttonSize = CGSize(width: 26.0, height: 26.0)
let buttonFrame = CGRect(origin: CGPoint(x: itemSize.width - buttonSize.width - 12.0, y: 12.0), size: buttonSize)
transition.updateFrame(node: self.buttonNode, frame: buttonFrame)
self.extractedContainerNode.frame = CGRect(origin: CGPoint(), size: buttonSize)
self.extractedContainerNode.contentRect = CGRect(origin: CGPoint(), size: buttonSize)
self.buttonIconNode.frame = CGRect(origin: CGPoint(), size: buttonSize)
return itemSize
}
}
class InviteLinksGridNode: ASDisplayNode {
private var items: [ExportedInvitation]?
private var itemNodes: [String: ItemNode] = [:]
var action: ((ExportedInvitation) -> Void)?
var contextAction: ((ASDisplayNode, ExportedInvitation) -> Void)?
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
let result = super.hitTest(point, with: event)
return result
}
func update(size: CGSize, safeInset: CGFloat, items: [ExportedInvitation]?, count: Int, share: Bool, presentationData: ItemListPresentationData, transition: ContainedViewLayoutTransition) -> CGSize {
self.items = items
var contentSize: CGSize = size
var contentHeight: CGFloat = 0.0
let sideInset: CGFloat = 16.0 + safeInset
var validIds = Set<String>()
let count = items?.count ?? count
for i in 0 ..< count {
let invite: ExportedInvitation?
let id: String
if let items = items, i < items.count {
invite = items[i]
id = invite!.link
} else {
invite = nil
id = "placeholder_\(i)"
}
validIds.insert(id)
var itemNode: ItemNode?
var wasAdded = false
if let current = self.itemNodes[id] {
itemNode = current
} else {
wasAdded = true
let addedItemNode = ItemNode()
itemNode = addedItemNode
self.itemNodes[id] = addedItemNode
self.addSubnode(addedItemNode)
}
if let itemNode = itemNode {
let col = CGFloat(i % 2)
let row = floor(CGFloat(i) / 2.0)
let wide = (i == count - 1 && (count % 2) != 0)
let itemSize = itemNode.update(size: CGSize(width: size.width - sideInset * 2.0, height: size.height), wide: wide, share: share, invite: invite, presentationData: presentationData, transition: transition)
var itemFrame = CGRect(origin: CGPoint(x: sideInset, y: 4.0 + row * (122.0 + itemSpacing)), size: itemSize)
if !wide && col > 0 {
itemFrame.origin.x += itemSpacing + itemSize.width
}
contentHeight = max(contentHeight, itemFrame.maxY + itemSpacing)
if wasAdded {
itemNode.frame = itemFrame
} else {
transition.updateFrame(node: itemNode, frame: itemFrame)
}
itemNode.action = { [weak self] in
if let invite = invite {
self?.action?(invite)
}
}
itemNode.contextAction = { [weak self] node in
if let invite = invite {
self?.contextAction?(node, invite)
}
}
}
}
var removeIds: [String] = []
for (id, _) in self.itemNodes {
if !validIds.contains(id) {
removeIds.append(id)
}
}
for id in removeIds {
if let itemNode = self.itemNodes.removeValue(forKey: id) {
itemNode.removeFromSupernode()
}
}
contentSize.height = contentHeight
return contentSize
}
}
private struct ContentParticle {
var position: CGPoint
var direction: CGPoint
var velocity: CGFloat
var alpha: CGFloat
var lifetime: Double
var beginTime: Double
init(position: CGPoint, direction: CGPoint, velocity: CGFloat, alpha: CGFloat, lifetime: Double, beginTime: Double) {
self.position = position
self.direction = direction
self.velocity = velocity
self.alpha = alpha
self.lifetime = lifetime
self.beginTime = beginTime
}
}
private final class TimerNode: ASDisplayNode {
private struct Params: Equatable {
var color: UIColor
var creationTimestamp: Int32
var deadlineTimestamp: Int32
}
private let hierarchyTrackingNode: HierarchyTrackingNode
private var inHierarchyValue: Bool = false
private var animator: ConstantDisplayLinkAnimator?
private let contentNode: ASDisplayNode
private var particles: [ContentParticle] = []
private var currentParams: Params?
var reachedTimeout: (() -> Void)?
override init() {
var updateInHierarchy: ((Bool) -> Void)?
self.hierarchyTrackingNode = HierarchyTrackingNode({ value in
updateInHierarchy?(value)
})
self.contentNode = ASDisplayNode()
super.init()
self.addSubnode(self.contentNode)
updateInHierarchy = { [weak self] value in
guard let strongSelf = self else {
return
}
strongSelf.inHierarchyValue = value
strongSelf.animator?.isPaused = value
}
}
deinit {
self.animator?.invalidate()
}
func update(color: UIColor, creationTimestamp: Int32, deadlineTimestamp: Int32) {
let params = Params(
color: color,
creationTimestamp: creationTimestamp,
deadlineTimestamp: deadlineTimestamp
)
self.currentParams = params
self.updateValues()
}
private func updateValues() {
guard let params = self.currentParams else {
return
}
let color = params.color
let currentTimestamp = Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970)
var fraction = CGFloat(params.deadlineTimestamp - currentTimestamp) / CGFloat(params.deadlineTimestamp - params.creationTimestamp)
fraction = max(0.0001, 1.0 - max(0.0, min(1.0, fraction)))
let image: UIImage?
let diameter: CGFloat = 26.0
let inset: CGFloat = 8.0
let lineWidth: CGFloat = 2.0
let timestamp = CACurrentMediaTime()
let center = CGPoint(x: (diameter + inset) / 2.0, y: (diameter + inset) / 2.0)
let radius: CGFloat = (diameter - lineWidth / 2.0) / 2.0
let startAngle: CGFloat = -CGFloat.pi / 2.0
let endAngle: CGFloat = -CGFloat.pi / 2.0 + 2.0 * CGFloat.pi * fraction
let sparks = fraction > 0.1 && fraction != 1.0
if sparks {
let v = CGPoint(x: sin(endAngle), y: -cos(endAngle))
let c = CGPoint(x: -v.y * radius + center.x, y: v.x * radius + center.y)
let dt: CGFloat = 1.0 / 60.0
var removeIndices: [Int] = []
for i in 0 ..< self.particles.count {
let currentTime = timestamp - self.particles[i].beginTime
if currentTime > self.particles[i].lifetime {
removeIndices.append(i)
} else {
let input: CGFloat = CGFloat(currentTime / self.particles[i].lifetime)
let decelerated: CGFloat = (1.0 - (1.0 - input) * (1.0 - input))
self.particles[i].alpha = 1.0 - decelerated
var p = self.particles[i].position
let d = self.particles[i].direction
let v = self.particles[i].velocity
p = CGPoint(x: p.x + d.x * v * dt, y: p.y + d.y * v * dt)
self.particles[i].position = p
}
}
for i in removeIndices.reversed() {
self.particles.remove(at: i)
}
let newParticleCount = 1
for _ in 0 ..< newParticleCount {
let degrees: CGFloat = CGFloat(arc4random_uniform(140)) - 40.0
let angle: CGFloat = degrees * CGFloat.pi / 180.0
let direction = CGPoint(x: v.x * cos(angle) - v.y * sin(angle), y: v.x * sin(angle) + v.y * cos(angle))
let velocity = (20.0 + (CGFloat(arc4random()) / CGFloat(UINT32_MAX)) * 4.0) * 0.3
let lifetime = Double(0.4 + CGFloat(arc4random_uniform(100)) * 0.01)
let particle = ContentParticle(position: c, direction: direction, velocity: velocity, alpha: 1.0, lifetime: lifetime, beginTime: timestamp)
self.particles.append(particle)
}
}
image = generateImage(CGSize(width: diameter + inset, height: diameter + inset), rotatedContext: { size, context in
context.clear(CGRect(origin: CGPoint(), size: size))
context.setStrokeColor(color.cgColor)
context.setFillColor(color.cgColor)
context.setLineWidth(lineWidth)
context.setLineCap(.round)
let path = CGMutablePath()
path.addArc(center: center, radius: radius, startAngle: startAngle, endAngle: endAngle, clockwise: true)
context.addPath(path)
context.strokePath()
if sparks {
for particle in self.particles {
let size: CGFloat = 2.0
context.setAlpha(particle.alpha)
context.fillEllipse(in: CGRect(origin: CGPoint(x: particle.position.x - size / 2.0, y: particle.position.y - size / 2.0), size: CGSize(width: size, height: size)))
}
}
})
self.contentNode.contents = image?.cgImage
if let image = image {
self.contentNode.frame = CGRect(origin: CGPoint(), size: image.size)
}
if fraction <= .ulpOfOne {
self.animator?.invalidate()
self.animator = nil
} else {
if self.animator == nil {
let animator = ConstantDisplayLinkAnimator(update: { [weak self] in
self?.updateValues()
})
self.animator = animator
animator.isPaused = self.inHierarchyValue
}
}
}
}

View File

@ -1,267 +0,0 @@
import Foundation
import UIKit
import Display
import AsyncDisplayKit
import SwiftSignalKit
import SyncCore
import TelegramPresentationData
import ItemListUI
public class ItemListInviteLinkGridItem: ListViewItem, ItemListItem {
let presentationData: ItemListPresentationData
let invites: [ExportedInvitation]?
let count: Int
let share: Bool
public let sectionId: ItemListSectionId
let style: ItemListStyle
let tapAction: ((ExportedInvitation) -> Void)?
let contextAction: ((ExportedInvitation, ASDisplayNode) -> Void)?
public let tag: ItemListItemTag?
public init(
presentationData: ItemListPresentationData,
invites: [ExportedInvitation]?,
count: Int,
share: Bool,
sectionId: ItemListSectionId,
style: ItemListStyle,
tapAction: ((ExportedInvitation) -> Void)?,
contextAction: ((ExportedInvitation, ASDisplayNode) -> Void)?,
tag: ItemListItemTag? = nil
) {
self.presentationData = presentationData
self.invites = invites
self.count = count
self.share = share
self.sectionId = sectionId
self.style = style
self.tapAction = tapAction
self.contextAction = contextAction
self.tag = tag
}
public func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal<Void, NoError>?, (ListViewItemApply) -> Void)) -> Void) {
async {
let node = ItemListInviteLinkGridItemNode()
let (layout, apply) = node.asyncLayout()(self, params, itemListNeighbors(item: self, topItem: previousItem as? ItemListItem, bottomItem: nextItem as? ItemListItem))
node.contentSize = layout.contentSize
node.insets = layout.insets
Queue.mainQueue().async {
completion(node, {
return (nil, { _ in apply() })
})
}
}
}
public func updateNode(async: @escaping (@escaping () -> Void) -> Void, node: @escaping () -> ListViewItemNode, params: ListViewItemLayoutParams, previousItem: ListViewItem?, nextItem: ListViewItem?, animation: ListViewItemUpdateAnimation, completion: @escaping (ListViewItemNodeLayout, @escaping (ListViewItemApply) -> Void) -> Void) {
Queue.mainQueue().async {
if let nodeValue = node() as? ItemListInviteLinkGridItemNode {
let makeLayout = nodeValue.asyncLayout()
async {
let (layout, apply) = makeLayout(self, params, itemListNeighbors(item: self, topItem: previousItem as? ItemListItem, bottomItem: nextItem as? ItemListItem))
Queue.mainQueue().async {
completion(layout, { _ in
apply()
})
}
}
}
}
}
public var selectable: Bool = false
public func selected(listView: ListView){
}
}
public class ItemListInviteLinkGridItemNode: ListViewItemNode, ItemListItemNode {
private let backgroundNode: ASDisplayNode
private let topStripeNode: ASDisplayNode
private let bottomStripeNode: ASDisplayNode
private let maskNode: ASImageNode
private let gridNode: InviteLinksGridNode
private var item: ItemListInviteLinkGridItem?
override public var canBeSelected: Bool {
return false
}
public var tag: ItemListItemTag? {
return self.item?.tag
}
public init() {
self.backgroundNode = ASDisplayNode()
self.backgroundNode.isLayerBacked = true
self.backgroundNode.backgroundColor = .white
self.maskNode = ASImageNode()
self.topStripeNode = ASDisplayNode()
self.topStripeNode.isLayerBacked = true
self.bottomStripeNode = ASDisplayNode()
self.bottomStripeNode.isLayerBacked = true
self.gridNode = InviteLinksGridNode()
super.init(layerBacked: false, dynamicBounce: false)
self.addSubnode(self.gridNode)
}
public func asyncLayout() -> (_ item: ItemListInviteLinkGridItem, _ params: ListViewItemLayoutParams, _ insets: ItemListNeighbors) -> (ListViewItemNodeLayout, () -> Void) {
let currentItem = self.item
return { item, params, neighbors in
var updatedTheme: PresentationTheme?
if currentItem?.presentationData.theme !== item.presentationData.theme {
updatedTheme = item.presentationData.theme
}
let contentSize: CGSize
let insets: UIEdgeInsets
let separatorHeight = UIScreenPixel
let itemBackgroundColor: UIColor
let itemSeparatorColor: UIColor
let leftInset = 16.0 + params.leftInset
let topInset: CGFloat
if case .plain = item.style, case .otherSection = neighbors.top {
topInset = 16.0
} else if case .blocks = item.style, case .sameSection(true) = neighbors.top {
topInset = 16.0
} else {
topInset = 4.0
}
var height: CGFloat
let count = item.invites?.count ?? item.count
if count > 0 {
if count % 2 == 0 {
height = topInset + 122.0 + 6.0
} else {
height = topInset + 102.0 + 6.0
}
} else {
height = 0.001
}
switch item.style {
case .plain:
itemBackgroundColor = item.presentationData.theme.list.blocksBackgroundColor
itemSeparatorColor = item.presentationData.theme.list.blocksBackgroundColor
insets = UIEdgeInsets()
case .blocks:
itemBackgroundColor = item.presentationData.theme.list.itemBlocksBackgroundColor
itemSeparatorColor = item.presentationData.theme.list.itemBlocksSeparatorColor
insets = itemListNeighborsGroupedInsets(neighbors)
}
if case .sameSection(false) = neighbors.bottom {
} else {
height += 10.0
}
contentSize = CGSize(width: params.width, height: height)
return (ListViewItemNodeLayout(contentSize: contentSize, insets: insets), { [weak self] in
if let strongSelf = self {
strongSelf.item = item
if let _ = updatedTheme {
strongSelf.topStripeNode.backgroundColor = itemSeparatorColor
strongSelf.bottomStripeNode.backgroundColor = itemSeparatorColor
strongSelf.backgroundNode.backgroundColor = itemBackgroundColor
}
let gridSize = strongSelf.gridNode.update(size: contentSize, safeInset: params.leftInset, items: item.invites, count: item.count, share: item.share, presentationData: item.presentationData, transition: .immediate)
strongSelf.gridNode.frame = CGRect(origin: CGPoint(x: 0.0, y: topInset - 4.0), size: gridSize)
strongSelf.gridNode.action = { invite in
item.tapAction?(invite)
}
strongSelf.gridNode.contextAction = { node, invite in
item.contextAction?(invite, node)
}
switch item.style {
case .plain:
if strongSelf.backgroundNode.supernode == nil {
strongSelf.insertSubnode(strongSelf.backgroundNode, at: 0)
}
if strongSelf.topStripeNode.supernode != nil {
strongSelf.topStripeNode.removeFromSupernode()
}
if strongSelf.bottomStripeNode.supernode != nil {
strongSelf.bottomStripeNode.removeFromSupernode()
}
if strongSelf.maskNode.supernode != nil {
strongSelf.maskNode.removeFromSupernode()
}
strongSelf.backgroundNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: params.width, height: contentSize.height))
strongSelf.bottomStripeNode.frame = CGRect(origin: CGPoint(x: leftInset, y: contentSize.height - separatorHeight), size: CGSize(width: params.width - leftInset, height: separatorHeight))
case .blocks:
if strongSelf.backgroundNode.supernode == nil {
strongSelf.insertSubnode(strongSelf.backgroundNode, at: 0)
}
if strongSelf.topStripeNode.supernode == nil {
strongSelf.insertSubnode(strongSelf.topStripeNode, at: 1)
}
if strongSelf.bottomStripeNode.supernode == nil {
strongSelf.insertSubnode(strongSelf.bottomStripeNode, at: 2)
}
if strongSelf.maskNode.supernode == nil {
strongSelf.insertSubnode(strongSelf.maskNode, at: 3)
}
let hasCorners = itemListHasRoundedBlockLayout(params)
var hasTopCorners = false
var hasBottomCorners = false
switch neighbors.top {
case .sameSection(false):
strongSelf.topStripeNode.isHidden = true
default:
hasTopCorners = true
strongSelf.topStripeNode.isHidden = hasCorners
}
let bottomStripeInset: CGFloat
switch neighbors.bottom {
case .sameSection(false):
bottomStripeInset = leftInset
strongSelf.bottomStripeNode.isHidden = true
default:
bottomStripeInset = 0.0
hasBottomCorners = true
strongSelf.bottomStripeNode.isHidden = hasCorners
}
strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.presentationData.theme, top: hasTopCorners, bottom: hasBottomCorners) : nil
strongSelf.backgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: contentSize.height + min(insets.top, separatorHeight) + min(insets.bottom, separatorHeight)))
strongSelf.maskNode.frame = strongSelf.backgroundNode.frame.insetBy(dx: params.leftInset, dy: 0.0)
strongSelf.topStripeNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: separatorHeight))
strongSelf.bottomStripeNode.frame = CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height - separatorHeight), size: CGSize(width: params.width - bottomStripeInset, height: separatorHeight))
}
}
})
}
}
override public func animateInsertion(_ currentTimestamp: Double, duration: Double, short: Bool) {
self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.4)
}
override public func animateAdded(_ currentTimestamp: Double, duration: Double) {
self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
}
override public func animateRemoved(_ currentTimestamp: Double, duration: Double) {
self.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false)
}
}

View File

@ -363,6 +363,7 @@ public class ItemListInviteLinkItemNode: ListViewItemNode, ItemListItemNode {
if let expireDate = invite.expireDate, currentTime >= expireDate {
isExpired = true
}
var isFull = false
if let usageLimit = invite.usageLimit {
if !isExpired {
@ -376,6 +377,7 @@ public class ItemListInviteLinkItemNode: ListViewItemNode, ItemListItemNode {
timerValue = .fraction(fraction)
}
} else if remaining == 0 {
isFull = true
if !subtitleText.isEmpty {
subtitleText += ""
}
@ -383,7 +385,7 @@ public class ItemListInviteLinkItemNode: ListViewItemNode, ItemListItemNode {
}
}
}
if let expireDate = invite.expireDate {
if let expireDate = invite.expireDate, !isFull {
if !isExpired {
if !subtitleText.isEmpty {
subtitleText += ""
@ -570,7 +572,7 @@ public class ItemListInviteLinkItemNode: ListViewItemNode, ItemListItemNode {
timerNode = TimerNode()
timerNode.isUserInteractionEnabled = false
strongSelf.timerNode = timerNode
strongSelf.addSubnode(timerNode)
strongSelf.offsetContainerNode.addSubnode(timerNode)
}
timerNode.update(color: iconColor, value: timerValue)
} else if let timerNode = strongSelf.timerNode {
@ -785,7 +787,7 @@ private final class TimerNode: ASDisplayNode {
let startAngle: CGFloat = -CGFloat.pi / 2.0
let endAngle: CGFloat = -CGFloat.pi / 2.0 + 2.0 * CGFloat.pi * fraction
let sparks = fraction > 0.1 && fraction != 1.0
let sparks = fraction > 0.05 && fraction != 1.0
if sparks {
let v = CGPoint(x: sin(endAngle), y: -cos(endAngle))
let c = CGPoint(x: -v.y * radius + center.x, y: v.x * radius + center.y)

View File

@ -287,25 +287,6 @@ private func ChannelMembersControllerEntries(context: AccountContext, presentati
var index: Int32 = 0
let sortedParticipants = participants
/*
participants.sorted(by: { lhs, rhs in
let lhsInvitedAt: Int32
switch lhs.participant {
case .creator:
lhsInvitedAt = Int32.min
case let .member(_, invitedAt, _, _):
lhsInvitedAt = invitedAt
}
let rhsInvitedAt: Int32
switch rhs.participant {
case .creator:
rhsInvitedAt = Int32.min
case let .member(_, invitedAt, _, _):
rhsInvitedAt = invitedAt
}
return lhsInvitedAt < rhsInvitedAt
})
*/
for participant in sortedParticipants {
var editable = true
var canEditMembers = false

View File

@ -968,8 +968,17 @@ public func channelVisibilityController(context: AccountContext, peerId: PeerId,
return nil
} |> deliverOnMainQueue).start(next: { invite in
if let invite = invite {
let controller = InviteLinkQRCodeController(context: context, invite: invite)
presentControllerImpl?(controller, nil)
let _ = (context.account.postbox.loadedPeerWithId(peerId)
|> deliverOnMainQueue).start(next: { peer in
let isGroup: Bool
if let peer = peer as? TelegramChannel, case .broadcast = peer.info {
isGroup = false
} else {
isGroup = true
}
let controller = InviteLinkQRCodeController(context: context, invite: invite, isGroup: isGroup)
presentControllerImpl?(controller, nil)
})
}
})
})))

View File

@ -78,7 +78,7 @@ public func presentPeerReportOptions(context: AccountContext, parent: ViewContro
if let reportReason = reportReason {
switch subject {
case let .peer(peerId):
let _ = (reportPeer(account: context.account, peerId: peerId, reason: reportReason)
let _ = (reportPeer(account: context.account, peerId: peerId, reason: reportReason, message: "")
|> deliverOnMainQueue).start(completed: {
if let path = getAppBundle().path(forResource: "PoliceCar", ofType: "tgs") {
parent?.present(UndoOverlayController(presentationData: presentationData, content: .emoji(path: path, text: presentationData.strings.Report_Succeed), elevatedLayout: false, action: { _ in return false }), in: .current)
@ -86,7 +86,7 @@ public func presentPeerReportOptions(context: AccountContext, parent: ViewContro
completion(reportReason, true)
})
case let .messages(messageIds):
let _ = (reportPeerMessages(account: context.account, messageIds: messageIds, reason: reportReason)
let _ = (reportPeerMessages(account: context.account, messageIds: messageIds, reason: reportReason, message: "")
|> deliverOnMainQueue).start(completed: {
if let path = getAppBundle().path(forResource: "PoliceCar", ofType: "tgs") {
parent?.present(UndoOverlayController(presentationData: presentationData, content: .emoji(path: path, text: presentationData.strings.Report_Succeed), elevatedLayout: false, action: { _ in return false }), in: .current)
@ -155,12 +155,16 @@ public func peerReportOptionsController(context: AccountContext, subject: PeerRe
break
}
if let reportReason = reportReason {
var passthrough = passthrough
if case .fake = reportReason {
passthrough = false
}
switch subject {
case let .peer(peerId):
if passthrough {
completion(reportReason, true)
} else {
let _ = (reportPeer(account: context.account, peerId: peerId, reason: reportReason)
let _ = (reportPeer(account: context.account, peerId: peerId, reason: reportReason, message: "")
|> deliverOnMainQueue).start(completed: {
if let path = getAppBundle().path(forResource: "PoliceCar", ofType: "tgs") {
present(UndoOverlayController(presentationData: presentationData, content: .emoji(path: path, text: presentationData.strings.Report_Succeed), elevatedLayout: false, action: { _ in return false }), nil)
@ -172,7 +176,7 @@ public func peerReportOptionsController(context: AccountContext, subject: PeerRe
if passthrough {
completion(reportReason, true)
} else {
let _ = (reportPeerMessages(account: context.account, messageIds: messageIds, reason: reportReason)
let _ = (reportPeerMessages(account: context.account, messageIds: messageIds, reason: reportReason, message: "")
|> deliverOnMainQueue).start(completed: {
if let path = getAppBundle().path(forResource: "PoliceCar", ofType: "tgs") {
present(UndoOverlayController(presentationData: presentationData, content: .emoji(path: path, text: presentationData.strings.Report_Succeed), elevatedLayout: false, action: { _ in return false }), nil)
@ -326,7 +330,7 @@ private func peerReportController(context: AccountContext, subject: PeerReportSu
}
if !text.isEmpty {
let reportReason: ReportReason = .custom(text)
let reportReason: ReportReason = .custom
let completed: () -> Void = {
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
presentControllerImpl?(textAlertController(context: context, title: nil, text: presentationData.strings.ReportPeer_AlertSuccess, actions: [TextAlertAction.init(type: TextAlertActionType.defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil)
@ -335,12 +339,12 @@ private func peerReportController(context: AccountContext, subject: PeerReportSu
}
switch subject {
case let .peer(peerId):
reportDisposable.set((reportPeer(account: context.account, peerId: peerId, reason: reportReason)
reportDisposable.set((reportPeer(account: context.account, peerId: peerId, reason: reportReason, message: text)
|> deliverOnMainQueue).start(completed: {
completed()
}))
case let .messages(messageIds):
reportDisposable.set((reportPeerMessages(account: context.account, messageIds: messageIds, reason: reportReason)
reportDisposable.set((reportPeerMessages(account: context.account, messageIds: messageIds, reason: reportReason, message: text)
|> deliverOnMainQueue).start(completed: {
completed()
}))

View File

@ -1017,7 +1017,7 @@ public func userInfoController(context: AccountContext, peerId: PeerId, mode: Pe
let _ = removePeerChat(account: context.account, peerId: peerId, reportChatSpam: reportSpam).start()
popToRootImpl?()
} else if reportSpam {
let _ = reportPeer(account: context.account, peerId: peerId, reason: .spam).start()
let _ = reportPeer(account: context.account, peerId: peerId, reason: .spam, message: "").start()
}
deleteSendMessageIntents(peerId: peerId)

View File

@ -117,8 +117,14 @@ private class SectionHeaderItemNode: ListViewItemNode {
strongSelf.headerNode = headerNode
}
headerNode.title = item.title
switch item.additionalText {
case .none, .generic:
headerNode.actionType = .generic
case .destructive:
headerNode.actionType = .destructive
}
headerNode.action = item.additionalText.text
headerNode.frame = CGRect(origin: CGPoint(), size: contentSize)
headerNode.updateLayout(size: contentSize, leftInset: params.leftInset, rightInset: params.rightInset)
}

View File

@ -63,6 +63,7 @@ public final class ShareInputFieldNode: ASDisplayNode, ASEditableTextNodeDelegat
private let clearButton: HighlightableButtonNode
public var updateHeight: (() -> Void)?
public var updateText: ((String) -> Void)?
private let backgroundInsets = UIEdgeInsets(top: 16.0, left: 16.0, bottom: 1.0, right: 16.0)
private let inputInsets = UIEdgeInsets(top: 10.0, left: 8.0, bottom: 10.0, right: 16.0)
@ -168,6 +169,7 @@ public final class ShareInputFieldNode: ASDisplayNode, ASEditableTextNodeDelegat
@objc public func editableTextNodeDidUpdateText(_ editableTextNode: ASEditableTextNode) {
self.updateTextNodeText(animated: true)
self.updateText?(editableTextNode.attributedText?.string ?? "")
}
public func editableTextNodeDidBeginEditing(_ editableTextNode: ASEditableTextNode) {

View File

@ -144,6 +144,7 @@ public struct TelegramChannelFlags: OptionSet {
public static let hasVoiceChat = TelegramChannelFlags(rawValue: 1 << 4)
public static let hasActiveVoiceChat = TelegramChannelFlags(rawValue: 1 << 5)
public static let isFake = TelegramChannelFlags(rawValue: 1 << 6)
public static let isGigagroup = TelegramChannelFlags(rawValue: 1 << 7)
}
public final class TelegramChannel: Peer {

View File

@ -11,8 +11,8 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-457104426] = { return Api.InputGeoPoint.parse_inputGeoPointEmpty($0) }
dict[1210199983] = { return Api.InputGeoPoint.parse_inputGeoPoint($0) }
dict[-784000893] = { return Api.payments.ValidatedRequestedInfo.parse_validatedRequestedInfo($0) }
dict[-500874592] = { return Api.ChatFull.parse_chatFull($0) }
dict[-66811386] = { return Api.ChatFull.parse_channelFull($0) }
dict[-500874592] = { return Api.ChatFull.parse_chatFull($0) }
dict[-1159937629] = { return Api.PollResults.parse_pollResults($0) }
dict[-925415106] = { return Api.ChatParticipant.parse_chatParticipant($0) }
dict[-636267638] = { return Api.ChatParticipant.parse_chatParticipantCreator($0) }
@ -33,20 +33,20 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[1216809369] = { return Api.PageBlock.parse_pageBlockFooter($0) }
dict[-618614392] = { return Api.PageBlock.parse_pageBlockDivider($0) }
dict[-837994576] = { return Api.PageBlock.parse_pageBlockAnchor($0) }
dict[-454524911] = { return Api.PageBlock.parse_pageBlockList($0) }
dict[641563686] = { return Api.PageBlock.parse_pageBlockBlockquote($0) }
dict[1329878739] = { return Api.PageBlock.parse_pageBlockPullquote($0) }
dict[972174080] = { return Api.PageBlock.parse_pageBlockCover($0) }
dict[-283684427] = { return Api.PageBlock.parse_pageBlockChannel($0) }
dict[504660880] = { return Api.PageBlock.parse_pageBlockKicker($0) }
dict[-1085412734] = { return Api.PageBlock.parse_pageBlockTable($0) }
dict[391759200] = { return Api.PageBlock.parse_pageBlockPhoto($0) }
dict[2089805750] = { return Api.PageBlock.parse_pageBlockVideo($0) }
dict[972174080] = { return Api.PageBlock.parse_pageBlockCover($0) }
dict[-2143067670] = { return Api.PageBlock.parse_pageBlockAudio($0) }
dict[-1468953147] = { return Api.PageBlock.parse_pageBlockEmbed($0) }
dict[-229005301] = { return Api.PageBlock.parse_pageBlockEmbedPost($0) }
dict[1705048653] = { return Api.PageBlock.parse_pageBlockCollage($0) }
dict[52401552] = { return Api.PageBlock.parse_pageBlockSlideshow($0) }
dict[-283684427] = { return Api.PageBlock.parse_pageBlockChannel($0) }
dict[-2143067670] = { return Api.PageBlock.parse_pageBlockAudio($0) }
dict[504660880] = { return Api.PageBlock.parse_pageBlockKicker($0) }
dict[-1085412734] = { return Api.PageBlock.parse_pageBlockTable($0) }
dict[-454524911] = { return Api.PageBlock.parse_pageBlockList($0) }
dict[-1702174239] = { return Api.PageBlock.parse_pageBlockOrderedList($0) }
dict[1987480557] = { return Api.PageBlock.parse_pageBlockDetails($0) }
dict[370236054] = { return Api.PageBlock.parse_pageBlockRelatedArticles($0) }
@ -66,10 +66,10 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[590459437] = { return Api.Photo.parse_photoEmpty($0) }
dict[-82216347] = { return Api.Photo.parse_photo($0) }
dict[-1683826688] = { return Api.Chat.parse_chatEmpty($0) }
dict[1004149726] = { return Api.Chat.parse_chat($0) }
dict[120753115] = { return Api.Chat.parse_chatForbidden($0) }
dict[-753232354] = { return Api.Chat.parse_channel($0) }
dict[681420594] = { return Api.Chat.parse_channelForbidden($0) }
dict[1004149726] = { return Api.Chat.parse_chat($0) }
dict[-753232354] = { return Api.Chat.parse_channel($0) }
dict[1202287072] = { return Api.StatsURL.parse_statsURL($0) }
dict[1516793212] = { return Api.ChatInvite.parse_chatInviteAlready($0) }
dict[-540871282] = { return Api.ChatInvite.parse_chatInvite($0) }
@ -113,11 +113,10 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[414687501] = { return Api.DcOption.parse_dcOption($0) }
dict[997055186] = { return Api.PollAnswerVoters.parse_pollAnswerVoters($0) }
dict[-1705233435] = { return Api.account.PasswordSettings.parse_passwordSettings($0) }
dict[-1945767479] = { return Api.help.SupportName.parse_supportName($0) }
dict[-288727837] = { return Api.LangPackLanguage.parse_langPackLanguage($0) }
dict[-399391402] = { return Api.VideoSize.parse_videoSize($0) }
dict[497489295] = { return Api.help.AppUpdate.parse_appUpdate($0) }
dict[-1000708810] = { return Api.help.AppUpdate.parse_noAppUpdate($0) }
dict[497489295] = { return Api.help.AppUpdate.parse_appUpdate($0) }
dict[-209337866] = { return Api.LangPackDifference.parse_langPackDifference($0) }
dict[-815649386] = { return Api.PeerHistoryTTL.parse_peerHistoryTTLPM($0) }
dict[1041354473] = { return Api.PeerHistoryTTL.parse_peerHistoryTTL($0) }
@ -142,12 +141,10 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[1690108678] = { return Api.InputEncryptedFile.parse_inputEncryptedFileUploaded($0) }
dict[1511503333] = { return Api.InputEncryptedFile.parse_inputEncryptedFile($0) }
dict[767652808] = { return Api.InputEncryptedFile.parse_inputEncryptedFileBigUploaded($0) }
dict[1304052993] = { return Api.account.Takeout.parse_takeout($0) }
dict[-1456996667] = { return Api.messages.InactiveChats.parse_inactiveChats($0) }
dict[1690708501] = { return Api.GroupCallParticipant.parse_groupCallParticipant($0) }
dict[1443858741] = { return Api.messages.SentEncryptedMessage.parse_sentEncryptedMessage($0) }
dict[-1802240206] = { return Api.messages.SentEncryptedMessage.parse_sentEncryptedFile($0) }
dict[289586518] = { return Api.SavedContact.parse_savedPhoneContact($0) }
dict[1571494644] = { return Api.ExportedMessageLink.parse_exportedMessageLink($0) }
dict[-855308010] = { return Api.auth.Authorization.parse_authorization($0) }
dict[1148485274] = { return Api.auth.Authorization.parse_authorizationSignUpRequired($0) }
@ -211,14 +208,12 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-337352679] = { return Api.Update.parse_updateServiceNotification($0) }
dict[-298113238] = { return Api.Update.parse_updatePrivacy($0) }
dict[314130811] = { return Api.Update.parse_updateUserPhone($0) }
dict[-1667805217] = { return Api.Update.parse_updateReadHistoryInbox($0) }
dict[791617983] = { return Api.Update.parse_updateReadHistoryOutbox($0) }
dict[2139689491] = { return Api.Update.parse_updateWebPage($0) }
dict[1757493555] = { return Api.Update.parse_updateReadMessagesContents($0) }
dict[-352032773] = { return Api.Update.parse_updateChannelTooLong($0) }
dict[-1227598250] = { return Api.Update.parse_updateChannel($0) }
dict[1656358105] = { return Api.Update.parse_updateNewChannelMessage($0) }
dict[856380452] = { return Api.Update.parse_updateReadChannelInbox($0) }
dict[-1015733815] = { return Api.Update.parse_updateDeleteChannelMessages($0) }
dict[-1734268085] = { return Api.Update.parse_updateChannelMessageViews($0) }
dict[-1232070311] = { return Api.Update.parse_updateChatParticipantAdmin($0) }
@ -226,7 +221,6 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[196268545] = { return Api.Update.parse_updateStickerSetsOrder($0) }
dict[1135492588] = { return Api.Update.parse_updateStickerSets($0) }
dict[-1821035490] = { return Api.Update.parse_updateSavedGifs($0) }
dict[1059076315] = { return Api.Update.parse_updateBotInlineQuery($0) }
dict[239663460] = { return Api.Update.parse_updateBotInlineSend($0) }
dict[457133559] = { return Api.Update.parse_updateEditChannelMessage($0) }
dict[-415938591] = { return Api.Update.parse_updateBotCallbackQuery($0) }
@ -239,23 +233,25 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-1574314746] = { return Api.Update.parse_updateConfig($0) }
dict[861169551] = { return Api.Update.parse_updatePtsChanged($0) }
dict[1081547008] = { return Api.Update.parse_updateChannelWebPage($0) }
dict[1852826908] = { return Api.Update.parse_updateDialogPinned($0) }
dict[-99664734] = { return Api.Update.parse_updatePinnedDialogs($0) }
dict[-2095595325] = { return Api.Update.parse_updateBotWebhookJSON($0) }
dict[-1684914010] = { return Api.Update.parse_updateBotWebhookJSONQuery($0) }
dict[-523384512] = { return Api.Update.parse_updateBotShippingQuery($0) }
dict[1563376297] = { return Api.Update.parse_updateBotPrecheckoutQuery($0) }
dict[-1425052898] = { return Api.Update.parse_updatePhoneCall($0) }
dict[1180041828] = { return Api.Update.parse_updateLangPackTooLong($0) }
dict[1442983757] = { return Api.Update.parse_updateLangPack($0) }
dict[-451831443] = { return Api.Update.parse_updateFavedStickers($0) }
dict[-1987495099] = { return Api.Update.parse_updateChannelReadMessagesContents($0) }
dict[1887741886] = { return Api.Update.parse_updateContactsReset($0) }
dict[1893427255] = { return Api.Update.parse_updateChannelAvailableMessages($0) }
dict[-513517117] = { return Api.Update.parse_updateDialogUnreadMark($0) }
dict[1180041828] = { return Api.Update.parse_updateLangPackTooLong($0) }
dict[-1398708869] = { return Api.Update.parse_updateMessagePoll($0) }
dict[1421875280] = { return Api.Update.parse_updateChatDefaultBannedRights($0) }
dict[422972864] = { return Api.Update.parse_updateFolderPeers($0) }
dict[1852826908] = { return Api.Update.parse_updateDialogPinned($0) }
dict[-99664734] = { return Api.Update.parse_updatePinnedDialogs($0) }
dict[856380452] = { return Api.Update.parse_updateReadChannelInbox($0) }
dict[-1667805217] = { return Api.Update.parse_updateReadHistoryInbox($0) }
dict[1786671974] = { return Api.Update.parse_updatePeerSettings($0) }
dict[-1263546448] = { return Api.Update.parse_updatePeerLocated($0) }
dict[967122427] = { return Api.Update.parse_updateNewScheduledMessage($0) }
@ -279,15 +275,16 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[321954198] = { return Api.Update.parse_updateChat($0) }
dict[-219423922] = { return Api.Update.parse_updateGroupCallParticipants($0) }
dict[-1537295973] = { return Api.Update.parse_updateGroupCall($0) }
dict[1059076315] = { return Api.Update.parse_updateBotInlineQuery($0) }
dict[19291112] = { return Api.Update.parse_updatePeerHistoryTTL($0) }
dict[136574537] = { return Api.messages.VotesList.parse_votesList($0) }
dict[1558266229] = { return Api.PopularContact.parse_popularContact($0) }
dict[-373643672] = { return Api.FolderPeer.parse_folderPeer($0) }
dict[367766557] = { return Api.ChannelParticipant.parse_channelParticipant($0) }
dict[-1557620115] = { return Api.ChannelParticipant.parse_channelParticipantSelf($0) }
dict[1149094475] = { return Api.ChannelParticipant.parse_channelParticipantCreator($0) }
dict[-859915345] = { return Api.ChannelParticipant.parse_channelParticipantAdmin($0) }
dict[470789295] = { return Api.ChannelParticipant.parse_channelParticipantBanned($0) }
dict[-859915345] = { return Api.ChannelParticipant.parse_channelParticipantAdmin($0) }
dict[1149094475] = { return Api.ChannelParticipant.parse_channelParticipantCreator($0) }
dict[-1010402965] = { return Api.ChannelParticipant.parse_channelParticipantLeft($0) }
dict[-1567730343] = { return Api.MessageUserVote.parse_messageUserVote($0) }
dict[909603888] = { return Api.MessageUserVote.parse_messageUserVoteInputOption($0) }
@ -296,9 +293,9 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-513392236] = { return Api.contacts.Blocked.parse_blockedSlice($0) }
dict[-55902537] = { return Api.InputDialogPeer.parse_inputDialogPeer($0) }
dict[1684014375] = { return Api.InputDialogPeer.parse_inputDialogPeerFolder($0) }
dict[-994444869] = { return Api.Error.parse_error($0) }
dict[-1560655744] = { return Api.KeyboardButton.parse_keyboardButton($0) }
dict[629866245] = { return Api.KeyboardButton.parse_keyboardButtonUrl($0) }
dict[901503851] = { return Api.KeyboardButton.parse_keyboardButtonCallback($0) }
dict[-1318425559] = { return Api.KeyboardButton.parse_keyboardButtonRequestPhone($0) }
dict[-59151553] = { return Api.KeyboardButton.parse_keyboardButtonRequestGeoLocation($0) }
dict[90744648] = { return Api.KeyboardButton.parse_keyboardButtonSwitchInline($0) }
@ -307,6 +304,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[280464681] = { return Api.KeyboardButton.parse_keyboardButtonUrlAuth($0) }
dict[-802258988] = { return Api.KeyboardButton.parse_inputKeyboardButtonUrlAuth($0) }
dict[-1144565411] = { return Api.KeyboardButton.parse_keyboardButtonRequestPoll($0) }
dict[901503851] = { return Api.KeyboardButton.parse_keyboardButtonCallback($0) }
dict[-748155807] = { return Api.ContactStatus.parse_contactStatus($0) }
dict[1679398724] = { return Api.SecureFile.parse_secureFileEmpty($0) }
dict[-534283678] = { return Api.SecureFile.parse_secureFile($0) }
@ -328,32 +326,33 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[1881892265] = { return Api.account.WallPapers.parse_wallPapers($0) }
dict[1012306921] = { return Api.InputTheme.parse_inputTheme($0) }
dict[-175567375] = { return Api.InputTheme.parse_inputThemeSlug($0) }
dict[1158290442] = { return Api.messages.FoundGifs.parse_foundGifs($0) }
dict[-1132476723] = { return Api.FileLocation.parse_fileLocationToBeDeprecated($0) }
dict[-2032041631] = { return Api.Poll.parse_poll($0) }
dict[-1195615476] = { return Api.InputNotifyPeer.parse_inputNotifyPeer($0) }
dict[423314455] = { return Api.InputNotifyPeer.parse_inputNotifyUsers($0) }
dict[1251338318] = { return Api.InputNotifyPeer.parse_inputNotifyChats($0) }
dict[-1311015810] = { return Api.InputNotifyPeer.parse_inputNotifyBroadcasts($0) }
dict[-1195615476] = { return Api.InputNotifyPeer.parse_inputNotifyPeer($0) }
dict[-317144808] = { return Api.EncryptedMessage.parse_encryptedMessage($0) }
dict[594758406] = { return Api.EncryptedMessage.parse_encryptedMessageService($0) }
dict[-566281095] = { return Api.ChannelParticipantsFilter.parse_channelParticipantsRecent($0) }
dict[-1268741783] = { return Api.ChannelParticipantsFilter.parse_channelParticipantsAdmins($0) }
dict[-1548400251] = { return Api.ChannelParticipantsFilter.parse_channelParticipantsKicked($0) }
dict[-1328445861] = { return Api.ChannelParticipantsFilter.parse_channelParticipantsBots($0) }
dict[338142689] = { return Api.ChannelParticipantsFilter.parse_channelParticipantsBanned($0) }
dict[106343499] = { return Api.ChannelParticipantsFilter.parse_channelParticipantsSearch($0) }
dict[-1548400251] = { return Api.ChannelParticipantsFilter.parse_channelParticipantsKicked($0) }
dict[-1150621555] = { return Api.ChannelParticipantsFilter.parse_channelParticipantsContacts($0) }
dict[-531931925] = { return Api.ChannelParticipantsFilter.parse_channelParticipantsMentions($0) }
dict[-350980120] = { return Api.WebPage.parse_webPageEmpty($0) }
dict[-981018084] = { return Api.WebPage.parse_webPagePending($0) }
dict[-392411726] = { return Api.WebPage.parse_webPage($0) }
dict[1930545681] = { return Api.WebPage.parse_webPageNotModified($0) }
dict[864077702] = { return Api.InputBotInlineMessage.parse_inputBotInlineMessageMediaAuto($0) }
dict[1036876423] = { return Api.InputBotInlineMessage.parse_inputBotInlineMessageText($0) }
dict[-1768777083] = { return Api.InputBotInlineMessage.parse_inputBotInlineMessageMediaGeo($0) }
dict[1262639204] = { return Api.InputBotInlineMessage.parse_inputBotInlineMessageGame($0) }
dict[864077702] = { return Api.InputBotInlineMessage.parse_inputBotInlineMessageMediaAuto($0) }
dict[1098628881] = { return Api.InputBotInlineMessage.parse_inputBotInlineMessageMediaVenue($0) }
dict[-1494368259] = { return Api.InputBotInlineMessage.parse_inputBotInlineMessageMediaContact($0) }
dict[1262639204] = { return Api.InputBotInlineMessage.parse_inputBotInlineMessageGame($0) }
dict[-1768777083] = { return Api.InputBotInlineMessage.parse_inputBotInlineMessageMediaGeo($0) }
dict[2002815875] = { return Api.KeyboardButtonRow.parse_keyboardButtonRow($0) }
dict[1088567208] = { return Api.StickerSet.parse_stickerSet($0) }
dict[-1111085620] = { return Api.messages.ExportedChatInvites.parse_exportedChatInvites($0) }
@ -382,20 +381,21 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[2131196633] = { return Api.contacts.ResolvedPeer.parse_resolvedPeer($0) }
dict[-1964327229] = { return Api.SecureData.parse_secureData($0) }
dict[-1771768449] = { return Api.InputMedia.parse_inputMediaEmpty($0) }
dict[505969924] = { return Api.InputMedia.parse_inputMediaUploadedPhoto($0) }
dict[-1279654347] = { return Api.InputMedia.parse_inputMediaPhoto($0) }
dict[-104578748] = { return Api.InputMedia.parse_inputMediaGeoPoint($0) }
dict[-122978821] = { return Api.InputMedia.parse_inputMediaContact($0) }
dict[1530447553] = { return Api.InputMedia.parse_inputMediaUploadedDocument($0) }
dict[860303448] = { return Api.InputMedia.parse_inputMediaDocument($0) }
dict[1212395773] = { return Api.InputMedia.parse_inputMediaGifExternal($0) }
dict[-750828557] = { return Api.InputMedia.parse_inputMediaGame($0) }
dict[-1052959727] = { return Api.InputMedia.parse_inputMediaVenue($0) }
dict[-186607933] = { return Api.InputMedia.parse_inputMediaInvoice($0) }
dict[505969924] = { return Api.InputMedia.parse_inputMediaUploadedPhoto($0) }
dict[1530447553] = { return Api.InputMedia.parse_inputMediaUploadedDocument($0) }
dict[-1279654347] = { return Api.InputMedia.parse_inputMediaPhoto($0) }
dict[-440664550] = { return Api.InputMedia.parse_inputMediaPhotoExternal($0) }
dict[-78455655] = { return Api.InputMedia.parse_inputMediaDocumentExternal($0) }
dict[-750828557] = { return Api.InputMedia.parse_inputMediaGame($0) }
dict[-186607933] = { return Api.InputMedia.parse_inputMediaInvoice($0) }
dict[-1759532989] = { return Api.InputMedia.parse_inputMediaGeoLive($0) }
dict[-122978821] = { return Api.InputMedia.parse_inputMediaContact($0) }
dict[261416433] = { return Api.InputMedia.parse_inputMediaPoll($0) }
dict[-428884101] = { return Api.InputMedia.parse_inputMediaDice($0) }
dict[-1759532989] = { return Api.InputMedia.parse_inputMediaGeoLive($0) }
dict[860303448] = { return Api.InputMedia.parse_inputMediaDocument($0) }
dict[2134579434] = { return Api.InputPeer.parse_inputPeerEmpty($0) }
dict[2107670217] = { return Api.InputPeer.parse_inputPeerSelf($0) }
dict[396093539] = { return Api.InputPeer.parse_inputPeerChat($0) }
@ -408,8 +408,8 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-1078332329] = { return Api.help.PassportConfig.parse_passportConfigNotModified($0) }
dict[-1600596305] = { return Api.help.PassportConfig.parse_passportConfig($0) }
dict[1648543603] = { return Api.FileHash.parse_fileHash($0) }
dict[295067450] = { return Api.BotInlineResult.parse_botInlineResult($0) }
dict[400266251] = { return Api.BotInlineResult.parse_botInlineMediaResult($0) }
dict[295067450] = { return Api.BotInlineResult.parse_botInlineResult($0) }
dict[911761060] = { return Api.messages.BotCallbackAnswer.parse_botCallbackAnswer($0) }
dict[1314881805] = { return Api.payments.PaymentResult.parse_paymentResult($0) }
dict[-666824391] = { return Api.payments.PaymentResult.parse_paymentVerificationNeeded($0) }
@ -432,7 +432,6 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-421545947] = { return Api.ChannelAdminLogEventAction.parse_channelAdminLogEventActionChangeTitle($0) }
dict[1427671598] = { return Api.ChannelAdminLogEventAction.parse_channelAdminLogEventActionChangeAbout($0) }
dict[1783299128] = { return Api.ChannelAdminLogEventAction.parse_channelAdminLogEventActionChangeUsername($0) }
dict[1129042607] = { return Api.ChannelAdminLogEventAction.parse_channelAdminLogEventActionChangePhoto($0) }
dict[460916654] = { return Api.ChannelAdminLogEventAction.parse_channelAdminLogEventActionToggleInvites($0) }
dict[648939889] = { return Api.ChannelAdminLogEventAction.parse_channelAdminLogEventActionToggleSignatures($0) }
dict[-370660328] = { return Api.ChannelAdminLogEventAction.parse_channelAdminLogEventActionUpdatePinned($0) }
@ -447,6 +446,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[1599903217] = { return Api.ChannelAdminLogEventAction.parse_channelAdminLogEventActionTogglePreHistoryHidden($0) }
dict[771095562] = { return Api.ChannelAdminLogEventAction.parse_channelAdminLogEventActionDefaultBannedRights($0) }
dict[-1895328189] = { return Api.ChannelAdminLogEventAction.parse_channelAdminLogEventActionStopPoll($0) }
dict[1129042607] = { return Api.ChannelAdminLogEventAction.parse_channelAdminLogEventActionChangePhoto($0) }
dict[-1569748965] = { return Api.ChannelAdminLogEventAction.parse_channelAdminLogEventActionChangeLinkedChat($0) }
dict[241923758] = { return Api.ChannelAdminLogEventAction.parse_channelAdminLogEventActionChangeLocation($0) }
dict[1401984889] = { return Api.ChannelAdminLogEventAction.parse_channelAdminLogEventActionToggleSlowMode($0) }
@ -455,10 +455,10 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-115071790] = { return Api.ChannelAdminLogEventAction.parse_channelAdminLogEventActionParticipantMute($0) }
dict[-431740480] = { return Api.ChannelAdminLogEventAction.parse_channelAdminLogEventActionParticipantUnmute($0) }
dict[1456906823] = { return Api.ChannelAdminLogEventAction.parse_channelAdminLogEventActionToggleGroupCallSetting($0) }
dict[1557846647] = { return Api.ChannelAdminLogEventAction.parse_channelAdminLogEventActionParticipantJoinByInvite($0) }
dict[1515256996] = { return Api.ChannelAdminLogEventAction.parse_channelAdminLogEventActionExportedInviteDelete($0) }
dict[1091179342] = { return Api.ChannelAdminLogEventAction.parse_channelAdminLogEventActionExportedInviteRevoke($0) }
dict[-384910503] = { return Api.ChannelAdminLogEventAction.parse_channelAdminLogEventActionExportedInviteEdit($0) }
dict[1557846647] = { return Api.ChannelAdminLogEventAction.parse_channelAdminLogEventActionParticipantJoinByInvite($0) }
dict[1048537159] = { return Api.ChannelAdminLogEventAction.parse_channelAdminLogEventActionParticipantVolume($0) }
dict[1855199800] = { return Api.ChannelAdminLogEventAction.parse_channelAdminLogEventActionChangeHistoryTTL($0) }
dict[-543777747] = { return Api.auth.ExportedAuthorization.parse_exportedAuthorization($0) }
@ -471,7 +471,6 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-1036572727] = { return Api.account.PasswordInputSettings.parse_passwordInputSettings($0) }
dict[878078826] = { return Api.PageTableCell.parse_pageTableCell($0) }
dict[-1626209256] = { return Api.ChatBannedRights.parse_chatBannedRights($0) }
dict[1968737087] = { return Api.InputClientProxy.parse_inputClientProxy($0) }
dict[649453030] = { return Api.messages.MessageEditData.parse_messageEditData($0) }
dict[-886477832] = { return Api.LabeledPrice.parse_labeledPrice($0) }
dict[-438840932] = { return Api.messages.ChatFull.parse_chatFull($0) }
@ -485,15 +484,15 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[1490799288] = { return Api.ReportReason.parse_inputReportReasonSpam($0) }
dict[505595789] = { return Api.ReportReason.parse_inputReportReasonViolence($0) }
dict[777640226] = { return Api.ReportReason.parse_inputReportReasonPornography($0) }
dict[-1376497949] = { return Api.ReportReason.parse_inputReportReasonChildAbuse($0) }
dict[-1041980751] = { return Api.ReportReason.parse_inputReportReasonOther($0) }
dict[-1685456582] = { return Api.ReportReason.parse_inputReportReasonCopyright($0) }
dict[-1376497949] = { return Api.ReportReason.parse_inputReportReasonChildAbuse($0) }
dict[-606798099] = { return Api.ReportReason.parse_inputReportReasonGeoIrrelevant($0) }
dict[-170010905] = { return Api.ReportReason.parse_inputReportReasonFake($0) }
dict[-1041980751] = { return Api.ReportReason.parse_inputReportReasonOther($0) }
dict[-247351839] = { return Api.InputEncryptedChat.parse_inputEncryptedChat($0) }
dict[-524237339] = { return Api.PageTableRow.parse_pageTableRow($0) }
dict[453805082] = { return Api.DraftMessage.parse_draftMessageEmpty($0) }
dict[-40996577] = { return Api.DraftMessage.parse_draftMessage($0) }
dict[453805082] = { return Api.DraftMessage.parse_draftMessageEmpty($0) }
dict[-1014526429] = { return Api.help.Country.parse_country($0) }
dict[418631927] = { return Api.StatsGroupTopPoster.parse_statsGroupTopPoster($0) }
dict[-2128640689] = { return Api.account.SentEmailCode.parse_sentEmailCode($0) }
@ -509,13 +508,13 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-2036501105] = { return Api.SecureValueError.parse_secureValueError($0) }
dict[-1592506512] = { return Api.SecureValueError.parse_secureValueErrorTranslationFile($0) }
dict[878931416] = { return Api.SecureValueError.parse_secureValueErrorTranslationFiles($0) }
dict[-6249322] = { return Api.InputStickerSetItem.parse_inputStickerSetItem($0) }
dict[-1728664459] = { return Api.help.PromoData.parse_promoDataEmpty($0) }
dict[-1942390465] = { return Api.help.PromoData.parse_promoData($0) }
dict[-1613493288] = { return Api.NotifyPeer.parse_notifyPeer($0) }
dict[-1261946036] = { return Api.NotifyPeer.parse_notifyUsers($0) }
dict[-1073230141] = { return Api.NotifyPeer.parse_notifyChats($0) }
dict[-703403793] = { return Api.NotifyPeer.parse_notifyBroadcasts($0) }
dict[-582464156] = { return Api.wallet.KeySecretSalt.parse_secretSalt($0) }
dict[1335282456] = { return Api.InputPrivacyKey.parse_inputPrivacyKeyStatusTimestamp($0) }
dict[-1107622874] = { return Api.InputPrivacyKey.parse_inputPrivacyKeyChatInvite($0) }
dict[-88417185] = { return Api.InputPrivacyKey.parse_inputPrivacyKeyPhoneCall($0) }
@ -537,13 +536,13 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-316748368] = { return Api.SecureValueHash.parse_secureValueHash($0) }
dict[1722485756] = { return Api.phone.GroupCall.parse_groupCall($0) }
dict[-398136321] = { return Api.messages.SearchCounter.parse_searchCounter($0) }
dict[-2128698738] = { return Api.auth.CheckedPhone.parse_checkedPhone($0) }
dict[-1188055347] = { return Api.PageListItem.parse_pageListItemText($0) }
dict[635466748] = { return Api.PageListItem.parse_pageListItemBlocks($0) }
dict[-386039788] = { return Api.PeerBlocked.parse_peerBlocked($0) }
dict[-1182234929] = { return Api.InputUser.parse_inputUserEmpty($0) }
dict[-138301121] = { return Api.InputUser.parse_inputUserSelf($0) }
dict[-668391402] = { return Api.InputUser.parse_inputUser($0) }
dict[756118935] = { return Api.InputUser.parse_inputUserFromMessage($0) }
dict[-1738178803] = { return Api.Page.parse_page($0) }
dict[871426631] = { return Api.SecureCredentialsEncrypted.parse_secureCredentialsEncrypted($0) }
dict[-875679776] = { return Api.StatsPercentValue.parse_statsPercentValue($0) }
@ -576,50 +575,51 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-1502174430] = { return Api.InputMessage.parse_inputMessageID($0) }
dict[-1160215659] = { return Api.InputMessage.parse_inputMessageReplyTo($0) }
dict[-2037963464] = { return Api.InputMessage.parse_inputMessagePinned($0) }
dict[-1392895362] = { return Api.InputMessage.parse_inputMessageCallbackQuery($0) }
dict[-58224696] = { return Api.PhoneCallProtocol.parse_phoneCallProtocol($0) }
dict[-1237848657] = { return Api.StatsDateRangeDays.parse_statsDateRangeDays($0) }
dict[-275956116] = { return Api.messages.AffectedFoundMessages.parse_affectedFoundMessages($0) }
dict[-1567175714] = { return Api.MessageFwdAuthor.parse_messageFwdAuthor($0) }
dict[-1539849235] = { return Api.WallPaper.parse_wallPaper($0) }
dict[-1963717851] = { return Api.WallPaper.parse_wallPaperNoFile($0) }
dict[-1938715001] = { return Api.messages.Messages.parse_messages($0) }
dict[978610270] = { return Api.messages.Messages.parse_messagesSlice($0) }
dict[1682413576] = { return Api.messages.Messages.parse_channelMessages($0) }
dict[1951620897] = { return Api.messages.Messages.parse_messagesNotModified($0) }
dict[1682413576] = { return Api.messages.Messages.parse_channelMessages($0) }
dict[978610270] = { return Api.messages.Messages.parse_messagesSlice($0) }
dict[-1022713000] = { return Api.Invoice.parse_invoice($0) }
dict[1933519201] = { return Api.PeerSettings.parse_peerSettings($0) }
dict[1577067778] = { return Api.auth.SentCode.parse_sentCode($0) }
dict[480546647] = { return Api.InputChatPhoto.parse_inputChatPhotoEmpty($0) }
dict[-968723890] = { return Api.InputChatPhoto.parse_inputChatUploadedPhoto($0) }
dict[-1991004873] = { return Api.InputChatPhoto.parse_inputChatPhoto($0) }
dict[-968723890] = { return Api.InputChatPhoto.parse_inputChatUploadedPhoto($0) }
dict[-1228606141] = { return Api.messages.MessageViews.parse_messageViews($0) }
dict[375566091] = { return Api.messages.HistoryImport.parse_historyImport($0) }
dict[-368917890] = { return Api.PaymentCharge.parse_paymentCharge($0) }
dict[-1387279939] = { return Api.MessageInteractionCounters.parse_messageInteractionCounters($0) }
dict[-1107852396] = { return Api.stats.BroadcastStats.parse_broadcastStats($0) }
dict[-484987010] = { return Api.Updates.parse_updatesTooLong($0) }
dict[-84936653] = { return Api.Updates.parse_updateShortMessage($0) }
dict[290961496] = { return Api.Updates.parse_updateShortChatMessage($0) }
dict[2027216577] = { return Api.Updates.parse_updateShort($0) }
dict[1918567619] = { return Api.Updates.parse_updatesCombined($0) }
dict[1957577280] = { return Api.Updates.parse_updates($0) }
dict[-84936653] = { return Api.Updates.parse_updateShortMessage($0) }
dict[290961496] = { return Api.Updates.parse_updateShortChatMessage($0) }
dict[-1877614335] = { return Api.Updates.parse_updateShortSentMessage($0) }
dict[-276825834] = { return Api.stats.MegagroupStats.parse_megagroupStats($0) }
dict[-884757282] = { return Api.StatsAbsValueAndPrev.parse_statsAbsValueAndPrev($0) }
dict[1038967584] = { return Api.MessageMedia.parse_messageMediaEmpty($0) }
dict[1766936791] = { return Api.MessageMedia.parse_messageMediaPhoto($0) }
dict[1457575028] = { return Api.MessageMedia.parse_messageMediaGeo($0) }
dict[-873313984] = { return Api.MessageMedia.parse_messageMediaContact($0) }
dict[-1618676578] = { return Api.MessageMedia.parse_messageMediaUnsupported($0) }
dict[-1666158377] = { return Api.MessageMedia.parse_messageMediaDocument($0) }
dict[-1557277184] = { return Api.MessageMedia.parse_messageMediaWebPage($0) }
dict[784356159] = { return Api.MessageMedia.parse_messageMediaVenue($0) }
dict[-38694904] = { return Api.MessageMedia.parse_messageMediaGame($0) }
dict[-2074799289] = { return Api.MessageMedia.parse_messageMediaInvoice($0) }
dict[-1186937242] = { return Api.MessageMedia.parse_messageMediaGeoLive($0) }
dict[784356159] = { return Api.MessageMedia.parse_messageMediaVenue($0) }
dict[1766936791] = { return Api.MessageMedia.parse_messageMediaPhoto($0) }
dict[-1666158377] = { return Api.MessageMedia.parse_messageMediaDocument($0) }
dict[-873313984] = { return Api.MessageMedia.parse_messageMediaContact($0) }
dict[1272375192] = { return Api.MessageMedia.parse_messageMediaPoll($0) }
dict[1065280907] = { return Api.MessageMedia.parse_messageMediaDice($0) }
dict[-1186937242] = { return Api.MessageMedia.parse_messageMediaGeoLive($0) }
dict[-842892769] = { return Api.PaymentSavedCredentials.parse_paymentSavedCredentialsCard($0) }
dict[1450380236] = { return Api.Null.parse_null($0) }
dict[1923290508] = { return Api.auth.CodeType.parse_codeTypeSms($0) }
dict[1948046307] = { return Api.auth.CodeType.parse_codeTypeCall($0) }
dict[577556219] = { return Api.auth.CodeType.parse_codeTypeFlashCall($0) }
@ -649,21 +649,21 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-1231326505] = { return Api.messages.ChatAdminsWithInvites.parse_chatAdminsWithInvites($0) }
dict[-1729618630] = { return Api.BotInfo.parse_botInfo($0) }
dict[-1519637954] = { return Api.updates.State.parse_state($0) }
dict[372165663] = { return Api.FoundGif.parse_foundGif($0) }
dict[-1670052855] = { return Api.FoundGif.parse_foundGifCached($0) }
dict[537022650] = { return Api.User.parse_userEmpty($0) }
dict[-1820043071] = { return Api.User.parse_user($0) }
dict[678405636] = { return Api.Message.parse_messageService($0) }
dict[-1868117372] = { return Api.Message.parse_messageEmpty($0) }
dict[-1125940270] = { return Api.Message.parse_message($0) }
dict[678405636] = { return Api.Message.parse_messageService($0) }
dict[831924812] = { return Api.StatsGroupTopInviter.parse_statsGroupTopInviter($0) }
dict[186120336] = { return Api.messages.RecentStickers.parse_recentStickersNotModified($0) }
dict[586395571] = { return Api.messages.RecentStickers.parse_recentStickers($0) }
dict[-539317279] = { return Api.InputFileLocation.parse_inputFileLocation($0) }
dict[-182231723] = { return Api.InputFileLocation.parse_inputEncryptedFileLocation($0) }
dict[-1160743548] = { return Api.InputFileLocation.parse_inputDocumentFileLocation($0) }
dict[-876089816] = { return Api.InputFileLocation.parse_inputSecureFileLocation($0) }
dict[700340377] = { return Api.InputFileLocation.parse_inputTakeoutFileLocation($0) }
dict[-539317279] = { return Api.InputFileLocation.parse_inputFileLocation($0) }
dict[1075322878] = { return Api.InputFileLocation.parse_inputPhotoFileLocation($0) }
dict[-667654413] = { return Api.InputFileLocation.parse_inputPhotoLegacyFileLocation($0) }
dict[-1160743548] = { return Api.InputFileLocation.parse_inputDocumentFileLocation($0) }
dict[668375447] = { return Api.InputFileLocation.parse_inputPeerPhotoFileLocation($0) }
dict[230353641] = { return Api.InputFileLocation.parse_inputStickerSetThumb($0) }
dict[286776671] = { return Api.GeoPoint.parse_geoPointEmpty($0) }
@ -710,8 +710,8 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[1816636575] = { return Api.LangPackString.parse_langPackStringPluralized($0) }
dict[695856818] = { return Api.LangPackString.parse_langPackStringDeleted($0) }
dict[-1036396922] = { return Api.InputWebFileLocation.parse_inputWebFileLocation($0) }
dict[1430205163] = { return Api.InputWebFileLocation.parse_inputWebFileGeoMessageLocation($0) }
dict[-1625153079] = { return Api.InputWebFileLocation.parse_inputWebFileGeoPointLocation($0) }
dict[-1275374751] = { return Api.EmojiLanguage.parse_emojiLanguage($0) }
dict[1601666510] = { return Api.MessageFwdHeader.parse_messageFwdHeader($0) }
dict[-1012849566] = { return Api.BaseTheme.parse_baseThemeClassic($0) }
dict[-69724536] = { return Api.BaseTheme.parse_baseThemeDay($0) }
@ -723,6 +723,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-1777752804] = { return Api.MessagesFilter.parse_inputMessagesFilterPhotos($0) }
dict[-1614803355] = { return Api.MessagesFilter.parse_inputMessagesFilterVideo($0) }
dict[1458172132] = { return Api.MessagesFilter.parse_inputMessagesFilterPhotoVideo($0) }
dict[-648121413] = { return Api.MessagesFilter.parse_inputMessagesFilterPhotoVideoDocuments($0) }
dict[-1629621880] = { return Api.MessagesFilter.parse_inputMessagesFilterDocument($0) }
dict[2129714567] = { return Api.MessagesFilter.parse_inputMessagesFilterUrl($0) }
dict[-3644025] = { return Api.MessagesFilter.parse_inputMessagesFilterGif($0) }
@ -733,6 +734,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[2054952868] = { return Api.MessagesFilter.parse_inputMessagesFilterRoundVoice($0) }
dict[-1253451181] = { return Api.MessagesFilter.parse_inputMessagesFilterRoundVideo($0) }
dict[-1040652646] = { return Api.MessagesFilter.parse_inputMessagesFilterMyMentions($0) }
dict[1187706024] = { return Api.MessagesFilter.parse_inputMessagesFilterMyMentionsUnread($0) }
dict[-419271411] = { return Api.MessagesFilter.parse_inputMessagesFilterGeo($0) }
dict[-530392189] = { return Api.MessagesFilter.parse_inputMessagesFilterContacts($0) }
dict[464520273] = { return Api.MessagesFilter.parse_inputMessagesFilterPinned($0) }
@ -744,12 +746,13 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[594408994] = { return Api.EmojiKeyword.parse_emojiKeywordDeleted($0) }
dict[-290921362] = { return Api.upload.CdnFile.parse_cdnFileReuploadNeeded($0) }
dict[-1449145777] = { return Api.upload.CdnFile.parse_cdnFile($0) }
dict[1984136919] = { return Api.wallet.LiteResponse.parse_liteResponse($0) }
dict[415997816] = { return Api.help.InviteText.parse_inviteText($0) }
dict[1984755728] = { return Api.BotInlineMessage.parse_botInlineMessageMediaAuto($0) }
dict[-1937807902] = { return Api.BotInlineMessage.parse_botInlineMessageText($0) }
dict[85477117] = { return Api.BotInlineMessage.parse_botInlineMessageMediaGeo($0) }
dict[1984755728] = { return Api.BotInlineMessage.parse_botInlineMessageMediaAuto($0) }
dict[-1970903652] = { return Api.BotInlineMessage.parse_botInlineMessageMediaVenue($0) }
dict[416402882] = { return Api.BotInlineMessage.parse_botInlineMessageMediaContact($0) }
dict[85477117] = { return Api.BotInlineMessage.parse_botInlineMessageMediaGeo($0) }
dict[-1673717362] = { return Api.InputPeerNotifySettings.parse_inputPeerNotifySettings($0) }
dict[-1634752813] = { return Api.messages.FavedStickers.parse_favedStickersNotModified($0) }
dict[-209768682] = { return Api.messages.FavedStickers.parse_favedStickers($0) }
@ -780,10 +783,10 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-732254058] = { return Api.PasswordKdfAlgo.parse_passwordKdfAlgoUnknown($0) }
dict[982592842] = { return Api.PasswordKdfAlgo.parse_passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow($0) }
dict[-1390001672] = { return Api.account.Password.parse_password($0) }
dict[-2000710887] = { return Api.InputBotInlineResult.parse_inputBotInlineResult($0) }
dict[-1462213465] = { return Api.InputBotInlineResult.parse_inputBotInlineResultPhoto($0) }
dict[-459324] = { return Api.InputBotInlineResult.parse_inputBotInlineResultDocument($0) }
dict[1336154098] = { return Api.InputBotInlineResult.parse_inputBotInlineResultGame($0) }
dict[-2000710887] = { return Api.InputBotInlineResult.parse_inputBotInlineResult($0) }
dict[1352683077] = { return Api.account.PrivacyRules.parse_privacyRules($0) }
dict[-123988] = { return Api.PrivacyRule.parse_privacyValueAllowContacts($0) }
dict[1698855810] = { return Api.PrivacyRule.parse_privacyValueAllowAll($0) }
@ -830,8 +833,8 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[1674235686] = { return Api.account.AutoDownloadSettings.parse_autoDownloadSettings($0) }
dict[-445792507] = { return Api.DialogPeer.parse_dialogPeer($0) }
dict[1363483106] = { return Api.DialogPeer.parse_dialogPeerFolder($0) }
dict[475467473] = { return Api.WebDocument.parse_webDocument($0) }
dict[-104284986] = { return Api.WebDocument.parse_webDocumentNoProxy($0) }
dict[475467473] = { return Api.WebDocument.parse_webDocument($0) }
dict[42930452] = { return Api.Theme.parse_theme($0) }
dict[-1290580579] = { return Api.contacts.Found.parse_found($0) }
dict[-368018716] = { return Api.ChannelAdminLogEventsFilter.parse_channelAdminLogEventsFilter($0) }
@ -844,8 +847,8 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[1775479590] = { return Api.UserProfilePhoto.parse_userProfilePhoto($0) }
dict[-74456004] = { return Api.payments.SavedInfo.parse_savedInfo($0) }
dict[1041346555] = { return Api.updates.ChannelDifference.parse_channelDifferenceEmpty($0) }
dict[-1531132162] = { return Api.updates.ChannelDifference.parse_channelDifferenceTooLong($0) }
dict[543450958] = { return Api.updates.ChannelDifference.parse_channelDifference($0) }
dict[-1531132162] = { return Api.updates.ChannelDifference.parse_channelDifferenceTooLong($0) }
dict[-1815339214] = { return Api.help.CountriesList.parse_countriesListNotModified($0) }
dict[-2016381538] = { return Api.help.CountriesList.parse_countriesList($0) }
dict[-309659827] = { return Api.channels.AdminLogResults.parse_adminLogResults($0) }
@ -883,8 +886,8 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-1730311882] = { return Api.PageListOrderedItem.parse_pageListOrderedItemBlocks($0) }
dict[-1417756512] = { return Api.EncryptedChat.parse_encryptedChatEmpty($0) }
dict[1006044124] = { return Api.EncryptedChat.parse_encryptedChatWaiting($0) }
dict[1651608194] = { return Api.EncryptedChat.parse_encryptedChatRequested($0) }
dict[-94974410] = { return Api.EncryptedChat.parse_encryptedChat($0) }
dict[1651608194] = { return Api.EncryptedChat.parse_encryptedChatRequested($0) }
dict[505183301] = { return Api.EncryptedChat.parse_encryptedChatDiscarded($0) }
dict[-901375139] = { return Api.PeerLocated.parse_peerLocated($0) }
dict[-118740917] = { return Api.PeerLocated.parse_peerSelfLocated($0) }
@ -1013,8 +1016,6 @@ public struct Api {
_1.serialize(buffer, boxed)
case let _1 as Api.account.PasswordSettings:
_1.serialize(buffer, boxed)
case let _1 as Api.help.SupportName:
_1.serialize(buffer, boxed)
case let _1 as Api.LangPackLanguage:
_1.serialize(buffer, boxed)
case let _1 as Api.VideoSize:
@ -1041,16 +1042,12 @@ public struct Api {
_1.serialize(buffer, boxed)
case let _1 as Api.InputEncryptedFile:
_1.serialize(buffer, boxed)
case let _1 as Api.account.Takeout:
_1.serialize(buffer, boxed)
case let _1 as Api.messages.InactiveChats:
_1.serialize(buffer, boxed)
case let _1 as Api.GroupCallParticipant:
_1.serialize(buffer, boxed)
case let _1 as Api.messages.SentEncryptedMessage:
_1.serialize(buffer, boxed)
case let _1 as Api.SavedContact:
_1.serialize(buffer, boxed)
case let _1 as Api.ExportedMessageLink:
_1.serialize(buffer, boxed)
case let _1 as Api.auth.Authorization:
@ -1089,6 +1086,8 @@ public struct Api {
_1.serialize(buffer, boxed)
case let _1 as Api.InputDialogPeer:
_1.serialize(buffer, boxed)
case let _1 as Api.Error:
_1.serialize(buffer, boxed)
case let _1 as Api.KeyboardButton:
_1.serialize(buffer, boxed)
case let _1 as Api.ContactStatus:
@ -1111,6 +1110,8 @@ public struct Api {
_1.serialize(buffer, boxed)
case let _1 as Api.InputTheme:
_1.serialize(buffer, boxed)
case let _1 as Api.messages.FoundGifs:
_1.serialize(buffer, boxed)
case let _1 as Api.FileLocation:
_1.serialize(buffer, boxed)
case let _1 as Api.Poll:
@ -1207,8 +1208,6 @@ public struct Api {
_1.serialize(buffer, boxed)
case let _1 as Api.ChatBannedRights:
_1.serialize(buffer, boxed)
case let _1 as Api.InputClientProxy:
_1.serialize(buffer, boxed)
case let _1 as Api.messages.MessageEditData:
_1.serialize(buffer, boxed)
case let _1 as Api.LabeledPrice:
@ -1247,12 +1246,12 @@ public struct Api {
_1.serialize(buffer, boxed)
case let _1 as Api.SecureValueError:
_1.serialize(buffer, boxed)
case let _1 as Api.InputStickerSetItem:
_1.serialize(buffer, boxed)
case let _1 as Api.help.PromoData:
_1.serialize(buffer, boxed)
case let _1 as Api.NotifyPeer:
_1.serialize(buffer, boxed)
case let _1 as Api.wallet.KeySecretSalt:
_1.serialize(buffer, boxed)
case let _1 as Api.InputPrivacyKey:
_1.serialize(buffer, boxed)
case let _1 as Api.help.RecentMeUrls:
@ -1275,6 +1274,8 @@ public struct Api {
_1.serialize(buffer, boxed)
case let _1 as Api.messages.SearchCounter:
_1.serialize(buffer, boxed)
case let _1 as Api.auth.CheckedPhone:
_1.serialize(buffer, boxed)
case let _1 as Api.PageListItem:
_1.serialize(buffer, boxed)
case let _1 as Api.PeerBlocked:
@ -1333,6 +1334,8 @@ public struct Api {
_1.serialize(buffer, boxed)
case let _1 as Api.messages.AffectedFoundMessages:
_1.serialize(buffer, boxed)
case let _1 as Api.MessageFwdAuthor:
_1.serialize(buffer, boxed)
case let _1 as Api.WallPaper:
_1.serialize(buffer, boxed)
case let _1 as Api.messages.Messages:
@ -1365,6 +1368,8 @@ public struct Api {
_1.serialize(buffer, boxed)
case let _1 as Api.PaymentSavedCredentials:
_1.serialize(buffer, boxed)
case let _1 as Api.Null:
_1.serialize(buffer, boxed)
case let _1 as Api.auth.CodeType:
_1.serialize(buffer, boxed)
case let _1 as Api.DocumentAttribute:
@ -1393,6 +1398,8 @@ public struct Api {
_1.serialize(buffer, boxed)
case let _1 as Api.updates.State:
_1.serialize(buffer, boxed)
case let _1 as Api.FoundGif:
_1.serialize(buffer, boxed)
case let _1 as Api.User:
_1.serialize(buffer, boxed)
case let _1 as Api.Message:
@ -1457,8 +1464,6 @@ public struct Api {
_1.serialize(buffer, boxed)
case let _1 as Api.InputWebFileLocation:
_1.serialize(buffer, boxed)
case let _1 as Api.EmojiLanguage:
_1.serialize(buffer, boxed)
case let _1 as Api.MessageFwdHeader:
_1.serialize(buffer, boxed)
case let _1 as Api.BaseTheme:
@ -1475,6 +1480,8 @@ public struct Api {
_1.serialize(buffer, boxed)
case let _1 as Api.upload.CdnFile:
_1.serialize(buffer, boxed)
case let _1 as Api.wallet.LiteResponse:
_1.serialize(buffer, boxed)
case let _1 as Api.help.InviteText:
_1.serialize(buffer, boxed)
case let _1 as Api.BotInlineMessage:

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -108,6 +108,9 @@ func parseTelegramGroupOrChannel(chat: Api.Chat) -> Peer? {
if (flags & Int32(1 << 25)) != 0 {
channelFlags.insert(.isFake)
}
if (flags & Int32(1 << 25)) != 0 {
channelFlags.insert(.isGigagroup)
}
let restrictionInfo: PeerAccessRestrictionInfo?
if let restrictionReason = restrictionReason {

View File

@ -62,6 +62,7 @@ public enum AdminLogEventAction {
case deleteExportedInvitation(ExportedInvitation)
case revokeExportedInvitation(ExportedInvitation)
case editExportedInvitation(previous: ExportedInvitation, updated: ExportedInvitation)
case participantJoinedViaInvite(ExportedInvitation)
}
public enum ChannelAdminLogEventError {
@ -241,12 +242,12 @@ public func channelAdminLogEvents(postbox: Postbox, network: Network, peerId: Pe
action = .revokeExportedInvitation(ExportedInvitation(apiExportedInvite: invite))
case let .channelAdminLogEventActionExportedInviteEdit(prevInvite, newInvite):
action = .editExportedInvitation(previous: ExportedInvitation(apiExportedInvite: prevInvite), updated: ExportedInvitation(apiExportedInvite: newInvite))
case let .channelAdminLogEventActionParticipantJoinByInvite(invite):
action = .participantJoinedViaInvite(ExportedInvitation(apiExportedInvite: invite))
case let .channelAdminLogEventActionParticipantVolume(participant):
let parsedParticipant = GroupCallParticipantsContext.Update.StateUpdate.ParticipantUpdate(participant)
action = .groupCallUpdateParticipantVolume(peerId: parsedParticipant.peerId, volume: parsedParticipant.volume ?? 10000)
case let .channelAdminLogEventActionParticipantJoinByInvite(invite):
action = nil
case .channelAdminLogEventActionChangeHistoryTTL(prevValue: let prevValue, newValue: let newValue):
case let .channelAdminLogEventActionChangeHistoryTTL(prevValue, newValue):
action = nil
}
let peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: userId)

View File

@ -856,6 +856,7 @@ public final class PeerInvitationImportersContext {
public struct ExportedInvitationCreator : Equatable {
public let peer: RenderedPeer
public let count: Int32
public let revokedCount: Int32
}
public func peerExportedInvitationsCreators(account: Account, peerId: PeerId) -> Signal<[ExportedInvitationCreator], NoError> {
@ -892,7 +893,7 @@ public func peerExportedInvitationsCreators(account: Account, peerId: PeerId) ->
case let .chatAdminWithInvites(adminId, invitesCount, revokedInvitesCount):
let peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: adminId)
if let peer = peersMap[peerId], peerId != account.peerId {
creators.append(ExportedInvitationCreator(peer: RenderedPeer(peer: peer), count: invitesCount))
creators.append(ExportedInvitationCreator(peer: RenderedPeer(peer: peer), count: invitesCount, revokedCount: revokedInvitesCount))
}
}
}

View File

@ -84,7 +84,7 @@ public enum ReportReason: Equatable {
case childAbuse
case copyright
case irrelevantLocation
case custom(String)
case custom
}
private extension ReportReason {
@ -110,16 +110,9 @@ private extension ReportReason {
}
}
public func reportPeer(account: Account, peerId: PeerId, reason: ReportReason) -> Signal<Void, NoError> {
public func reportPeer(account: Account, peerId: PeerId, reason: ReportReason, message: String) -> Signal<Void, NoError> {
return account.postbox.transaction { transaction -> Signal<Void, NoError> in
if let peer = transaction.getPeer(peerId), let inputPeer = apiInputPeer(peer) {
var message: String = ""
switch reason {
case let .custom(text):
message = text
default:
break
}
return account.network.request(Api.functions.account.reportPeer(peer: inputPeer, reason: reason.apiReason, message: message))
|> `catch` { _ -> Signal<Api.Bool, NoError> in
return .single(.boolFalse)
@ -133,20 +126,13 @@ public func reportPeer(account: Account, peerId: PeerId, reason: ReportReason) -
} |> switchToLatest
}
public func reportPeerMessages(account: Account, messageIds: [MessageId], reason: ReportReason) -> Signal<Void, NoError> {
public func reportPeerMessages(account: Account, messageIds: [MessageId], reason: ReportReason, message: String) -> Signal<Void, NoError> {
return account.postbox.transaction { transaction -> Signal<Void, NoError> in
let groupedIds = messagesIdsGroupedByPeerId(messageIds)
let signals = groupedIds.values.compactMap { ids -> Signal<Void, NoError>? in
guard let peerId = ids.first?.peerId, let peer = transaction.getPeer(peerId), let inputPeer = apiInputPeer(peer) else {
return nil
}
var message: String = ""
switch reason {
case let .custom(text):
message = text
default:
break
}
return account.network.request(Api.functions.messages.report(peer: inputPeer, id: ids.map { $0.id }, reason: reason.apiReason, message: message))
|> `catch` { _ -> Signal<Api.Bool, NoError> in
return .single(.boolFalse)

View File

@ -359,7 +359,7 @@ public func makeDefaultDarkPresentationTheme(extendingThemeReference: Presentati
pageIndicatorInactiveColor: UIColor(white: 1.0, alpha: 0.3),
inputClearButtonColor: UIColor(rgb: 0x8b9197),
itemBarChart: PresentationThemeItemBarChart(color1: UIColor(rgb: 0xffffff), color2: UIColor(rgb: 0x929196), color3: UIColor(rgb: 0x333333)),
itemInputField: PresentationInputFieldTheme(backgroundColor: UIColor(rgb: 0x1c1c1d), strokeColor: UIColor(rgb: 0x1c1c1d), placeholderColor: UIColor(rgb: 0x8f8f8f), primaryColor: UIColor(rgb: 0xffffff), controlColor: UIColor(rgb: 0x8f8f8f))
itemInputField: PresentationInputFieldTheme(backgroundColor: UIColor(rgb: 0x0f0f0f), strokeColor: UIColor(rgb: 0x0f0f0f), placeholderColor: UIColor(rgb: 0x8f8f8f), primaryColor: UIColor(rgb: 0xffffff), controlColor: UIColor(rgb: 0x8f8f8f))
)
let chatList = PresentationThemeChatList(

View File

@ -612,7 +612,7 @@ public func makeDefaultDarkTintedPresentationTheme(extendingThemeReference: Pres
pageIndicatorInactiveColor: mainSecondaryTextColor.withAlphaComponent(0.4),
inputClearButtonColor: mainSecondaryColor,
itemBarChart: PresentationThemeItemBarChart(color1: accentColor, color2: mainSecondaryTextColor.withAlphaComponent(0.5), color3: accentColor.withMultiplied(hue: 1.038, saturation: 0.329, brightness: 0.33)),
itemInputField: PresentationInputFieldTheme(backgroundColor: mainBackgroundColor, strokeColor: mainBackgroundColor, placeholderColor: mainSecondaryColor, primaryColor: UIColor(rgb: 0xffffff), controlColor: mainSecondaryColor)
itemInputField: PresentationInputFieldTheme(backgroundColor: mainInputColor, strokeColor: mainInputColor, placeholderColor: mainSecondaryColor, primaryColor: UIColor(rgb: 0xffffff), controlColor: mainSecondaryColor)
)
let chatList = PresentationThemeChatList(

View File

@ -2767,10 +2767,29 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
if let peer = peerViewMainPeer(peerView) {
if let selectionState = presentationInterfaceState.interfaceState.selectionState {
if selectionState.selectedIds.count > 0 {
strongSelf.chatTitleView?.titleContent = .custom(strongSelf.presentationData.strings.Conversation_SelectedMessages(Int32(selectionState.selectedIds.count ?? 1)), nil, false)
strongSelf.chatTitleView?.titleContent = .custom(strongSelf.presentationData.strings.Conversation_SelectedMessages(Int32(selectionState.selectedIds.count)), nil, false)
} else {
if let reportReason = presentationInterfaceState.reportReason {
strongSelf.chatTitleView?.titleContent = .custom("Report Smth", strongSelf.presentationInterfaceState.strings.Conversation_SelectMessages, false)
let title: String
switch reportReason {
case .spam:
title = strongSelf.presentationData.strings.ReportPeer_ReasonSpam
case .fake:
title = strongSelf.presentationData.strings.ReportPeer_ReasonFake
case .violence:
title = strongSelf.presentationData.strings.ReportPeer_ReasonViolence
case .porno:
title = strongSelf.presentationData.strings.ReportPeer_ReasonPornography
case .childAbuse:
title = strongSelf.presentationData.strings.ReportPeer_ReasonChildAbuse
case .copyright:
title = strongSelf.presentationData.strings.ReportPeer_ReasonCopyright
case .custom:
title = strongSelf.presentationData.strings.ReportPeer_ReasonOther
case .irrelevantLocation:
title = ""
}
strongSelf.chatTitleView?.titleContent = .custom(title, strongSelf.presentationInterfaceState.strings.Conversation_SelectMessages, false)
} else {
strongSelf.chatTitleView?.titleContent = .custom(strongSelf.presentationInterfaceState.strings.Conversation_SelectMessages, nil, false)
}
@ -4753,13 +4772,16 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
self?.view.window?.endEditing(true)
controller?.dismissAnimated()
}
var message = ""
var items: [ActionSheetItem] = []
items.append(ReportPeerHeaderActionSheetItem(context: strongSelf.context, text: presentationData.strings.Report_AdditionalDetailsText))
items.append(ReportPeerDetailsActionSheetItem(context: strongSelf.context, placeholderText: presentationData.strings.Report_AdditionalDetailsPlaceholder))
items.append(ReportPeerDetailsActionSheetItem(context: strongSelf.context, placeholderText: presentationData.strings.Report_AdditionalDetailsPlaceholder, textUpdated: { text in
message = text
}))
items.append(ActionSheetButtonItem(title: presentationData.strings.Report_Report, color: .accent, font: .bold, enabled: true, action: {
dismissAction()
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, { $0.updatedInterfaceState { $0.withoutSelectionState() } }, completion: { _ in
let _ = (reportPeerMessages(account: strongSelf.context.account, messageIds: Array(messageIds), reason: reportReason)
let _ = (reportPeerMessages(account: strongSelf.context.account, messageIds: Array(messageIds), reason: reportReason, message: message)
|> deliverOnMainQueue).start(completed: { [weak self] in
if let strongSelf = self, let path = getAppBundle().path(forResource: "PoliceCar", ofType: "tgs") {
strongSelf.present(UndoOverlayController(presentationData: presentationData, content: .emoji(path: path, text: presentationData.strings.Report_Succeed), elevatedLayout: false, action: { _ in return false }), in: .current)
@ -6207,7 +6229,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
guard let strongSelf = self else {
return
}
strongSelf.reportIrrelvantGeoDisposable = (TelegramCore.reportPeer(account: strongSelf.context.account, peerId: peerId, reason: .irrelevantLocation)
strongSelf.reportIrrelvantGeoDisposable = (TelegramCore.reportPeer(account: strongSelf.context.account, peerId: peerId, reason: .irrelevantLocation, message: "")
|> deliverOnMainQueue).start(completed: { [weak self] in
if let strongSelf = self {
strongSelf.reportIrrelvantGeoNoticePromise.set(.single(true))
@ -10667,7 +10689,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
let _ = removePeerChat(account: strongSelf.context.account, peerId: chatPeer.id, reportChatSpam: reportSpam).start()
strongSelf.effectiveNavigationController?.filterController(strongSelf, animated: true)
} else if reportSpam {
let _ = TelegramCore.reportPeer(account: strongSelf.context.account, peerId: peer.id, reason: .spam).start()
let _ = TelegramCore.reportPeer(account: strongSelf.context.account, peerId: peer.id, reason: .spam, message: "").start()
}
})
] as [ActionSheetItem])

View File

@ -716,7 +716,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable {
(.canPostMessages, self.presentationData.strings.Channel_AdminLog_CanSendMessages),
(.canDeleteMessages, self.presentationData.strings.Channel_AdminLog_CanDeleteMessagesOfOthers),
(.canEditMessages, self.presentationData.strings.Channel_AdminLog_CanEditMessages),
(.canInviteUsers, self.presentationData.strings.Channel_AdminLog_CanInviteUsers),
(.canInviteUsers, self.presentationData.strings.Channel_AdminLog_CanInviteUsersViaLink),
(.canPinMessages, self.presentationData.strings.Channel_AdminLog_CanPinMessages),
(.canAddAdmins, self.presentationData.strings.Channel_AdminLog_CanAddAdmins),
(.canManageCalls, self.presentationData.strings.Channel_AdminLog_CanManageCalls)
@ -726,7 +726,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable {
(.canChangeInfo, self.presentationData.strings.Channel_AdminLog_CanChangeInfo),
(.canDeleteMessages, self.presentationData.strings.Channel_AdminLog_CanDeleteMessages),
(.canBanUsers, self.presentationData.strings.Channel_AdminLog_CanBanUsers),
(.canInviteUsers, self.presentationData.strings.Channel_AdminLog_CanInviteUsers),
(.canInviteUsers, self.presentationData.strings.Channel_AdminLog_CanInviteUsersViaLink),
(.canPinMessages, self.presentationData.strings.Channel_AdminLog_CanPinMessages),
(.canBeAnonymous, self.presentationData.strings.Channel_AdminLog_CanBeAnonymous),
(.canAddAdmins, self.presentationData.strings.Channel_AdminLog_CanAddAdmins),
@ -1192,6 +1192,8 @@ struct ChatRecentActionsEntry: Comparable, Identifiable {
appendAttributedText(text: rawText, generateEntities: { index in
if index == 0, let author = author {
return [.TextMention(peerId: author.id)]
} else if index == 1 {
return [.Bold]
}
return []
}, to: &text, entities: &entities)
@ -1211,11 +1213,13 @@ struct ChatRecentActionsEntry: Comparable, Identifiable {
var text: String = ""
var entities: [MessageTextEntity] = []
let rawText: (String, [(Int, NSRange)]) = self.presentationData.strings.Channel_AdminLog_DeletedInviteLink(author?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "", invite.link)
let rawText: (String, [(Int, NSRange)]) = self.presentationData.strings.Channel_AdminLog_DeletedInviteLink(author?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "", invite.link.replacingOccurrences(of: "https://", with: ""))
appendAttributedText(text: rawText, generateEntities: { index in
if index == 0, let author = author {
return [.TextMention(peerId: author.id)]
} else if index == 1 {
return [.Bold]
}
return []
}, to: &text, entities: &entities)
@ -1235,11 +1239,13 @@ struct ChatRecentActionsEntry: Comparable, Identifiable {
var text: String = ""
var entities: [MessageTextEntity] = []
let rawText: (String, [(Int, NSRange)]) = self.presentationData.strings.Channel_AdminLog_RevokedInviteLink(author?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "", invite.link)
let rawText: (String, [(Int, NSRange)]) = self.presentationData.strings.Channel_AdminLog_RevokedInviteLink(author?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "", invite.link.replacingOccurrences(of: "https://", with: ""))
appendAttributedText(text: rawText, generateEntities: { index in
if index == 0, let author = author {
return [.TextMention(peerId: author.id)]
} else if index == 1 {
return [.Bold]
}
return []
}, to: &text, entities: &entities)
@ -1259,11 +1265,39 @@ struct ChatRecentActionsEntry: Comparable, Identifiable {
var text: String = ""
var entities: [MessageTextEntity] = []
let rawText: (String, [(Int, NSRange)]) = self.presentationData.strings.Channel_AdminLog_EditedInviteLink(author?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "", updatedInvite.link)
let rawText: (String, [(Int, NSRange)]) = self.presentationData.strings.Channel_AdminLog_EditedInviteLink(author?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "", updatedInvite.link.replacingOccurrences(of: "https://", with: ""))
appendAttributedText(text: rawText, generateEntities: { index in
if index == 0, let author = author {
return [.TextMention(peerId: author.id)]
} else if index == 1 {
return [.Bold]
}
return []
}, to: &text, entities: &entities)
let action = TelegramMediaActionType.customText(text: text, entities: entities)
let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [])
return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes()))
case let .participantJoinedViaInvite(invite):
var peers = SimpleDictionary<PeerId, Peer>()
var author: Peer?
if let peer = self.entry.peers[self.entry.event.peerId] {
author = peer
peers[peer.id] = peer
}
var text: String = ""
var entities: [MessageTextEntity] = []
let rawText: (String, [(Int, NSRange)]) = self.presentationData.strings.Channel_AdminLog_JoinedViaInviteLink(author?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "", invite.link.replacingOccurrences(of: "https://", with: ""))
appendAttributedText(text: rawText, generateEntities: { index in
if index == 0, let author = author {
return [.TextMention(peerId: author.id)]
} else if index == 1 {
return [.Bold]
}
return []
}, to: &text, entities: &entities)

View File

@ -87,7 +87,7 @@ private func peerButtons(_ state: ChatPresentationInterfaceState) -> [ChatReport
buttons.append(.shareMyPhoneNumber)
}
}
} else if let _ = state.renderedPeer?.chatMainPeer {
} else if let _ = state.renderedPeer?.chatMainPeer, case .peer = state.chatLocation {
if let contactStatus = state.contactStatus, let peerStatusSettings = contactStatus.peerStatusSettings, peerStatusSettings.contains(.suggestAddMembers) {
buttons.append(.addMembers)
} else if let contactStatus = state.contactStatus, contactStatus.canReportIrrelevantLocation, let peerStatusSettings = contactStatus.peerStatusSettings, peerStatusSettings.contains(.canReportIrrelevantGeoLocation) {

View File

@ -656,7 +656,7 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
if currentInvitationsContext == nil {
var canManageInvitations = false
if let channel = peerViewMainPeer(peerView) as? TelegramChannel, let cachedData = peerView.cachedData as? CachedChannelData, channel.flags.contains(.isCreator) || ((channel.adminRights != nil && channel.hasPermission(.pinMessages)) && cachedData.flags.contains(.canChangeUsername)) {
if let channel = peerViewMainPeer(peerView) as? TelegramChannel, let cachedData = peerView.cachedData as? CachedChannelData, channel.flags.contains(.isCreator) || channel.hasPermission(.inviteMembers) {
canManageInvitations = true
}
if canManageInvitations {
@ -813,9 +813,13 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
if currentInvitationsContext == nil {
var canManageInvitations = false
if let group = peerViewMainPeer(peerView) as? TelegramGroup, case .creator = group.role {
canManageInvitations = true
} else if let channel = peerViewMainPeer(peerView) as? TelegramChannel, let cachedData = peerView.cachedData as? CachedChannelData, channel.flags.contains(.isCreator) || ((channel.adminRights != nil && channel.hasPermission(.pinMessages)) && cachedData.flags.contains(.canChangeUsername)) {
if let group = peerViewMainPeer(peerView) as? TelegramGroup {
if case .creator = group.role {
canManageInvitations = true
} else if case let .admin(rights, _) = group.role, rights.flags.contains(.canInviteUsers) {
canManageInvitations = true
}
} else if let channel = peerViewMainPeer(peerView) as? TelegramChannel, channel.flags.contains(.isCreator) || channel.hasPermission(.inviteMembers) {
canManageInvitations = true
}
if canManageInvitations {
@ -1081,14 +1085,33 @@ func peerInfoHeaderButtons(peer: Peer?, cachedData: CachedPeerData?, isOpenedFro
return result
}
func peerInfoCanEdit(peer: Peer?, cachedData: CachedPeerData?) -> Bool {
func peerInfoCanEdit(peer: Peer?, cachedData: CachedPeerData?, isContact: Bool?) -> Bool {
if let user = peer as? TelegramUser {
if user.isDeleted {
return false
}
if let isContact = isContact, !isContact {
return false
}
return true
} else if peer is TelegramChannel || peer is TelegramGroup {
return true
} else if let peer = peer as? TelegramChannel {
if peer.flags.contains(.isCreator) {
return true
} else if let adminRights = peer.adminRights, adminRights.flags.contains(.canAddAdmins) || adminRights.flags.contains(.canBanUsers) || adminRights.flags.contains(.canChangeInfo) || adminRights.flags.contains(.canInviteUsers) {
return true
}
return false
} else if let peer = peer as? TelegramGroup {
if case .creator = peer.role {
return true
} else if case let .admin(rights, _) = peer.role {
if rights.flags.contains(.canAddAdmins) || rights.flags.contains(.canBanUsers) || rights.flags.contains(.canChangeInfo) || rights.flags.contains(.canInviteUsers) {
return true
}
return false
} else {
return false
}
}
return false
}

View File

@ -1201,18 +1201,19 @@ private func editingItems(data: PeerInfoScreenData?, context: AccountContext, pr
}))
}
if channel.flags.contains(.isCreator) || (channel.adminRights != nil && channel.hasPermission(.pinMessages)) {
if channel.flags.contains(.isCreator) || (channel.adminRights?.flags.contains(.canInviteUsers) == true) {
let invitesText: String
if let count = data.invitations?.count, count > 0 {
invitesText = "\(count)"
} else {
invitesText = ""
}
items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemInviteLinks, label: .text(invitesText), text: presentationData.strings.GroupInfo_InviteLinks, icon: UIImage(bundleImageName: "Chat/Info/GroupLinksIcon"), action: {
interaction.editingOpenInviteLinksSetup()
}))
}
if channel.flags.contains(.isCreator) || (channel.adminRights != nil && channel.hasPermission(.pinMessages)) {
let discussionGroupTitle: String
if let _ = data.cachedData as? CachedChannelData {
if let peer = data.linkedDiscussionPeer {
@ -1336,39 +1337,22 @@ private func editingItems(data: PeerInfoScreenData?, context: AccountContext, pr
interaction.editingOpenPreHistorySetup()
}))
}
if channel.hasPermission(.inviteMembers) {
let invitesText: String
if let count = data.invitations?.count, count > 0 {
invitesText = "\(count)"
} else {
invitesText = ""
}
items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemInviteLinks, label: .text(invitesText), text: presentationData.strings.GroupInfo_InviteLinks, icon: UIImage(bundleImageName: "Chat/Info/GroupLinksIcon"), action: {
interaction.editingOpenInviteLinksSetup()
}))
}
/*if channel.hasPermission(.changeInfo) {
let timeoutString: String
if case let .known(value) = cachedData.autoremoveTimeout {
if let value = value?.effectiveValue {
timeoutString = timeIntervalString(strings: presentationData.strings, value: value)
} else {
timeoutString = presentationData.strings.PeerInfo_AutoremoveMessagesDisabled
}
} else {
timeoutString = ""
}
items[.peerPublicSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemAutoremove, label: .text(timeoutString), text: presentationData.strings.PeerInfo_AutoremoveMessages, action: {
interaction.editingOpenAutoremoveMesages()
}))
}*/
}
}
if isCreator || (channel.hasPermission(.inviteMembers)) {
let invitesText: String
if let count = data.invitations?.count, count > 0 {
invitesText = "\(count)"
} else {
invitesText = ""
}
items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemInviteLinks, label: .text(invitesText), text: presentationData.strings.GroupInfo_InviteLinks, icon: UIImage(bundleImageName: "Chat/Info/GroupLinksIcon"), action: {
interaction.editingOpenInviteLinksSetup()
}))
}
if cachedData.flags.contains(.canSetStickerSet) && canEditPeerInfo(context: context, peer: channel) {
items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemStickerPack, label: .text(cachedData.stickerPack?.title ?? presentationData.strings.GroupInfo_SharedMediaNone), text: presentationData.strings.Stickers_GroupStickers, icon: UIImage(bundleImageName: "Settings/MenuIcons/Stickers"), action: {
interaction.editingOpenStickerPackSetup()
@ -1472,8 +1456,19 @@ private func editingItems(data: PeerInfoScreenData?, context: AccountContext, pr
items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemAdmins, text: presentationData.strings.GroupInfo_Administrators, icon: UIImage(bundleImageName: "Chat/Info/GroupAdminsIcon"), action: {
interaction.openParticipantsSection(.admins)
}))
} else if case .admin = group.role {
} else if case let .admin(rights, _) = group.role {
if rights.flags.contains(.canInviteUsers) {
let invitesText: String
if let count = data.invitations?.count, count > 0 {
invitesText = "\(count)"
} else {
invitesText = ""
}
items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemInviteLinks, label: .text(invitesText), text: presentationData.strings.GroupInfo_InviteLinks, icon: UIImage(bundleImageName: "Chat/Info/GroupLinksIcon"), action: {
interaction.editingOpenInviteLinksSetup()
}))
}
}
}
}
@ -3744,7 +3739,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
let _ = removePeerChat(account: strongSelf.context.account, peerId: strongSelf.peerId, reportChatSpam: reportSpam).start()
(strongSelf.controller?.navigationController as? NavigationController)?.popToRoot(animated: true)
} else if reportSpam {
let _ = reportPeer(account: strongSelf.context.account, peerId: strongSelf.peerId, reason: .spam).start()
let _ = reportPeer(account: strongSelf.context.account, peerId: strongSelf.peerId, reason: .spam, message: "").start()
}
deleteSendMessageIntents(peerId: strongSelf.peerId)
@ -3875,6 +3870,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
}
private func editingOpenInviteLinksSetup() {
self.controller?.push(inviteLinkListController(context: self.context, peerId: self.peerId, admin: nil))
}
@ -5307,7 +5303,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
if self.isSettings {
navigationButtons.append(PeerInfoHeaderNavigationButtonSpec(key: .edit, isForExpandedView: false))
navigationButtons.append(PeerInfoHeaderNavigationButtonSpec(key: .search, isForExpandedView: true))
} else if peerInfoCanEdit(peer: self.data?.peer, cachedData: self.data?.cachedData) {
} else if peerInfoCanEdit(peer: self.data?.peer, cachedData: self.data?.cachedData, isContact: self.data?.isContact) {
navigationButtons.append(PeerInfoHeaderNavigationButtonSpec(key: .edit, isForExpandedView: false))
}
if self.state.selectedMessageIds == nil {
@ -6230,14 +6226,16 @@ func presentAddMembers(context: AccountContext, parentController: ViewController
var canCreateInviteLink = false
if let group = groupPeer as? TelegramGroup {
switch group.role {
case .creator, .admin:
case .creator:
canCreateInviteLink = true
case let .admin(rights, _):
canCreateInviteLink = rights.flags.contains(.canInviteUsers)
default:
break
}
} else if let channel = groupPeer as? TelegramChannel {
if channel.hasPermission(.inviteMembers) {
if channel.flags.contains(.isCreator) || (channel.adminRights != nil && channel.username == nil) {
if channel.flags.contains(.isCreator) || (channel.hasPermission(.inviteMembers)) {
canCreateInviteLink = true
}
}

View File

@ -13,14 +13,16 @@ import AppBundle
public final class ReportPeerDetailsActionSheetItem: ActionSheetItem {
let context: AccountContext
let placeholderText: String
let textUpdated: (String) -> Void
public init(context: AccountContext, placeholderText: String) {
public init(context: AccountContext, placeholderText: String, textUpdated: @escaping (String) -> Void) {
self.context = context
self.placeholderText = placeholderText
self.textUpdated = textUpdated
}
public func node(theme: ActionSheetControllerTheme) -> ActionSheetItemNode {
return ReportPeerDetailsActionSheetItemNode(theme: theme, context: self.context, placeholderText: self.placeholderText)
return ReportPeerDetailsActionSheetItemNode(theme: theme, context: self.context, placeholderText: self.placeholderText, textUpdated: self.textUpdated)
}
public func updateNode(_ node: ActionSheetItemNode) {
@ -34,7 +36,7 @@ private final class ReportPeerDetailsActionSheetItemNode: ActionSheetItemNode {
private let accessibilityArea: AccessibilityAreaNode
init(theme: ActionSheetControllerTheme, context: AccountContext, placeholderText: String) {
init(theme: ActionSheetControllerTheme, context: AccountContext, placeholderText: String, textUpdated: @escaping (String) -> Void) {
self.theme = theme
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
@ -48,7 +50,12 @@ private final class ReportPeerDetailsActionSheetItemNode: ActionSheetItemNode {
self.addSubnode(self.inputFieldNode)
// self.inputFieldNode.
self.inputFieldNode.updateText = { text in
textUpdated(text)
}
self.inputFieldNode.updateHeight = {
}
}
override func calculateSizeThatFits(_ constrainedSize: CGSize) -> CGSize {

View File

@ -207,7 +207,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
self.textNode.attributedText = attributedText
self.textNode.maximumNumberOfLines = 2
displayUndo = false
self.originalRemainingSeconds = 4
self.originalRemainingSeconds = 3
case let .banned(text):
self.avatarNode = nil
self.iconNode = nil