Merge branch 'master' into experiments/parallel-swiftmodule

This commit is contained in:
Ali 2020-10-27 01:17:54 +04:00
commit eafbe0dc23
153 changed files with 6884 additions and 5870 deletions

View file

@ -3,7 +3,7 @@
include Utils.makefile
APP_VERSION="7.1.2"
APP_VERSION="7.2"
CORE_COUNT=$(shell sysctl -n hw.logicalcpu)
CORE_COUNT_MINUS_ONE=$(shell expr ${CORE_COUNT} \- 1)

View file

@ -65,22 +65,9 @@
"PUSH_CHANNEL_MESSAGE_GEOLIVE" = "%1$@|posted a live location";
"PUSH_CHANNEL_MESSAGE_GIF" = "%1$@|posted a GIF";
"PUSH_PINNED_TEXT" = "%1$@ pinned \"%2$@\" ";
"PUSH_PINNED_NOTEXT" = "%1$@ pinned a message";
"PUSH_PINNED_PHOTO" = "%1$@ pinned a photo";
"PUSH_PINNED_VIDEO" = "%1$@ pinned a video";
"PUSH_PINNED_ROUND" = "%1$@ pinned a video message";
"PUSH_PINNED_DOC" = "%1$@ pinned a file";
"PUSH_PINNED_STICKER" = "%1$@ pinned a %2$@sticker";
"PUSH_PINNED_AUDIO" = "%1$@ pinned a voice message";
"PUSH_PINNED_GEO" = "%1$@ pinned a map";
"PUSH_PINNED_GEOLIVE" = "%1$@ pinned a live location";
"PUSH_PINNED_GIF" = "%1$@ pinned a GIF";
"PUSH_MESSAGE_GAME" = "%1$@|invited you to play %2$@";
"PUSH_CHANNEL_MESSAGE_GAME" = "%1$@|invited you to play %2$@";
"PUSH_CHAT_MESSAGE_GAME" = "%2$@|%1$@ invited the group to play %3$@";
"PUSH_PINNED_GAME" = "%1$@ pinned a game";
"PUSH_MESSAGE_TEXT" = "%1$@|%2$@";
"PUSH_MESSAGE_NOTEXT" = "%1$@|sent you a message";
@ -117,6 +104,10 @@
"PUSH_MESSAGES_1" = "%1$@|sent you a message";
"PUSH_MESSAGES_any" = "%1$@|sent you %2$d messages";
"PUSH_ALBUM" = "%1$@|sent you an album";
"PUSH_MESSAGE_DOCS" = "%1$@|sent you %2$d files";
"PUSH_MESSAGE_DOCS_1" = "%1$@|sent you a file";
"PUSH_MESSAGE_DOCS_any" = "%1$@|sent you %2$d files";
"PUSH_CHANNEL_MESSAGE_TEXT" = "%1$@|%2$@";
"PUSH_CHANNEL_MESSAGE_NOTEXT" = "%1$@|posted a message";
@ -149,6 +140,9 @@
"PUSH_CHANNEL_MESSAGES_1" = "%1$@|posted a message";
"PUSH_CHANNEL_MESSAGES_any" = "%1$@|posted %2$d messages";
"PUSH_CHANNEL_ALBUM" = "%1$@|posted an album";
"PUSH_CHANNEL_MESSAGE_DOCS" = "%1$@|posted %2$d files";
"PUSH_CHANNEL_MESSAGE_DOCS_1" = "%1$@|posted a file";
"PUSH_CHANNEL_MESSAGE_DOCS_any" = "%1$@|posted %2$d files";
"PUSH_CHAT_MESSAGE_TEXT" = "%2$@|%1$@:%3$@";
"PUSH_CHAT_MESSAGE_NOTEXT" = "%2$@|%1$@ sent a message to the group";
@ -191,6 +185,9 @@
"PUSH_CHAT_MESSAGES_1" = "%2$@|%1$@ sent a message";
"PUSH_CHAT_MESSAGES_any" = "%2$@|%1$@ sent %3$d messages";
"PUSH_CHAT_ALBUM" = "%2$@|%1$@ sent an album";
"PUSH_CHAT_MESSAGE_DOCS" = "%2$@|%1$@ sent %3$d files";
"PUSH_CHAT_MESSAGE_DOCS_1" = "%2$@|%1$@ sent a file";
"PUSH_CHAT_MESSAGE_DOCS_any" = "%2$@|%1$@ sent %3$d files";
"PUSH_PINNED_TEXT" = "%1$@|pinned \"%2$@\" ";
"PUSH_PINNED_NOTEXT" = "%1$@|pinned a message";
@ -1954,6 +1951,7 @@
"Message.PinnedStickerMessage" = "pinned sticker";
"Message.PinnedLocationMessage" = "pinned location";
"Message.PinnedContactMessage" = "pinned contact";
"Message.PinnedGenericMessage" = "%@ pinned a message";
"Notification.PinnedMessage" = "pinned message";
@ -5822,7 +5820,7 @@ Any member of this group will be able to see messages in the channel.";
"Conversation.ContextViewStats" = "View Statistics";
"ChatList.MessageMusic_1" = "1 Music File";
"ChatList.MessageMusic_1" = "%@ Music File";
"ChatList.MessageMusic_any" = "%@ Music Files";
"Conversation.PinOlderMessageAlertTitle" = "Pin Message";
@ -5840,7 +5838,8 @@ Any member of this group will be able to see messages in the channel.";
"Conversation.Dice.u1F3B0" = "Send a slot machine emoji to try your luck.";
"Notification.ProximityReached" = "%1$@ is now within %2$@ from you";
"Notification.ProximityReached" = "%1$@ is now within %2$@ from %3$@";
"Notification.ProximityReachedYou" = "%1$@ is now within %2$@ from you";
"Location.ProximityNotification.Title" = "Notification";
"Location.ProximityNotification.Notify" = "Notify me within %@";
@ -5848,24 +5847,37 @@ Any member of this group will be able to see messages in the channel.";
"Location.ProximityNotification.DistanceKM" = "KM";
"Location.ProximityNotification.DistanceMI" = "MI";
"Location.LiveLocationRequired.Title" = "Share Location";
"Location.LiveLocationRequired.Description" = "For the alert to work, please share your live location in this chat.";
"Location.LiveLocationRequired.ShareLocation" = "Share Location";
"Location.ProximityTip" = "Alert when %@ is close";
"Location.ProximityGroupTip" = "Alert when any group member is close";
"ChatList.MessageFiles_1" = "%@ File";
"ChatList.MessageFiles_any" = "%@ Files";
"Chat.TitlePinnedMessages_1" = "Pinned Message";
"Chat.TitlePinnedMessages_any" = "%@ Pinned Messages";
"Chat.PanelHidePinnedMessages" = "Don't Show Pinned Messages";
"Chat.PanelUnpinAllMessages_1" = "Unpin Message";
"Chat.PanelUnpinAllMessages_any" = "Unpin All %@ Messages";
"Chat.UnpinAllMessagesConfirmation_1" = "Do you want to unpin 1 message in this chat?";
"Chat.UnpinAllMessagesConfirmation_any" = "Do you want to unpin all %@ messages in this chat?";
"Chat.PanelUnpinAllMessages" = "Unpin All Messages";
"Chat.MessagesUnpinned_1" = "Message Unpinned";
"Chat.MessagesUnpinned_any" = "%@ Messages Unpinned";
"Chat.PinnedMessagesHiddenTitle" = "Pinned Messages Hidden";
"Chat.PinnedMessagesHiddenText" = "You will see the bar with pinned messages only if a new message is pinned.";
"OpenFile.PotentiallyDangerousContentAlert" = "Previewing this file can potentially expose your IP address to its sender. Continue?";
"Chat.PinnedListPreview.ShowAllMessages" = "Show All Messages";
"Chat.PinnedListPreview.UnpinAllMessages" = "Unpin All Messages";
"Chat.PinnedListPreview.HidePinnedMessages" = "Hide Pinned Messages";
"Conversation.PinMessagesForMe" = "Pin for me";
"Conversation.PinMessagesFor" = "Pin for me and %@";
"Location.LiveLocationRequired.Title" = "Share Location";
"Location.LiveLocationRequired.Description" = "For the alert to work, please share your live location in this chat.";
"Location.LiveLocationRequired.ShareLocation" = "Share Location";
"Stats.Message.Views" = "Views";
"Stats.Message.PublicShares" = "Public Shares";
"Stats.Message.PrivateShares" = "Private Shares";

View file

@ -1,12 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder.WatchKit.Storyboard" version="3.0" toolsVersion="14313.18" targetRuntime="watchKit" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="umr-Wa-jBL">
<device id="watch38" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<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">
<device id="watch38"/>
<dependencies>
<deployment identifier="watchOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14283.14"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBWatchKitPlugin" version="14238.10"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17125"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBWatchKitPlugin" version="17034"/>
</dependencies>
<scenes>
<!--TGNeoConversationController-->
@ -1097,432 +1094,6 @@ contacts found.</string>
</objects>
<point key="canvasLocation" x="1130" y="463"/>
</scene>
<!--Static M-->
<scene sceneID="AEw-b0-oYE">
<objects>
<notificationController backgroundImage="BubbleNotification" spacing="0.0" id="YCC-NB-fut" userLabel="Static M">
<items>
<group width="1" alignment="left" radius="0.0" id="48N-YZ-6wz">
<items>
<label alignment="left" text="Text" numberOfLines="0" id="D1w-ZG-PyH">
<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="JfB-70-Muf">
<color key="titleColor" red="0.10051588714122772" green="0.10051287710666656" blue="0.10051460564136505" 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="D1w-ZG-PyH" id="jL5-6Q-Ttn"/>
<segue destination="dSi-eZ-mbH" kind="relationship" relationship="dynamicNotificationInterface" id="vxV-TA-VJY"/>
</connections>
</notificationController>
</objects>
<point key="canvasLocation" x="263" y="875"/>
</scene>
<!--Static R-->
<scene sceneID="Cez-Gq-mIP">
<objects>
<notificationController backgroundImage="BubbleNotification" spacing="0.0" id="duo-za-GTK" userLabel="Static R">
<items>
<group width="1" alignment="left" layout="vertical" radius="0.0" id="bh3-Uy-SPM">
<items>
<label alignment="left" text="Text" numberOfLines="0" id="bud-Nf-P9v">
<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="OCD-E6-DAX">
<color key="titleColor" red="0.10051588714122772" green="0.10051287710666656" blue="0.10051460564136505" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color key="sashColor" red="1" green="0.99997437000274658" blue="0.99999129772186279" 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="bud-Nf-P9v" id="380-p3-pxh"/>
<segue destination="Km6-N5-yeb" kind="relationship" relationship="dynamicNotificationInterface" id="D9i-4a-BEc"/>
</connections>
</notificationController>
</objects>
<point key="canvasLocation" x="268" y="1414"/>
</scene>
<!--Dynamic R-->
<scene sceneID="v88-RH-0Ql">
<objects>
<controller backgroundImage="BubbleNotification" spacing="0.0" id="Km6-N5-yeb" userLabel="Dynamic R" customClass="TGNotificationController">
<items>
<group width="1" alignment="left" layout="vertical" radius="0.0" spacing="0.0" id="mfC-xM-cxy">
<items>
<group width="1" alignment="left" layout="vertical" spacing="0.0" id="wyb-qB-Qel">
<items>
<label alignment="left" hidden="YES" text="Chat Title" id="nY4-PY-ouY">
<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="7s1-eh-fbh">
<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="7ca-gR-lQ3">
<items>
<label alignment="left" verticalAlignment="center" text="Forwarded from" id="ugR-GT-tRD" 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="t2S-5n-2vv" userLabel="ForwardFrom">
<color key="textColor" red="0.11312995851039886" green="0.50641471147537231" blue="0.96399867534637451" 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="mzm-dj-ikh" userLabel="ReplyHeader">
<items>
<group width="2" height="26" alignment="left" verticalAlignment="center" radius="0.0" spacing="0.0" id="yEy-WL-su3" userLabel="ReplyLine">
<color key="backgroundColor" red="0.11312995851039886" green="0.50641471147537231" blue="0.96399867534637451" 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="6N2-x0-qTe" 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="9WC-D4-VbM" userLabel="ReplyMessage">
<items>
<label alignment="left" text="Name" id="W56-AY-6jy" userLabel="ReplyAuthor">
<color key="textColor" red="0.11312995851039886" green="0.50641471147537231" blue="0.96399867534637451" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<fontDescription key="font" type="system" weight="medium" pointSize="12"/>
</label>
<label alignment="left" text="Text" id="kk0-zw-fCl" 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="nQW-fB-pYI">
<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="1il-cr-5K0" userLabel="WrapperGroup">
<items>
<group width="1" alignment="left" layout="vertical" radius="10" spacing="0.0" id="JJt-07-p6x" userLabel="LocationGroup">
<items>
<map height="92" alignment="left" id="ugQ-yo-LS8"/>
</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="ANN-OG-5Ze" userLabel="FileGroup">
<items>
<imageView width="26" height="26" alignment="left" verticalAlignment="center" hidden="YES" image="Location" contentMode="center" id="lG8-D0-tJV" userLabel="VenueIcon">
<color key="tintColor" red="0.35566622018814087" green="0.68838506937026978" blue="0.91561108827590942" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</imageView>
<group width="26" height="26" alignment="left" verticalAlignment="center" hidden="YES" radius="13" spacing="0.0" id="RaH-Pv-V61" userLabel="AudioGroup">
<items>
<imageView width="26" height="26" alignment="left" image="MediaAudioPlay" contentMode="center" id="Hcb-8c-4I2"/>
</items>
<color key="backgroundColor" red="0.35566622018814087" green="0.68838506937026978" blue="0.91561108827590942" 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="fSc-ay-AOb" userLabel="FileIconGroup">
<items>
<imageView alignment="center" verticalAlignment="center" image="File.png" contentMode="center" id="cdA-4F-D0E">
<color key="tintColor" red="0.14697439968585968" green="0.56079143285751343" blue="0.88162887096405029" 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="wNt-UG-hOY" userLabel="FileMetaGroup">
<items>
<label alignment="left" text="File Name" id="4kg-td-4gi">
<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="eca-TA-utp">
<color key="textColor" red="0.41865724325180054" green="0.41825520992279053" blue="0.43064218759536743" 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="JwB-Mx-wo3" userLabel="StickerWrapper">
<items>
<group width="84" height="84" alignment="left" contentMode="scaleAspectFit" id="l8v-gE-XXa" 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="TlV-9o-bQ0" userLabel="MediaGroup">
<items>
<group alignment="right" verticalAlignment="bottom" radius="10" id="bII-EE-L0C" userLabel="DurationGroup">
<items>
<label alignment="left" text="2:34" id="DT6-b5-gtK" userLabel="Duration">
<color key="textColor" red="0.24618944525718689" green="0.24618205428123474" blue="0.24618625640869141" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<fontDescription key="font" type="system" pointSize="12"/>
</label>
</items>
<color key="backgroundColor" red="0.89292949438095093" green="0.91148859262466431" blue="0.93112039566040039" 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="0to-MY-UuU" userLabel="CaptionGroup">
<items>
<label alignment="left" text="Caption" numberOfLines="0" id="kaM-1v-3k6">
<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="RaH-Pv-V61" id="aSC-wS-UlL"/>
<outlet property="captionGroup" destination="0to-MY-UuU" id="3Qb-RO-mt0"/>
<outlet property="captionLabel" destination="kaM-1v-3k6" id="Hdx-Uo-2pn"/>
<outlet property="chatTitleLabel" destination="nY4-PY-ouY" id="eKb-F4-IDZ"/>
<outlet property="durationGroup" destination="bII-EE-L0C" id="i4e-JX-dgt"/>
<outlet property="durationLabel" destination="DT6-b5-gtK" id="zeN-m6-tAn"/>
<outlet property="fileGroup" destination="ANN-OG-5Ze" id="Xvn-kD-ijA"/>
<outlet property="fileIconGroup" destination="fSc-ay-AOb" id="A6N-g4-8fv"/>
<outlet property="forwardFromLabel" destination="t2S-5n-2vv" id="8Oi-9Y-2Ns"/>
<outlet property="forwardHeaderGroup" destination="7ca-gR-lQ3" id="zs1-fj-L1I"/>
<outlet property="forwardTitleLabel" destination="ugR-GT-tRD" id="1X1-vd-ZQa"/>
<outlet property="map" destination="ugQ-yo-LS8" id="7DS-eu-d65"/>
<outlet property="mapGroup" destination="JJt-07-p6x" id="xDn-aB-wYf"/>
<outlet property="mediaGroup" destination="TlV-9o-bQ0" id="Iyf-d8-jpI"/>
<outlet property="messageTextLabel" destination="nQW-fB-pYI" id="S2O-rN-7Sm"/>
<outlet property="nameLabel" destination="7s1-eh-fbh" id="A3Q-xr-EMc"/>
<outlet property="replyAuthorNameLabel" destination="W56-AY-6jy" id="z6z-jA-Qjn"/>
<outlet property="replyHeaderGroup" destination="mzm-dj-ikh" id="tIx-pO-cmO"/>
<outlet property="replyHeaderImageGroup" destination="6N2-x0-qTe" id="Ss5-RZ-eLX"/>
<outlet property="replyMessageTextLabel" destination="kk0-zw-fCl" id="HQ6-Ia-JdV"/>
<outlet property="stickerGroup" destination="l8v-gE-XXa" id="Rv0-mx-Gif"/>
<outlet property="stickerWrapperGroup" destination="JwB-Mx-wo3" id="Fey-0F-yeS"/>
<outlet property="subtitleLabel" destination="eca-TA-utp" id="5Q6-0R-mLP"/>
<outlet property="titleLabel" destination="4kg-td-4gi" id="eFn-0c-ZNU"/>
<outlet property="venueIcon" destination="lG8-D0-tJV" id="RJo-6C-tAD"/>
<outlet property="wrapperGroup" destination="1il-cr-5K0" id="dHU-Wx-4Zd"/>
</connections>
</controller>
</objects>
<point key="canvasLocation" x="503" y="1413.75"/>
</scene>
<!--Dynamic M-->
<scene sceneID="cqp-oW-egv">
<objects>
<controller backgroundImage="BubbleNotification" id="dSi-eZ-mbH" userLabel="Dynamic M" customClass="TGNotificationController">
<items>
<group width="1" alignment="left" layout="vertical" radius="0.0" spacing="0.0" id="NQT-HX-BaM">
<items>
<group width="1" alignment="left" layout="vertical" spacing="0.0" id="NU6-vv-ud2">
<items>
<label alignment="left" hidden="YES" text="Chat Title" id="nxH-Wj-uqy">
<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="3Z9-Rd-bAn">
<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="Xq9-eF-ZCN">
<items>
<label alignment="left" verticalAlignment="center" text="Forwarded from" id="skX-7L-Ch5" 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="0Za-Qu-4HB" userLabel="ForwardFrom">
<color key="textColor" red="0.11312995851039886" green="0.50641471147537231" blue="0.96399867534637451" 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="HRa-PM-Aqr" userLabel="ReplyHeader">
<items>
<group width="2" height="26" alignment="left" verticalAlignment="center" radius="0.0" spacing="0.0" id="HDc-Da-N0m" userLabel="ReplyLine">
<color key="backgroundColor" red="0.11312995851039886" green="0.50641471147537231" blue="0.96399867534637451" 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="gdR-w4-b1Y" 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="MVH-mF-cZ8" userLabel="ReplyMessage">
<items>
<label alignment="left" text="Name" id="hZA-Ux-hB3" userLabel="ReplyAuthor">
<color key="textColor" red="0.11312995851039886" green="0.50641471147537231" blue="0.96399867534637451" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<fontDescription key="font" type="system" weight="medium" pointSize="12"/>
</label>
<label alignment="left" text="Text" id="7DV-4a-9iO" 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="obt-pK-Aw0">
<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="V2H-mQ-YmP" userLabel="WrapperGroup">
<items>
<group width="1" alignment="left" layout="vertical" radius="10" spacing="0.0" id="T64-Bs-ZO0" userLabel="LocationGroup">
<items>
<map height="92" alignment="left" id="0qp-I0-Lit"/>
</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="Rgj-f7-zBW" userLabel="FileGroup">
<items>
<imageView width="26" height="26" alignment="left" verticalAlignment="center" hidden="YES" image="Location" contentMode="center" id="yrG-g1-vgS" userLabel="VenueIcon">
<color key="tintColor" red="0.35566622018814087" green="0.68838506937026978" blue="0.91561108827590942" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</imageView>
<group width="26" height="26" alignment="left" verticalAlignment="center" hidden="YES" radius="13" spacing="0.0" id="Jhq-Fp-zJI" userLabel="AudioGroup">
<items>
<imageView width="26" height="26" alignment="left" image="MediaAudioPlay" contentMode="center" id="ReC-bD-5hg"/>
</items>
<color key="backgroundColor" red="0.35566622018814087" green="0.68838506937026978" blue="0.91561108827590942" 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="c6f-XR-VkD" userLabel="FileIconGroup">
<items>
<imageView alignment="center" verticalAlignment="center" image="File.png" contentMode="center" id="J4f-BW-IzH">
<color key="tintColor" red="0.14697439968585968" green="0.56079143285751343" blue="0.88162887096405029" 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="1FL-Q3-zR0" userLabel="FileMetaGroup">
<items>
<label alignment="left" text="File Name" id="vYh-VG-Kbb">
<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="yfu-XJ-oXa">
<color key="textColor" red="0.41865724325180054" green="0.41825520992279053" blue="0.43064218759536743" 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="WVZ-hR-PhJ" userLabel="StickerWrapper">
<items>
<group width="0.5" height="64" alignment="left" contentMode="scaleAspectFit" id="AzN-iK-uh2" 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="ppU-pp-l1S" userLabel="MediaGroup">
<items>
<group alignment="right" verticalAlignment="bottom" radius="10" id="O3C-f6-Vdb" userLabel="DurationGroup">
<items>
<label alignment="left" text="2:34" id="d4e-qi-dHW" userLabel="Duration">
<color key="textColor" red="0.24618944525718689" green="0.24618205428123474" blue="0.24618625640869141" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<fontDescription key="font" type="system" pointSize="12"/>
</label>
</items>
<color key="backgroundColor" red="0.89292949438095093" green="0.91148859262466431" blue="0.93112039566040039" 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="q49-gu-zF9" userLabel="CaptionGroup">
<items>
<label alignment="left" text="Caption" numberOfLines="0" id="DFG-U5-QhE">
<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="Jhq-Fp-zJI" id="td2-MW-fXd"/>
<outlet property="captionGroup" destination="q49-gu-zF9" id="VpF-Mn-NPc"/>
<outlet property="captionLabel" destination="DFG-U5-QhE" id="0HU-Lc-aA1"/>
<outlet property="chatTitleLabel" destination="nxH-Wj-uqy" id="M9k-c8-5Jv"/>
<outlet property="durationGroup" destination="O3C-f6-Vdb" id="oAU-5e-HbS"/>
<outlet property="durationLabel" destination="d4e-qi-dHW" id="g8n-fB-TBl"/>
<outlet property="fileGroup" destination="Rgj-f7-zBW" id="Oew-x9-kp2"/>
<outlet property="fileIconGroup" destination="c6f-XR-VkD" id="0vF-hC-7Rp"/>
<outlet property="forwardFromLabel" destination="0Za-Qu-4HB" id="nnp-Ld-lml"/>
<outlet property="forwardHeaderGroup" destination="Xq9-eF-ZCN" id="dNN-9b-RXQ"/>
<outlet property="forwardTitleLabel" destination="skX-7L-Ch5" id="kPe-OY-vjQ"/>
<outlet property="map" destination="0qp-I0-Lit" id="2qQ-iW-AFY"/>
<outlet property="mapGroup" destination="T64-Bs-ZO0" id="Sgr-JR-Db8"/>
<outlet property="mediaGroup" destination="ppU-pp-l1S" id="2M8-CP-Noy"/>
<outlet property="messageTextLabel" destination="obt-pK-Aw0" id="57m-0N-bX8"/>
<outlet property="nameLabel" destination="3Z9-Rd-bAn" id="8kM-mT-2ZN"/>
<outlet property="replyAuthorNameLabel" destination="hZA-Ux-hB3" id="hlt-W5-k5P"/>
<outlet property="replyHeaderGroup" destination="HRa-PM-Aqr" id="I10-gZ-KfR"/>
<outlet property="replyHeaderImageGroup" destination="gdR-w4-b1Y" id="qQS-ZH-fWl"/>
<outlet property="replyMessageTextLabel" destination="7DV-4a-9iO" id="Wzy-4A-GdY"/>
<outlet property="stickerGroup" destination="AzN-iK-uh2" id="Wgo-GU-lNF"/>
<outlet property="stickerWrapperGroup" destination="WVZ-hR-PhJ" id="aR5-Gb-GX2"/>
<outlet property="subtitleLabel" destination="yfu-XJ-oXa" id="Pf7-PQ-6rB"/>
<outlet property="titleLabel" destination="vYh-VG-Kbb" id="bXz-ee-o3E"/>
<outlet property="venueIcon" destination="yrG-g1-vgS" id="VWh-0b-QoX"/>
<outlet property="wrapperGroup" destination="V2H-mQ-YmP" id="LTE-m1-bVJ"/>
</connections>
</controller>
</objects>
<point key="canvasLocation" x="503" y="874.75"/>
</scene>
<!--Telegram-->
<scene sceneID="A9d-DL-l8r">
<objects>
@ -2000,4 +1571,20 @@ contacts found.</string>
</scene>
</scenes>
<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="File.png" 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"/>
<image name="MediaLocation" width="11" height="15"/>
<image name="MessageStatusDot" width="128" height="128"/>
<image name="MicAccessIcon" width="128" height="128"/>
<image name="MicIcon" width="128" height="128"/>
<image name="PickLocation" width="128" height="128"/>
<image name="RemotePlayVideo" width="128" height="128"/>
<image name="Spinner" width="128" height="128"/>
<image name="StickerIcon" width="128" height="128"/>
<image name="VerifiedProfile" width="128" height="128"/>
</resources>
</document>

View file

@ -38,7 +38,7 @@
+ (UIColor *)colorForUserId:(int32_t)userId myUserId:(int32_t)myUserId
{
return [self placeholderColors][userId % 7];
return [self placeholderColors][abs(userId) % 7];
}
+ (UIColor *)colorForGroupId:(int64_t)groupId

View file

@ -54,9 +54,11 @@ BAZEL_OPTIONS=(\
--swiftcopt=-j${CORE_COUNT_MINUS_ONE} \
)
if [ "$BAZEL_CACHE_DIR" != "" ]; then
if [ "$BAZEL_HTTP_CACHE_URL" != "" ]; then
BAZEL_OPTIONS=("${BAZEL_OPTIONS[@]}" --remote_cache="$(echo $BAZEL_HTTP_CACHE_URL | sed -e 's/[\/&]/\\&/g')")
elif [ "$BAZEL_CACHE_DIR" != "" ]; then
BAZEL_OPTIONS=("${BAZEL_OPTIONS[@]}" --disk_cache="$(echo $BAZEL_CACHE_DIR | sed -e 's/[\/&]/\\&/g')")
fi
fi
"$TULSI" -- \
--verbose \

View file

@ -0,0 +1,16 @@
load("//Config:buck_rule_macros.bzl", "static_library")
static_library(
name = "AnimatedNavigationStripeNode",
srcs = glob([
"Sources/**/*.swift",
]),
deps = [
"//submodules/Display:Display#shared",
"//submodules/AsyncDisplayKit:AsyncDisplayKit#shared",
],
frameworks = [
"$SDKROOT/System/Library/Frameworks/Foundation.framework",
"$SDKROOT/System/Library/Frameworks/UIKit.framework",
],
)

View file

@ -0,0 +1,16 @@
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
swift_library(
name = "AnimatedNavigationStripeNode",
module_name = "AnimatedNavigationStripeNode",
srcs = glob([
"Sources/**/*.swift",
]),
deps = [
"//submodules/Display:Display",
"//submodules/AsyncDisplayKit:AsyncDisplayKit",
],
visibility = [
"//visibility:public",
],
)

View file

@ -0,0 +1,299 @@
import Foundation
import UIKit
import Display
import AsyncDisplayKit
public final class AnimatedNavigationStripeNode: ASDisplayNode {
public struct Colors: Equatable {
public var foreground: UIColor
public var background: UIColor
public var clearBackground: UIColor
public init(
foreground: UIColor,
background: UIColor,
clearBackground: UIColor
) {
self.foreground = foreground
self.background = background
self.clearBackground = clearBackground
}
public static func ==(lhs: Colors, rhs: Colors) -> Bool {
if !lhs.foreground.isEqual(rhs.foreground) {
return false
}
if !lhs.background.isEqual(rhs.background) {
return false
}
if !lhs.clearBackground.isEqual(rhs.clearBackground) {
return false
}
return true
}
}
public struct Configuration: Equatable {
public var height: CGFloat
public var index: Int
public var count: Int
public init(height: CGFloat, index: Int, count: Int) {
self.height = height
self.index = index
self.count = count
}
}
private final class BackgroundLineNode {
let lineNode: ASImageNode
let overlayNode: ASImageNode
init() {
self.lineNode = ASImageNode()
self.overlayNode = ASImageNode()
}
}
private var currentColors: Colors?
private var currentConfiguration: Configuration?
private let foregroundLineNode: ASImageNode
private var backgroundLineNodes: [Int: BackgroundLineNode] = [:]
private var removingBackgroundLineNodes: [BackgroundLineNode] = []
private let topShadowNode: ASImageNode
private let bottomShadowNode: ASImageNode
private var currentForegroundImage: UIImage?
private var currentBackgroundImage: UIImage?
private var currentClearBackgroundImage: UIImage?
override public init() {
self.foregroundLineNode = ASImageNode()
self.topShadowNode = ASImageNode()
self.bottomShadowNode = ASImageNode()
super.init()
self.clipsToBounds = true
self.addSubnode(self.foregroundLineNode)
self.addSubnode(self.topShadowNode)
self.addSubnode(self.bottomShadowNode)
}
public func update(colors: Colors, configuration: Configuration, transition: ContainedViewLayoutTransition) {
var transition = transition
let segmentSpacing: CGFloat = 2.0
if self.currentColors != colors {
self.currentColors = colors
self.currentForegroundImage = generateFilledCircleImage(diameter: 2.0, color: colors.foreground)?.resizableImage(withCapInsets: UIEdgeInsets(top: 1.0, left: 0.0, bottom: 1.0, right: 0.0), resizingMode: .stretch)
self.currentBackgroundImage = generateFilledCircleImage(diameter: 2.0, color: colors.background)?.resizableImage(withCapInsets: UIEdgeInsets(top: 1.0, left: 0.0, bottom: 1.0, right: 0.0), resizingMode: .stretch)
self.currentClearBackgroundImage = generateImage(CGSize(width: 2.0, height: 4.0 + segmentSpacing * 2.0 + 1.0 * 2.0), contextGenerator: { size, context in
context.setFillColor(colors.clearBackground.cgColor)
context.fill(CGRect(origin: CGPoint(), size: size))
context.setFillColor(UIColor.clear.cgColor)
context.setBlendMode(.copy)
let ellipseFudge: CGFloat = 0.02
let topEllipse = CGRect(origin: CGPoint(x: -ellipseFudge, y: 1.0 + segmentSpacing), size: CGSize(width: 2.0 + ellipseFudge * 2.0, height: 2.0))
let bottomEllipse = CGRect(origin: CGPoint(x: -ellipseFudge, y: size.height - (1.0 + segmentSpacing) - 2.0), size: CGSize(width: 2.0 + ellipseFudge * 2.0, height: 2.0))
context.fillEllipse(in: topEllipse)
context.fillEllipse(in: bottomEllipse)
context.fill(CGRect(origin: CGPoint(x: 0.0, y: topEllipse.midY), size: CGSize(width: 2.0, height: bottomEllipse.midY - topEllipse.midY)))
context.fillEllipse(in: CGRect(origin: CGPoint(x: 0.0, y: -1.0), size: CGSize(width: 2.0, height: 2.0)))
context.fillEllipse(in: CGRect(origin: CGPoint(x: 0.0, y: size.height - 1.0), size: CGSize(width: 2.0, height: 2.0)))
})?.resizableImage(withCapInsets: UIEdgeInsets(top: 1.0 + segmentSpacing + 2.0, left: 1.0, bottom: 1.0 + segmentSpacing + 2.0, right: 1.0), resizingMode: .stretch)
self.foregroundLineNode.image = self.currentForegroundImage
for (_, itemNode) in self.backgroundLineNodes {
itemNode.lineNode.image = self.currentBackgroundImage
itemNode.overlayNode.image = self.currentClearBackgroundImage
}
self.topShadowNode.image = generateImage(CGSize(width: 2.0, height: 7.0), contextGenerator: { size, context in
context.clear(CGRect(origin: CGPoint(), size: size))
var locations: [CGFloat] = [1.0, 0.0]
let colors: [CGColor] = [colors.clearBackground.cgColor, colors.clearBackground.withAlphaComponent(0.0).cgColor]
let colorSpace = CGColorSpaceCreateDeviceRGB()
let gradient = CGGradient(colorsSpace: colorSpace, colors: colors as CFArray, locations: &locations)!
context.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: 0.0), end: CGPoint(x: 0.0, y: size.height), options: CGGradientDrawingOptions())
})
self.bottomShadowNode.image = generateImage(CGSize(width: 2.0, height: 7.0), rotatedContext: { size, context in
context.clear(CGRect(origin: CGPoint(), size: size))
var locations: [CGFloat] = [1.0, 0.0]
let colors: [CGColor] = [colors.clearBackground.cgColor, colors.clearBackground.withAlphaComponent(0.0).cgColor]
let colorSpace = CGColorSpaceCreateDeviceRGB()
let gradient = CGGradient(colorsSpace: colorSpace, colors: colors as CFArray, locations: &locations)!
context.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: 0.0), end: CGPoint(x: 0.0, y: size.height), options: CGGradientDrawingOptions())
})
}
if self.currentConfiguration == nil {
transition = .immediate
}
if self.currentConfiguration != configuration {
var isCycledJump = false
if let currentConfiguration = self.currentConfiguration, currentConfiguration.count == configuration.count, currentConfiguration.index == 0, currentConfiguration.count > 4, configuration.index == configuration.count - 1 {
isCycledJump = true
}
self.currentConfiguration = configuration
let defaultVerticalInset: CGFloat = 7.0
let minSegmentHeight: CGFloat = 8.0
transition.updateFrame(node: self.topShadowNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: 2.0, height: defaultVerticalInset)))
transition.updateFrame(node: self.bottomShadowNode, frame: CGRect(origin: CGPoint(x: 0.0, y: configuration.height - defaultVerticalInset), size: CGSize(width: 2.0, height: defaultVerticalInset)))
let availableVerticalHeight: CGFloat = configuration.height - defaultVerticalInset * 2.0
let proposedSegmentHeight: CGFloat = (availableVerticalHeight - segmentSpacing * CGFloat(configuration.count) + segmentSpacing) / CGFloat(configuration.count)
let segmentHeight = max(proposedSegmentHeight, minSegmentHeight)
let allItemsHeight = CGFloat(configuration.count) * segmentHeight + max(0.0, CGFloat(configuration.count - 1)) * segmentSpacing
var verticalInset = defaultVerticalInset
if allItemsHeight > availableVerticalHeight && allItemsHeight - 2.0 <= availableVerticalHeight {
verticalInset -= 2.0
}
let topItemsHeight = CGFloat(configuration.index) * (segmentHeight + segmentSpacing)
let bottomItemsHeight = allItemsHeight - topItemsHeight - segmentHeight
var itemScreenOffset = floorToScreenPixels((configuration.height - segmentHeight) / 2.0)
if itemScreenOffset - topItemsHeight > verticalInset {
itemScreenOffset = topItemsHeight + verticalInset
}
if itemScreenOffset + segmentHeight + bottomItemsHeight < configuration.height - verticalInset {
itemScreenOffset = configuration.height - verticalInset - (segmentHeight + bottomItemsHeight)
}
var backgroundItemNodesToOffset: [BackgroundLineNode] = []
var resolvedOffset: CGFloat = 0.0
func updateBackgroundLine(index: Int) -> Bool {
let indexDifference = index - configuration.index
let offsetDistance = CGFloat(indexDifference) * (segmentHeight + segmentSpacing)
let itemFrame = CGRect(origin: CGPoint(x: 0.0, y: itemScreenOffset + offsetDistance), size: CGSize(width: 2.0, height: segmentHeight))
if itemFrame.maxY <= 0.0 || itemFrame.minY > configuration.height {
return false
}
var itemNodeTransition = transition
let itemNode: BackgroundLineNode
if let current = self.backgroundLineNodes[index] {
itemNode = current
let offset = itemFrame.minY - itemNode.lineNode.frame.minY
if abs(offset) > abs(resolvedOffset) {
resolvedOffset = offset
}
} else {
itemNodeTransition = .immediate
itemNode = BackgroundLineNode()
itemNode.lineNode.image = self.currentBackgroundImage
itemNode.overlayNode.image = self.currentClearBackgroundImage
self.backgroundLineNodes[index] = itemNode
self.insertSubnode(itemNode.lineNode, belowSubnode: self.foregroundLineNode)
self.insertSubnode(itemNode.overlayNode, belowSubnode: self.topShadowNode)
backgroundItemNodesToOffset.append(itemNode)
}
itemNodeTransition.updateFrame(node: itemNode.lineNode, frame: itemFrame, beginWithCurrentState: true)
itemNodeTransition.updateFrame(node: itemNode.overlayNode, frame: itemFrame.insetBy(dx: 0.0, dy: -(1.0 + segmentSpacing)), beginWithCurrentState: true)
return true
}
var validIndices = Set<Int>()
if configuration.index >= 0 {
for i in (0 ... configuration.index).reversed() {
if updateBackgroundLine(index: i) {
validIndices.insert(i)
} else {
break
}
}
}
if configuration.index < configuration.count {
for i in configuration.index + 1 ..< configuration.count {
if updateBackgroundLine(index: i) {
validIndices.insert(i)
} else {
break
}
}
}
if !resolvedOffset.isZero {
for itemNode in backgroundItemNodesToOffset {
transition.animatePositionAdditive(node: itemNode.lineNode, offset: CGPoint(x: 0.0, y: -resolvedOffset))
transition.animatePositionAdditive(node: itemNode.overlayNode, offset: CGPoint(x: 0.0, y: -resolvedOffset))
}
for itemNode in self.removingBackgroundLineNodes {
transition.animatePosition(node: itemNode.lineNode, to: CGPoint(x: 0.0, y: resolvedOffset), removeOnCompletion: false, additive: true)
transition.animatePosition(node: itemNode.overlayNode, to: CGPoint(x: 0.0, y: resolvedOffset), removeOnCompletion: false, additive: true)
}
}
var removeIndices: [Int] = []
for (index, itemNode) in self.backgroundLineNodes {
if !validIndices.contains(index) {
removeIndices.append(index)
if transition.isAnimated {
removingBackgroundLineNodes.append(itemNode)
transition.animatePosition(node: itemNode.overlayNode, to: CGPoint(x: 0.0, y: resolvedOffset), removeOnCompletion: false, additive: true)
transition.animatePosition(node: itemNode.lineNode, to: CGPoint(x: 0.0, y: resolvedOffset), removeOnCompletion: false, additive: true, completion: { [weak self, weak itemNode] _ in
guard let strongSelf = self, let itemNode = itemNode else {
return
}
strongSelf.removingBackgroundLineNodes.removeAll(where: { $0 === itemNode })
itemNode.lineNode.removeFromSupernode()
itemNode.overlayNode.removeFromSupernode()
})
} else {
itemNode.lineNode.removeFromSupernode()
itemNode.overlayNode.removeFromSupernode()
}
}
}
for index in removeIndices {
self.backgroundLineNodes.removeValue(forKey: index)
}
transition.updateFrame(node: self.foregroundLineNode, frame: CGRect(origin: CGPoint(x: 0.0, y: itemScreenOffset), size: CGSize(width: 2.0, height: segmentHeight)), beginWithCurrentState: true)
if transition.isAnimated && isCycledJump {
let duration: Double = 0.18
let maxOffset: CGFloat = -8.0
let offsetAnimation0 = self.layer.makeAnimation(from: 0.0 as NSNumber, to: maxOffset as NSNumber, keyPath: "bounds.origin.y", timingFunction: CAMediaTimingFunctionName.linear.rawValue, duration: duration / 2.0, removeOnCompletion: false, additive: true, completion: { [weak self] _ in
guard let strongSelf = self else {
return
}
let offsetAnimation1 = strongSelf.layer.makeAnimation(from: maxOffset as NSNumber, to: 0.0 as NSNumber, keyPath: "bounds.origin.y", timingFunction: CAMediaTimingFunctionName.linear.rawValue, duration: duration / 2.0, additive: true)
strongSelf.layer.add(offsetAnimation1, forKey: "cycleShake")
})
self.layer.add(offsetAnimation0, forKey: "cycleShake")
}
}
}
}

View file

@ -743,6 +743,8 @@ public final class AnimatedStickerNode: ASDisplayNode {
public var started: () -> Void = {}
private var reportedStarted = false
public var completed: (Bool) -> Void = { _ in }
private let timer = Atomic<SwiftSignalKit.Timer?>(value: nil)
private let frameSource = Atomic<QueueLocalObject<AnimatedStickerFrameSourceWrapper>?>(value: nil)
@ -760,6 +762,14 @@ public final class AnimatedStickerNode: ASDisplayNode {
return self.playbackStatus.get()
}
public var autoplay = true {
didSet {
if self.autoplay != oldValue {
self.updateIsPlaying()
}
}
}
public var visibility = false {
didSet {
if self.visibility != oldValue {
@ -825,6 +835,7 @@ public final class AnimatedStickerNode: ASDisplayNode {
strongSelf.directData = (directData, path, width, height, cachePathPrefix, source.fitzModifier)
}
if case let .still(position) = playbackMode {
strongSelf.play(firstFrame: true)
strongSelf.seekTo(position)
} else if strongSelf.isPlaying {
strongSelf.play()
@ -877,7 +888,7 @@ public final class AnimatedStickerNode: ASDisplayNode {
if self.isPlaying != isPlaying {
self.isPlaying = isPlaying
if isPlaying {
self.play()
self.play(firstFrame: !self.autoplay)
} else{
self.pause()
}
@ -950,11 +961,17 @@ public final class AnimatedStickerNode: ASDisplayNode {
}
})
if case .once = strongSelf.playbackMode, frame.isLastFrame {
strongSelf.stop()
strongSelf.isPlaying = false
if frame.isLastFrame {
var stopped = false
if case .once = strongSelf.playbackMode {
strongSelf.stop()
strongSelf.isPlaying = false
stopped = true
}
strongSelf.completed(stopped)
}
let timestamp: Double = frameRate > 0 ? Double(frame.index) / Double(frameRate) : 0
strongSelf.playbackStatus.set(.single(AnimatedStickerStatus(playing: strongSelf.isPlaying, duration: duration, timestamp: timestamp)))
}
@ -1021,11 +1038,17 @@ public final class AnimatedStickerNode: ASDisplayNode {
}
})
if case .once = strongSelf.playbackMode, frame.isLastFrame {
strongSelf.stop()
strongSelf.isPlaying = false
if frame.isLastFrame {
var stopped = false
if case .once = strongSelf.playbackMode {
strongSelf.stop()
strongSelf.isPlaying = false
stopped = true
}
strongSelf.completed(stopped)
}
let timestamp: Double = frameRate > 0 ? Double(frame.index) / Double(frameRate) : 0
strongSelf.playbackStatus.set(.single(AnimatedStickerStatus(playing: strongSelf.isPlaying, duration: duration, timestamp: timestamp)))
}

View file

@ -95,7 +95,7 @@ private final class ContextControllerContentSourceImpl: ContextControllerContent
}
}
public class ChatListControllerImpl: TelegramBaseController, ChatListController, UIViewControllerPreviewingDelegate {
public class ChatListControllerImpl: TelegramBaseController, ChatListController {
private var validLayout: ContainerViewLayout?
public let context: AccountContext
@ -1780,111 +1780,6 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
(self.navigationController as? NavigationController)?.pushViewController(controller)
}
public func previewingContext(_ previewingContext: UIViewControllerPreviewing, viewControllerForLocation location: CGPoint) -> UIViewController? {
if #available(iOSApplicationExtension 9.0, iOS 9.0, *) {
if let (controller, rect) = self.previewingController(from: previewingContext.sourceView, for: location) {
previewingContext.sourceRect = rect
return controller
} else {
return nil
}
} else {
return nil
}
}
func previewingController(from sourceView: UIView, for location: CGPoint) -> (UIViewController, CGRect)? {
guard let layout = self.validLayout, case .phone = layout.deviceMetrics.type else {
return nil
}
let boundsSize = self.view.bounds.size
let contentSize: CGSize
if case .unknown = layout.deviceMetrics {
contentSize = boundsSize
} else {
contentSize = layout.deviceMetrics.previewingContentSize(inLandscape: boundsSize.width > boundsSize.height)
}
if let searchController = self.chatListDisplayNode.searchDisplayController {
if let (view, bounds, action) = searchController.previewViewAndActionAtLocation(location) {
if let peerId = action as? PeerId, peerId.namespace != Namespaces.Peer.SecretChat {
var sourceRect = view.superview!.convert(view.frame, to: sourceView)
sourceRect = CGRect(x: sourceRect.minX, y: sourceRect.minY + bounds.minY, width: bounds.width, height: bounds.height)
sourceRect.size.height -= UIScreenPixel
let chatController = self.context.sharedContext.makeChatController(context: self.context, chatLocation: .peer(peerId), subject: nil, botStart: nil, mode: .standard(previewing: true))
chatController.canReadHistory.set(false)
chatController.containerLayoutUpdated(ContainerViewLayout(size: contentSize, metrics: LayoutMetrics(), deviceMetrics: layout.deviceMetrics, intrinsicInsets: UIEdgeInsets(), safeInsets: UIEdgeInsets(), statusBarHeight: nil, inputHeight: nil, inputHeightIsInteractivellyChanging: false, inVoiceOver: false), transition: .immediate)
return (chatController, sourceRect)
} else if let messageId = action as? MessageId, messageId.peerId.namespace != Namespaces.Peer.SecretChat {
var sourceRect = view.superview!.convert(view.frame, to: sourceView)
sourceRect = CGRect(x: sourceRect.minX, y: sourceRect.minY + bounds.minY, width: bounds.width, height: bounds.height)
sourceRect.size.height -= UIScreenPixel
let chatController = self.context.sharedContext.makeChatController(context: self.context, chatLocation: .peer(messageId.peerId), subject: .message(id: messageId, highlight: true), botStart: nil, mode: .standard(previewing: true))
chatController.canReadHistory.set(false)
chatController.containerLayoutUpdated(ContainerViewLayout(size: contentSize, metrics: LayoutMetrics(), deviceMetrics: layout.deviceMetrics, intrinsicInsets: UIEdgeInsets(), safeInsets: UIEdgeInsets(), statusBarHeight: nil, inputHeight: nil, inputHeightIsInteractivellyChanging: false, inVoiceOver: false), transition: .immediate)
return (chatController, sourceRect)
}
}
return nil
}
let listLocation = self.view.convert(location, to: self.chatListDisplayNode.containerNode.currentItemNode.view)
var selectedNode: ChatListItemNode?
self.chatListDisplayNode.containerNode.currentItemNode.forEachItemNode { itemNode in
if let itemNode = itemNode as? ChatListItemNode, itemNode.frame.contains(listLocation), !itemNode.isDisplayingRevealedOptions {
selectedNode = itemNode
}
}
if let selectedNode = selectedNode, let item = selectedNode.item {
var sourceRect = selectedNode.view.superview!.convert(selectedNode.frame, to: sourceView)
sourceRect.size.height -= UIScreenPixel
switch item.content {
case let .peer(_, peer, _, _, _, _, _, _, _, _, _, _):
if peer.peerId.namespace != Namespaces.Peer.SecretChat {
let chatController = self.context.sharedContext.makeChatController(context: self.context, chatLocation: .peer(peer.peerId), subject: nil, botStart: nil, mode: .standard(previewing: true))
chatController.canReadHistory.set(false)
chatController.containerLayoutUpdated(ContainerViewLayout(size: contentSize, metrics: LayoutMetrics(), deviceMetrics: layout.deviceMetrics, intrinsicInsets: UIEdgeInsets(), safeInsets: UIEdgeInsets(), statusBarHeight: nil, inputHeight: nil, inputHeightIsInteractivellyChanging: false, inVoiceOver: false), transition: .immediate)
return (chatController, sourceRect)
} else {
return nil
}
case let .groupReference(groupId, _, _, _, _):
let chatListController = ChatListControllerImpl(context: self.context, groupId: groupId, controlsHistoryPreload: false, enableDebugActions: false)
chatListController.navigationPresentation = .master
chatListController.containerLayoutUpdated(ContainerViewLayout(size: contentSize, metrics: LayoutMetrics(), deviceMetrics: layout.deviceMetrics, intrinsicInsets: UIEdgeInsets(), safeInsets: UIEdgeInsets(), statusBarHeight: nil, inputHeight: nil, inputHeightIsInteractivellyChanging: false, inVoiceOver: false), transition: .immediate)
return (chatListController, sourceRect)
}
} else {
return nil
}
}
public func previewingContext(_ previewingContext: UIViewControllerPreviewing, commit viewControllerToCommit: UIViewController) {
self.previewingCommit(viewControllerToCommit)
}
func previewingCommit(_ viewControllerToCommit: UIViewController) {
if let viewControllerToCommit = viewControllerToCommit as? ViewController {
if let chatController = viewControllerToCommit as? ChatController {
chatController.canReadHistory.set(true)
chatController.updatePresentationMode(.standard(previewing: false))
if let navigationController = self.navigationController as? NavigationController {
self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, chatController: chatController, context: self.context, chatLocation: chatController.chatLocation, animated: false))
self.chatListDisplayNode.containerNode.currentItemNode.clearHighlightAnimated(true)
}
} else if let chatListController = viewControllerToCommit as? ChatListController {
if let navigationController = self.navigationController as? NavigationController {
navigationController.pushViewController(chatListController, animated: false, completion: {})
self.chatListDisplayNode.containerNode.currentItemNode.clearHighlightAnimated(true)
}
}
}
}
public override var keyShortcuts: [KeyShortcut] {
let strings = self.presentationData.strings

View file

@ -14,18 +14,6 @@ import SearchBarNode
import SearchUI
import ContextUI
private final class ChatListControllerNodeView: UITracingLayerView, PreviewingHostView {
var previewingDelegate: PreviewingHostViewDelegate? {
return PreviewingHostViewDelegate(controllerForLocation: { [weak self] sourceView, point in
return self?.controller?.previewingController(from: sourceView, for: point)
}, commitController: { [weak self] controller in
self?.controller?.previewingCommit(controller)
})
}
weak var controller: ChatListControllerImpl?
}
enum ChatListContainerNodeFilter: Equatable {
case all
case filter(ChatListFilter)
@ -1027,7 +1015,7 @@ final class ChatListControllerNode: ASDisplayNode {
super.init()
self.setViewBlock({
return ChatListControllerNodeView()
return UITracingLayerView()
})
self.backgroundColor = presentationData.theme.chatList.backgroundColor
@ -1056,8 +1044,6 @@ final class ChatListControllerNode: ASDisplayNode {
override func didLoad() {
super.didLoad()
(self.view as? ChatListControllerNodeView)?.controller = self.controller
let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:)))
self.tapRecognizer = tapRecognizer
self.view.addGestureRecognizer(tapRecognizer)

View file

@ -628,9 +628,11 @@ final class ChatListFilterTabContainerNode: ASDisplayNode {
self.selectedLineNode.image = generateImage(CGSize(width: 5.0, height: 3.0), rotatedContext: { size, context in
context.clear(CGRect(origin: CGPoint(), size: size))
context.setFillColor(presentationData.theme.list.itemAccentColor.cgColor)
context.fillEllipse(in: CGRect(origin: CGPoint(), size: CGSize(width: size.width, height: size.height + 1.0)))
context.fillEllipse(in: CGRect(origin: CGPoint(), size: CGSize(width: 4.0, height: 4.0)))
context.fillEllipse(in: CGRect(origin: CGPoint(x: size.width - 4.0, y: 0.0), size: CGSize(width: 4.0, height: 4.0)))
context.fill(CGRect(x: 2.0, y: 0.0, width: size.width - 4.0, height: 4.0))
context.fill(CGRect(x: 0.0, y: 2.0, width: size.width, height: 2.0))
})?.stretchableImage(withLeftCapWidth: 2, topCapHeight: 2)
})?.resizableImage(withCapInsets: UIEdgeInsets(top: 3.0, left: 3.0, bottom: 0.0, right: 3.0), resizingMode: .stretch)
}
if isReordering {

View file

@ -51,7 +51,7 @@ final class ChatListBadgeNode: ASDisplayNode {
self.textNode = TextNode()
self.textNode.isUserInteractionEnabled = false
self.textNode.displaysAsynchronously = true
self.textNode.displaysAsynchronously = false
self.measureTextNode = TextNode()

View file

@ -795,7 +795,13 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
}
embeddedState = embeddedStateValue
summaryInfo = summaryInfoValue
inputActivities = inputActivitiesValue
if let peerPresence = peerPresence as? TelegramUserPresence, case .present = peerPresence.status {
inputActivities = inputActivitiesValue
} else {
inputActivities = nil
}
isPeerGroup = false
promoInfo = promoInfoValue
displayAsMessage = displayAsMessageValue
@ -1320,6 +1326,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
var inputActivitiesSize: CGSize?
var inputActivitiesApply: (() -> Void)?
if let inputActivities = inputActivities, !inputActivities.isEmpty {
let (size, apply) = inputActivitiesLayout(CGSize(width: rawContentWidth - badgeSize, height: 40.0), item.presentationData, item.presentationData.theme.chatList.messageTextColor, item.index.messageIndex.id.peerId, inputActivities)
inputActivitiesSize = size
inputActivitiesApply = apply

View file

@ -11,6 +11,7 @@ private enum MessageGroupType {
case photos
case videos
case music
case files
case generic
}
@ -25,6 +26,7 @@ private func singleMessageType(message: Message) -> MessageGroupType {
if file.isVideo && !file.isInstantVideo {
return .videos
}
return .files
}
}
return .generic
@ -91,6 +93,13 @@ public func chatListItemStrings(strings: PresentationStrings, nameDisplayOrder:
messageText = strings.ChatList_MessageMusic(Int32(messages.count))
textIsReady = true
}
case .files:
if !messageText.isEmpty {
textIsReady = true
} else {
messageText = strings.ChatList_MessageFiles(Int32(messages.count))
textIsReady = true
}
case .generic:
break
}

View file

@ -396,80 +396,6 @@ public class ContactsController: ViewController {
self.contactsNode.containerLayoutUpdated(layout, navigationBarHeight: self.navigationInsetHeight, actualNavigationBarHeight: self.navigationHeight, transition: transition)
}
func previewingController(from sourceView: UIView, for location: CGPoint) -> (UIViewController, CGRect)? {
guard let layout = self.validLayout, case .phone = layout.deviceMetrics.type else {
return nil
}
let boundsSize = self.view.bounds.size
let contentSize: CGSize
if case .unknown = layout.deviceMetrics {
contentSize = boundsSize
} else {
contentSize = layout.deviceMetrics.previewingContentSize(inLandscape: boundsSize.width > boundsSize.height)
}
var selectedNode: ContactsPeerItemNode?
if let searchController = self.contactsNode.searchDisplayController {
guard let contentNode = searchController.contentNode as? ContactsSearchContainerNode else {
return nil
}
let listLocation = self.view.convert(location, to: contentNode.listNode.view)
contentNode.listNode.forEachItemNode { itemNode in
if let itemNode = itemNode as? ContactsPeerItemNode, itemNode.frame.contains(listLocation), !itemNode.isDisplayingRevealedOptions {
selectedNode = itemNode
}
}
} else {
let listLocation = self.view.convert(location, to: self.contactsNode.contactListNode.listNode.view)
self.contactsNode.contactListNode.listNode.forEachItemNode { itemNode in
if let itemNode = itemNode as? ContactsPeerItemNode, itemNode.frame.contains(listLocation), !itemNode.isDisplayingRevealedOptions {
selectedNode = itemNode
}
}
}
if let selectedNode = selectedNode, let peer = selectedNode.item?.peer {
var sourceRect = selectedNode.view.superview!.convert(selectedNode.frame, to: sourceView)
sourceRect.size.height -= UIScreenPixel
switch peer {
case let .peer(peer, _):
guard let peer = peer else {
return nil
}
if peer.id.namespace != Namespaces.Peer.SecretChat {
let chatController = self.context.sharedContext.makeChatController(context: self.context, chatLocation: .peer(peer.id), subject: nil, botStart: nil, mode: .standard(previewing: true))
chatController.canReadHistory.set(false)
chatController.containerLayoutUpdated(ContainerViewLayout(size: contentSize, metrics: LayoutMetrics(), deviceMetrics: layout.deviceMetrics, intrinsicInsets: UIEdgeInsets(), safeInsets: UIEdgeInsets(), statusBarHeight: nil, inputHeight: nil, inputHeightIsInteractivellyChanging: false, inVoiceOver: false), transition: .immediate)
return (chatController, sourceRect)
} else {
return nil
}
case .deviceContact:
return nil
}
} else {
return nil
}
}
func previewingCommit(_ viewControllerToCommit: UIViewController) {
if let viewControllerToCommit = viewControllerToCommit as? ViewController {
if let chatController = viewControllerToCommit as? ChatController {
chatController.canReadHistory.set(true)
chatController.updatePresentationMode(.standard(previewing: false))
if let navigationController = self.navigationController as? NavigationController {
self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, chatController: chatController, context: self.context, chatLocation: chatController.chatLocation, animated: false))
self.contactsNode.contactListNode.listNode.clearHighlightAnimated(true)
}
}
}
}
private func activateSearch() {
if self.displayNavigationBar {
if let searchContentNode = self.searchContentNode {

View file

@ -15,18 +15,6 @@ import SearchUI
import AppBundle
import ContextUI
private final class ContactsControllerNodeView: UITracingLayerView, PreviewingHostView {
var previewingDelegate: PreviewingHostViewDelegate? {
return PreviewingHostViewDelegate(controllerForLocation: { [weak self] sourceView, point in
return self?.controller?.previewingController(from: sourceView, for: point)
}, commitController: { [weak self] controller in
self?.controller?.previewingCommit(controller)
})
}
weak var controller: ContactsController?
}
private final class ContextControllerContentSourceImpl: ContextControllerContentSource {
let controller: ViewController
weak var sourceNode: ASDisplayNode?
@ -108,7 +96,7 @@ final class ContactsControllerNode: ASDisplayNode {
super.init()
self.setViewBlock({
return ContactsControllerNodeView()
return UITracingLayerView()
})
self.backgroundColor = self.presentationData.theme.chatList.backgroundColor
@ -150,12 +138,6 @@ final class ContactsControllerNode: ASDisplayNode {
self.presentationDataDisposable?.dispose()
}
override func didLoad() {
super.didLoad()
(self.view as? ContactsControllerNodeView)?.controller = self.controller
}
private func updateThemeAndStrings() {
self.backgroundColor = self.presentationData.theme.chatList.backgroundColor
self.searchDisplayController?.updatePresentationData(self.presentationData)

View file

@ -276,8 +276,8 @@ public extension ContainedViewLayoutTransition {
}
}
func animatePosition(node: ASDisplayNode, to position: CGPoint, removeOnCompletion: Bool = true, completion: ((Bool) -> Void)? = nil) {
if node.position.equalTo(position) {
func animatePosition(node: ASDisplayNode, to position: CGPoint, removeOnCompletion: Bool = true, additive: Bool = false, completion: ((Bool) -> Void)? = nil) {
if !additive && node.position.equalTo(position) {
completion?(true)
} else {
switch self {
@ -286,7 +286,7 @@ public extension ContainedViewLayoutTransition {
completion(true)
}
case let .animated(duration, curve):
node.layer.animatePosition(from: node.position, to: position, duration: duration, timingFunction: curve.timingFunction, mediaTimingFunction: curve.mediaTimingFunction, removeOnCompletion: removeOnCompletion, completion: { result in
node.layer.animatePosition(from: additive ? CGPoint() : node.position, to: position, duration: duration, timingFunction: curve.timingFunction, mediaTimingFunction: curve.mediaTimingFunction, removeOnCompletion: removeOnCompletion, additive: additive, completion: { result in
if let completion = completion {
completion(result)
}

View file

@ -12,6 +12,9 @@ public enum DeviceMetrics: CaseIterable, Equatable {
case iPhone6Plus
case iPhoneX
case iPhoneXSMax
case iPhone12Mini
case iPhone12
case iPhone12ProMax
case iPad
case iPadPro10Inch
case iPadPro11Inch
@ -27,6 +30,9 @@ public enum DeviceMetrics: CaseIterable, Equatable {
.iPhone6Plus,
.iPhoneX,
.iPhoneXSMax,
.iPhone12Mini,
.iPhone12,
.iPhone12ProMax,
.iPad,
.iPadPro10Inch,
.iPadPro11Inch,
@ -89,6 +95,12 @@ public enum DeviceMetrics: CaseIterable, Equatable {
return CGSize(width: 375.0, height: 812.0)
case .iPhoneXSMax:
return CGSize(width: 414.0, height: 896.0)
case .iPhone12Mini:
return CGSize(width: 360.0, height: 780.0)
case .iPhone12:
return CGSize(width: 390.0, height: 844.0)
case .iPhone12ProMax:
return CGSize(width: 428.0, height: 926.0)
case .iPad:
return CGSize(width: 768.0, height: 1024.0)
case .iPadPro10Inch:
@ -104,7 +116,7 @@ public enum DeviceMetrics: CaseIterable, Equatable {
func safeInsets(inLandscape: Bool) -> UIEdgeInsets {
switch self {
case .iPhoneX, .iPhoneXSMax:
case .iPhoneX, .iPhoneXSMax, .iPhone12Mini, .iPhone12, .iPhone12ProMax:
return inLandscape ? UIEdgeInsets(top: 0.0, left: 44.0, bottom: 0.0, right: 44.0) : UIEdgeInsets(top: 44.0, left: 0.0, bottom: 0.0, right: 0.0)
default:
return UIEdgeInsets.zero
@ -113,7 +125,7 @@ public enum DeviceMetrics: CaseIterable, Equatable {
func onScreenNavigationHeight(inLandscape: Bool, systemOnScreenNavigationHeight: CGFloat?) -> CGFloat? {
switch self {
case .iPhoneX, .iPhoneXSMax:
case .iPhoneX, .iPhoneXSMax, .iPhone12Mini, .iPhone12, .iPhone12ProMax:
return inLandscape ? 21.0 : 34.0
case .iPadPro3rdGen, .iPadPro11Inch:
return 21.0
@ -146,7 +158,7 @@ public enum DeviceMetrics: CaseIterable, Equatable {
var statusBarHeight: CGFloat {
switch self {
case .iPhoneX, .iPhoneXSMax:
case .iPhoneX, .iPhoneXSMax, .iPhone12Mini, .iPhone12, .iPhone12ProMax:
return 44.0
case .iPadPro11Inch, .iPadPro3rdGen:
return 24.0
@ -164,7 +176,7 @@ public enum DeviceMetrics: CaseIterable, Equatable {
return 162.0
case .iPhone6, .iPhone6Plus:
return 163.0
case .iPhoneX, .iPhoneXSMax:
case .iPhoneX, .iPhoneXSMax, .iPhone12Mini, .iPhone12, .iPhone12ProMax:
return 172.0
case .iPad, .iPadPro10Inch:
return 348.0
@ -183,9 +195,9 @@ public enum DeviceMetrics: CaseIterable, Equatable {
return 216.0
case .iPhone6Plus:
return 226.0
case .iPhoneX:
case .iPhoneX, .iPhone12Mini, .iPhone12:
return 291.0
case .iPhoneXSMax:
case .iPhoneXSMax, .iPhone12ProMax:
return 302.0
case .iPad, .iPadPro10Inch:
return 263.0
@ -204,7 +216,7 @@ public enum DeviceMetrics: CaseIterable, Equatable {
func predictiveInputHeight(inLandscape: Bool) -> CGFloat {
if inLandscape {
switch self {
case .iPhone4, .iPhone5, .iPhone6, .iPhone6Plus, .iPhoneX, .iPhoneXSMax:
case .iPhone4, .iPhone5, .iPhone6, .iPhone6Plus, .iPhoneX, .iPhoneXSMax, .iPhone12Mini, .iPhone12, .iPhone12ProMax:
return 37.0
case .iPad, .iPadPro10Inch, .iPadPro11Inch, .iPadPro, .iPadPro3rdGen:
return 50.0
@ -215,7 +227,7 @@ public enum DeviceMetrics: CaseIterable, Equatable {
switch self {
case .iPhone4, .iPhone5:
return 37.0
case .iPhone6, .iPhoneX, .iPhoneXSMax:
case .iPhone6, .iPhoneX, .iPhoneXSMax, .iPhone12Mini, .iPhone12, .iPhone12ProMax:
return 44.0
case .iPhone6Plus:
return 45.0
@ -227,44 +239,9 @@ public enum DeviceMetrics: CaseIterable, Equatable {
}
}
public func previewingContentSize(inLandscape: Bool) -> CGSize {
let screenSize = self.screenSize
if inLandscape {
switch self {
case .iPhone5:
return CGSize(width: screenSize.height, height: screenSize.width - 10.0)
case .iPhone6:
return CGSize(width: screenSize.height, height: screenSize.width - 22.0)
case .iPhone6Plus:
return CGSize(width: screenSize.height, height: screenSize.width - 22.0)
case .iPhoneX:
return CGSize(width: screenSize.height, height: screenSize.width + 48.0)
case .iPhoneXSMax:
return CGSize(width: screenSize.height, height: screenSize.width - 30.0)
default:
return CGSize(width: screenSize.height, height: screenSize.width - 10.0)
}
} else {
switch self {
case .iPhone5:
return CGSize(width: screenSize.width, height: screenSize.height - 50.0)
case .iPhone6:
return CGSize(width: screenSize.width, height: screenSize.height - 97.0)
case .iPhone6Plus:
return CGSize(width: screenSize.width, height: screenSize.height - 95.0)
case .iPhoneX:
return CGSize(width: screenSize.width, height: screenSize.height - 154.0)
case .iPhoneXSMax:
return CGSize(width: screenSize.width, height: screenSize.height - 84.0)
default:
return CGSize(width: screenSize.width, height: screenSize.height - 50.0)
}
}
}
public var hasTopNotch: Bool {
switch self {
case .iPhoneX, .iPhoneXSMax:
case .iPhoneX, .iPhoneXSMax, .iPhone12Mini, .iPhone12, .iPhone12ProMax:
return true
default:
return false

View file

@ -5,19 +5,21 @@ import AsyncDisplayKit
open class ASImageNode: ASDisplayNode {
public var image: UIImage? {
didSet {
if let image = self.image {
let capInsets = image.capInsets
if capInsets.left.isZero && capInsets.top.isZero {
self.contentsScale = image.scale
self.contents = image.cgImage
if self.isNodeLoaded {
if let image = self.image {
let capInsets = image.capInsets
if capInsets.left.isZero && capInsets.top.isZero {
self.contentsScale = image.scale
self.contents = image.cgImage
} else {
ASDisplayNodeSetResizableContents(self.layer, image)
}
} else {
ASDisplayNodeSetResizableContents(self, image)
self.contents = nil
}
if self.image?.size != oldValue?.size {
self.invalidateCalculatedLayout()
}
} else {
self.contents = nil
}
if self.image?.size != oldValue?.size {
self.invalidateCalculatedLayout()
}
}
}
@ -28,6 +30,20 @@ open class ASImageNode: ASDisplayNode {
super.init()
}
override open func didLoad() {
super.didLoad()
if let image = self.image {
let capInsets = image.capInsets
if capInsets.left.isZero && capInsets.top.isZero {
self.contentsScale = image.scale
self.contents = image.cgImage
} else {
ASDisplayNodeSetResizableContents(self.layer, image)
}
}
}
override public func calculateSizeThatFits(_ contrainedSize: CGSize) -> CGSize {
return self.image?.size ?? CGSize()
}

View file

@ -8,6 +8,8 @@ open class ASButtonNode: ASControlNode {
public let disabledTitleNode: ImmediateTextNode
public let imageNode: ASImageNode
public let highlightedImageNode: ASImageNode
public let selectedImageNode: ASImageNode
public let highlightedSelectedImageNode: ASImageNode
public let disabledImageNode: ASImageNode
public let backgroundImageNode: ASImageNode
public let highlightedBackgroundImageNode: ASImageNode
@ -70,11 +72,21 @@ open class ASButtonNode: ASControlNode {
self.imageNode.displaysAsynchronously = false
self.imageNode.displayWithoutProcessing = true
self.selectedImageNode = ASImageNode()
self.selectedImageNode.isUserInteractionEnabled = false
self.selectedImageNode.displaysAsynchronously = false
self.selectedImageNode.displayWithoutProcessing = true
self.highlightedImageNode = ASImageNode()
self.highlightedImageNode.isUserInteractionEnabled = false
self.highlightedImageNode.displaysAsynchronously = false
self.highlightedImageNode.displayWithoutProcessing = true
self.highlightedSelectedImageNode = ASImageNode()
self.highlightedSelectedImageNode.isUserInteractionEnabled = false
self.highlightedSelectedImageNode.displaysAsynchronously = false
self.highlightedSelectedImageNode.displayWithoutProcessing = true
self.disabledImageNode = ASImageNode()
self.disabledImageNode.isUserInteractionEnabled = false
self.disabledImageNode.displaysAsynchronously = false
@ -101,8 +113,12 @@ open class ASButtonNode: ASControlNode {
self.addSubnode(self.disabledTitleNode)
self.disabledTitleNode.isHidden = true
self.addSubnode(self.imageNode)
self.addSubnode(self.selectedImageNode)
self.selectedImageNode.isHidden = true
self.addSubnode(self.highlightedImageNode)
self.highlightedImageNode.isHidden = true
self.addSubnode(self.highlightedSelectedImageNode)
self.highlightedSelectedImageNode.isHidden = true
self.addSubnode(self.disabledImageNode)
self.disabledImageNode.isHidden = true
}
@ -226,9 +242,12 @@ open class ASButtonNode: ASControlNode {
self.disabledImageNode.image = image
} else if state == [] {
self.imageNode.image = image
} else if state == .highlighted {
self.highlightedImageNode.image = image
} else if state == .highlighted || state == .selected || state == [.selected, .highlighted] {
self.highlightedImageNode.image = image
} else if state == .selected {
self.selectedImageNode.image = image
} else if state == [.selected, .highlighted] {
self.highlightedSelectedImageNode.image = image
} else {
self.imageNode.image = image
}
@ -262,15 +281,15 @@ open class ASButtonNode: ASControlNode {
didSet {
if self.isSelected != oldValue {
if self.isSelected {
if self.highlightedImageNode.image != nil {
self.highlightedImageNode.isHidden = false
if self.selectedImageNode.image != nil {
self.selectedImageNode.isHidden = false
self.imageNode.isHidden = true
} else {
self.highlightedImageNode.isHidden = true
self.selectedImageNode.isHidden = true
self.imageNode.isHidden = false
}
} else {
self.highlightedImageNode.isHidden = true
self.selectedImageNode.isHidden = true
self.imageNode.isHidden = false
}
}
@ -280,7 +299,7 @@ open class ASButtonNode: ASControlNode {
override open var isHighlighted: Bool {
didSet {
if self.isHighlighted != oldValue {
if self.isHighlighted || self.isSelected {
if self.isHighlighted {
if self.highlightedTitleNode.attributedText != nil {
self.highlightedTitleNode.isHidden = false
self.titleNode.isHidden = true
@ -295,10 +314,17 @@ open class ASButtonNode: ASControlNode {
self.highlightedBackgroundImageNode.isHidden = true
self.backgroundImageNode.isHidden = false
}
if self.highlightedImageNode.image != nil {
if self.isSelected && self.highlightedSelectedImageNode.image != nil {
self.highlightedSelectedImageNode.isHidden = false
self.highlightedImageNode.isHidden = true
self.selectedImageNode.isHidden = true
self.imageNode.isHidden = true
} else if self.highlightedImageNode.image != nil {
self.highlightedSelectedImageNode.isHidden = true
self.highlightedImageNode.isHidden = false
self.imageNode.isHidden = true
} else {
self.highlightedSelectedImageNode.isHidden = true
self.highlightedImageNode.isHidden = true
self.imageNode.isHidden = false
}
@ -309,8 +335,15 @@ open class ASButtonNode: ASControlNode {
self.highlightedBackgroundImageNode.isHidden = true
self.backgroundImageNode.isHidden = false
self.highlightedSelectedImageNode.isHidden = true
self.highlightedImageNode.isHidden = true
self.imageNode.isHidden = false
if self.isSelected && self.selectedImageNode.image != nil {
self.selectedImageNode.isHidden = false
self.imageNode.isHidden = true
} else {
self.selectedImageNode.isHidden = true
self.imageNode.isHidden = false
}
}
}
}
@ -384,7 +417,9 @@ open class ASButtonNode: ASControlNode {
self.highlightedTitleNode.frame = CGRect(origin: highlightedTitleOrigin, size: self.calculatedHighlightedTitleSize)
self.disabledTitleNode.frame = CGRect(origin: disabledTitleOrigin, size: self.calculatedDisabledTitleSize)
self.imageNode.frame = CGRect(origin: imageOrigin, size: imageSize)
self.selectedImageNode.frame = CGRect(origin: imageOrigin, size: imageSize)
self.highlightedImageNode.frame = CGRect(origin: imageOrigin, size: imageSize)
self.highlightedSelectedImageNode.frame = CGRect(origin: imageOrigin, size: imageSize)
self.disabledImageNode.frame = CGRect(origin: imageOrigin, size: imageSize)
self.backgroundImageNode.frame = CGRect(origin: CGPoint(), size: size)

View file

@ -95,6 +95,9 @@ public enum TabBarItemContextActionType {
if !self.lockOrientation {
self.lockedOrientation = nil
}
if let window = self.window {
window.invalidateSupportedOrientations()
}
}
}
}

View file

@ -18,6 +18,7 @@ import PresentationDataUtils
enum ChatMediaGalleryThumbnail: Equatable {
case image(ImageMediaReference)
case video(FileMediaReference)
case file(FileMediaReference)
static func ==(lhs: ChatMediaGalleryThumbnail, rhs: ChatMediaGalleryThumbnail) -> Bool {
switch lhs {
@ -33,6 +34,12 @@ enum ChatMediaGalleryThumbnail: Equatable {
} else {
return false
}
case let .file(lhsFile):
if case let .file(rhsFile) = rhs, lhsFile.media.isEqual(to: rhsFile.media) {
return true
} else {
return false
}
}
}
}
@ -45,8 +52,12 @@ final class ChatMediaGalleryThumbnailItem: GalleryThumbnailItem {
self.account = account
if let imageReference = mediaReference.concrete(TelegramMediaImage.self) {
self.thumbnail = .image(imageReference)
} else if let fileReference = mediaReference.concrete(TelegramMediaFile.self), fileReference.media.isVideo {
self.thumbnail = .video(fileReference)
} else if let fileReference = mediaReference.concrete(TelegramMediaFile.self) {
if fileReference.media.isVideo {
self.thumbnail = .video(fileReference)
} else {
self.thumbnail = .file(fileReference)
}
} else {
return nil
}
@ -74,6 +85,12 @@ final class ChatMediaGalleryThumbnailItem: GalleryThumbnailItem {
} else {
return (.single({ _ in return nil }), CGSize(width: 128.0, height: 128.0))
}
case let .file(fileReference):
if let representation = smallestImageRepresentation(fileReference.media.previewRepresentations) {
return (chatWebpageSnippetFile(account: self.account, fileReference: fileReference, representation: representation), representation.dimensions.cgSize)
} else {
return (.single({ _ in return nil }), CGSize(width: 128.0, height: 128.0))
}
}
}
}
@ -153,7 +170,7 @@ class ChatImageGalleryItem: GalleryItem {
for m in self.message.media {
if let m = m as? TelegramMediaImage {
mediaReference = .message(message: MessageReference(self.message), media: m)
} else if let m = m as? TelegramMediaFile, m.isVideo {
} else if let m = m as? TelegramMediaFile {
mediaReference = .message(message: MessageReference(self.message), media: m)
}
}

View file

@ -816,7 +816,7 @@ func layoutInstantPageBlock(webpage: TelegramMediaWebpage, rtl: Bool, block: Ins
}
}
let map = TelegramMediaMap(latitude: latitude, longitude: longitude, heading: nil, accuracyRadius: nil, geoPlace: nil, venue: nil, liveBroadcastingTimeout: nil)
let map = TelegramMediaMap(latitude: latitude, longitude: longitude, heading: nil, accuracyRadius: nil, geoPlace: nil, venue: nil, liveBroadcastingTimeout: nil, liveProximityNotificationRadius: nil)
let attributes: [InstantPageImageAttribute] = [InstantPageMapAttribute(zoom: zoom, dimensions: dimensions.cgSize)]
var contentSize = CGSize(width: boundingWidth - safeInset * 2.0, height: 0.0)

View file

@ -224,9 +224,6 @@ open class ItemListController: ViewController, KeyShortcutResponder, Presentable
}
}
public var previewItemWithTag: ((ItemListItemTag) -> UIViewController?)?
public var commitPreview: ((UIViewController) -> Void)?
public var willDisappear: ((Bool) -> Void)?
public var didDisappear: ((Bool) -> Void)?
@ -557,52 +554,11 @@ open class ItemListController: ViewController, KeyShortcutResponder, Presentable
public func afterLayout(_ f: @escaping () -> Void) {
(self.displayNode as! ItemListControllerNode).afterLayout(f)
}
public func previewingController(from sourceView: UIView, for location: CGPoint) -> (UIViewController, CGRect)? {
guard let layout = self.validLayout, case .phone = layout.deviceMetrics.type else {
return nil
}
let boundsSize = self.view.bounds.size
let contentSize: CGSize
if case .unknown = layout.deviceMetrics {
contentSize = boundsSize
} else {
contentSize = layout.deviceMetrics.previewingContentSize(inLandscape: boundsSize.width > boundsSize.height)
}
var selectedNode: ItemListItemNode?
let listLocation = self.view.convert(location, to: (self.displayNode as! ItemListControllerNode).listNode.view)
(self.displayNode as! ItemListControllerNode).listNode.forEachItemNode { itemNode in
if itemNode.frame.contains(listLocation), let itemNode = itemNode as? ItemListItemNode {
selectedNode = itemNode
}
}
if let selectedNode = selectedNode as? (ItemListItemNode & ListViewItemNode), let tag = selectedNode.tag {
var sourceRect = selectedNode.view.superview!.convert(selectedNode.frame, to: sourceView)
sourceRect.size.height -= UIScreenPixel
if let controller = self.previewItemWithTag?(tag) {
if let controller = controller as? ContainableController {
controller.containerLayoutUpdated(ContainerViewLayout(size: contentSize, metrics: LayoutMetrics(), deviceMetrics: layout.deviceMetrics, intrinsicInsets: UIEdgeInsets(), safeInsets: UIEdgeInsets(), statusBarHeight: nil, inputHeight: nil, inputHeightIsInteractivellyChanging: false, inVoiceOver: false), transition: .immediate)
}
return (controller, sourceRect)
} else {
return nil
}
} else {
return nil
}
}
public func clearItemNodesHighlight(animated: Bool = false) {
(self.displayNode as! ItemListControllerNode).listNode.clearHighlightAnimated(animated)
}
public func previewingCommit(_ viewControllerToCommit: UIViewController) {
self.commitPreview?(viewControllerToCommit)
}
public var keyShortcuts: [KeyShortcut] {
return [KeyShortcut(input: UIKeyCommand.inputEscape, action: { [weak self] in
if !(self?.navigationController?.topViewController is TabBarController) {

View file

@ -138,7 +138,7 @@ public final class ItemListNodeVisibleEntries: Sequence {
}
}
public final class ItemListControllerNodeView: UITracingLayerView, PreviewingHostView {
public final class ItemListControllerNodeView: UITracingLayerView {
var onLayout: (() -> Void)?
init(controller: ItemListController?) {
@ -171,14 +171,6 @@ public final class ItemListControllerNodeView: UITracingLayerView, PreviewingHos
}
}
public var previewingDelegate: PreviewingHostViewDelegate? {
return PreviewingHostViewDelegate(controllerForLocation: { [weak self] sourceView, point in
return self?.controller?.previewingController(from: sourceView, for: point)
}, commitController: { [weak self] controller in
self?.controller?.previewingCommit(controller)
})
}
weak var controller: ItemListController?
}

View file

@ -720,6 +720,23 @@
- (void)updateEditorButtonsForItem:(id<TGModernGalleryItem>)item animated:(bool)animated
{
__weak TGMediaPickerGalleryInterfaceView *weakSelf = self;
id<TGModernGalleryEditableItem> galleryEditableItem = (id<TGModernGalleryEditableItem>)item;
if ([item conformsToProtocol:@protocol(TGModernGalleryEditableItem)])
{
id<TGMediaEditableItem> editableMediaItem = [galleryEditableItem editableMediaItem];
[_captionDisposable setDisposable:[[galleryEditableItem.editingContext captionSignalForItem:editableMediaItem] startWithNext:^(NSDictionary *captionWithEntities)
{
__strong TGMediaPickerGalleryInterfaceView *strongSelf = weakSelf;
if (strongSelf == nil)
return;
NSString *caption = captionWithEntities[@"caption"];
NSArray *entities = captionWithEntities[@"entities"];
[strongSelf->_captionMixin setCaption:caption entities:entities animated:animated];
}]];
}
if (_editingContext == nil || _editingContext.inhibitEditing)
{
[_portraitToolbarView setEditButtonsHidden:true animated:false];
@ -757,12 +774,10 @@
return;
}
id<TGModernGalleryEditableItem> galleryEditableItem = (id<TGModernGalleryEditableItem>)item;
if ([item conformsToProtocol:@protocol(TGModernGalleryEditableItem)])
{
id<TGMediaEditableItem> editableMediaItem = [galleryEditableItem editableMediaItem];
__weak TGMediaPickerGalleryInterfaceView *weakSelf = self;
__weak id<TGModernGalleryEditableItem> weakGalleryEditableItem = galleryEditableItem;
[_adjustmentsDisposable setDisposable:[[[[galleryEditableItem.editingContext adjustmentsSignalForItem:editableMediaItem] mapToSignal:^SSignal *(id<TGMediaEditAdjustments> adjustments) {
__strong id<TGModernGalleryEditableItem> strongGalleryEditableItem = weakGalleryEditableItem;
@ -803,17 +818,6 @@
[strongSelf updateEditorButtonsForAdjustments:adjustments dimensions:originalSize timer:timer];
}]];
[_captionDisposable setDisposable:[[galleryEditableItem.editingContext captionSignalForItem:editableMediaItem] startWithNext:^(NSDictionary *captionWithEntities)
{
__strong TGMediaPickerGalleryInterfaceView *strongSelf = weakSelf;
if (strongSelf == nil)
return;
NSString *caption = captionWithEntities[@"caption"];
NSArray *entities = captionWithEntities[@"entities"];
[strongSelf->_captionMixin setCaption:caption entities:entities animated:animated];
}]];
}
else
{

View file

@ -553,7 +553,7 @@ private func loadLegacyMessages(account: TemporaryAccount, basePath: String, acc
if let v = item.venue {
venue = MapVenue(title: v.title ?? "", address: v.address ?? "", provider: v.provider == "" ? nil : v.provider, id: v.venueId == "" ? nil : v.venueId, type: v.type == "" ? nil : v.type)
}
parsedMedia.append(TelegramMediaMap(latitude: item.latitude, longitude: item.longitude, heading: nil, accuracyRadius: nil, geoPlace: nil, venue: venue, liveBroadcastingTimeout: nil))
parsedMedia.append(TelegramMediaMap(latitude: item.latitude, longitude: item.longitude, heading: nil, accuracyRadius: nil, geoPlace: nil, venue: venue, liveBroadcastingTimeout: nil, liveProximityNotificationRadius: nil))
}
}
}

View file

@ -158,7 +158,7 @@ public final class LiveLocationManagerImpl: LiveLocationManager {
let addedStopped = stopMessageIds.subtracting(self.stopMessageIds)
self.stopMessageIds = stopMessageIds
for id in addedStopped {
self.editMessageDisposables.set((requestEditLiveLocation(postbox: self.postbox, network: self.network, stateManager: self.stateManager, messageId: id, coordinate: nil, heading: nil)
self.editMessageDisposables.set((requestEditLiveLocation(postbox: self.postbox, network: self.network, stateManager: self.stateManager, messageId: id, stop: true, coordinate: nil, heading: nil, proximityNotificationRadius: nil)
|> deliverOn(self.queue)).start(completed: { [weak self] in
if let strongSelf = self {
strongSelf.editMessageDisposables.set(nil, forKey: id)
@ -213,7 +213,7 @@ public final class LiveLocationManagerImpl: LiveLocationManager {
let ids = self.broadcastToMessageIds
let remainingIds = Atomic<Set<MessageId>>(value: Set(ids.keys))
for id in ids.keys {
self.editMessageDisposables.set((requestEditLiveLocation(postbox: self.postbox, network: self.network, stateManager: self.stateManager, messageId: id, coordinate: (latitude: coordinate.latitude, longitude: coordinate.longitude, accuracyRadius: Int32(accuracyRadius)), heading: Int32(heading ?? 0))
self.editMessageDisposables.set((requestEditLiveLocation(postbox: self.postbox, network: self.network, stateManager: self.stateManager, messageId: id, stop: false, coordinate: (latitude: coordinate.latitude, longitude: coordinate.longitude, accuracyRadius: Int32(accuracyRadius)), heading: Int32(heading ?? 0), proximityNotificationRadius: nil)
|> deliverOn(self.queue)).start(completed: { [weak self] in
if let strongSelf = self {
strongSelf.editMessageDisposables.set(nil, forKey: id)
@ -247,7 +247,7 @@ public final class LiveLocationManagerImpl: LiveLocationManager {
let timestamp = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)
for i in 0 ..< updatedMedia.count {
if let media = updatedMedia[i] as? TelegramMediaMap, let _ = media.liveBroadcastingTimeout {
updatedMedia[i] = TelegramMediaMap(latitude: media.latitude, longitude: media.longitude, heading: media.heading, accuracyRadius: media.accuracyRadius, geoPlace: media.geoPlace, venue: media.venue, liveBroadcastingTimeout: max(0, timestamp - currentMessage.timestamp - 1))
updatedMedia[i] = TelegramMediaMap(latitude: media.latitude, longitude: media.longitude, heading: media.heading, accuracyRadius: media.accuracyRadius, geoPlace: media.geoPlace, venue: media.venue, liveBroadcastingTimeout: max(0, timestamp - currentMessage.timestamp - 1), liveProximityNotificationRadius: nil)
}
}
return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: currentMessage.attributes, media: updatedMedia))

View file

@ -78,7 +78,7 @@ private func generateHeadingArrowImage() -> UIImage? {
public final class ChatMessageLiveLocationPositionNode: ASDisplayNode {
public enum Mode {
case liveLocation(peer: Peer, active: Bool, latitude: Double, longitude: Double, heading: Double?)
case liveLocation(peer: Peer, active: Bool, latitude: Double, longitude: Double, heading: Int32?)
case location(TelegramMediaMap?)
}
@ -152,7 +152,7 @@ public final class ChatMessageLiveLocationPositionNode: ASDisplayNode {
backgroundImage = avatarBackgroundImage
hasPulse = active
coordinate = (latitude, longitude)
heading = headingValue
heading = headingValue.flatMap { Double($0) }
case let .location(location):
let venueType = location?.venue?.type ?? ""
let color = venueType.isEmpty ? theme.list.itemAccentColor : venueIconColor(type: venueType)
@ -166,7 +166,6 @@ public final class ChatMessageLiveLocationPositionNode: ASDisplayNode {
return degrees * Double.pi / 180.0
}
if heading == nil, let currentCoordinate = currentCoordinate, let coordinate = coordinate {
let lat1 = degToRad(currentCoordinate.0)
let lon1 = degToRad(currentCoordinate.1)

View file

@ -122,7 +122,7 @@ private func telegramMap(for location: TGLocationMediaAttachment) -> TelegramMed
} else {
mapVenue = nil
}
return TelegramMediaMap(latitude: location.latitude, longitude: location.longitude, heading: nil, accuracyRadius: nil, geoPlace: nil, venue: mapVenue, liveBroadcastingTimeout: nil)
return TelegramMediaMap(latitude: location.latitude, longitude: location.longitude, heading: nil, accuracyRadius: nil, geoPlace: nil, venue: mapVenue, liveBroadcastingTimeout: nil, liveProximityNotificationRadius: nil)
}
func legacyLocationPalette(from theme: PresentationTheme) -> TGLocationPallete {

View file

@ -105,7 +105,7 @@ final class LocationActionListItem: ListViewItem {
async {
let node = LocationActionListItemNode()
let makeLayout = node.asyncLayout()
let (nodeLayout, nodeApply) = makeLayout(self, params, nextItem is LocationActionListItem)
let (nodeLayout, nodeApply) = makeLayout(self, params, nextItem is LocationActionListItem || nextItem is LocationLiveListItem)
node.contentSize = nodeLayout.contentSize
node.insets = nodeLayout.insets
@ -118,7 +118,7 @@ final class LocationActionListItem: ListViewItem {
if let nodeValue = node() as? LocationActionListItemNode {
let layout = nodeValue.asyncLayout()
async {
let (nodeLayout, apply) = layout(self, params, nextItem is LocationActionListItem)
let (nodeLayout, apply) = layout(self, params, nextItem is LocationActionListItem || nextItem is LocationLiveListItem)
Queue.mainQueue().async {
completion(nodeLayout, { info in
apply().1(info)

View file

@ -43,7 +43,7 @@ class LocationPinAnnotation: NSObject, MKAnnotation {
let peer: Peer?
let message: Message?
let forcedSelection: Bool
var heading: Double? {
var heading: Int32? {
willSet {
self.willChangeValue(forKey: "heading")
}
@ -78,7 +78,7 @@ class LocationPinAnnotation: NSObject, MKAnnotation {
super.init()
}
init(context: AccountContext, theme: PresentationTheme, message: Message, selfPeer: Peer?, heading: Double?) {
init(context: AccountContext, theme: PresentationTheme, message: Message, selfPeer: Peer?, heading: Int32?) {
self.context = context
self.theme = theme
self.location = nil
@ -96,7 +96,9 @@ class LocationPinAnnotation: NSObject, MKAnnotation {
}
var id: String {
if let peer = self.peer {
if let message = self.message {
return "\(message.id.id)"
} else if let peer = self.peer {
return "\(peer.id.toInt64())"
} else if let venueId = self.location?.venue?.id {
return venueId
@ -257,8 +259,8 @@ class LocationPinAnnotationView: MKAnnotationView {
self.dotNode.isHidden = false
self.backgroundNode.image = UIImage(bundleImageName: "Location/PinBackground")
if let author = message.author, let peer = message.peers[author.id] {
self.setPeer(context: annotation.context, theme: annotation.theme, peer: peer)
if let author = message.author {
self.setPeer(context: annotation.context, theme: annotation.theme, peer: author)
} else if let selfPeer = annotation.selfPeer {
self.setPeer(context: annotation.context, theme: annotation.theme, peer: selfPeer)
}

View file

@ -206,7 +206,7 @@ class LocationDistancePickerScreenNode: ViewControllerTracingNode, UIScrollViewD
self.wrappingScrollNode.view.canCancelContentTouches = true
self.dimNode = ASDisplayNode()
// self.dimNode.backgroundColor = UIColor(white: 0.0, alpha: 0.5)
self.dimNode.backgroundColor = UIColor(white: 0.0, alpha: 0.5)
self.contentContainerNode = ASDisplayNode()
self.contentContainerNode.isOpaque = false
@ -314,9 +314,9 @@ class LocationDistancePickerScreenNode: ViewControllerTracingNode, UIScrollViewD
pickerView.selectRow(0, inComponent: 0, animated: false)
if self.usesMetricSystem() {
pickerView.selectRow(30, inComponent: 1, animated: false)
pickerView.selectRow(50, inComponent: 1, animated: false)
} else {
pickerView.selectRow(20, inComponent: 1, animated: false)
pickerView.selectRow(30, inComponent: 1, animated: false)
}
self.contentContainerNode.view.addSubview(pickerView)
self.pickerView = pickerView
@ -325,7 +325,11 @@ class LocationDistancePickerScreenNode: ViewControllerTracingNode, UIScrollViewD
}
private func usesMetricSystem() -> Bool {
return localeWithStrings(self.presentationData.strings).usesMetricSystem
let locale = localeWithStrings(self.presentationData.strings)
if locale.identifier.hasSuffix("GB") {
return false
}
return locale.usesMetricSystem
}
func numberOfComponents(in pickerView: UIPickerView) -> Int {
@ -334,8 +338,14 @@ class LocationDistancePickerScreenNode: ViewControllerTracingNode, UIScrollViewD
private func updateDoneButtonTitle() {
if let pickerView = self.pickerView {
let largeValue = unitValues[pickerView.selectedRow(inComponent: 0)]
let smallValue = smallUnitValues[pickerView.selectedRow(inComponent: 1)]
let selectedLargeRow = pickerView.selectedRow(inComponent: 0)
var selectedSmallRow = pickerView.selectedRow(inComponent: 1)
if selectedLargeRow == 0 && selectedSmallRow == 0 {
selectedSmallRow = 1
}
let largeValue = unitValues[selectedLargeRow]
let smallValue = smallUnitValues[selectedSmallRow]
var value = largeValue * 1000 + smallValue * 10
if !self.usesMetricSystem() {
@ -349,7 +359,7 @@ class LocationDistancePickerScreenNode: ViewControllerTracingNode, UIScrollViewD
self.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .immediate)
}
if let distance = self.distances.first, Double(value) > distance {
if let distance = self.distances.last, Double(value) > distance {
self.doneButton.alpha = 0.0
self.doneButton.isUserInteractionEnabled = false
self.textNode.alpha = 1.0
@ -375,8 +385,12 @@ class LocationDistancePickerScreenNode: ViewControllerTracingNode, UIScrollViewD
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
self.updateDoneButtonTitle()
self.update()
if pickerView.selectedRow(inComponent: 0) == 0 && pickerView.selectedRow(inComponent: 1) == 0 {
pickerView.selectRow(1, inComponent: 1, animated: true)
} else {
self.updateDoneButtonTitle()
self.update()
}
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
@ -389,16 +403,19 @@ class LocationDistancePickerScreenNode: ViewControllerTracingNode, UIScrollViewD
}
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
func pickerView(_ pickerView: UIPickerView, attributedTitleForRow row: Int, forComponent component: Int) -> NSAttributedString? {
let font = Font.regular(17.0)
let string: String
if component == 0 {
let value = unitValues[row]
return "\(value)"
string = "\(value)"
} else if component == 1 {
let value = String(format: "%.2d", smallUnitValues[row])
return ".\(value)"
string = ".\(value)"
} else {
return self.usesMetricSystem() ? self.presentationData.strings.Location_ProximityNotification_DistanceKM : self.presentationData.strings.Location_ProximityNotification_DistanceMI
string = self.usesMetricSystem() ? self.presentationData.strings.Location_ProximityNotification_DistanceKM : self.presentationData.strings.Location_ProximityNotification_DistanceMI
}
return NSAttributedString(string: string, font: font, textColor: self.presentationData.theme.actionSheet.primaryTextColor)
}
func updatePresentationData(_ presentationData: PresentationData) {
@ -423,6 +440,8 @@ class LocationDistancePickerScreenNode: ViewControllerTracingNode, UIScrollViewD
self.cancelButton.setTitle(self.presentationData.strings.Common_Cancel, with: Font.regular(17.0), with: self.presentationData.theme.actionSheet.controlAccentColor, for: .normal)
self.doneButton.updateTheme(SolidRoundedButtonTheme(theme: self.presentationData.theme))
self.updateDoneButtonTitle()
}
override func didLoad() {
@ -444,13 +463,10 @@ class LocationDistancePickerScreenNode: ViewControllerTracingNode, UIScrollViewD
}
func animateIn() {
self.dimNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.4)
self.dimNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
let offset = self.bounds.size.height - self.contentBackgroundNode.frame.minY
let dimPosition = self.dimNode.layer.position
self.dimNode.layer.animatePosition(from: CGPoint(x: dimPosition.x, y: dimPosition.y - offset), to: dimPosition, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring)
self.layer.animateBoundsOriginYAdditive(from: -offset, to: 0.0, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring)
self.wrappingScrollNode.layer.animateBoundsOriginYAdditive(from: -offset, to: 0.0, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring)
}
func animateOut(completion: (() -> Void)? = nil) {
@ -470,9 +486,7 @@ class LocationDistancePickerScreenNode: ViewControllerTracingNode, UIScrollViewD
})
let offset = self.bounds.size.height - self.contentBackgroundNode.frame.minY
let dimPosition = self.dimNode.layer.position
self.dimNode.layer.animatePosition(from: dimPosition, to: CGPoint(x: dimPosition.x, y: dimPosition.y - offset), duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false)
self.layer.animateBoundsOriginYAdditive(from: 0.0, to: -offset, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, completion: { _ in
self.wrappingScrollNode.layer.animateBoundsOriginYAdditive(from: 0.0, to: -offset, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, completion: { _ in
offsetCompleted = true
internalCompletion()
})
@ -524,7 +538,7 @@ class LocationDistancePickerScreenNode: ViewControllerTracingNode, UIScrollViewD
transition.updateFrame(node: self.effectNode, frame: CGRect(origin: CGPoint(), size: backgroundFrame.size))
transition.updateFrame(node: self.contentBackgroundNode, frame: CGRect(origin: CGPoint(), size: backgroundFrame.size))
transition.updateFrame(node: self.wrappingScrollNode, frame: CGRect(origin: CGPoint(), size: layout.size))
transition.updateFrame(node: self.dimNode, frame: CGRect(origin: CGPoint(), size: layout.size))
transition.updateFrame(node: self.dimNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: layout.size.width, height: insets.top + 66.0 + UIScreenPixel)))
let titleSize = self.titleNode.measure(CGSize(width: width, height: titleHeight))
let titleFrame = CGRect(origin: CGPoint(x: floor((contentFrame.width - titleSize.width) / 2.0), y: 16.0), size: titleSize)

View file

@ -0,0 +1,277 @@
import Foundation
import UIKit
import AsyncDisplayKit
import Postbox
import Display
import SwiftSignalKit
import TelegramCore
import SyncCore
import AccountContext
import TelegramPresentationData
import TelegramUIPreferences
import TelegramStringFormatting
import ItemListUI
import LocationResources
import AppBundle
import AvatarNode
import LiveLocationTimerNode
final class LocationLiveListItem: ListViewItem {
let presentationData: ItemListPresentationData
let dateTimeFormat: PresentationDateTimeFormat
let nameDisplayOrder: PresentationPersonNameOrder
let context: AccountContext
let message: Message
let distance: Double?
let action: () -> Void
let longTapAction: () -> Void
public init(presentationData: ItemListPresentationData, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, context: AccountContext, message: Message, distance: Double?, action: @escaping () -> Void, longTapAction: @escaping () -> Void = { }) {
self.presentationData = presentationData
self.dateTimeFormat = dateTimeFormat
self.nameDisplayOrder = nameDisplayOrder
self.context = context
self.message = message
self.distance = distance
self.action = action
self.longTapAction = longTapAction
}
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 = LocationLiveListItemNode()
let makeLayout = node.asyncLayout()
let (nodeLayout, nodeApply) = makeLayout(self, params, nextItem is LocationLiveListItem)
node.contentSize = nodeLayout.contentSize
node.insets = nodeLayout.insets
completion(node, nodeApply)
}
}
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? LocationLiveListItemNode {
let layout = nodeValue.asyncLayout()
async {
let (nodeLayout, apply) = layout(self, params, nextItem is LocationLiveListItem)
Queue.mainQueue().async {
completion(nodeLayout, { info in
apply().1(info)
})
}
}
}
}
}
public var selectable: Bool {
return true
}
public func selected(listView: ListView) {
listView.clearHighlightAnimated(false)
self.action()
}
}
private let avatarFont = avatarPlaceholderFont(size: floor(40.0 * 16.0 / 37.0))
final class LocationLiveListItemNode: ListViewItemNode {
private let backgroundNode: ASDisplayNode
private let separatorNode: ASDisplayNode
private let highlightedBackgroundNode: ASDisplayNode
private var titleNode: TextNode?
private var subtitleNode: TextNode?
private let avatarNode: AvatarNode
private var timerNode: ChatMessageLiveLocationTimerNode?
private var item: LocationLiveListItem?
private var layoutParams: ListViewItemLayoutParams?
required init() {
self.backgroundNode = ASDisplayNode()
self.backgroundNode.isLayerBacked = true
self.separatorNode = ASDisplayNode()
self.separatorNode.isLayerBacked = true
self.highlightedBackgroundNode = ASDisplayNode()
self.highlightedBackgroundNode.isLayerBacked = true
self.avatarNode = AvatarNode(font: avatarFont)
self.avatarNode.isLayerBacked = !smartInvertColorsEnabled()
super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false)
self.addSubnode(self.backgroundNode)
self.addSubnode(self.separatorNode)
self.addSubnode(self.avatarNode)
}
override func layoutForParams(_ params: ListViewItemLayoutParams, item: ListViewItem, previousItem: ListViewItem?, nextItem: ListViewItem?) {
if let item = self.item {
let makeLayout = self.asyncLayout()
let (nodeLayout, nodeApply) = makeLayout(item, params, nextItem is LocationLiveListItem)
self.contentSize = nodeLayout.contentSize
self.insets = nodeLayout.insets
let _ = nodeApply()
}
}
override func setHighlighted(_ highlighted: Bool, at point: CGPoint, animated: Bool) {
super.setHighlighted(highlighted, at: point, animated: animated)
if highlighted {
self.highlightedBackgroundNode.alpha = 1.0
if self.highlightedBackgroundNode.supernode == nil {
self.insertSubnode(self.highlightedBackgroundNode, aboveSubnode: self.separatorNode)
}
} else {
if self.highlightedBackgroundNode.supernode != nil {
if animated {
self.highlightedBackgroundNode.layer.animateAlpha(from: self.highlightedBackgroundNode.alpha, to: 0.0, duration: 0.4, completion: { [weak self] completed in
if let strongSelf = self {
if completed {
strongSelf.highlightedBackgroundNode.removeFromSupernode()
}
}
})
self.highlightedBackgroundNode.alpha = 0.0
} else {
self.highlightedBackgroundNode.removeFromSupernode()
}
}
}
}
func asyncLayout() -> (_ item: LocationLiveListItem, _ params: ListViewItemLayoutParams, _ hasSeparator: Bool) -> (ListViewItemNodeLayout, () -> (Signal<Void, NoError>?, (ListViewItemApply) -> Void)) {
let currentItem = self.item
let makeTitleLayout = TextNode.asyncLayout(self.titleNode)
let makeSubtitleLayout = TextNode.asyncLayout(self.subtitleNode)
return { [weak self] item, params, hasSeparator in
let leftInset: CGFloat = 65.0 + params.leftInset
let rightInset: CGFloat = params.rightInset
let verticalInset: CGFloat = 8.0
let titleFont = Font.medium(item.presentationData.fontSize.itemListBaseFontSize)
let subtitleFont = Font.regular(floor(item.presentationData.fontSize.itemListBaseFontSize * 14.0 / 17.0))
var title: String = ""
if let author = item.message.author {
title = author.displayTitle(strings: item.presentationData.strings, displayOrder: item.nameDisplayOrder)
}
let titleAttributedString = NSAttributedString(string: title, font: titleFont, textColor: item.presentationData.theme.list.itemPrimaryTextColor)
let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: titleAttributedString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - rightInset - 54.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
var updateTimestamp = item.message.timestamp
for attribute in item.message.attributes {
if let attribute = attribute as? EditedMessageAttribute {
updateTimestamp = attribute.date
break
}
}
let timestamp = CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970
let timeString = stringForRelativeLiveLocationTimestamp(strings: item.presentationData.strings, relativeTimestamp: Int32(updateTimestamp), relativeTo: Int32(timestamp), dateTimeFormat: item.dateTimeFormat)
var subtitle = timeString
if let distance = item.distance {
let distanceString = item.presentationData.strings.Map_DistanceAway(stringForDistance(strings: item.presentationData.strings, distance: distance)).0
subtitle = "\(timeString)\(distanceString)"
}
let subtitleAttributedString = NSAttributedString(string: subtitle, font: subtitleFont, textColor: item.presentationData.theme.list.itemSecondaryTextColor)
let (subtitleLayout, subtitleApply) = makeSubtitleLayout(TextNodeLayoutArguments(attributedString: subtitleAttributedString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - rightInset - 54.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
let titleSpacing: CGFloat = 1.0
let contentSize = CGSize(width: params.width, height: verticalInset * 2.0 + titleLayout.size.height + titleSpacing + subtitleLayout.size.height)
let nodeLayout = ListViewItemNodeLayout(contentSize: contentSize, insets: UIEdgeInsets())
return (nodeLayout, { [weak self] in
var updatedTheme: PresentationTheme?
if currentItem?.presentationData.theme !== item.presentationData.theme {
updatedTheme = item.presentationData.theme
}
return (self?.avatarNode.ready, { _ in
if let strongSelf = self {
strongSelf.item = item
strongSelf.layoutParams = params
if let _ = updatedTheme {
strongSelf.separatorNode.backgroundColor = item.presentationData.theme.list.itemPlainSeparatorColor
strongSelf.backgroundNode.backgroundColor = item.presentationData.theme.list.plainBackgroundColor
strongSelf.highlightedBackgroundNode.backgroundColor = item.presentationData.theme.list.itemHighlightedBackgroundColor
}
let titleNode = titleApply()
if strongSelf.titleNode == nil {
strongSelf.titleNode = titleNode
strongSelf.addSubnode(titleNode)
}
let subtitleNode = subtitleApply()
if strongSelf.subtitleNode == nil {
strongSelf.subtitleNode = subtitleNode
strongSelf.addSubnode(subtitleNode)
}
let titleFrame = CGRect(origin: CGPoint(x: leftInset, y: verticalInset), size: titleLayout.size)
titleNode.frame = titleFrame
let subtitleFrame = CGRect(origin: CGPoint(x: leftInset, y: verticalInset + titleLayout.size.height + titleSpacing), size: subtitleLayout.size)
subtitleNode.frame = subtitleFrame
let separatorHeight = UIScreenPixel
let topHighlightInset: CGFloat = separatorHeight
let avatarSize: CGFloat = 40.0
if let peer = item.message.author {
strongSelf.avatarNode.setPeer(context: item.context, theme: item.presentationData.theme, peer: peer, overrideImage: nil, emptyColor: item.presentationData.theme.list.mediaPlaceholderColor, synchronousLoad: false)
}
strongSelf.avatarNode.frame = CGRect(origin: CGPoint(x: params.leftInset + 15.0, y: floorToScreenPixels((contentSize.height - avatarSize) / 2.0)), size: CGSize(width: avatarSize, height: avatarSize))
strongSelf.backgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: contentSize.width, height: contentSize.height))
strongSelf.highlightedBackgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -nodeLayout.insets.top - topHighlightInset), size: CGSize(width: contentSize.width, height: contentSize.height + topHighlightInset))
strongSelf.separatorNode.frame = CGRect(origin: CGPoint(x: leftInset, y: nodeLayout.contentSize.height - separatorHeight), size: CGSize(width: nodeLayout.size.width, height: separatorHeight))
strongSelf.separatorNode.isHidden = !hasSeparator
var liveBroadcastingTimeout: Int32 = 0
if let location = getLocation(from: item.message), let timeout = location.liveBroadcastingTimeout {
liveBroadcastingTimeout = timeout
}
let currentTimestamp = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)
if currentTimestamp < item.message.timestamp + liveBroadcastingTimeout {
let timerNode: ChatMessageLiveLocationTimerNode
if let current = strongSelf.timerNode {
timerNode = current
} else {
timerNode = ChatMessageLiveLocationTimerNode()
strongSelf.addSubnode(timerNode)
strongSelf.timerNode = timerNode
}
let timerSize = CGSize(width: 28.0, height: 28.0)
timerNode.update(backgroundColor: item.presentationData.theme.list.itemAccentColor.withAlphaComponent(0.4), foregroundColor: item.presentationData.theme.list.itemAccentColor, textColor: item.presentationData.theme.list.itemAccentColor, beginTimestamp: Double(item.message.timestamp), timeout: Double(liveBroadcastingTimeout), strings: item.presentationData.strings)
timerNode.frame = CGRect(origin: CGPoint(x: contentSize.width - 16.0 - timerSize.width, y: floorToScreenPixels((contentSize.height - timerSize.height) / 2.0)), size: timerSize)
} else if let timerNode = strongSelf.timerNode {
strongSelf.timerNode = nil
timerNode.removeFromSupernode()
}
}
})
})
}
}
override func animateInsertion(_ currentTimestamp: Double, duration: Double, short: Bool) {
self.layer.animateAlpha(from: 0.0, to: 1.0, duration: duration * 0.5)
}
override func animateRemoved(_ currentTimestamp: Double, duration: Double) {
self.layer.animateAlpha(from: 1.0, to: 0.0, duration: duration * 0.5, removeOnCompletion: false)
}
}

View file

@ -45,6 +45,8 @@ final class LocationMapHeaderNode: ASDisplayNode {
private var proximityNotification: Bool?
let mapNode: LocationMapNode
var trackingMode: LocationTrackingMode = .none
private let optionsBackgroundNode: ASImageNode
private let optionsSeparatorNode: ASDisplayNode
private let optionsSecondSeparatorNode: ASDisplayNode
@ -129,11 +131,14 @@ final class LocationMapHeaderNode: ASDisplayNode {
self.placesButtonNode.addTarget(self, action: #selector(self.placesPressed), forControlEvents: .touchUpInside)
}
func updateState(mapMode: LocationMapMode, displayingMapModeOptions: Bool, displayingPlacesButton: Bool, proximityNotification: Bool?, animated: Bool) {
func updateState(mapMode: LocationMapMode, trackingMode: LocationTrackingMode, displayingMapModeOptions: Bool, displayingPlacesButton: Bool, proximityNotification: Bool?, animated: Bool) {
self.mapNode.mapMode = mapMode
self.trackingMode = trackingMode
self.infoButtonNode.isSelected = displayingMapModeOptions
self.notificationButtonNode.isSelected = proximityNotification ?? false
self.locationButtonNode.setImage(generateTintedImage(image: self.iconForTracking(), color: self.presentationData.theme.rootController.navigationBar.buttonColor), for: .normal)
let updateLayout = self.displayingPlacesButton != displayingPlacesButton || self.proximityNotification != proximityNotification
self.displayingPlacesButton = displayingPlacesButton
self.proximityNotification = proximityNotification
@ -153,14 +158,25 @@ final class LocationMapHeaderNode: ASDisplayNode {
self.infoButtonNode.setImage(generateTintedImage(image: UIImage(bundleImageName: "Location/InfoIcon"), color: presentationData.theme.rootController.navigationBar.buttonColor), for: .normal)
self.infoButtonNode.setImage(generateTintedImage(image: UIImage(bundleImageName: "Location/InfoActiveIcon"), color: presentationData.theme.rootController.navigationBar.buttonColor), for: .selected)
self.infoButtonNode.setImage(generateTintedImage(image: UIImage(bundleImageName: "Location/InfoActiveIcon"), color: presentationData.theme.rootController.navigationBar.buttonColor), for: [.selected, .highlighted])
self.locationButtonNode.setImage(generateTintedImage(image: UIImage(bundleImageName: "Location/TrackIcon"), color: presentationData.theme.rootController.navigationBar.buttonColor), for: .normal)
self.notificationButtonNode.setImage(generateTintedImage(image: UIImage(bundleImageName: "Location/InfoIcon"), color: presentationData.theme.rootController.navigationBar.buttonColor), for: .normal)
self.notificationButtonNode.setImage(generateTintedImage(image: UIImage(bundleImageName: "Location/InfoActiveIcon"), color: presentationData.theme.rootController.navigationBar.buttonColor), for: .selected)
self.notificationButtonNode.setImage(generateTintedImage(image: UIImage(bundleImageName: "Location/InfoActiveIcon"), color: presentationData.theme.rootController.navigationBar.buttonColor), for: [.selected, .highlighted])
self.locationButtonNode.setImage(generateTintedImage(image: self.iconForTracking(), color: presentationData.theme.rootController.navigationBar.buttonColor), for: .normal)
self.notificationButtonNode.setImage(generateTintedImage(image: UIImage(bundleImageName: "Location/NotificationIcon"), color: presentationData.theme.rootController.navigationBar.buttonColor), for: .normal)
self.notificationButtonNode.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Title Panels/MuteIcon"), color: presentationData.theme.rootController.navigationBar.buttonColor), for: .selected)
self.notificationButtonNode.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Title Panels/MuteIcon"), color: presentationData.theme.rootController.navigationBar.buttonColor), for: [.selected, .highlighted])
self.placesBackgroundNode.image = generateBackgroundImage(theme: presentationData.theme)
self.shadowNode.image = generateShadowImage(theme: presentationData.theme, highlighted: false)
}
private func iconForTracking() -> UIImage? {
switch self.trackingMode {
case .none:
return UIImage(bundleImageName: "Location/TrackIcon")
case .follow:
return UIImage(bundleImageName: "Location/TrackActiveIcon")
case .followWithHeading:
return UIImage(bundleImageName: "Location/TrackHeadingIcon")
}
}
func updateLayout(layout: ContainerViewLayout, navigationBarHeight: CGFloat, topPadding: CGFloat, offset: CGFloat, size: CGSize, transition: ContainedViewLayoutTransition) {
self.validLayout = (layout, navigationBarHeight, topPadding, offset, size)

View file

@ -26,6 +26,23 @@ public enum LocationMapMode {
}
}
public enum LocationTrackingMode {
case none
case follow
case followWithHeading
var userTrackingMode: MKUserTrackingMode {
switch self {
case .follow:
return .follow
case .followWithHeading:
return .followWithHeading
default:
return .none
}
}
}
private class PickerAnnotationContainerView: UIView {
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
let result = super.hitTest(point, with: event)
@ -151,7 +168,7 @@ final class LocationMapNode: ASDisplayNode, MKMapViewDelegate {
class InvertedProximityCircleRenderer: MKOverlayRenderer {
var radius: Double = 0.0
var fillColor: UIColor = UIColor(rgb: 0x000000, alpha: 0.4)
var fillColor: UIColor = UIColor(rgb: 0x000000, alpha: 0.5)
override func draw(_ mapRect: MKMapRect, zoomScale: MKZoomScale, in context: CGContext) {
guard let overlay = self.overlay as? InvertedProximityCircle else {
@ -168,7 +185,7 @@ final class LocationMapNode: ASDisplayNode, MKMapViewDelegate {
context.setAlpha(alpha)
let path = UIBezierPath(rect: CGRect(x: mapRect.origin.x, y: mapRect.origin.y, width: mapRect.size.width, height: mapRect.size.height))
let radiusInMap = overlay.radius * MKMapPointsPerMeterAtLatitude(overlay.coordinate.latitude)
let radiusInMap = overlay.radius * MKMapPointsPerMeterAtLatitude(overlay.coordinate.latitude) * 2.0
let mapSize: MKMapSize = MKMapSize(width: radiusInMap, height: radiusInMap)
let regionOrigin = MKMapPoint(overlay.coordinate)
var regionRect: MKMapRect = MKMapRect(origin: regionOrigin, size: mapSize)
@ -352,6 +369,12 @@ final class LocationMapNode: ASDisplayNode, MKMapViewDelegate {
}
}
var trackingMode: LocationTrackingMode = .none {
didSet {
self.mapView?.userTrackingMode = self.trackingMode.userTrackingMode
}
}
func setMapCenter(coordinate: CLLocationCoordinate2D, span: MKCoordinateSpan = defaultMapSpan, offset: CGPoint = CGPoint(), isUserLocation: Bool = false, hidePicker: Bool = false, animated: Bool = false) {
let region = MKCoordinateRegion(center: coordinate, span: span)
self.ignoreRegionChanges = true
@ -525,7 +548,7 @@ final class LocationMapNode: ASDisplayNode, MKMapViewDelegate {
subscriber.putCompletion()
return EmptyDisposable
}
let annotationsPoll = (poll |> then(.complete() |> delay(3.0, queue: Queue.concurrentDefaultQueue()))) |> restart
let annotationsPoll = (poll |> then(.complete() |> delay(1.0, queue: Queue.concurrentDefaultQueue()))) |> restart
return combineLatest(self.userLocation, annotationsPoll)
|> map { userLocation, annotations -> [Double] in
@ -724,15 +747,21 @@ final class LocationMapNode: ASDisplayNode, MKMapViewDelegate {
}
annotations.append(contentsOf: self.annotations)
var zoomRect: MKMapRect = MKMapRect()
var zoomRect: MKMapRect?
for annotation in annotations {
let pointRegionRect = MKMapRect(region: MKCoordinateRegion(center: annotation.coordinate, latitudinalMeters: 100, longitudinalMeters: 100))
zoomRect = zoomRect.union(pointRegionRect)
if let currentZoomRect = zoomRect {
zoomRect = currentZoomRect.union(pointRegionRect)
} else {
zoomRect = pointRegionRect
}
}
let insets = UIEdgeInsets()
zoomRect = mapView.mapRectThatFits(zoomRect, edgePadding: insets)
mapView.setVisibleMapRect(zoomRect, animated: animated)
if let zoomRect = zoomRect {
let insets = UIEdgeInsets(top: 88.0, left: 80.0, bottom: 160.0, right: 80.0)
let fittedZoomRect = mapView.mapRectThatFits(zoomRect, edgePadding: insets)
mapView.setVisibleMapRect(fittedZoomRect, animated: animated)
}
}
func updateLayout(size: CGSize) {

View file

@ -103,7 +103,7 @@ public final class LocationPickerController: ViewController {
})
let locationWithTimeout: (CLLocationCoordinate2D, Int32?) -> TelegramMediaMap = { coordinate, timeout in
return TelegramMediaMap(latitude: coordinate.latitude, longitude: coordinate.longitude, heading: nil, accuracyRadius: nil, geoPlace: nil, venue: nil, liveBroadcastingTimeout: timeout)
return TelegramMediaMap(latitude: coordinate.latitude, longitude: coordinate.longitude, heading: nil, accuracyRadius: nil, geoPlace: nil, venue: nil, liveBroadcastingTimeout: timeout, liveProximityNotificationRadius: nil)
}
self.interaction = LocationPickerInteraction(sendLocation: { [weak self] coordinate in
@ -168,7 +168,7 @@ public final class LocationPickerController: ViewController {
}
let venueType = venue.venue?.type ?? ""
if ["home", "work"].contains(venueType) {
completion(TelegramMediaMap(latitude: venue.latitude, longitude: venue.longitude, heading: nil, accuracyRadius: nil, geoPlace: nil, venue: nil, liveBroadcastingTimeout: nil), nil)
completion(TelegramMediaMap(latitude: venue.latitude, longitude: venue.longitude, heading: nil, accuracyRadius: nil, geoPlace: nil, venue: nil, liveBroadcastingTimeout: nil, liveProximityNotificationRadius: nil), nil)
} else {
completion(venue, nil)
}

View file

@ -387,10 +387,10 @@ final class LocationPickerControllerNode: ViewControllerTracingNode, CLLocationM
|> map { homeCoordinate, workCoordinate -> [TelegramMediaMap]? in
var venues: [TelegramMediaMap] = []
if let (latitude, longitude) = homeCoordinate, let address = homeAddress {
venues.append(TelegramMediaMap(latitude: latitude, longitude: longitude, heading: nil, accuracyRadius: nil, geoPlace: nil, venue: MapVenue(title: presentationData.strings.Map_Home, address: address.displayString, provider: nil, id: "home", type: "home"), liveBroadcastingTimeout: nil))
venues.append(TelegramMediaMap(latitude: latitude, longitude: longitude, heading: nil, accuracyRadius: nil, geoPlace: nil, venue: MapVenue(title: presentationData.strings.Map_Home, address: address.displayString, provider: nil, id: "home", type: "home"), liveBroadcastingTimeout: nil, liveProximityNotificationRadius: nil))
}
if let (latitude, longitude) = workCoordinate, let address = workAddress {
venues.append(TelegramMediaMap(latitude: latitude, longitude: longitude, heading: nil, accuracyRadius: nil, geoPlace: nil, venue: MapVenue(title: presentationData.strings.Map_Work, address: address.displayString, provider: nil, id: "work", type: "work"), liveBroadcastingTimeout: nil))
venues.append(TelegramMediaMap(latitude: latitude, longitude: longitude, heading: nil, accuracyRadius: nil, geoPlace: nil, venue: MapVenue(title: presentationData.strings.Map_Work, address: address.displayString, provider: nil, id: "work", type: "work"), liveBroadcastingTimeout: nil, liveProximityNotificationRadius: nil))
}
return venues
}
@ -569,7 +569,7 @@ final class LocationPickerControllerNode: ViewControllerTracingNode, CLLocationM
strongSelf.headerNode.mapNode.setMapCenter(coordinate: venue.coordinate, hidePicker: true, animated: true)
}
strongSelf.headerNode.updateState(mapMode: state.mapMode, displayingMapModeOptions: state.displayingMapModeOptions, displayingPlacesButton: displayingPlacesButton, proximityNotification: nil, animated: true)
strongSelf.headerNode.updateState(mapMode: state.mapMode, trackingMode: .none, displayingMapModeOptions: state.displayingMapModeOptions, displayingPlacesButton: displayingPlacesButton, proximityNotification: nil, animated: true)
let annotations: [LocationPinAnnotation]
if let venues = displayedVenues {
@ -884,6 +884,7 @@ final class LocationPickerControllerNode: ViewControllerTracingNode, CLLocationM
let optionsFrame = CGRect(x: 0.0, y: optionsOffset, width: layout.size.width, height: optionsHeight)
transition.updateFrame(node: self.optionsNode, frame: optionsFrame)
self.optionsNode.updateLayout(size: optionsFrame.size, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, transition: transition)
self.optionsNode.isUserInteractionEnabled = self.state.displayingMapModeOptions
if let searchContainerNode = self.searchContainerNode {
searchContainerNode.frame = CGRect(origin: CGPoint(), size: layout.size)

View file

@ -180,7 +180,7 @@ final class LocationSearchContainerNode: ASDisplayNode {
guard let placemarkLocation = placemark.location else {
continue
}
let location = TelegramMediaMap(latitude: placemarkLocation.coordinate.latitude, longitude: placemarkLocation.coordinate.longitude, heading: nil, accuracyRadius: nil, geoPlace: nil, venue: nil, liveBroadcastingTimeout: nil)
let location = TelegramMediaMap(latitude: placemarkLocation.coordinate.latitude, longitude: placemarkLocation.coordinate.longitude, heading: nil, accuracyRadius: nil, geoPlace: nil, venue: nil, liveBroadcastingTimeout: nil, liveProximityNotificationRadius: nil)
entries.append(LocationSearchEntry(index: index, theme: themeAndStrings.0, location: location, title: placemark.name ?? "Name", distance: placemarkLocation.distance(from: currentLocation)))

View file

@ -7,8 +7,8 @@ import TelegramStringFormatting
import MapKit
extension TelegramMediaMap {
convenience init(coordinate: CLLocationCoordinate2D, liveBroadcastingTimeout: Int32? = nil) {
self.init(latitude: coordinate.latitude, longitude: coordinate.longitude, heading: nil, accuracyRadius: nil, geoPlace: nil, venue: nil, liveBroadcastingTimeout: liveBroadcastingTimeout)
convenience init(coordinate: CLLocationCoordinate2D, liveBroadcastingTimeout: Int32? = nil, proximityNotificationRadius: Int32? = nil) {
self.init(latitude: coordinate.latitude, longitude: coordinate.longitude, heading: nil, accuracyRadius: nil, geoPlace: nil, venue: nil, liveBroadcastingTimeout: liveBroadcastingTimeout, liveProximityNotificationRadius: proximityNotificationRadius)
}
var coordinate: CLLocationCoordinate2D {

View file

@ -38,7 +38,7 @@ enum LocationViewRightBarButton {
class LocationViewInteraction {
let toggleMapModeSelection: () -> Void
let updateMapMode: (LocationMapMode) -> Void
let goToUserLocation: () -> Void
let toggleTrackingMode: () -> Void
let goToCoordinate: (CLLocationCoordinate2D) -> Void
let requestDirections: () -> Void
let share: () -> Void
@ -49,10 +49,10 @@ class LocationViewInteraction {
let updateRightBarButton: (LocationViewRightBarButton) -> Void
let present: (ViewController) -> Void
init(toggleMapModeSelection: @escaping () -> Void, updateMapMode: @escaping (LocationMapMode) -> Void, goToUserLocation: @escaping () -> Void, goToCoordinate: @escaping (CLLocationCoordinate2D) -> Void, requestDirections: @escaping () -> Void, share: @escaping () -> Void, setupProximityNotification: @escaping (Bool, CLLocationCoordinate2D?, MessageId?) -> Void, updateSendActionHighlight: @escaping (Bool) -> Void, sendLiveLocation: @escaping (CLLocationCoordinate2D, Int32?) -> Void, stopLiveLocation: @escaping () -> Void, updateRightBarButton: @escaping (LocationViewRightBarButton) -> Void, present: @escaping (ViewController) -> Void) {
init(toggleMapModeSelection: @escaping () -> Void, updateMapMode: @escaping (LocationMapMode) -> Void, toggleTrackingMode: @escaping () -> Void, goToCoordinate: @escaping (CLLocationCoordinate2D) -> Void, requestDirections: @escaping () -> Void, share: @escaping () -> Void, setupProximityNotification: @escaping (Bool, CLLocationCoordinate2D?, MessageId?) -> Void, updateSendActionHighlight: @escaping (Bool) -> Void, sendLiveLocation: @escaping (CLLocationCoordinate2D, Int32?) -> Void, stopLiveLocation: @escaping () -> Void, updateRightBarButton: @escaping (LocationViewRightBarButton) -> Void, present: @escaping (ViewController) -> Void) {
self.toggleMapModeSelection = toggleMapModeSelection
self.updateMapMode = updateMapMode
self.goToUserLocation = goToUserLocation
self.toggleTrackingMode = toggleTrackingMode
self.goToCoordinate = goToCoordinate
self.requestDirections = requestDirections
self.share = share
@ -65,8 +65,6 @@ class LocationViewInteraction {
}
}
var CURRENT_DISTANCE: Double? = nil
public final class LocationViewController: ViewController {
private var controllerNode: LocationViewControllerNode {
return self.displayNode as! LocationViewControllerNode
@ -131,14 +129,21 @@ public final class LocationViewController: ViewController {
state.displayingMapModeOptions = false
return state
}
}, goToUserLocation: { [weak self] in
}, toggleTrackingMode: { [weak self] in
guard let strongSelf = self else {
return
}
strongSelf.controllerNode.updateState { state in
var state = state
state.displayingMapModeOptions = false
state.selectedLocation = .user
switch state.trackingMode {
case .none:
state.trackingMode = .follow
case .follow:
state.trackingMode = .followWithHeading
case .followWithHeading:
state.trackingMode = .none
}
return state
}
}, goToCoordinate: { [weak self] coordinate in
@ -174,13 +179,9 @@ public final class LocationViewController: ViewController {
}
if reset {
strongSelf.controllerNode.updateState { state -> LocationViewState in
var state = state
state.proximityRadius = nil
return state
if let messageId = messageId {
let _ = requestEditLiveLocation(postbox: context.account.postbox, network: context.account.network, stateManager: context.account.stateManager, messageId: messageId, stop: false, coordinate: nil, heading: nil, proximityNotificationRadius: 0).start()
}
CURRENT_DISTANCE = nil
} else {
strongSelf.controllerNode.setProximityIndicator(radius: 0)
@ -195,22 +196,14 @@ public final class LocationViewController: ViewController {
}
if let messageId = messageId {
completion()
strongSelf.controllerNode.updateState { state -> LocationViewState in
var state = state
state.proximityRadius = Double(distance)
return state
}
let _ = requestProximityNotification(postbox: context.account.postbox, network: context.account.network, messageId: messageId, distance: distance).start()
CURRENT_DISTANCE = Double(distance)
let _ = requestEditLiveLocation(postbox: context.account.postbox, network: context.account.network, stateManager: context.account.stateManager, messageId: messageId, stop: false, coordinate: nil, heading: nil, proximityNotificationRadius: distance).start()
} else if let coordinate = coordinate {
strongSelf.present(textAlertController(context: strongSelf.context, title: strongSelf.presentationData.strings.Location_LiveLocationRequired_Title, text: strongSelf.presentationData.strings.Location_LiveLocationRequired_Description, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Location_LiveLocationRequired_ShareLocation, action: { [weak self] in
strongSelf.present(textAlertController(context: strongSelf.context, title: strongSelf.presentationData.strings.Location_LiveLocationRequired_Title, text: strongSelf.presentationData.strings.Location_LiveLocationRequired_Description, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Location_LiveLocationRequired_ShareLocation, action: {
completion()
self?.interaction?.sendLiveLocation(coordinate, distance)
strongSelf.interaction?.sendLiveLocation(coordinate, distance)
}), TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {})], actionLayout: .vertical), in: .window(.root))
}
completion()
}, willDismiss: { [weak self] in
if let strongSelf = self {
strongSelf.controllerNode.setProximityIndicator(radius: nil)
@ -236,54 +229,44 @@ public final class LocationViewController: ViewController {
return
}
let _ = (context.account.postbox.loadedPeerWithId(subject.id.peerId)
|> deliverOnMainQueue).start(next: { [weak self] peer in
let controller = ActionSheetController(presentationData: strongSelf.presentationData)
var title = strongSelf.presentationData.strings.Map_LiveLocationGroupDescription
if let user = peer as? TelegramUser {
title = strongSelf.presentationData.strings.Map_LiveLocationPrivateDescription(user.compactDisplayTitle).0
}
let sendLiveLocationImpl: (Int32) -> Void = { [weak self, weak controller] period in
controller?.dismissAnimated()
if let strongSelf = self {
params.sendLiveLocation(TelegramMediaMap(coordinate: coordinate, liveBroadcastingTimeout: period))
if let distance = distance {
strongSelf.controllerNode.updateState { state -> LocationViewState in
var state = state
state.proximityRadius = Double(distance)
return state
}
strongSelf.controllerNode.ownLiveLocationStartedAction = { messageId in
let _ = requestProximityNotification(postbox: context.account.postbox, network: context.account.network, messageId: messageId, distance: distance).start()
}
}
if let distance = distance {
params.sendLiveLocation(TelegramMediaMap(coordinate: coordinate, liveBroadcastingTimeout: 30 * 60, proximityNotificationRadius: distance))
} else {
let _ = (context.account.postbox.loadedPeerWithId(subject.id.peerId)
|> deliverOnMainQueue).start(next: { [weak self] peer in
let controller = ActionSheetController(presentationData: strongSelf.presentationData)
var title = strongSelf.presentationData.strings.Map_LiveLocationGroupDescription
if let user = peer as? TelegramUser {
title = strongSelf.presentationData.strings.Map_LiveLocationPrivateDescription(user.compactDisplayTitle).0
}
}
controller.setItemGroups([
ActionSheetItemGroup(items: [
ActionSheetTextItem(title: title),
ActionSheetButtonItem(title: strongSelf.presentationData.strings.Map_LiveLocationFor15Minutes, color: .accent, action: {
sendLiveLocationImpl(15 * 60)
}),
ActionSheetButtonItem(title: strongSelf.presentationData.strings.Map_LiveLocationFor1Hour, color: .accent, action: {
sendLiveLocationImpl(60 * 60 - 1)
}),
ActionSheetButtonItem(title: strongSelf.presentationData.strings.Map_LiveLocationFor8Hours, color: .accent, action: {
sendLiveLocationImpl(8 * 60 * 60)
})
]),
ActionSheetItemGroup(items: [
ActionSheetButtonItem(title: strongSelf.presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak controller] in
controller?.dismissAnimated()
})
let sendLiveLocationImpl: (Int32) -> Void = { [weak self, weak controller] period in
controller?.dismissAnimated()
params.sendLiveLocation(TelegramMediaMap(coordinate: coordinate, liveBroadcastingTimeout: period))
}
controller.setItemGroups([
ActionSheetItemGroup(items: [
ActionSheetTextItem(title: title),
ActionSheetButtonItem(title: strongSelf.presentationData.strings.Map_LiveLocationFor15Minutes, color: .accent, action: {
sendLiveLocationImpl(15 * 60)
}),
ActionSheetButtonItem(title: strongSelf.presentationData.strings.Map_LiveLocationFor1Hour, color: .accent, action: {
sendLiveLocationImpl(60 * 60 - 1)
}),
ActionSheetButtonItem(title: strongSelf.presentationData.strings.Map_LiveLocationFor8Hours, color: .accent, action: {
sendLiveLocationImpl(8 * 60 * 60)
})
]),
ActionSheetItemGroup(items: [
ActionSheetButtonItem(title: strongSelf.presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak controller] in
controller?.dismissAnimated()
})
])
])
])
strongSelf.present(controller, in: .window(.root))
})
strongSelf.present(controller, in: .window(.root))
})
}
}
}, stopLiveLocation: { [weak self] in
params.stopLiveLocation(nil)
@ -330,12 +313,6 @@ public final class LocationViewController: ViewController {
self.displayNode = LocationViewControllerNode(context: self.context, presentationData: self.presentationData, subject: self.subject, interaction: interaction, locationManager: self.locationManager)
self.displayNodeDidLoad()
self.controllerNode.updateState { state -> LocationViewState in
var state = state
state.proximityRadius = CURRENT_DISTANCE
return state
}
}
private func updateRightBarButton() {

View file

@ -11,6 +11,7 @@ import ItemListUI
import ItemListVenueItem
import TelegramPresentationData
import TelegramStringFormatting
import TelegramUIPreferences
import TelegramNotices
import AccountContext
import AppBundle
@ -48,7 +49,7 @@ private enum LocationViewEntryId: Hashable {
private enum LocationViewEntry: Comparable, Identifiable {
case info(PresentationTheme, TelegramMediaMap, String?, Double?, Double?)
case toggleLiveLocation(PresentationTheme, String, String, CLLocationCoordinate2D?, Double?, Double?)
case liveLocation(PresentationTheme, Message, Double?, Int)
case liveLocation(PresentationTheme, PresentationDateTimeFormat, PresentationPersonNameOrder, Message, Double?, Int)
var stableId: LocationViewEntryId {
switch self {
@ -56,7 +57,7 @@ private enum LocationViewEntry: Comparable, Identifiable {
return .info
case .toggleLiveLocation:
return .toggleLiveLocation
case let .liveLocation(_, message, _, _):
case let .liveLocation(_, _, _, message, _, _):
return .liveLocation(message.stableId)
}
}
@ -75,8 +76,8 @@ private enum LocationViewEntry: Comparable, Identifiable {
} else {
return false
}
case let .liveLocation(lhsTheme, lhsMessage, lhsDistance, lhsIndex):
if case let .liveLocation(rhsTheme, rhsMessage, rhsDistance, rhsIndex) = rhs, lhsTheme === rhsTheme, areMessagesEqual(lhsMessage, rhsMessage), lhsDistance == rhsDistance, lhsIndex == rhsIndex {
case let .liveLocation(lhsTheme, lhsDateTimeFormat, lhsNameDisplayOrder, lhsMessage, lhsDistance, lhsIndex):
if case let .liveLocation(rhsTheme, rhsDateTimeFormat, rhsNameDisplayOrder, rhsMessage, rhsDistance, rhsIndex) = rhs, lhsTheme === rhsTheme, lhsDateTimeFormat == rhsDateTimeFormat, lhsNameDisplayOrder == rhsNameDisplayOrder, areMessagesEqual(lhsMessage, rhsMessage), lhsDistance == rhsDistance, lhsIndex == rhsIndex {
return true
} else {
return false
@ -100,17 +101,17 @@ private enum LocationViewEntry: Comparable, Identifiable {
case .liveLocation:
return true
}
case let .liveLocation(_, _, _, lhsIndex):
case let .liveLocation(_, _, _, _, _, lhsIndex):
switch rhs {
case .info, .toggleLiveLocation:
return false
case let .liveLocation(_, _, _, rhsIndex):
case let .liveLocation(_, _, _, _, _, rhsIndex):
return lhsIndex < rhsIndex
}
}
}
func item(account: Account, presentationData: PresentationData, interaction: LocationViewInteraction?) -> ListViewItem {
func item(context: AccountContext, presentationData: PresentationData, interaction: LocationViewInteraction?) -> ListViewItem {
switch self {
case let .info(_, location, address, distance, time):
let addressString: String?
@ -126,7 +127,7 @@ private enum LocationViewEntry: Comparable, Identifiable {
distanceString = nil
}
let eta = time.flatMap { stringForEstimatedDuration(strings: presentationData.strings, eta: $0) }
return LocationInfoListItem(presentationData: ItemListPresentationData(presentationData), account: account, location: location, address: addressString, distance: distanceString, eta: eta, action: {
return LocationInfoListItem(presentationData: ItemListPresentationData(presentationData), account: context.account, location: location, address: addressString, distance: distanceString, eta: eta, action: {
interaction?.goToCoordinate(location.coordinate)
}, getDirections: {
interaction?.requestDirections()
@ -138,7 +139,7 @@ private enum LocationViewEntry: Comparable, Identifiable {
} else {
beginTimeAndTimeout = nil
}
return LocationActionListItem(presentationData: ItemListPresentationData(presentationData), account: account, title: title, subtitle: subtitle, icon: beginTimeAndTimeout != nil ? .stopLiveLocation : .liveLocation, beginTimeAndTimeout: beginTimeAndTimeout, action: {
return LocationActionListItem(presentationData: ItemListPresentationData(presentationData), account: context.account, title: title, subtitle: subtitle, icon: beginTimeAndTimeout != nil ? .stopLiveLocation : .liveLocation, beginTimeAndTimeout: beginTimeAndTimeout, action: {
if beginTimeAndTimeout != nil {
interaction?.stopLiveLocation()
} else if let coordinate = coordinate {
@ -147,24 +148,18 @@ private enum LocationViewEntry: Comparable, Identifiable {
}, highlighted: { highlight in
interaction?.updateSendActionHighlight(highlight)
})
case let .liveLocation(_, message, distance, _):
let distanceString: String?
if let distance = distance {
distanceString = distance < 10 ? presentationData.strings.Map_YouAreHere : presentationData.strings.Map_DistanceAway(stringForDistance(strings: presentationData.strings, distance: distance)).0
} else {
distanceString = nil
}
return ItemListTextItem(presentationData: ItemListPresentationData(presentationData), text: .plain(""), sectionId: 0)
case let .liveLocation(_, dateTimeFormat, nameDisplayOrder, message, distance, _):
return LocationLiveListItem(presentationData: ItemListPresentationData(presentationData), dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, context: context, message: message, distance: distance, action: {}, longTapAction: {})
}
}
}
private func preparedTransition(from fromEntries: [LocationViewEntry], to toEntries: [LocationViewEntry], account: Account, presentationData: PresentationData, interaction: LocationViewInteraction?) -> LocationViewTransaction {
private func preparedTransition(from fromEntries: [LocationViewEntry], to toEntries: [LocationViewEntry], context: AccountContext, presentationData: PresentationData, interaction: LocationViewInteraction?) -> LocationViewTransaction {
let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries)
let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) }
let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, presentationData: presentationData, interaction: interaction), directionHint: nil) }
let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, presentationData: presentationData, interaction: interaction), directionHint: nil) }
let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, presentationData: presentationData, interaction: interaction), directionHint: nil) }
let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, presentationData: presentationData, interaction: interaction), directionHint: nil) }
return LocationViewTransaction(deletions: deletions, insertions: insertions, updates: updates)
}
@ -180,13 +175,13 @@ struct LocationViewState {
var mapMode: LocationMapMode
var displayingMapModeOptions: Bool
var selectedLocation: LocationViewLocation
var proximityRadius: Double?
var trackingMode: LocationTrackingMode
init() {
self.mapMode = .map
self.displayingMapModeOptions = false
self.selectedLocation = .initial
self.proximityRadius = nil
self.trackingMode = .none
}
}
@ -231,7 +226,7 @@ final class LocationViewControllerNode: ViewControllerTracingNode, CLLocationMan
self.listNode.verticalScrollIndicatorFollowsOverscroll = true
var setupProximityNotificationImpl: ((Bool) -> Void)?
self.headerNode = LocationMapHeaderNode(presentationData: presentationData, toggleMapModeSelection: interaction.toggleMapModeSelection, goToUserLocation: interaction.goToUserLocation, setupProximityNotification: { reset in
self.headerNode = LocationMapHeaderNode(presentationData: presentationData, toggleMapModeSelection: interaction.toggleMapModeSelection, goToUserLocation: interaction.toggleTrackingMode, setupProximityNotification: { reset in
setupProximityNotificationImpl?(reset)
})
self.headerNode.mapNode.isRotateEnabled = false
@ -304,7 +299,7 @@ final class LocationViewControllerNode: ViewControllerTracingNode, CLLocationMan
let selfPeer = context.account.postbox.transaction { transaction -> Peer? in
return transaction.getPeer(context.account.peerId)
}
self.disposable = (combineLatest(self.presentationDataPromise.get(), self.statePromise.get(), selfPeer, liveLocations, self.headerNode.mapNode.userLocation, userLocation, address, eta)
|> deliverOnMainQueue).start(next: { [weak self] presentationData, state, selfPeer, liveLocations, userLocation, distance, address, eta in
if let strongSelf = self, let location = getLocation(from: subject) {
@ -316,6 +311,7 @@ final class LocationViewControllerNode: ViewControllerTracingNode, CLLocationMan
let currentTime = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)
var proximityNotification: Bool? = nil
var proximityNotificationRadius: Int32?
var index: Int = 0
if location.liveBroadcastingTimeout == nil {
@ -330,15 +326,14 @@ final class LocationViewControllerNode: ViewControllerTracingNode, CLLocationMan
for message in effectiveLiveLocations {
if message.localTags.contains(.OutgoingLiveLocation) {
activeOwnLiveLocation = message
if let location = getLocation(from: message), let radius = location.liveProximityNotificationRadius {
proximityNotificationRadius = radius
proximityNotification = true
}
break
}
}
if let activeOwnLiveLocation = activeOwnLiveLocation, let ownLiveLocationStartedAction = strongSelf.ownLiveLocationStartedAction {
strongSelf.ownLiveLocationStartedAction = nil
ownLiveLocationStartedAction(activeOwnLiveLocation.id)
}
let title: String
let subtitle: String
let beginTime: Double?
@ -369,7 +364,10 @@ final class LocationViewControllerNode: ViewControllerTracingNode, CLLocationMan
timeout = nil
}
entries.append(.toggleLiveLocation(presentationData.theme, title, subtitle, userLocation?.coordinate, beginTime, timeout))
if let channel = subject.author as? TelegramChannel, case .broadcast = channel.info {
} else {
entries.append(.toggleLiveLocation(presentationData.theme, title, subtitle, userLocation?.coordinate, beginTime, timeout))
}
var sortedLiveLocations: [Message] = []
@ -388,40 +386,43 @@ final class LocationViewControllerNode: ViewControllerTracingNode, CLLocationMan
}
effectiveLiveLocations = sortedLiveLocations
}
for message in effectiveLiveLocations {
var liveBroadcastingTimeout: Int32 = 0
if let location = getLocation(from: message), let timeout = location.liveBroadcastingTimeout {
liveBroadcastingTimeout = timeout
}
let remainingTime = max(0, message.timestamp + liveBroadcastingTimeout - currentTime)
if message.flags.contains(.Incoming) && remainingTime != 0 {
proximityNotification = state.proximityRadius != nil
if message.flags.contains(.Incoming) && remainingTime != 0 && proximityNotification == nil {
proximityNotification = false
}
let subjectLocation = CLLocation(latitude: location.latitude, longitude: location.longitude)
let distance = userLocation.flatMap { subjectLocation.distance(from: $0) }
entries.append(.liveLocation(presentationData.theme, message, distance, index))
if message.localTags.contains(.OutgoingLiveLocation), let selfPeer = selfPeer {
userAnnotation = LocationPinAnnotation(context: context, theme: presentationData.theme, message: message, selfPeer: selfPeer, heading: location.heading)
} else {
annotations.append(LocationPinAnnotation(context: context, theme: presentationData.theme, message: message, selfPeer: selfPeer, heading: location.heading))
entries.append(.liveLocation(presentationData.theme, presentationData.dateTimeFormat, presentationData.nameDisplayOrder, message, distance, index))
}
index += 1
}
if subject.id.peerId.namespace != Namespaces.Peer.CloudUser {
proximityNotification = state.proximityRadius != nil
if subject.id.peerId.namespace != Namespaces.Peer.CloudUser, proximityNotification == nil {
proximityNotification = false
}
if let channel = subject.author as? TelegramChannel, case .broadcast = channel.info {
proximityNotification = nil
}
let previousEntries = previousEntries.swap(entries)
let previousState = previousState.swap(state)
let transition = preparedTransition(from: previousEntries ?? [], to: entries, account: context.account, presentationData: presentationData, interaction: strongSelf.interaction)
let transition = preparedTransition(from: previousEntries ?? [], to: entries, context: context, presentationData: presentationData, interaction: strongSelf.interaction)
strongSelf.enqueueTransition(transition)
strongSelf.headerNode.updateState(mapMode: state.mapMode, displayingMapModeOptions: state.displayingMapModeOptions, displayingPlacesButton: false, proximityNotification: proximityNotification, animated: false)
strongSelf.headerNode.updateState(mapMode: state.mapMode, trackingMode: state.trackingMode, displayingMapModeOptions: state.displayingMapModeOptions, displayingPlacesButton: false, proximityNotification: proximityNotification, animated: false)
if let proximityNotification = proximityNotification, !proximityNotification && !strongSelf.displayedProximityAlertTooltip {
strongSelf.displayedProximityAlertTooltip = true
@ -452,7 +453,8 @@ final class LocationViewControllerNode: ViewControllerTracingNode, CLLocationMan
case .custom:
break
}
strongSelf.headerNode.mapNode.trackingMode = state.trackingMode
let previousAnnotations = previousAnnotations.swap(annotations)
let previousUserAnnotation = previousUserAnnotation.swap(userAnnotation)
if (userAnnotation == nil) != (previousUserAnnotation == nil) {
@ -462,8 +464,11 @@ final class LocationViewControllerNode: ViewControllerTracingNode, CLLocationMan
strongSelf.headerNode.mapNode.annotations = annotations
}
strongSelf.headerNode.mapNode.activeProximityRadius = state.proximityRadius
if let _ = proximityNotification {
strongSelf.headerNode.mapNode.activeProximityRadius = proximityNotificationRadius.flatMap { Double($0) }
} else {
strongSelf.headerNode.mapNode.activeProximityRadius = nil
}
let rightBarButtonAction: LocationViewRightBarButton
if location.liveBroadcastingTimeout != nil {
if liveLocations.count > 1 {
@ -520,6 +525,7 @@ final class LocationViewControllerNode: ViewControllerTracingNode, CLLocationMan
var state = state
state.displayingMapModeOptions = false
state.selectedLocation = .custom
state.trackingMode = .none
return state
}
}
@ -592,6 +598,7 @@ final class LocationViewControllerNode: ViewControllerTracingNode, CLLocationMan
self.updateState { state in
var state = state
state.selectedLocation = .coordinate(coordinate, true)
state.trackingMode = .none
return state
}
}
@ -603,13 +610,12 @@ final class LocationViewControllerNode: ViewControllerTracingNode, CLLocationMan
self.updateState { state in
var state = state
state.selectedLocation = .user
state.trackingMode = .none
return state
}
}
}
var ownLiveLocationStartedAction: ((MessageId) -> Void)?
func showAll() {
self.headerNode.mapNode.showAll()
}
@ -687,5 +693,6 @@ final class LocationViewControllerNode: ViewControllerTracingNode, CLLocationMan
let optionsFrame = CGRect(x: 0.0, y: optionsOffset, width: layout.size.width, height: optionsHeight)
transition.updateFrame(node: self.optionsNode, frame: optionsFrame)
self.optionsNode.updateLayout(size: optionsFrame.size, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, transition: transition)
self.optionsNode.isUserInteractionEnabled = self.state.displayingMapModeOptions
}
}

View file

@ -10,6 +10,7 @@ static_library(
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit#shared",
"//submodules/AsyncDisplayKit:AsyncDisplayKit#shared",
"//submodules/Postbox:Postbox#shared",
"//submodules/TelegramCore:TelegramCore#shared",
"//submodules/GZip:GZip",
"//submodules/rlottie:RLottieBinding",
"//submodules/AppBundle:AppBundle",

View file

@ -11,6 +11,7 @@ swift_library(
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit",
"//submodules/AsyncDisplayKit:AsyncDisplayKit",
"//submodules/Postbox:Postbox",
"//submodules/TelegramCore:TelegramCore",
"//submodules/GZip:GZip",
"//submodules/rlottie:RLottieBinding",
"//submodules/AppBundle:AppBundle",

View file

@ -3,6 +3,7 @@ import UIKit
import Display
import AsyncDisplayKit
import Postbox
import TelegramCore
import RLottieBinding
import AppBundle
import GZip
@ -72,7 +73,7 @@ public enum ManagedAnimationFrameRange: Equatable {
public enum ManagedAnimationSource: Equatable {
case local(String)
case resource(MediaBox, MediaResource)
case resource(Account, MediaResource)
var cacheKey: String {
switch self {
@ -87,8 +88,8 @@ public enum ManagedAnimationSource: Equatable {
switch self {
case let .local(name):
return getAppBundle().path(forResource: name, ofType: "tgs")
case let .resource(mediaBox, resource):
return mediaBox.completedResourcePath(resource)
case let .resource(account, resource):
return account.postbox.mediaBox.completedResourcePath(resource)
}
}
@ -100,8 +101,8 @@ public enum ManagedAnimationSource: Equatable {
} else {
return false
}
case let .resource(lhsMediaBox, lhsResource):
if case let .resource(rhsMediaBox, rhsResource) = rhs, lhsMediaBox === rhsMediaBox, lhsResource.isEqual(to: rhsResource) {
case let .resource(lhsAccount, lhsResource):
if case let .resource(rhsAccount, rhsResource) = rhs, lhsAccount === rhsAccount, lhsResource.isEqual(to: rhsResource) {
return true
} else {
return false
@ -112,9 +113,9 @@ public enum ManagedAnimationSource: Equatable {
public struct ManagedAnimationItem {
public let source: ManagedAnimationSource
var frames: ManagedAnimationFrameRange?
var duration: Double?
var loop: Bool
public var frames: ManagedAnimationFrameRange?
public var duration: Double?
public var loop: Bool
var callbacks: [(Int, () -> Void)]
public init(source: ManagedAnimationSource, frames: ManagedAnimationFrameRange? = nil, duration: Double? = nil, loop: Bool = false, callbacks: [(Int, () -> Void)] = []) {

View file

@ -67,7 +67,7 @@ struct PasscodeKeyboardLayout {
self.topOffset = 226.0
self.biometricsOffset = 30.0
self.deleteOffset = 20.0
case .iPhoneX:
case .iPhoneX, .iPhone12Mini, .iPhone12:
self.buttonSize = 75.0
self.horizontalSecond = 103.0
self.horizontalThird = 206.0
@ -78,7 +78,7 @@ struct PasscodeKeyboardLayout {
self.topOffset = 294.0
self.biometricsOffset = 30.0
self.deleteOffset = 20.0
case .iPhoneXSMax:
case .iPhoneXSMax, .iPhone12ProMax:
self.buttonSize = 85.0
self.horizontalSecond = 115.0
self.horizontalThird = 230.0
@ -151,11 +151,11 @@ public struct PasscodeLayout {
self.titleOffset = 112.0
self.subtitleOffset = -6.0
self.inputFieldOffset = 156.0
case .iPhoneX:
case .iPhoneX, .iPhone12Mini, .iPhone12:
self.titleOffset = 162.0
self.subtitleOffset = 0.0
self.inputFieldOffset = 206.0
case .iPhoneXSMax:
case .iPhoneXSMax, .iPhone12ProMax:
self.titleOffset = 180.0
self.subtitleOffset = 0.0
self.inputFieldOffset = 226.0

View file

@ -2112,7 +2112,7 @@ public func groupInfoController(context: AccountContext, peerId originalPeerId:
return
}
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
let mapMedia = TelegramMediaMap(latitude: location.latitude, longitude: location.longitude, heading: nil, accuracyRadius: nil, geoPlace: nil, venue: MapVenue(title: peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder), address: location.address, provider: nil, id: nil, type: nil), liveBroadcastingTimeout: nil)
let mapMedia = TelegramMediaMap(latitude: location.latitude, longitude: location.longitude, heading: nil, accuracyRadius: nil, geoPlace: nil, venue: MapVenue(title: peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder), address: location.address, provider: nil, id: nil, type: nil), liveBroadcastingTimeout: nil, liveProximityNotificationRadius: nil)
let controller = legacyLocationController(message: nil, mapMedia: mapMedia, context: context, openPeer: { _ in }, sendLiveLocation: { _, _ in }, stopLiveLocation: {}, openUrl: { url in
context.sharedContext.applicationBindings.openUrl(url)
})

View file

@ -2465,8 +2465,8 @@ public func chatWebFileImage(account: Account, file: TelegramMediaWebFile) -> Si
private let precomposedSmallAlbumArt = Atomic<UIImage?>(value: nil)
private func albumArtThumbnailData(postbox: Postbox, thumbnail: MediaResource) -> Signal<Data?, NoError> {
let thumbnailResource = postbox.mediaBox.resourceData(thumbnail)
private func albumArtThumbnailData(postbox: Postbox, thumbnail: MediaResource, attemptSynchronously: Bool = false) -> Signal<Data?, NoError> {
let thumbnailResource = postbox.mediaBox.resourceData(thumbnail, attemptSynchronously: attemptSynchronously)
let signal = thumbnailResource |> take(1) |> mapToSignal { maybeData -> Signal<Data?, NoError> in
if maybeData.complete {
@ -2602,7 +2602,7 @@ private func drawAlbumArtPlaceholder(into c: CGContext, arguments: TransformImag
}
}
public func playerAlbumArt(postbox: Postbox, fileReference: FileMediaReference?, albumArt: SharedMediaPlaybackAlbumArt?, thumbnail: Bool, overlayColor: UIColor? = nil, emptyColor: UIColor? = nil, drawPlaceholderWhenEmpty: Bool = true) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> {
public func playerAlbumArt(postbox: Postbox, fileReference: FileMediaReference?, albumArt: SharedMediaPlaybackAlbumArt?, thumbnail: Bool, overlayColor: UIColor? = nil, emptyColor: UIColor? = nil, drawPlaceholderWhenEmpty: Bool = true, attemptSynchronously: Bool = false) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> {
var fileArtworkData: Signal<Data?, NoError> = .single(nil)
if let fileReference = fileReference {
let size = thumbnail ? CGSize(width: 48.0, height: 48.0) : CGSize(width: 320.0, height: 320.0)
@ -2628,7 +2628,7 @@ public func playerAlbumArt(postbox: Postbox, fileReference: FileMediaReference?,
let thumbnail = Signal<Data?, NoError> { subscriber in
let fetchedDisposable = fetchedThumbnail.start()
let thumbnailDisposable = postbox.mediaBox.resourceData(thumbnailResource).start(next: { next in
let thumbnailDisposable = postbox.mediaBox.resourceData(thumbnailResource, attemptSynchronously: attemptSynchronously).start(next: { next in
subscriber.putNext(next.size == 0 ? nil : try? Data(contentsOf: URL(fileURLWithPath: next.path), options: []))
}, error: subscriber.putError, completed: subscriber.putCompletion)
@ -2643,7 +2643,7 @@ public func playerAlbumArt(postbox: Postbox, fileReference: FileMediaReference?,
}
} else if let albumArt = albumArt {
if thumbnail {
immediateArtworkData = albumArtThumbnailData(postbox: postbox, thumbnail: albumArt.thumbnailResource)
immediateArtworkData = albumArtThumbnailData(postbox: postbox, thumbnail: albumArt.thumbnailResource, attemptSynchronously: attemptSynchronously)
|> map { thumbnailData in
return Tuple(thumbnailData, nil, false)
}

View file

@ -86,7 +86,17 @@ final class AccountManagerImpl {
for record in self.legacyRecordTable.getRecords() {
legacyRecordDict[record.id] = record
}
self.currentAtomicState = AccountManagerAtomicState(records: legacyRecordDict, currentRecordId: self.legacyMetadataTable.getCurrentAccountId(), currentAuthRecord: self.legacyMetadataTable.getCurrentAuthAccount())
self.currentAtomicState = AccountManagerAtomicState(records: legacyRecordDict, currentRecordId: self.legacyMetadataTable.getCurrentAccountId(), currentAuthRecord: self.legacyMetadataTable.getCurrentAuthAccount(), accessChallengeData: self.legacyMetadataTable.getAccessChallengeData())
self.syncAtomicStateToFile()
}
let tableAccessChallengeData = self.legacyMetadataTable.getAccessChallengeData()
if self.currentAtomicState.accessChallengeData != .none {
if tableAccessChallengeData == .none {
self.legacyMetadataTable.setAccessChallengeData(self.currentAtomicState.accessChallengeData)
}
} else if tableAccessChallengeData != .none {
self.currentAtomicState.accessChallengeData = tableAccessChallengeData
self.syncAtomicStateToFile()
}
@ -165,7 +175,9 @@ final class AccountManagerImpl {
return self.legacyMetadataTable.getAccessChallengeData()
}, setAccessChallengeData: { data in
self.currentUpdatedAccessChallengeData = data
self.currentAtomicStateUpdated = true
self.legacyMetadataTable.setAccessChallengeData(data)
self.currentAtomicState.accessChallengeData = data
}, getVersion: {
return self.legacyMetadataTable.getVersion()
}, setVersion: { version in

View file

@ -5,16 +5,19 @@ final class AccountManagerAtomicState: Codable {
case records
case currentRecordId
case currentAuthRecord
case accessChallengeData
}
var records: [AccountRecordId: AccountRecord]
var currentRecordId: AccountRecordId?
var currentAuthRecord: AuthAccountRecord?
var accessChallengeData: PostboxAccessChallengeData
init(records: [AccountRecordId: AccountRecord] = [:], currentRecordId: AccountRecordId? = nil, currentAuthRecord: AuthAccountRecord? = nil) {
init(records: [AccountRecordId: AccountRecord] = [:], currentRecordId: AccountRecordId? = nil, currentAuthRecord: AuthAccountRecord? = nil, accessChallengeData: PostboxAccessChallengeData = .none) {
self.records = records
self.currentRecordId = currentRecordId
self.currentAuthRecord = currentAuthRecord
self.accessChallengeData = accessChallengeData
}
init(from decoder: Decoder) throws {
@ -34,6 +37,12 @@ final class AccountManagerAtomicState: Codable {
self.currentRecordId = try container.decodeIfPresent(AccountRecordId.self, forKey: .currentRecordId)
}
self.currentAuthRecord = try container.decodeIfPresent(AuthAccountRecord.self, forKey: .currentAuthRecord)
if let accessChallengeData = try? container.decodeIfPresent(PostboxAccessChallengeData.self, forKey: .accessChallengeData) {
self.accessChallengeData = accessChallengeData
} else {
self.accessChallengeData = .none
}
}
public func encode(to encoder: Encoder) throws {
@ -43,5 +52,6 @@ final class AccountManagerAtomicState: Codable {
let currentRecordIdString: String? = self.currentRecordId.flatMap({ "\($0.rawValue)" })
try container.encodeIfPresent(currentRecordIdString, forKey: .currentRecordId)
try container.encodeIfPresent(self.currentAuthRecord, forKey: .currentAuthRecord)
try container.encode(self.accessChallengeData, forKey: .accessChallengeData)
}
}

View file

@ -12,7 +12,12 @@ public struct AccessChallengeAttempts: Equatable {
}
}
public enum PostboxAccessChallengeData: PostboxCoding, Equatable {
public enum PostboxAccessChallengeData: PostboxCoding, Equatable, Codable {
enum CodingKeys: String, CodingKey {
case numericalPassword
case plaintextPassword
}
case none
case numericalPassword(value: String)
case plaintextPassword(value: String)
@ -44,6 +49,29 @@ public enum PostboxAccessChallengeData: PostboxCoding, Equatable {
}
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
if let value = try? container.decode(String.self, forKey: .numericalPassword) {
self = .numericalPassword(value: value)
} else if let value = try? container.decode(String.self, forKey: .plaintextPassword) {
self = .plaintextPassword(value: value)
} else {
self = .none
}
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
switch self {
case .none:
break
case let .numericalPassword(value):
try container.encode(value, forKey: .numericalPassword)
case let .plaintextPassword(value):
try container.encode(value, forKey: .plaintextPassword)
}
}
public var isLockable: Bool {
if case .none = self {
return false

View file

@ -47,7 +47,7 @@ final class MutableMessageOfInterestHolesView: MutablePostboxView {
switch self.location {
case let .peer(id):
mainPeerId = id
peerIds = postbox.peerIdsForLocation(.peer(id))
peerIds = postbox.peerIdsForLocation(.peer(id), ignoreRelatedChats: false)
}
self.peerIds = peerIds
var anchor: HistoryViewInputAnchor = .upperBound
@ -130,7 +130,7 @@ final class MutableMessageOfInterestHolesView: MutablePostboxView {
let peerIds: MessageHistoryViewInput
switch self.location {
case let .peer(id):
peerIds = postbox.peerIdsForLocation(.peer(id))
peerIds = postbox.peerIdsForLocation(.peer(id), ignoreRelatedChats: false)
}
self.wrappedView = MutableMessageHistoryView(postbox: postbox, orderStatistics: [], clipHoles: true, peerIds: peerIds, anchor: self.anchor, combinedReadStates: nil, transientReadStates: nil, tag: nil, namespaces: .all, count: self.count, topTaggedMessages: [:], additionalDatas: [], getMessageCountInRange: { _, _ in return 0})
return self.updateFromView()
@ -161,7 +161,7 @@ final class MutableMessageOfInterestHolesView: MutablePostboxView {
let peerIds: MessageHistoryViewInput
switch self.location {
case let .peer(id):
peerIds = postbox.peerIdsForLocation(.peer(id))
peerIds = postbox.peerIdsForLocation(.peer(id), ignoreRelatedChats: false)
}
self.wrappedView = MutableMessageHistoryView(postbox: postbox, orderStatistics: [], clipHoles: true, peerIds: peerIds, anchor: self.anchor, combinedReadStates: nil, transientReadStates: nil, tag: nil, namespaces: .all, count: self.count, topTaggedMessages: [:], additionalDatas: [], getMessageCountInRange: { _, _ in return 0})
}

View file

@ -1756,7 +1756,7 @@ public final class Postbox {
}
fileprivate func applyInteractiveReadMaxIndex(_ messageIndex: MessageIndex) -> [MessageId] {
let peerIds = self.peerIdsForLocation(.peer(messageIndex.id.peerId))
let peerIds = self.peerIdsForLocation(.peer(messageIndex.id.peerId), ignoreRelatedChats: false)
switch peerIds {
case let .associated(_, messageId):
if let messageId = messageId, let readState = self.readStateTable.getCombinedState(messageId.peerId), readState.count != 0 {
@ -2250,6 +2250,7 @@ public final class Postbox {
fileprivate func removeItemCacheEntry(id: ItemCacheEntryId) {
self.itemCacheTable.remove(id: id, metaTable: self.itemCacheMetaTable)
self.currentUpdatedCacheEntryKeys.insert(id)
}
fileprivate func replaceGlobalMessageTagsHole(transaction: Transaction, globalTags: GlobalMessageTags, index: MessageIndex, with updatedIndex: MessageIndex?, messages: [StoreMessage]) {
@ -2445,12 +2446,12 @@ public final class Postbox {
}
}
func peerIdsForLocation(_ chatLocation: ResolvedChatLocationInput) -> MessageHistoryViewInput {
func peerIdsForLocation(_ chatLocation: ResolvedChatLocationInput, ignoreRelatedChats: Bool) -> MessageHistoryViewInput {
var peerIds: MessageHistoryViewInput
switch chatLocation {
case let .peer(peerId):
peerIds = .single(peerId)
if let associatedMessageId = self.cachedPeerDataTable.get(peerId)?.associatedHistoryMessageId, associatedMessageId.peerId != peerId {
if !ignoreRelatedChats, let associatedMessageId = self.cachedPeerDataTable.get(peerId)?.associatedHistoryMessageId, associatedMessageId.peerId != peerId {
peerIds = .associated(peerId, associatedMessageId)
}
case let .external(input):
@ -2465,7 +2466,7 @@ public final class Postbox {
let (chatLocation, isHoleFill) = chatLocationData
let signal: Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> = self.transactionSignal(userInteractive: true, { subscriber, transaction in
let peerIds = self.peerIdsForLocation(chatLocation)
let peerIds = self.peerIdsForLocation(chatLocation, ignoreRelatedChats: false)
var anchor: HistoryViewInputAnchor = .upperBound
switch peerIds {
@ -2529,12 +2530,12 @@ public final class Postbox {
}
}
public func aroundIdMessageHistoryViewForLocation(_ chatLocation: ChatLocationInput, count: Int, clipHoles: Bool = true, messageId: MessageId, topTaggedMessageIdNamespaces: Set<MessageId.Namespace>, tagMask: MessageTags?, namespaces: MessageIdNamespaces, orderStatistics: MessageHistoryViewOrderStatistics, additionalData: [AdditionalMessageHistoryViewData] = []) -> Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> {
public func aroundIdMessageHistoryViewForLocation(_ chatLocation: ChatLocationInput, count: Int, clipHoles: Bool = true, ignoreRelatedChats: Bool = false, messageId: MessageId, topTaggedMessageIdNamespaces: Set<MessageId.Namespace>, tagMask: MessageTags?, namespaces: MessageIdNamespaces, orderStatistics: MessageHistoryViewOrderStatistics, additionalData: [AdditionalMessageHistoryViewData] = []) -> Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> {
return self.resolvedChatLocationInput(chatLocation: chatLocation)
|> mapToSignal { chatLocationData in
let (chatLocation, isHoleFill) = chatLocationData
let signal: Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> = self.transactionSignal { subscriber, transaction in
let peerIds = self.peerIdsForLocation(chatLocation)
let peerIds = self.peerIdsForLocation(chatLocation, ignoreRelatedChats: ignoreRelatedChats)
return self.syncAroundMessageHistoryViewForPeerId(subscriber: subscriber, peerIds: peerIds, count: count, clipHoles: clipHoles, anchor: .message(messageId), fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: topTaggedMessageIdNamespaces, tagMask: tagMask, namespaces: namespaces, orderStatistics: orderStatistics, additionalData: additionalData)
}
@ -2549,12 +2550,12 @@ public final class Postbox {
}
}
public func aroundMessageHistoryViewForLocation(_ chatLocation: ChatLocationInput, anchor: HistoryViewInputAnchor, count: Int, clipHoles: Bool = true, fixedCombinedReadStates: MessageHistoryViewReadState?, topTaggedMessageIdNamespaces: Set<MessageId.Namespace>, tagMask: MessageTags?, namespaces: MessageIdNamespaces, orderStatistics: MessageHistoryViewOrderStatistics, additionalData: [AdditionalMessageHistoryViewData] = []) -> Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> {
public func aroundMessageHistoryViewForLocation(_ chatLocation: ChatLocationInput, anchor: HistoryViewInputAnchor, count: Int, clipHoles: Bool = true, ignoreRelatedChats: Bool = false, fixedCombinedReadStates: MessageHistoryViewReadState?, topTaggedMessageIdNamespaces: Set<MessageId.Namespace>, tagMask: MessageTags?, namespaces: MessageIdNamespaces, orderStatistics: MessageHistoryViewOrderStatistics, additionalData: [AdditionalMessageHistoryViewData] = []) -> Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> {
return self.resolvedChatLocationInput(chatLocation: chatLocation)
|> mapToSignal { chatLocationData -> Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> in
let (chatLocation, isHoleFill) = chatLocationData
let signal: Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> = self.transactionSignal { subscriber, transaction in
let peerIds = self.peerIdsForLocation(chatLocation)
let peerIds = self.peerIdsForLocation(chatLocation, ignoreRelatedChats: ignoreRelatedChats)
return self.syncAroundMessageHistoryViewForPeerId(subscriber: subscriber, peerIds: peerIds, count: count, clipHoles: clipHoles, anchor: anchor, fixedCombinedReadStates: fixedCombinedReadStates, topTaggedMessageIdNamespaces: topTaggedMessageIdNamespaces, tagMask: tagMask, namespaces: namespaces, orderStatistics: orderStatistics, additionalData: additionalData)
}

View file

@ -153,22 +153,22 @@ final class PostboxTransaction {
if !self.currentUpdatedPendingPeerNotificationSettings.isEmpty {
return false
}
if replacedAdditionalChatListItems != nil {
if self.replacedAdditionalChatListItems != nil {
return false
}
if !updatedNoticeEntryKeys.isEmpty {
if !self.updatedNoticeEntryKeys.isEmpty {
return false
}
if !updatedCacheEntryKeys.isEmpty {
if !self.updatedCacheEntryKeys.isEmpty {
return false
}
if !updatedFailedMessagePeerIds.isEmpty {
if !self.updatedFailedMessagePeerIds.isEmpty {
return false
}
if !updatedFailedMessageIds.isEmpty {
if !self.updatedFailedMessageIds.isEmpty {
return false
}
if updatedGlobalNotificationSettings {
if self.updatedGlobalNotificationSettings {
return false
}
return true

View file

@ -566,33 +566,120 @@ private extension SemanticStatusNodeState {
private final class SemanticStatusNodeTransitionDrawingState {
let transition: CGFloat
let drawingState: SemanticStatusNodeStateDrawingState
let drawingState: SemanticStatusNodeStateDrawingState?
let appearanceState: SemanticStatusNodeAppearanceDrawingState?
init(transition: CGFloat, drawingState: SemanticStatusNodeStateDrawingState) {
init(transition: CGFloat, drawingState: SemanticStatusNodeStateDrawingState?, appearanceState: SemanticStatusNodeAppearanceDrawingState?) {
self.transition = transition
self.drawingState = drawingState
self.appearanceState = appearanceState
}
}
private final class SemanticStatusNodeAppearanceContext {
let background: UIColor
let foreground: UIColor
let backgroundImage: UIImage?
let overlayForeground: UIColor?
let cutout: CGRect?
init(background: UIColor, foreground: UIColor, backgroundImage: UIImage?, overlayForeground: UIColor?, cutout: CGRect?) {
self.background = background
self.foreground = foreground
self.backgroundImage = backgroundImage
self.overlayForeground = overlayForeground
self.cutout = cutout
}
func drawingState(backgroundTransitionFraction: CGFloat, foregroundTransitionFraction: CGFloat) -> SemanticStatusNodeAppearanceDrawingState {
return SemanticStatusNodeAppearanceDrawingState(backgroundTransitionFraction: backgroundTransitionFraction, foregroundTransitionFraction: foregroundTransitionFraction, background: self.background, foreground: self.foreground, backgroundImage: self.backgroundImage, overlayForeground: self.overlayForeground, cutout: self.cutout)
}
func withUpdatedBackground(_ background: UIColor) -> SemanticStatusNodeAppearanceContext {
return SemanticStatusNodeAppearanceContext(background: background, foreground: self.foreground, backgroundImage: self.backgroundImage, overlayForeground: self.overlayForeground, cutout: cutout)
}
func withUpdatedForeground(_ foreground: UIColor) -> SemanticStatusNodeAppearanceContext {
return SemanticStatusNodeAppearanceContext(background: self.background, foreground: foreground, backgroundImage: self.backgroundImage, overlayForeground: self.overlayForeground, cutout: cutout)
}
func withUpdatedBackgroundImage(_ backgroundImage: UIImage?) -> SemanticStatusNodeAppearanceContext {
return SemanticStatusNodeAppearanceContext(background: self.background, foreground: self.foreground, backgroundImage: backgroundImage, overlayForeground: self.overlayForeground, cutout: cutout)
}
func withUpdatedOverlayForeground(_ overlayForeground: UIColor?) -> SemanticStatusNodeAppearanceContext {
return SemanticStatusNodeAppearanceContext(background: self.background, foreground: self.foreground, backgroundImage: self.backgroundImage, overlayForeground: overlayForeground, cutout: cutout)
}
func withUpdatedCutout(_ cutout: CGRect?) -> SemanticStatusNodeAppearanceContext {
return SemanticStatusNodeAppearanceContext(background: self.background, foreground: self.foreground, backgroundImage: self.backgroundImage, overlayForeground: self.overlayForeground, cutout: cutout)
}
}
private final class SemanticStatusNodeAppearanceDrawingState {
let backgroundTransitionFraction: CGFloat
let foregroundTransitionFraction: CGFloat
let background: UIColor
let foreground: UIColor
let backgroundImage: UIImage?
let overlayForeground: UIColor?
let cutout: CGRect?
var effectiveForegroundColor: UIColor {
if let _ = self.backgroundImage, let overlayForeground = self.overlayForeground {
return overlayForeground
} else {
return self.foreground
}
}
init(backgroundTransitionFraction: CGFloat, foregroundTransitionFraction: CGFloat, background: UIColor, foreground: UIColor, backgroundImage: UIImage?, overlayForeground: UIColor?, cutout: CGRect?) {
self.backgroundTransitionFraction = backgroundTransitionFraction
self.foregroundTransitionFraction = foregroundTransitionFraction
self.background = background
self.foreground = foreground
self.backgroundImage = backgroundImage
self.overlayForeground = overlayForeground
self.cutout = cutout
}
func drawBackground(context: CGContext, size: CGSize) {
let bounds = CGRect(origin: CGPoint(), size: size)
context.setBlendMode(.normal)
if let backgroundImage = self.backgroundImage?.cgImage {
context.saveGState()
context.translateBy(x: 0.0, y: bounds.height)
context.scaleBy(x: 1.0, y: -1.0)
context.setAlpha(self.backgroundTransitionFraction)
context.draw(backgroundImage, in: bounds)
context.restoreGState()
} else {
context.setFillColor(self.background.cgColor)
context.fillEllipse(in: CGRect(origin: CGPoint(), size: bounds.size))
}
}
func drawForeground(context: CGContext, size: CGSize) {
if let cutout = self.cutout {
let size = CGSize(width: cutout.width * self.foregroundTransitionFraction, height: cutout.height * self.foregroundTransitionFraction)
let rect = CGRect(origin: CGPoint(x: cutout.midX - size.width / 2.0, y: cutout.midY - size.height / 2.0), size: size)
context.setBlendMode(.clear)
context.fillEllipse(in: rect)
}
}
}
private final class SemanticStatusNodeDrawingState: NSObject {
let background: UIColor
let foreground: UIColor
let hollow: Bool
let transitionState: SemanticStatusNodeTransitionDrawingState?
let drawingState: SemanticStatusNodeStateDrawingState
let backgroundImage: UIImage?
let overlayForeground: UIColor?
let cutout: SemanticStatusNode.Cutout?
let appearanceState: SemanticStatusNodeAppearanceDrawingState
init(background: UIColor, foreground: UIColor, hollow: Bool, transitionState: SemanticStatusNodeTransitionDrawingState?, drawingState: SemanticStatusNodeStateDrawingState, backgroundImage: UIImage?, overlayForeground: UIColor?, cutout: SemanticStatusNode.Cutout?) {
self.background = background
self.foreground = foreground
self.hollow = hollow
init(transitionState: SemanticStatusNodeTransitionDrawingState?, drawingState: SemanticStatusNodeStateDrawingState, appearanceState: SemanticStatusNodeAppearanceDrawingState) {
self.transitionState = transitionState
self.drawingState = drawingState
self.backgroundImage = backgroundImage
self.overlayForeground = overlayForeground
self.cutout = cutout
self.appearanceState = appearanceState
super.init()
}
@ -601,65 +688,96 @@ private final class SemanticStatusNodeDrawingState: NSObject {
private final class SemanticStatusNodeTransitionContext {
let startTime: Double
let duration: Double
let previousStateContext: SemanticStatusNodeStateContext
let previousStateContext: SemanticStatusNodeStateContext?
let previousAppearanceContext: SemanticStatusNodeAppearanceContext?
let completion: () -> Void
init(startTime: Double, duration: Double, previousStateContext: SemanticStatusNodeStateContext, completion: @escaping () -> Void) {
init(startTime: Double, duration: Double, previousStateContext: SemanticStatusNodeStateContext?, previousAppearanceContext: SemanticStatusNodeAppearanceContext?, completion: @escaping () -> Void) {
self.startTime = startTime
self.duration = duration
self.previousStateContext = previousStateContext
self.previousAppearanceContext = previousAppearanceContext
self.completion = completion
}
}
public final class SemanticStatusNode: ASControlNode {
final class Cutout {
}
public var backgroundNodeColor: UIColor {
didSet {
if !self.backgroundNodeColor.isEqual(oldValue) {
get {
return self.appearanceContext.background
}
set {
if !self.appearanceContext.background.isEqual(newValue) {
self.appearanceContext = self.appearanceContext.withUpdatedBackground(newValue)
self.setNeedsDisplay()
}
}
}
public var foregroundNodeColor: UIColor {
didSet {
if !self.foregroundNodeColor.isEqual(oldValue) {
get {
return self.appearanceContext.foreground
}
set {
if !self.appearanceContext.foreground.isEqual(newValue) {
self.appearanceContext = self.appearanceContext.withUpdatedForeground(newValue)
self.setNeedsDisplay()
}
}
}
public var overlayForegroundNodeColor: UIColor? {
didSet {
if !(self.overlayForegroundNodeColor?.isEqual(oldValue) ?? true) {
get {
return self.appearanceContext.overlayForeground
}
set {
if !(self.appearanceContext.overlayForeground?.isEqual(newValue) ?? false) {
self.appearanceContext = self.appearanceContext.withUpdatedOverlayForeground(newValue)
self.setNeedsDisplay()
}
}
}
private let hollow: Bool
public var cutout: CGRect? {
get {
return self.appearanceContext.cutout
}
set {
self.setCutout(newValue, animated: false)
}
}
public func setCutout(_ cutout: CGRect?, animated: Bool) {
guard cutout != self.appearanceContext.cutout else {
return
}
if animated {
self.transitionContext = SemanticStatusNodeTransitionContext(startTime: CACurrentMediaTime(), duration: 0.2, previousStateContext: nil, previousAppearanceContext: self.appearanceContext, completion: {})
self.appearanceContext = self.appearanceContext.withUpdatedCutout(cutout)
self.updateAnimations()
self.setNeedsDisplay()
} else {
self.appearanceContext = self.appearanceContext.withUpdatedCutout(cutout)
self.setNeedsDisplay()
}
}
private var animator: ConstantDisplayLinkAnimator?
private var hasState: Bool = false
public private(set) var state: SemanticStatusNodeState
private var transtionContext: SemanticStatusNodeTransitionContext?
private var transitionContext: SemanticStatusNodeTransitionContext?
private var stateContext: SemanticStatusNodeStateContext
private var appearanceContext: SemanticStatusNodeAppearanceContext
private var disposable: Disposable?
private var backgroundNodeImage: UIImage?
public init(backgroundNodeColor: UIColor, foregroundNodeColor: UIColor, image: Signal<(TransformImageArguments) -> DrawingContext?, NoError>? = nil, overlayForegroundNodeColor: UIColor? = nil, hollow: Bool = false) {
self.backgroundNodeColor = backgroundNodeColor
self.foregroundNodeColor = foregroundNodeColor
self.overlayForegroundNodeColor = overlayForegroundNodeColor
self.hollow = hollow
public init(backgroundNodeColor: UIColor, foregroundNodeColor: UIColor, image: Signal<(TransformImageArguments) -> DrawingContext?, NoError>? = nil, overlayForegroundNodeColor: UIColor? = nil, cutout: CGRect? = nil) {
self.state = .none
self.stateContext = self.state.context(current: nil)
self.appearanceContext = SemanticStatusNodeAppearanceContext(background: backgroundNodeColor, foreground: foregroundNodeColor, backgroundImage: nil, overlayForeground: overlayForegroundNodeColor, cutout: cutout)
super.init()
@ -667,14 +785,22 @@ public final class SemanticStatusNode: ASControlNode {
self.displaysAsynchronously = true
if let image = image {
let start = CACurrentMediaTime()
self.disposable = (image
|> deliverOnMainQueue).start(next: { [weak self] transform in
guard let strongSelf = self else {
return
}
let context = transform(TransformImageArguments(corners: ImageCorners(radius: strongSelf.bounds.width / 2.0), imageSize: strongSelf.bounds.size, boundingSize: strongSelf.bounds.size, intrinsicInsets: UIEdgeInsets()))
self?.backgroundNodeImage = context?.generateImage()
self?.setNeedsDisplay()
let previousAppearanceContext = strongSelf.appearanceContext
strongSelf.appearanceContext = strongSelf.appearanceContext.withUpdatedBackgroundImage(context?.generateImage())
if CACurrentMediaTime() - start > 0.3 {
strongSelf.transitionContext = SemanticStatusNodeTransitionContext(startTime: CACurrentMediaTime(), duration: 0.18, previousStateContext: nil, previousAppearanceContext: previousAppearanceContext, completion: {})
strongSelf.updateAnimations()
}
strongSelf.setNeedsDisplay()
})
}
}
@ -687,9 +813,9 @@ public final class SemanticStatusNode: ASControlNode {
var animate = false
let timestamp = CACurrentMediaTime()
if let transtionContext = self.transtionContext {
if let transtionContext = self.transitionContext {
if transtionContext.startTime + transtionContext.duration < timestamp {
self.transtionContext = nil
self.transitionContext = nil
transtionContext.completion()
} else {
animate = true
@ -724,13 +850,13 @@ public final class SemanticStatusNode: ASControlNode {
self.hasState = true
animated = false
}
if self.state != state {
if self.state != state || self.appearanceContext.cutout != cutout {
self.state = state
let previousStateContext = self.stateContext
self.stateContext = self.state.context(current: self.stateContext)
if animated && previousStateContext !== self.stateContext {
self.transtionContext = SemanticStatusNodeTransitionContext(startTime: CACurrentMediaTime(), duration: 0.18, previousStateContext: previousStateContext, completion: completion)
self.transitionContext = SemanticStatusNodeTransitionContext(startTime: CACurrentMediaTime(), duration: 0.18, previousStateContext: previousStateContext, previousAppearanceContext: nil, completion: completion)
} else {
completion()
}
@ -745,15 +871,31 @@ public final class SemanticStatusNode: ASControlNode {
override public func drawParameters(forAsyncLayer layer: _ASDisplayLayer) -> NSObjectProtocol? {
var transitionState: SemanticStatusNodeTransitionDrawingState?
var transitionFraction: CGFloat = 1.0
if let transitionContext = self.transtionContext {
var appearanceBackgroundTransitionFraction: CGFloat = 1.0
var appearanceForegroundTransitionFraction: CGFloat = 1.0
if let transitionContext = self.transitionContext {
let timestamp = CACurrentMediaTime()
var t = CGFloat((timestamp - transitionContext.startTime) / transitionContext.duration)
t = min(1.0, max(0.0, t))
transitionFraction = t
transitionState = SemanticStatusNodeTransitionDrawingState(transition: t, drawingState: transitionContext.previousStateContext.drawingState(transitionFraction: 1.0 - t))
if let _ = transitionContext.previousStateContext {
transitionFraction = t
}
var foregroundTransitionFraction: CGFloat = 1.0
if let previousContext = transitionContext.previousAppearanceContext {
if previousContext.backgroundImage != self.appearanceContext.backgroundImage {
appearanceBackgroundTransitionFraction = t
}
if previousContext.cutout != self.appearanceContext.cutout {
appearanceForegroundTransitionFraction = t
foregroundTransitionFraction = 1.0 - t
}
}
transitionState = SemanticStatusNodeTransitionDrawingState(transition: t, drawingState: transitionContext.previousStateContext?.drawingState(transitionFraction: 1.0 - t), appearanceState: transitionContext.previousAppearanceContext?.drawingState(backgroundTransitionFraction: 1.0, foregroundTransitionFraction: foregroundTransitionFraction))
}
return SemanticStatusNodeDrawingState(background: self.backgroundNodeColor, foreground: self.foregroundNodeColor, hollow: self.hollow, transitionState: transitionState, drawingState: self.stateContext.drawingState(transitionFraction: transitionFraction), backgroundImage: self.backgroundNodeImage, overlayForeground: self.overlayForegroundNodeColor, cutout: nil)
return SemanticStatusNodeDrawingState(transitionState: transitionState, drawingState: self.stateContext.drawingState(transitionFraction: transitionFraction), appearanceState: self.appearanceContext.drawingState(backgroundTransitionFraction: appearanceBackgroundTransitionFraction, foregroundTransitionFraction: appearanceForegroundTransitionFraction))
}
@objc override public class func draw(_ bounds: CGRect, withParameters parameters: Any?, isCancelled: () -> Bool, isRasterizing: Bool) {
@ -768,32 +910,20 @@ public final class SemanticStatusNode: ASControlNode {
guard let parameters = parameters as? SemanticStatusNodeDrawingState else {
return
}
var foregroundColor = parameters.foreground
if let backgroundImage = parameters.backgroundImage?.cgImage {
context.saveGState()
context.translateBy(x: 0.0, y: bounds.height)
context.scaleBy(x: 1.0, y: -1.0)
context.draw(backgroundImage, in: bounds)
context.restoreGState()
if let overlayForegroundColor = parameters.overlayForeground {
foregroundColor = overlayForegroundColor
} else {
foregroundColor = .white
}
} else {
context.setFillColor(parameters.background.cgColor)
context.fillEllipse(in: CGRect(origin: CGPoint(), size: bounds.size))
if let transitionAppearanceState = parameters.transitionState?.appearanceState {
transitionAppearanceState.drawBackground(context: context, size: bounds.size)
}
if let transitionState = parameters.transitionState {
transitionState.drawingState.draw(context: context, size: bounds.size, foregroundColor: foregroundColor)
parameters.appearanceState.drawBackground(context: context, size: bounds.size)
if let transitionDrawingState = parameters.transitionState?.drawingState {
transitionDrawingState.draw(context: context, size: bounds.size, foregroundColor: parameters.appearanceState.effectiveForegroundColor)
}
parameters.drawingState.draw(context: context, size: bounds.size, foregroundColor: foregroundColor)
parameters.drawingState.draw(context: context, size: bounds.size, foregroundColor: parameters.appearanceState.effectiveForegroundColor)
if parameters.hollow {
context.setBlendMode(.clear)
context.fillEllipse(in: bounds.insetBy(dx: 8.0, dy: 8.0))
if let transitionAppearanceState = parameters.transitionState?.appearanceState {
transitionAppearanceState.drawForeground(context: context, size: bounds.size)
}
parameters.appearanceState.drawForeground(context: context, size: bounds.size)
}
}

View file

@ -738,12 +738,12 @@ private enum DebugControllerEntry: ItemListNodeEntry {
})
case .voiceConference:
return ItemListDisclosureItem(presentationData: presentationData, title: "Voice Conference (Test)", label: "", sectionId: self.section, style: .blocks, action: {
guard let context = arguments.context else {
/*guard let context = arguments.context else {
return
}
let controller = GroupCallController(context: context)
controller.navigationPresentation = .modal
arguments.pushController(controller)
arguments.pushController(controller)*/
})
case let .preferredVideoCodec(_, title, value, isSelected):
return ItemListCheckboxItem(presentationData: presentationData, title: title, style: .right, checked: isSelected, zeroSeparatorInsets: false, sectionId: self.section, action: {
@ -824,7 +824,7 @@ private func debugControllerEntries(presentationData: PresentationData, loggingS
entries.append(.playerEmbedding(experimentalSettings.playerEmbedding))
entries.append(.playlistPlayback(experimentalSettings.playlistPlayback))
entries.append(.voiceConference)
//entries.append(.voiceConference)
let codecs: [(String, String?)] = [
("No Preference", nil),

View file

@ -1924,42 +1924,6 @@ public func settingsController(context: AccountContext, accountManager: AccountM
updateNotifyExceptions()
updateActiveSessions()
}
controller.previewItemWithTag = { tag in
if let tag = tag as? SettingsEntryTag, case let .account(id) = tag {
var selectedAccount: Account?
let _ = (accountsAndPeers.get()
|> take(1)
|> deliverOnMainQueue).start(next: { accountsAndPeers in
for (account, _, _) in accountsAndPeers.1 {
if account.id == id {
selectedAccount = account
break
}
}
})
var sharedContext: SharedAccountContext?
let _ = (contextValue.get()
|> deliverOnMainQueue
|> take(1)).start(next: { context in
sharedContext = context.sharedContext
})
if let selectedAccount = selectedAccount, let sharedContext = sharedContext {
let accountContext = sharedContext.makeTempAccountContext(account: selectedAccount)
let chatListController = accountContext.sharedContext.makeChatListController(context: accountContext, groupId: .root, controlsHistoryPreload: false, hideNetworkActivityStatus: true, previewing: true, enableDebugActions: enableDebugActions)
return chatListController
}
}
return nil
}
controller.commitPreview = { previewController in
if let chatListController = previewController as? ChatListController {
let _ = (contextValue.get()
|> deliverOnMainQueue
|> take(1)).start(next: { context in
context.sharedContext.switchToAccount(id: chatListController.context.account.id, fromSettingsController: nil, withChatListController: chatListController)
})
}
}
controller.switchToAccount = { id in
let _ = (contextValue.get()
|> take(1)

View file

@ -260,9 +260,9 @@ private func preparedShareItem(account: Account, to peerId: PeerId, value: [Stri
let disposable = TGShareLocationSignals.locationMessageContent(for: url).start(next: { value in
if let value = value as? TGShareLocationResult {
if let title = value.title {
subscriber.putNext(.done(.media(.media(.standalone(media: TelegramMediaMap(latitude: value.latitude, longitude: value.longitude, heading: nil, accuracyRadius: nil, geoPlace: nil, venue: MapVenue(title: title, address: value.address, provider: value.provider, id: value.venueId, type: value.venueType), liveBroadcastingTimeout: nil))))))
subscriber.putNext(.done(.media(.media(.standalone(media: TelegramMediaMap(latitude: value.latitude, longitude: value.longitude, heading: nil, accuracyRadius: nil, geoPlace: nil, venue: MapVenue(title: title, address: value.address, provider: value.provider, id: value.venueId, type: value.venueType), liveBroadcastingTimeout: nil, liveProximityNotificationRadius: nil))))))
} else {
subscriber.putNext(.done(.media(.media(.standalone(media: TelegramMediaMap(latitude: value.latitude, longitude: value.longitude, heading: nil, accuracyRadius: nil, geoPlace: nil, venue: nil, liveBroadcastingTimeout: nil))))))
subscriber.putNext(.done(.media(.media(.standalone(media: TelegramMediaMap(latitude: value.latitude, longitude: value.longitude, heading: nil, accuracyRadius: nil, geoPlace: nil, venue: nil, liveBroadcastingTimeout: nil, liveProximityNotificationRadius: nil))))))
}
subscriber.putCompletion()
} else if let value = value as? String {

View file

@ -0,0 +1,26 @@
load("//Config:buck_rule_macros.bzl", "static_library")
static_library(
name = "SlotMachineAnimationNode",
srcs = glob([
"Sources/**/*.swift",
]),
deps = [
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit#shared",
"//submodules/AsyncDisplayKit:AsyncDisplayKit#shared",
"//submodules/Display:Display#shared",
"//submodules/Postbox:Postbox#shared",
"//submodules/SyncCore:SyncCore#shared",
"//submodules/TelegramCore:TelegramCore#shared",
"//submodules/AccountContext:AccountContext",
"//submodules/StickerResources:StickerResources",
"//submodules/ManagedAnimationNode:ManagedAnimationNode",
"//submodules/AnimatedStickerNode:AnimatedStickerNode",
"//submodules/TelegramAnimatedStickerNode:TelegramAnimatedStickerNode",
"//submodules/AppBundle:AppBundle",
],
frameworks = [
"$SDKROOT/System/Library/Frameworks/Foundation.framework",
"$SDKROOT/System/Library/Frameworks/UIKit.framework",
],
)

View file

@ -0,0 +1,26 @@
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
swift_library(
name = "SlotMachineAnimationNode",
module_name = "SlotMachineAnimationNode",
srcs = glob([
"Sources/**/*.swift",
]),
deps = [
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit",
"//submodules/AsyncDisplayKit:AsyncDisplayKit",
"//submodules/Display:Display",
"//submodules/Postbox:Postbox",
"//submodules/SyncCore:SyncCore",
"//submodules/TelegramCore:TelegramCore",
"//submodules/AccountContext:AccountContext",
"//submodules/StickerResources:StickerResources",
"//submodules/ManagedAnimationNode:ManagedAnimationNode",
"//submodules/AnimatedStickerNode:AnimatedStickerNode",
"//submodules/TelegramAnimatedStickerNode:TelegramAnimatedStickerNode",
"//submodules/AppBundle:AppBundle",
],
visibility = [
"//visibility:public",
],
)

View file

@ -5,9 +5,11 @@ import Postbox
import SyncCore
import TelegramCore
import SwiftSignalKit
import AccountContext
import StickerResources
import ManagedAnimationNode
import AnimatedStickerNode
import TelegramAnimatedStickerNode
import AppBundle
private struct SlotMachineValue {
enum ReelValue {
@ -137,27 +139,32 @@ private func rightReelAnimationItem(value: SlotMachineValue.ReelValue, immediate
}
}
final class SlotMachineAnimationNode: ASDisplayNode, GenericAnimatedStickerNode {
private let context: AccountContext
public enum ManagedSlotMachineAnimationState: Equatable {
case rolling
case value(Int32, Bool)
}
public final class SlotMachineAnimationNode: ASDisplayNode {
private let backNode: ManagedAnimationNode
private let leftReelNode: ManagedAnimationNode
private let centerReelNode: ManagedAnimationNode
private let rightReelNode: ManagedAnimationNode
private let frontNode: ManagedAnimationNode
private var diceState: ManagedDiceAnimationState? = nil
private var diceState: ManagedSlotMachineAnimationState? = nil
private let disposables = DisposableSet()
init(context: AccountContext) {
self.context = context
let size = CGSize(width: 184.0, height: 184.0)
self.backNode = ManagedAnimationNode(size: size)
self.leftReelNode = ManagedAnimationNode(size: size)
self.centerReelNode = ManagedAnimationNode(size: size)
self.rightReelNode = ManagedAnimationNode(size: size)
self.frontNode = ManagedAnimationNode(size: size)
private let animationSize: CGSize
public var success: ((Bool) -> Void)?
public init(size: CGSize = CGSize(width: 184.0, height: 184.0)) {
self.animationSize = size
self.backNode = ManagedAnimationNode(size: self.animationSize)
self.leftReelNode = ManagedAnimationNode(size: self.animationSize)
self.centerReelNode = ManagedAnimationNode(size: self.animationSize)
self.rightReelNode = ManagedAnimationNode(size: self.animationSize)
self.frontNode = ManagedAnimationNode(size: self.animationSize)
super.init()
@ -172,7 +179,7 @@ final class SlotMachineAnimationNode: ASDisplayNode, GenericAnimatedStickerNode
self.disposables.dispose()
}
override func layout() {
public override func layout() {
super.layout()
self.backNode.frame = self.bounds
@ -182,7 +189,7 @@ final class SlotMachineAnimationNode: ASDisplayNode, GenericAnimatedStickerNode
self.frontNode.frame = self.bounds
}
func setState(_ diceState: ManagedDiceAnimationState) {
public func setState(_ diceState: ManagedSlotMachineAnimationState) {
let previousState = self.diceState
self.diceState = diceState
@ -195,6 +202,7 @@ final class SlotMachineAnimationNode: ASDisplayNode, GenericAnimatedStickerNode
if slotValue.isThreeOfSame {
Queue.mainQueue().after(1.5) {
self.backNode.trackTo(item: ManagedAnimationItem(source: .local("Slot_Back_Win"), loop: false))
self.success?(!slotValue.is777)
}
} else {
self.backNode.trackTo(item: ManagedAnimationItem(source: .local("Slot_Back_Win"), frames: .still(.start), loop: false))
@ -219,10 +227,10 @@ final class SlotMachineAnimationNode: ASDisplayNode, GenericAnimatedStickerNode
}
}
} else {
self.backNode.trackTo(item: ManagedAnimationItem(source: .local("Slot_Back_Win"), frames: .still(.start), loop: false))
switch diceState {
case let .value(value, immediate):
self.backNode.trackTo(item: ManagedAnimationItem(source: .local("Slot_Back_Win"), frames: .still(.start), loop: false))
let slotValue = SlotMachineValue(rawValue: value)
self.leftReelNode.trackTo(item: leftReelAnimationItem(value: slotValue.left, immediate: immediate))
self.centerReelNode.trackTo(item: centerReelAnimationItem(value: slotValue.center, immediate: immediate))
@ -231,7 +239,6 @@ final class SlotMachineAnimationNode: ASDisplayNode, GenericAnimatedStickerNode
let frames: ManagedAnimationFrameRange? = immediate ? .still(.end) : nil
self.frontNode.trackTo(item: ManagedAnimationItem(source: .local("Slot_Front_Pull"), frames: frames, loop: false))
case .rolling:
self.backNode.trackTo(item: ManagedAnimationItem(source: .local("Slot_Back_Win"), frames: .still(.start), loop: false))
self.leftReelNode.trackTo(item: leftReelAnimationItem(value: .rolling))
self.centerReelNode.trackTo(item: centerReelAnimationItem(value: .rolling))
self.rightReelNode.trackTo(item: rightReelAnimationItem(value: .rolling))
@ -240,3 +247,97 @@ final class SlotMachineAnimationNode: ASDisplayNode, GenericAnimatedStickerNode
}
}
}
class DiceAnimatedStickerNode: ASDisplayNode {
public let intrinsicSize: CGSize
private let animationNode: AnimatedStickerNode
public var state: ManagedAnimationState?
public var trackStack: [ManagedAnimationItem] = []
public var didTryAdvancingState = false
init(size: CGSize) {
self.intrinsicSize = size
self.animationNode = AnimatedStickerNode()
self.animationNode.visibility = true
super.init()
self.addSubnode(self.animationNode)
self.animationNode.completed = { [weak self] willStop in
guard let strongSelf = self, !strongSelf.didTryAdvancingState, let state = strongSelf.state else {
return
}
if state.item.loop && strongSelf.trackStack.isEmpty {
} else {
strongSelf.didTryAdvancingState = true
strongSelf.advanceState()
}
}
}
private func advanceState() {
guard !self.trackStack.isEmpty else {
return
}
let item = self.trackStack.removeFirst()
if let state = self.state, state.item.source == item.source {
self.state = ManagedAnimationState(displaySize: self.intrinsicSize, item: item, current: state)
} else {
self.state = ManagedAnimationState(displaySize: self.intrinsicSize, item: item, current: nil)
}
var source: AnimatedStickerNodeSource?
switch item.source {
case let .local(animationName):
if let path = getAppBundle().path(forResource: animationName, ofType: "tgs") {
source = AnimatedStickerNodeLocalFileSource(path: path)
}
case let .resource(account, resource):
source = AnimatedStickerResourceSource(account: account, resource: resource)
}
let playbackMode: AnimatedStickerPlaybackMode
if item.loop {
playbackMode = .loop
} else if let frames = item.frames, case let .still(position) = frames {
playbackMode = .still(position == .start ? .start : .end)
} else {
playbackMode = .once
}
if let source = source {
self.animationNode.setup(source: source, width: Int(self.intrinsicSize.width), height: Int(self.intrinsicSize.height), playbackMode: playbackMode, mode: .direct(cachePathPrefix: nil))
}
self.didTryAdvancingState = false
}
func trackTo(item: ManagedAnimationItem) {
if let currentItem = self.state?.item {
if currentItem.source == item.source && currentItem.frames == item.frames && currentItem.loop == item.loop {
return
}
}
self.trackStack.append(item)
self.didTryAdvancingState = false
if !self.animationNode.isPlaying {
self.advanceState()
}
}
override func layout() {
super.layout()
self.animationNode.updateLayout(size: self.bounds.size)
self.animationNode.frame = self.bounds
}
}

View file

@ -168,11 +168,11 @@ class MessageStatsOverviewItemNode: ListViewItemNode {
rightValueLabelLayoutAndApply = makeRightValueLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.publicShares.flatMap { "\( compactNumericCountString(item.stats.forwards - Int($0)))" } ?? "", font: valueFont, textColor: item.presentationData.theme.list.itemPrimaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
leftTitleLabelLayoutAndApply = makeLeftTitleLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: "Views", font: titleFont, textColor: item.presentationData.theme.list.sectionHeaderTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
leftTitleLabelLayoutAndApply = makeLeftTitleLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.presentationData.strings.Stats_Message_Views, font: titleFont, textColor: item.presentationData.theme.list.sectionHeaderTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
centerTitleLabelLayoutAndApply = makeCenterTitleLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: "Public Shares", font: titleFont, textColor: item.presentationData.theme.list.sectionHeaderTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
centerTitleLabelLayoutAndApply = makeCenterTitleLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.presentationData.strings.Stats_Message_PublicShares, font: titleFont, textColor: item.presentationData.theme.list.sectionHeaderTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
rightTitleLabelLayoutAndApply = makeRightTitleLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: "Private Shares", font: titleFont, textColor: item.presentationData.theme.list.sectionHeaderTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
rightTitleLabelLayoutAndApply = makeRightTitleLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.presentationData.strings.Stats_Message_PrivateShares, font: titleFont, textColor: item.presentationData.theme.list.sectionHeaderTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
height += rightValueLabelLayoutAndApply!.0.size.height + rightTitleLabelLayoutAndApply!.0.size.height
@ -257,7 +257,7 @@ class MessageStatsOverviewItemNode: ListViewItemNode {
let horizontalSpacing = min(60, (params.width - leftInset - rightInset - sideInset * 2.0 - maxLeftWidth - maxCenterWidth - maxRightWidth) / 2.0)
var x: CGFloat = leftInset + (params.width - maxLeftWidth - maxCenterWidth - maxRightWidth - horizontalSpacing * 2.0) / 2.0
var x: CGFloat = leftInset + (params.width - leftInset - rightInset - maxLeftWidth - maxCenterWidth - maxRightWidth - horizontalSpacing * 2.0) / 2.0
if let leftValueLabelLayout = leftValueLabelLayoutAndApply?.0, let leftTitleLabelLayout = leftTitleLabelLayoutAndApply?.0 {
strongSelf.leftValueLabel.frame = CGRect(origin: CGPoint(x: x, y: topInset), size: leftValueLabelLayout.size)
strongSelf.leftTitleLabel.frame = CGRect(origin: CGPoint(x: x, y: strongSelf.leftValueLabel.frame.maxY), size: leftTitleLabelLayout.size)

View file

@ -72,6 +72,7 @@ public struct Namespaces {
public static let cachedThemesConfiguration: Int8 = 8
public static let cachedPollResults: Int8 = 9
public static let cachedContextResults: Int8 = 10
public static let proximityNotificationStoredState: Int8 = 11
}
public struct UnorderedItemList {

View file

@ -45,7 +45,7 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable {
case botSentSecureValues(types: [SentSecureValueType])
case peerJoined
case phoneNumberRequest
case geoProximityReached(distance: Int32)
case geoProximityReached(from: PeerId, to: PeerId, distance: Int32)
public init(decoder: PostboxDecoder) {
let rawValue: Int32 = decoder.decodeInt32ForKey("_rawValue", orElse: 0)
@ -97,7 +97,7 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable {
case 20:
self = .phoneNumberRequest
case 21:
self = .geoProximityReached(distance: (decoder.decodeInt32ForKey("dst", orElse: 0)))
self = .geoProximityReached(from: PeerId(decoder.decodeInt64ForKey("fromId", orElse: 0)), to: PeerId(decoder.decodeInt64ForKey("toId", orElse: 0)), distance: (decoder.decodeInt32ForKey("dst", orElse: 0)))
default:
self = .unknown
}
@ -183,8 +183,10 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable {
encoder.encodeInt32(19, forKey: "_rawValue")
case .phoneNumberRequest:
encoder.encodeInt32(20, forKey: "_rawValue")
case let .geoProximityReached(distance):
case let .geoProximityReached(from, to, distance):
encoder.encodeInt32(21, forKey: "_rawValue")
encoder.encodeInt64(from.toInt64(), forKey: "fromId")
encoder.encodeInt64(to.toInt64(), forKey: "toId")
encoder.encodeInt32(distance, forKey: "dst")
}
}
@ -201,6 +203,8 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable {
return [groupId]
case let .groupMigratedToChannel(channelId):
return [channelId]
case let .geoProximityReached(from, to, _):
return [from, to]
default:
return []
}

View file

@ -133,16 +133,17 @@ public final class MapVenue: PostboxCoding, Equatable {
public final class TelegramMediaMap: Media {
public let latitude: Double
public let longitude: Double
public let heading: Double?
public let heading: Int32?
public let accuracyRadius: Double?
public let geoPlace: NamedGeoPlace?
public let venue: MapVenue?
public let liveBroadcastingTimeout: Int32?
public let liveProximityNotificationRadius: Int32?
public let id: MediaId? = nil
public let peerIds: [PeerId] = []
public init(latitude: Double, longitude: Double, heading: Double?, accuracyRadius: Double?, geoPlace: NamedGeoPlace?, venue: MapVenue?, liveBroadcastingTimeout: Int32?) {
public init(latitude: Double, longitude: Double, heading: Int32?, accuracyRadius: Double?, geoPlace: NamedGeoPlace?, venue: MapVenue?, liveBroadcastingTimeout: Int32?, liveProximityNotificationRadius: Int32?) {
self.latitude = latitude
self.longitude = longitude
self.heading = heading
@ -150,23 +151,25 @@ public final class TelegramMediaMap: Media {
self.geoPlace = geoPlace
self.venue = venue
self.liveBroadcastingTimeout = liveBroadcastingTimeout
self.liveProximityNotificationRadius = liveProximityNotificationRadius
}
public init(decoder: PostboxDecoder) {
self.latitude = decoder.decodeDoubleForKey("la", orElse: 0.0)
self.longitude = decoder.decodeDoubleForKey("lo", orElse: 0.0)
self.heading = decoder.decodeOptionalDoubleForKey("hdg")
self.heading = decoder.decodeOptionalInt32ForKey("hdg")
self.accuracyRadius = decoder.decodeOptionalDoubleForKey("acc")
self.geoPlace = decoder.decodeObjectForKey("gp", decoder: { NamedGeoPlace(decoder: $0) }) as? NamedGeoPlace
self.venue = decoder.decodeObjectForKey("ve", decoder: { MapVenue(decoder: $0) }) as? MapVenue
self.liveBroadcastingTimeout = decoder.decodeOptionalInt32ForKey("bt")
self.liveProximityNotificationRadius = decoder.decodeOptionalInt32ForKey("pnr")
}
public func encode(_ encoder: PostboxEncoder) {
encoder.encodeDouble(self.latitude, forKey: "la")
encoder.encodeDouble(self.longitude, forKey: "lo")
if let heading = self.heading {
encoder.encodeDouble(heading, forKey: "hdg")
encoder.encodeInt32(heading, forKey: "hdg")
} else {
encoder.encodeNil(forKey: "hdg")
}
@ -190,6 +193,11 @@ public final class TelegramMediaMap: Media {
} else {
encoder.encodeNil(forKey: "bt")
}
if let liveProximityNotificationRadius = self.liveProximityNotificationRadius {
encoder.encodeInt32(liveProximityNotificationRadius, forKey: "pnr")
} else {
encoder.encodeNil(forKey: "pnr")
}
}
public func isEqual(to other: Media) -> Bool {
@ -212,6 +220,9 @@ public final class TelegramMediaMap: Media {
if self.liveBroadcastingTimeout != other.liveBroadcastingTimeout {
return false
}
if self.liveProximityNotificationRadius != other.liveProximityNotificationRadius {
return false
}
return true
}
return false

View file

@ -373,7 +373,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
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[-1574158066] = { return Api.InputMedia.parse_inputMediaGeoLive($0) }
dict[-1759532989] = { return Api.InputMedia.parse_inputMediaGeoLive($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) }
@ -573,7 +573,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
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[-967079536] = { return Api.MessageMedia.parse_messageMediaGeoLive($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) }
@ -704,10 +704,10 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[1984136919] = { return Api.wallet.LiteResponse.parse_liteResponse($0) }
dict[415997816] = { return Api.help.InviteText.parse_inviteText($0) }
dict[-1937807902] = { return Api.BotInlineMessage.parse_botInlineMessageText($0) }
dict[-1222451611] = { 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) }

View file

@ -11177,7 +11177,7 @@ public extension Api {
case inputMediaContact(phoneNumber: String, firstName: String, lastName: String, vcard: String)
case inputMediaPoll(flags: Int32, poll: Api.Poll, correctAnswers: [Buffer]?, solution: String?, solutionEntities: [Api.MessageEntity]?)
case inputMediaDice(emoticon: String)
case inputMediaGeoLive(flags: Int32, geoPoint: Api.InputGeoPoint, heading: Int32, period: Int32?)
case inputMediaGeoLive(flags: Int32, geoPoint: Api.InputGeoPoint, heading: Int32?, period: Int32?, proximityNotificationRadius: Int32?)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
@ -11329,14 +11329,15 @@ public extension Api {
}
serializeString(emoticon, buffer: buffer, boxed: false)
break
case .inputMediaGeoLive(let flags, let geoPoint, let heading, let period):
case .inputMediaGeoLive(let flags, let geoPoint, let heading, let period, let proximityNotificationRadius):
if boxed {
buffer.appendInt32(-1574158066)
buffer.appendInt32(-1759532989)
}
serializeInt32(flags, buffer: buffer, boxed: false)
geoPoint.serialize(buffer, true)
serializeInt32(heading, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 2) != 0 {serializeInt32(heading!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 1) != 0 {serializeInt32(period!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 3) != 0 {serializeInt32(proximityNotificationRadius!, buffer: buffer, boxed: false)}
break
}
}
@ -11373,8 +11374,8 @@ public extension Api {
return ("inputMediaPoll", [("flags", flags), ("poll", poll), ("correctAnswers", correctAnswers), ("solution", solution), ("solutionEntities", solutionEntities)])
case .inputMediaDice(let emoticon):
return ("inputMediaDice", [("emoticon", emoticon)])
case .inputMediaGeoLive(let flags, let geoPoint, let heading, let period):
return ("inputMediaGeoLive", [("flags", flags), ("geoPoint", geoPoint), ("heading", heading), ("period", period)])
case .inputMediaGeoLive(let flags, let geoPoint, let heading, let period, let proximityNotificationRadius):
return ("inputMediaGeoLive", [("flags", flags), ("geoPoint", geoPoint), ("heading", heading), ("period", period), ("proximityNotificationRadius", proximityNotificationRadius)])
}
}
@ -11691,15 +11692,18 @@ public extension Api {
_2 = Api.parse(reader, signature: signature) as? Api.InputGeoPoint
}
var _3: Int32?
_3 = reader.readInt32()
if Int(_1!) & Int(1 << 2) != 0 {_3 = reader.readInt32() }
var _4: Int32?
if Int(_1!) & Int(1 << 1) != 0 {_4 = reader.readInt32() }
var _5: Int32?
if Int(_1!) & Int(1 << 3) != 0 {_5 = reader.readInt32() }
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = _3 != nil
let _c3 = (Int(_1!) & Int(1 << 2) == 0) || _3 != nil
let _c4 = (Int(_1!) & Int(1 << 1) == 0) || _4 != nil
if _c1 && _c2 && _c3 && _c4 {
return Api.InputMedia.inputMediaGeoLive(flags: _1!, geoPoint: _2!, heading: _3!, period: _4)
let _c5 = (Int(_1!) & Int(1 << 3) == 0) || _5 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 {
return Api.InputMedia.inputMediaGeoLive(flags: _1!, geoPoint: _2!, heading: _3, period: _4, proximityNotificationRadius: _5)
}
else {
return nil
@ -16442,7 +16446,7 @@ public extension Api {
case messageMediaContact(phoneNumber: String, firstName: String, lastName: String, vcard: String, userId: Int32)
case messageMediaPoll(poll: Api.Poll, results: Api.PollResults)
case messageMediaDice(value: Int32, emoticon: String)
case messageMediaGeoLive(geo: Api.GeoPoint, heading: Int32, period: Int32)
case messageMediaGeoLive(flags: Int32, geo: Api.GeoPoint, heading: Int32?, period: Int32, proximityNotificationRadius: Int32?)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
@ -16540,13 +16544,15 @@ public extension Api {
serializeInt32(value, buffer: buffer, boxed: false)
serializeString(emoticon, buffer: buffer, boxed: false)
break
case .messageMediaGeoLive(let geo, let heading, let period):
case .messageMediaGeoLive(let flags, let geo, let heading, let period, let proximityNotificationRadius):
if boxed {
buffer.appendInt32(-967079536)
buffer.appendInt32(-1186937242)
}
serializeInt32(flags, buffer: buffer, boxed: false)
geo.serialize(buffer, true)
serializeInt32(heading, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 0) != 0 {serializeInt32(heading!, buffer: buffer, boxed: false)}
serializeInt32(period, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 1) != 0 {serializeInt32(proximityNotificationRadius!, buffer: buffer, boxed: false)}
break
}
}
@ -16577,8 +16583,8 @@ public extension Api {
return ("messageMediaPoll", [("poll", poll), ("results", results)])
case .messageMediaDice(let value, let emoticon):
return ("messageMediaDice", [("value", value), ("emoticon", emoticon)])
case .messageMediaGeoLive(let geo, let heading, let period):
return ("messageMediaGeoLive", [("geo", geo), ("heading", heading), ("period", period)])
case .messageMediaGeoLive(let flags, let geo, let heading, let period, let proximityNotificationRadius):
return ("messageMediaGeoLive", [("flags", flags), ("geo", geo), ("heading", heading), ("period", period), ("proximityNotificationRadius", proximityNotificationRadius)])
}
}
@ -16783,19 +16789,25 @@ public extension Api {
}
}
public static func parse_messageMediaGeoLive(_ reader: BufferReader) -> MessageMedia? {
var _1: Api.GeoPoint?
var _1: Int32?
_1 = reader.readInt32()
var _2: Api.GeoPoint?
if let signature = reader.readInt32() {
_1 = Api.parse(reader, signature: signature) as? Api.GeoPoint
_2 = Api.parse(reader, signature: signature) as? Api.GeoPoint
}
var _2: Int32?
_2 = reader.readInt32()
var _3: Int32?
_3 = reader.readInt32()
if Int(_1!) & Int(1 << 0) != 0 {_3 = reader.readInt32() }
var _4: Int32?
_4 = reader.readInt32()
var _5: Int32?
if Int(_1!) & Int(1 << 1) != 0 {_5 = reader.readInt32() }
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = _3 != nil
if _c1 && _c2 && _c3 {
return Api.MessageMedia.messageMediaGeoLive(geo: _1!, heading: _2!, period: _3!)
let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil
let _c4 = _4 != nil
let _c5 = (Int(_1!) & Int(1 << 1) == 0) || _5 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 {
return Api.MessageMedia.messageMediaGeoLive(flags: _1!, geo: _2!, heading: _3, period: _4!, proximityNotificationRadius: _5)
}
else {
return nil
@ -19959,10 +19971,10 @@ public extension Api {
}
public enum BotInlineMessage: TypeConstructorDescription {
case botInlineMessageText(flags: Int32, message: String, entities: [Api.MessageEntity]?, replyMarkup: Api.ReplyMarkup?)
case botInlineMessageMediaGeo(flags: Int32, geo: Api.GeoPoint, period: Int32, replyMarkup: Api.ReplyMarkup?)
case botInlineMessageMediaAuto(flags: Int32, message: String, entities: [Api.MessageEntity]?, replyMarkup: Api.ReplyMarkup?)
case botInlineMessageMediaVenue(flags: Int32, geo: Api.GeoPoint, title: String, address: String, provider: String, venueId: String, venueType: String, replyMarkup: Api.ReplyMarkup?)
case botInlineMessageMediaContact(flags: Int32, phoneNumber: String, firstName: String, lastName: String, vcard: String, replyMarkup: Api.ReplyMarkup?)
case botInlineMessageMediaGeo(flags: Int32, geo: Api.GeoPoint, heading: Int32?, period: Int32?, proximityNotificationRadius: Int32?, replyMarkup: Api.ReplyMarkup?)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
@ -19979,15 +19991,6 @@ public extension Api {
}}
if Int(flags) & Int(1 << 2) != 0 {replyMarkup!.serialize(buffer, true)}
break
case .botInlineMessageMediaGeo(let flags, let geo, let period, let replyMarkup):
if boxed {
buffer.appendInt32(-1222451611)
}
serializeInt32(flags, buffer: buffer, boxed: false)
geo.serialize(buffer, true)
serializeInt32(period, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 2) != 0 {replyMarkup!.serialize(buffer, true)}
break
case .botInlineMessageMediaAuto(let flags, let message, let entities, let replyMarkup):
if boxed {
buffer.appendInt32(1984755728)
@ -20025,6 +20028,17 @@ public extension Api {
serializeString(vcard, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 2) != 0 {replyMarkup!.serialize(buffer, true)}
break
case .botInlineMessageMediaGeo(let flags, let geo, let heading, let period, let proximityNotificationRadius, let replyMarkup):
if boxed {
buffer.appendInt32(85477117)
}
serializeInt32(flags, buffer: buffer, boxed: false)
geo.serialize(buffer, true)
if Int(flags) & Int(1 << 0) != 0 {serializeInt32(heading!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 1) != 0 {serializeInt32(period!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 3) != 0 {serializeInt32(proximityNotificationRadius!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 2) != 0 {replyMarkup!.serialize(buffer, true)}
break
}
}
@ -20032,14 +20046,14 @@ public extension Api {
switch self {
case .botInlineMessageText(let flags, let message, let entities, let replyMarkup):
return ("botInlineMessageText", [("flags", flags), ("message", message), ("entities", entities), ("replyMarkup", replyMarkup)])
case .botInlineMessageMediaGeo(let flags, let geo, let period, let replyMarkup):
return ("botInlineMessageMediaGeo", [("flags", flags), ("geo", geo), ("period", period), ("replyMarkup", replyMarkup)])
case .botInlineMessageMediaAuto(let flags, let message, let entities, let replyMarkup):
return ("botInlineMessageMediaAuto", [("flags", flags), ("message", message), ("entities", entities), ("replyMarkup", replyMarkup)])
case .botInlineMessageMediaVenue(let flags, let geo, let title, let address, let provider, let venueId, let venueType, let replyMarkup):
return ("botInlineMessageMediaVenue", [("flags", flags), ("geo", geo), ("title", title), ("address", address), ("provider", provider), ("venueId", venueId), ("venueType", venueType), ("replyMarkup", replyMarkup)])
case .botInlineMessageMediaContact(let flags, let phoneNumber, let firstName, let lastName, let vcard, let replyMarkup):
return ("botInlineMessageMediaContact", [("flags", flags), ("phoneNumber", phoneNumber), ("firstName", firstName), ("lastName", lastName), ("vcard", vcard), ("replyMarkup", replyMarkup)])
case .botInlineMessageMediaGeo(let flags, let geo, let heading, let period, let proximityNotificationRadius, let replyMarkup):
return ("botInlineMessageMediaGeo", [("flags", flags), ("geo", geo), ("heading", heading), ("period", period), ("proximityNotificationRadius", proximityNotificationRadius), ("replyMarkup", replyMarkup)])
}
}
@ -20067,30 +20081,6 @@ public extension Api {
return nil
}
}
public static func parse_botInlineMessageMediaGeo(_ reader: BufferReader) -> BotInlineMessage? {
var _1: Int32?
_1 = reader.readInt32()
var _2: Api.GeoPoint?
if let signature = reader.readInt32() {
_2 = Api.parse(reader, signature: signature) as? Api.GeoPoint
}
var _3: Int32?
_3 = reader.readInt32()
var _4: Api.ReplyMarkup?
if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() {
_4 = Api.parse(reader, signature: signature) as? Api.ReplyMarkup
} }
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = _3 != nil
let _c4 = (Int(_1!) & Int(1 << 2) == 0) || _4 != nil
if _c1 && _c2 && _c3 && _c4 {
return Api.BotInlineMessage.botInlineMessageMediaGeo(flags: _1!, geo: _2!, period: _3!, replyMarkup: _4)
}
else {
return nil
}
}
public static func parse_botInlineMessageMediaAuto(_ reader: BufferReader) -> BotInlineMessage? {
var _1: Int32?
_1 = reader.readInt32()
@ -20179,6 +20169,36 @@ public extension Api {
return nil
}
}
public static func parse_botInlineMessageMediaGeo(_ reader: BufferReader) -> BotInlineMessage? {
var _1: Int32?
_1 = reader.readInt32()
var _2: Api.GeoPoint?
if let signature = reader.readInt32() {
_2 = Api.parse(reader, signature: signature) as? Api.GeoPoint
}
var _3: Int32?
if Int(_1!) & Int(1 << 0) != 0 {_3 = reader.readInt32() }
var _4: Int32?
if Int(_1!) & Int(1 << 1) != 0 {_4 = reader.readInt32() }
var _5: Int32?
if Int(_1!) & Int(1 << 3) != 0 {_5 = reader.readInt32() }
var _6: Api.ReplyMarkup?
if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() {
_6 = Api.parse(reader, signature: signature) as? Api.ReplyMarkup
} }
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil
let _c4 = (Int(_1!) & Int(1 << 1) == 0) || _4 != nil
let _c5 = (Int(_1!) & Int(1 << 3) == 0) || _5 != nil
let _c6 = (Int(_1!) & Int(1 << 2) == 0) || _6 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 {
return Api.BotInlineMessage.botInlineMessageMediaGeo(flags: _1!, geo: _2!, heading: _3, period: _4, proximityNotificationRadius: _5, replyMarkup: _6)
}
else {
return nil
}
}
}
public enum InputPeerNotifySettings: TypeConstructorDescription {

View file

@ -3781,24 +3781,6 @@ public extension Api {
})
}
public static func requestProximityNotification(flags: Int32, peer: Api.InputPeer, msgId: Int32, ownLocation: Api.InputGeoPoint?, maxDistance: Int32?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Bool>) {
let buffer = Buffer()
buffer.appendInt32(-699657935)
serializeInt32(flags, buffer: buffer, boxed: false)
peer.serialize(buffer, true)
serializeInt32(msgId, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 0) != 0 {ownLocation!.serialize(buffer, true)}
if Int(flags) & Int(1 << 0) != 0 {serializeInt32(maxDistance!, buffer: buffer, boxed: false)}
return (FunctionDescription(name: "messages.requestProximityNotification", parameters: [("flags", flags), ("peer", peer), ("msgId", msgId), ("ownLocation", ownLocation), ("maxDistance", maxDistance)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in
let reader = BufferReader(buffer)
var result: Api.Bool?
if let signature = reader.readInt32() {
result = Api.parse(reader, signature: signature) as? Api.Bool
}
return result
})
}
public static func unpinAllMessages(peer: Api.InputPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.messages.AffectedHistory>) {
let buffer = Buffer()
buffer.appendInt32(-265962357)

View file

@ -133,14 +133,15 @@ final class CallControllerButtonItemNode: HighlightTrackingButtonNode {
self.currentContent = content
if content.hasProgress {
let statusFrame = CGRect(origin: CGPoint(), size: CGSize(width: self.largeButtonSize, height: self.largeButtonSize))
if self.statusNode == nil {
let statusNode = SemanticStatusNode(backgroundNodeColor: .white, foregroundNodeColor: .clear, hollow: true)
let statusNode = SemanticStatusNode(backgroundNodeColor: .white, foregroundNodeColor: .clear, cutout: statusFrame.insetBy(dx: 8.0, dy: 8.0))
self.statusNode = statusNode
self.contentContainer.insertSubnode(statusNode, belowSubnode: self.contentNode)
statusNode.transitionToState(.progress(value: nil, cancelEnabled: false, appearance: SemanticStatusNodeState.ProgressAppearance(inset: 4.0, lineWidth: 3.0)), animated: false, completion: {})
}
if let statusNode = self.statusNode {
statusNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: self.largeButtonSize, height: self.largeButtonSize))
statusNode.frame = statusFrame
if transition.isAnimated {
statusNode.layer.animateScale(from: 0.1, to: 1.0, duration: 0.2)
statusNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)

View file

@ -10,7 +10,8 @@ import TelegramAudio
import AccountContext
public final class GroupCallController: ViewController {
private final class Node: ViewControllerTracingNode {
}
/* private final class Node: ViewControllerTracingNode {
private let context: AccountContext
private let presentationData: PresentationData
@ -145,3 +146,4 @@ public final class GroupCallController: ViewController {
self.controllerNode.containerLayoutUpdated(layout, transition: transition)
}
}
*/

View file

@ -2992,6 +2992,9 @@ func replayFinalState(accountManager: AccountManager, postbox: Postbox, accountP
}
if upperId >= messageId.id {
transaction.addHole(peerId: messageId.peerId, namespace: messageId.namespace, space: .everywhere, range: messageId.id ... upperId)
transaction.addHole(peerId: messageId.peerId, namespace: messageId.namespace, space: .tag(.pinned), range: 1 ... upperId)
Logger.shared.log("State", "adding hole for peer \(messageId.peerId), \(messageId.id) ... \(upperId)")
} else {
Logger.shared.log("State", "not adding hole for peer \(messageId.peerId), \(upperId) >= \(messageId.id) = false")

View file

@ -1457,16 +1457,16 @@ public final class AccountViewTracker {
}
}
public func aroundIdMessageHistoryViewForLocation(_ chatLocation: ChatLocationInput, count: Int, messageId: MessageId, tagMask: MessageTags? = nil, orderStatistics: MessageHistoryViewOrderStatistics = [], additionalData: [AdditionalMessageHistoryViewData] = []) -> Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> {
public func aroundIdMessageHistoryViewForLocation(_ chatLocation: ChatLocationInput, count: Int, ignoreRelatedChats: Bool, messageId: MessageId, tagMask: MessageTags? = nil, orderStatistics: MessageHistoryViewOrderStatistics = [], additionalData: [AdditionalMessageHistoryViewData] = []) -> Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> {
if let account = self.account {
let signal = account.postbox.aroundIdMessageHistoryViewForLocation(chatLocation, count: count, messageId: messageId, topTaggedMessageIdNamespaces: [Namespaces.Message.Cloud], tagMask: tagMask, namespaces: .not(Namespaces.Message.allScheduled), orderStatistics: orderStatistics, additionalData: wrappedHistoryViewAdditionalData(chatLocation: chatLocation, additionalData: additionalData))
let signal = account.postbox.aroundIdMessageHistoryViewForLocation(chatLocation, count: count, ignoreRelatedChats: ignoreRelatedChats, messageId: messageId, topTaggedMessageIdNamespaces: [Namespaces.Message.Cloud], tagMask: tagMask, namespaces: .not(Namespaces.Message.allScheduled), orderStatistics: orderStatistics, additionalData: wrappedHistoryViewAdditionalData(chatLocation: chatLocation, additionalData: additionalData))
return wrappedMessageHistorySignal(chatLocation: chatLocation, signal: signal, addHoleIfNeeded: false)
} else {
return .never()
}
}
public func aroundMessageHistoryViewForLocation(_ chatLocation: ChatLocationInput, index: MessageHistoryAnchorIndex, anchorIndex: MessageHistoryAnchorIndex, count: Int, clipHoles: Bool = true, fixedCombinedReadStates: MessageHistoryViewReadState?, tagMask: MessageTags? = nil, orderStatistics: MessageHistoryViewOrderStatistics = [], additionalData: [AdditionalMessageHistoryViewData] = []) -> Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> {
public func aroundMessageHistoryViewForLocation(_ chatLocation: ChatLocationInput, index: MessageHistoryAnchorIndex, anchorIndex: MessageHistoryAnchorIndex, count: Int, clipHoles: Bool = true, ignoreRelatedChats: Bool = false, fixedCombinedReadStates: MessageHistoryViewReadState?, tagMask: MessageTags? = nil, orderStatistics: MessageHistoryViewOrderStatistics = [], additionalData: [AdditionalMessageHistoryViewData] = []) -> Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> {
if let account = self.account {
let inputAnchor: HistoryViewInputAnchor
switch index {
@ -1477,7 +1477,7 @@ public final class AccountViewTracker {
case let .message(index):
inputAnchor = .index(index)
}
let signal = account.postbox.aroundMessageHistoryViewForLocation(chatLocation, anchor: inputAnchor, count: count, clipHoles: clipHoles, fixedCombinedReadStates: fixedCombinedReadStates, topTaggedMessageIdNamespaces: [Namespaces.Message.Cloud], tagMask: tagMask, namespaces: .not(Namespaces.Message.allScheduled), orderStatistics: orderStatistics, additionalData: wrappedHistoryViewAdditionalData(chatLocation: chatLocation, additionalData: additionalData))
let signal = account.postbox.aroundMessageHistoryViewForLocation(chatLocation, anchor: inputAnchor, count: count, clipHoles: clipHoles, ignoreRelatedChats: ignoreRelatedChats, fixedCombinedReadStates: fixedCombinedReadStates, topTaggedMessageIdNamespaces: [Namespaces.Message.Cloud], tagMask: tagMask, namespaces: .not(Namespaces.Message.allScheduled), orderStatistics: orderStatistics, additionalData: wrappedHistoryViewAdditionalData(chatLocation: chatLocation, additionalData: additionalData))
return wrappedMessageHistorySignal(chatLocation: chatLocation, signal: signal, addHoleIfNeeded: false)
} else {
return .never()

View file

@ -423,15 +423,15 @@ extension ChatContextResultMessage {
parsedReplyMarkup = ReplyMarkupMessageAttribute(apiMarkup: replyMarkup)
}
self = .text(text: message, entities: parsedEntities, disableUrlPreview: (flags & (1 << 0)) != 0, replyMarkup: parsedReplyMarkup)
case let .botInlineMessageMediaGeo(_, geo, period, replyMarkup):
let media = telegramMediaMapFromApiGeoPoint(geo, title: nil, address: nil, provider: nil, venueId: nil, venueType: nil, liveBroadcastingTimeout: period, heading: nil)
case let .botInlineMessageMediaGeo(_, geo, heading, period, proximityNotificationRadius, replyMarkup):
let media = telegramMediaMapFromApiGeoPoint(geo, title: nil, address: nil, provider: nil, venueId: nil, venueType: nil, liveBroadcastingTimeout: period, liveProximityNotificationRadius: proximityNotificationRadius, heading: heading)
var parsedReplyMarkup: ReplyMarkupMessageAttribute?
if let replyMarkup = replyMarkup {
parsedReplyMarkup = ReplyMarkupMessageAttribute(apiMarkup: replyMarkup)
}
self = .mapLocation(media: media, replyMarkup: parsedReplyMarkup)
case let .botInlineMessageMediaVenue(_, geo, title, address, provider, venueId, venueType, replyMarkup):
let media = telegramMediaMapFromApiGeoPoint(geo, title: title, address: address, provider: provider, venueId: venueId, venueType: venueType, liveBroadcastingTimeout: nil, heading: nil)
let media = telegramMediaMapFromApiGeoPoint(geo, title: title, address: address, provider: provider, venueId: venueId, venueType: venueType, liveBroadcastingTimeout: nil, liveProximityNotificationRadius: nil, heading: nil)
var parsedReplyMarkup: ReplyMarkupMessageAttribute?
if let replyMarkup = replyMarkup {
parsedReplyMarkup = ReplyMarkupMessageAttribute(apiMarkup: replyMarkup)

View file

@ -416,6 +416,9 @@ private func hashForMessages(_ messages: [Message], withChannelIds: Bool) -> Int
break inner
}
}
if message.tags.contains(.pinned) {
acc = (acc &* 20261) &+ UInt32(1)
}
acc = (acc &* 20261) &+ UInt32(timestamp)
}
return Int32(bitPattern: acc & UInt32(0x7FFFFFFF))

View file

@ -139,7 +139,7 @@ private func requestActivity(postbox: Postbox, network: Network, accountPeerId:
break
case let .present(statusTimestamp):
let timestamp = Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970)
if statusTimestamp < timestamp {
if statusTimestamp < timestamp - 30 {
return .complete()
}
}

View file

@ -1,6 +1,7 @@
import Foundation
import Postbox
import SwiftSignalKit
import TelegramApi
import SyncCore
@ -9,7 +10,7 @@ public func topPeerActiveLiveLocationMessages(viewTracker: AccountViewTracker, a
|> map { (view, _, _) -> (Peer?, [Message]) in
var accountPeer: Peer?
for entry in view.additionalData {
if case let .peer(id, peer) = entry {
if case let .peer(_, peer) = entry {
accountPeer = peer
break
}

View file

@ -142,12 +142,19 @@ func mediaContentToUpload(network: Network, postbox: Postbox, auxiliaryMethods:
return .single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .media(input, text), reuploadInfo: nil)))
} else if let map = media as? TelegramMediaMap {
let input: Api.InputMedia
var flags: Int32 = 1 << 1
if let _ = map.heading {
flags |= 1 << 2
}
if let _ = map.liveProximityNotificationRadius {
flags |= 1 << 3
}
var geoFlags: Int32 = 0
if let _ = map.accuracyRadius {
geoFlags |= 1 << 0
}
if let liveBroadcastingTimeout = map.liveBroadcastingTimeout {
input = .inputMediaGeoLive(flags: 1 << 1, geoPoint: Api.InputGeoPoint.inputGeoPoint(flags: geoFlags, lat: map.latitude, long: map.longitude, accuracyRadius: map.accuracyRadius.flatMap({ Int32($0) })), heading: 0, period: liveBroadcastingTimeout)
input = .inputMediaGeoLive(flags: flags, geoPoint: Api.InputGeoPoint.inputGeoPoint(flags: geoFlags, lat: map.latitude, long: map.longitude, accuracyRadius: map.accuracyRadius.flatMap({ Int32($0) })), heading: map.heading, period: liveBroadcastingTimeout, proximityNotificationRadius: map.liveProximityNotificationRadius.flatMap({ Int32($0) }))
} else if let venue = map.venue {
input = .inputMediaVenue(geoPoint: Api.InputGeoPoint.inputGeoPoint(flags: geoFlags, lat: map.latitude, long: map.longitude, accuracyRadius: map.accuracyRadius.flatMap({ Int32($0) })), title: venue.title, address: venue.address ?? "", provider: venue.provider ?? "", venueId: venue.id ?? "", venueType: venue.type ?? "")
} else {

View file

@ -794,11 +794,11 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32
case let .decryptedMessageMediaWebPage(url):
parsedMedia.append(TelegramMediaWebpage(webpageId: MediaId(namespace: Namespaces.Media.LocalWebpage, id: arc4random64()), content: .Pending(0, url)))
case let .decryptedMessageMediaGeoPoint(lat, long):
parsedMedia.append(TelegramMediaMap(latitude: lat, longitude: long, heading: nil, accuracyRadius: nil, geoPlace: nil, venue: nil, liveBroadcastingTimeout: nil))
parsedMedia.append(TelegramMediaMap(latitude: lat, longitude: long, heading: nil, accuracyRadius: nil, geoPlace: nil, venue: nil, liveBroadcastingTimeout: nil, liveProximityNotificationRadius: nil))
case let .decryptedMessageMediaContact(phoneNumber, firstName, lastName, userId):
parsedMedia.append(TelegramMediaContact(firstName: firstName, lastName: lastName, phoneNumber: phoneNumber, peerId: userId == 0 ? nil : PeerId(namespace: Namespaces.Peer.CloudUser, id: userId), vCardData: nil))
case let .decryptedMessageMediaVenue(lat, long, title, address, provider, venueId):
parsedMedia.append(TelegramMediaMap(latitude: lat, longitude: long, heading: nil, accuracyRadius: nil, geoPlace: nil, venue: MapVenue(title: title, address: address, provider: provider, id: venueId, type: nil), liveBroadcastingTimeout: nil))
parsedMedia.append(TelegramMediaMap(latitude: lat, longitude: long, heading: nil, accuracyRadius: nil, geoPlace: nil, venue: MapVenue(title: title, address: address, provider: provider, id: venueId, type: nil), liveBroadcastingTimeout: nil, liveProximityNotificationRadius: nil))
case .decryptedMessageMediaEmpty:
break
}
@ -1013,11 +1013,11 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32
case let .decryptedMessageMediaWebPage(url):
parsedMedia.append(TelegramMediaWebpage(webpageId: MediaId(namespace: Namespaces.Media.LocalWebpage, id: arc4random64()), content: .Pending(0, url)))
case let .decryptedMessageMediaGeoPoint(lat, long):
parsedMedia.append(TelegramMediaMap(latitude: lat, longitude: long, heading: nil, accuracyRadius: nil, geoPlace: nil, venue: nil, liveBroadcastingTimeout: nil))
parsedMedia.append(TelegramMediaMap(latitude: lat, longitude: long, heading: nil, accuracyRadius: nil, geoPlace: nil, venue: nil, liveBroadcastingTimeout: nil, liveProximityNotificationRadius: nil))
case let .decryptedMessageMediaContact(phoneNumber, firstName, lastName, userId):
parsedMedia.append(TelegramMediaContact(firstName: firstName, lastName: lastName, phoneNumber: phoneNumber, peerId: userId == 0 ? nil : PeerId(namespace: Namespaces.Peer.CloudUser, id: userId), vCardData: nil))
case let .decryptedMessageMediaVenue(lat, long, title, address, provider, venueId):
parsedMedia.append(TelegramMediaMap(latitude: lat, longitude: long, heading: nil, accuracyRadius: nil, geoPlace: nil, venue: MapVenue(title: title, address: address, provider: provider, id: venueId, type: nil), liveBroadcastingTimeout: nil))
parsedMedia.append(TelegramMediaMap(latitude: lat, longitude: long, heading: nil, accuracyRadius: nil, geoPlace: nil, venue: MapVenue(title: title, address: address, provider: provider, id: venueId, type: nil), liveBroadcastingTimeout: nil, liveProximityNotificationRadius: nil))
case .decryptedMessageMediaEmpty:
break
}
@ -1251,11 +1251,11 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32
case let .decryptedMessageMediaWebPage(url):
parsedMedia.append(TelegramMediaWebpage(webpageId: MediaId(namespace: Namespaces.Media.LocalWebpage, id: arc4random64()), content: .Pending(0, url)))
case let .decryptedMessageMediaGeoPoint(lat, long):
parsedMedia.append(TelegramMediaMap(latitude: lat, longitude: long, heading: nil, accuracyRadius: nil, geoPlace: nil, venue: nil, liveBroadcastingTimeout: nil))
parsedMedia.append(TelegramMediaMap(latitude: lat, longitude: long, heading: nil, accuracyRadius: nil, geoPlace: nil, venue: nil, liveBroadcastingTimeout: nil, liveProximityNotificationRadius: nil))
case let .decryptedMessageMediaContact(phoneNumber, firstName, lastName, userId):
parsedMedia.append(TelegramMediaContact(firstName: firstName, lastName: lastName, phoneNumber: phoneNumber, peerId: userId == 0 ? nil : PeerId(namespace: Namespaces.Peer.CloudUser, id: userId), vCardData: nil))
case let .decryptedMessageMediaVenue(lat, long, title, address, provider, venueId):
parsedMedia.append(TelegramMediaMap(latitude: lat, longitude: long, heading: nil, accuracyRadius: nil, geoPlace: nil, venue: MapVenue(title: title, address: address, provider: provider, id: venueId, type: nil), liveBroadcastingTimeout: nil))
parsedMedia.append(TelegramMediaMap(latitude: lat, longitude: long, heading: nil, accuracyRadius: nil, geoPlace: nil, venue: MapVenue(title: title, address: address, provider: provider, id: venueId, type: nil), liveBroadcastingTimeout: nil, liveProximityNotificationRadius: nil))
case .decryptedMessageMediaEmpty:
break
}

View file

@ -255,7 +255,7 @@ private func requestEditMessageInternal(postbox: Postbox, network: Network, stat
}
}
public func requestEditLiveLocation(postbox: Postbox, network: Network, stateManager: AccountStateManager, messageId: MessageId, coordinate: (latitude: Double, longitude: Double, accuracyRadius: Int32?)?, heading: Int32?) -> Signal<Void, NoError> {
public func requestEditLiveLocation(postbox: Postbox, network: Network, stateManager: AccountStateManager, messageId: MessageId, stop: Bool, coordinate: (latitude: Double, longitude: Double, accuracyRadius: Int32?)?, heading: Int32?, proximityNotificationRadius: Int32?) -> Signal<Void, NoError> {
return postbox.transaction { transaction -> (Api.InputPeer, TelegramMediaMap)? in
guard let inputPeer = transaction.getPeer(messageId.peerId).flatMap(apiInputPeer) else {
return nil
@ -275,15 +275,33 @@ public func requestEditLiveLocation(postbox: Postbox, network: Network, stateMan
return .complete()
}
let inputMedia: Api.InputMedia
if let coordinate = coordinate, let liveBroadcastingTimeout = media.liveBroadcastingTimeout {
var geoFlags: Int32 = 0
if let _ = coordinate.accuracyRadius {
geoFlags |= 1 << 0
if let liveBroadcastingTimeout = media.liveBroadcastingTimeout, !stop {
var flags: Int32 = 1 << 1
let inputGeoPoint: Api.InputGeoPoint
if let coordinate = coordinate {
var geoFlags: Int32 = 0
if let _ = coordinate.accuracyRadius {
geoFlags |= 1 << 0
}
inputGeoPoint = .inputGeoPoint(flags: geoFlags, lat: coordinate.latitude, long: coordinate.longitude, accuracyRadius: coordinate.accuracyRadius.flatMap({ Int32($0) }))
} else {
var geoFlags: Int32 = 0
if let _ = media.accuracyRadius {
geoFlags |= 1 << 0
}
inputGeoPoint = .inputGeoPoint(flags: geoFlags, lat: media.latitude, long: media.longitude, accuracyRadius: media.accuracyRadius.flatMap({ Int32($0) }))
}
inputMedia = .inputMediaGeoLive(flags: 1 << 1, geoPoint: .inputGeoPoint(flags: geoFlags, lat: coordinate.latitude, long: coordinate.longitude, accuracyRadius: coordinate.accuracyRadius.flatMap({ Int32($0) })), heading: heading ?? 0, period: liveBroadcastingTimeout)
if let _ = heading {
flags |= 1 << 2
}
if let _ = proximityNotificationRadius {
flags |= 1 << 3
}
inputMedia = .inputMediaGeoLive(flags: flags, geoPoint: inputGeoPoint, heading: heading, period: liveBroadcastingTimeout, proximityNotificationRadius: proximityNotificationRadius)
} else {
inputMedia = .inputMediaGeoLive(flags: 1 << 0, geoPoint: .inputGeoPoint(flags: 0, lat: media.latitude, long: media.longitude, accuracyRadius: nil), heading: 0, period: nil)
inputMedia = .inputMediaGeoLive(flags: 1 << 0, geoPoint: .inputGeoPoint(flags: 0, lat: media.latitude, long: media.longitude, accuracyRadius: nil), heading: nil, period: nil, proximityNotificationRadius: nil)
}
return network.request(Api.functions.messages.editMessage(flags: 1 << 14, peer: inputPeer, id: messageId.id, message: nil, media: inputMedia, replyMarkup: nil, entities: nil, scheduleDate: nil))
|> map(Optional.init)
|> `catch` { _ -> Signal<Api.Updates?, NoError> in
@ -293,7 +311,7 @@ public func requestEditLiveLocation(postbox: Postbox, network: Network, stateMan
if let updates = updates {
stateManager.addUpdates(updates)
}
if coordinate == nil {
if coordinate == nil && proximityNotificationRadius == nil {
return postbox.transaction { transaction -> Void in
transaction.updateMessage(messageId, update: { currentMessage in
var storeForwardInfo: StoreMessageForwardInfo?
@ -311,42 +329,3 @@ public func requestEditLiveLocation(postbox: Postbox, network: Network, stateMan
}
}
}
public func requestProximityNotification(postbox: Postbox, network: Network, messageId: MessageId, distance: Int32) -> Signal<Void, NoError> {
return postbox.transaction { transaction -> Api.InputPeer? in
return transaction.getPeer(messageId.peerId).flatMap(apiInputPeer)
}
|> mapToSignal { inputPeer -> Signal<Void, NoError> in
guard let inputPeer = inputPeer else {
return .complete()
}
let flags: Int32 = 1 << 0
return network.request(Api.functions.messages.requestProximityNotification(flags: flags, peer: inputPeer, msgId: messageId.id, ownLocation: nil, maxDistance: distance))
|> map(Optional.init)
|> `catch` { _ -> Signal<Api.Bool?, NoError> in
return .single(nil)
}
|> mapToSignal { _ -> Signal<Void, NoError> in
return .complete()
}
}
}
public func cancelProximityNotification(postbox: Postbox, network: Network, messageId: MessageId) -> Signal<Void, NoError> {
return postbox.transaction { transaction -> Api.InputPeer? in
return transaction.getPeer(messageId.peerId).flatMap(apiInputPeer)
}
|> mapToSignal { inputPeer -> Signal<Void, NoError> in
guard let inputPeer = inputPeer else {
return .complete()
}
return network.request(Api.functions.messages.requestProximityNotification(flags: 1 << 1, peer: inputPeer, msgId: messageId.id, ownLocation: nil, maxDistance: nil))
|> map(Optional.init)
|> `catch` { _ -> Signal<Api.Bool?, NoError> in
return .single(nil)
}
|> mapToSignal { _ -> Signal<Void, NoError> in
return .complete()
}
}
}

View file

@ -255,13 +255,13 @@ func textMediaAndExpirationTimerFromApiMedia(_ media: Api.MessageMedia?, _ peerI
let mediaContact = TelegramMediaContact(firstName: firstName, lastName: lastName, phoneNumber: phoneNumber, peerId: contactPeerId, vCardData: vcard.isEmpty ? nil : vcard)
return (mediaContact, nil)
case let .messageMediaGeo(geo):
let mediaMap = telegramMediaMapFromApiGeoPoint(geo, title: nil, address: nil, provider: nil, venueId: nil, venueType: nil, liveBroadcastingTimeout: nil, heading: nil)
let mediaMap = telegramMediaMapFromApiGeoPoint(geo, title: nil, address: nil, provider: nil, venueId: nil, venueType: nil, liveBroadcastingTimeout: nil, liveProximityNotificationRadius: nil, heading: nil)
return (mediaMap, nil)
case let .messageMediaVenue(geo, title, address, provider, venueId, venueType):
let mediaMap = telegramMediaMapFromApiGeoPoint(geo, title: title, address: address, provider: provider, venueId: venueId, venueType: venueType, liveBroadcastingTimeout: nil, heading: nil)
let mediaMap = telegramMediaMapFromApiGeoPoint(geo, title: title, address: address, provider: provider, venueId: venueId, venueType: venueType, liveBroadcastingTimeout: nil, liveProximityNotificationRadius: nil, heading: nil)
return (mediaMap, nil)
case let .messageMediaGeoLive(geo, heading, period):
let mediaMap = telegramMediaMapFromApiGeoPoint(geo, title: nil, address: nil, provider: nil, venueId: nil, venueType: nil, liveBroadcastingTimeout: period, heading: heading > 0 ? heading : nil)
case let .messageMediaGeoLive(_, geo, heading, period, proximityNotificationRadius):
let mediaMap = telegramMediaMapFromApiGeoPoint(geo, title: nil, address: nil, provider: nil, venueId: nil, venueType: nil, liveBroadcastingTimeout: period, liveProximityNotificationRadius: proximityNotificationRadius, heading: heading)
return (mediaMap, nil)
case let .messageMediaDocument(_, document, ttlSeconds):
if let document = document {

View file

@ -58,7 +58,7 @@ func telegramMediaActionFromApiAction(_ action: Api.MessageAction) -> TelegramMe
case .messageActionContactSignUp:
return TelegramMediaAction(action: .peerJoined)
case let .messageActionGeoProximityReached(fromId, toId, distance):
return TelegramMediaAction(action: .geoProximityReached(distance: distance))
return TelegramMediaAction(action: .geoProximityReached(from: fromId.peerId, to: toId.peerId, distance: distance))
}
}

View file

@ -4,15 +4,15 @@ import TelegramApi
import SyncCore
func telegramMediaMapFromApiGeoPoint(_ geo: Api.GeoPoint, title: String?, address: String?, provider: String?, venueId: String?, venueType: String?, liveBroadcastingTimeout: Int32?, heading: Int32?) -> TelegramMediaMap {
func telegramMediaMapFromApiGeoPoint(_ geo: Api.GeoPoint, title: String?, address: String?, provider: String?, venueId: String?, venueType: String?, liveBroadcastingTimeout: Int32?, liveProximityNotificationRadius: Int32?, heading: Int32?) -> TelegramMediaMap {
var venue: MapVenue?
if let title = title {
venue = MapVenue(title: title, address: address, provider: provider, id: venueId, type: venueType)
}
switch geo {
case let .geoPoint(_, long, lat, _, accuracyRadius):
return TelegramMediaMap(latitude: lat, longitude: long, heading: heading.flatMap { Double($0) }, accuracyRadius: accuracyRadius.flatMap { Double($0) }, geoPlace: nil, venue: venue, liveBroadcastingTimeout: liveBroadcastingTimeout)
return TelegramMediaMap(latitude: lat, longitude: long, heading: heading, accuracyRadius: accuracyRadius.flatMap { Double($0) }, geoPlace: nil, venue: venue, liveBroadcastingTimeout: liveBroadcastingTimeout, liveProximityNotificationRadius: liveProximityNotificationRadius)
case .geoPointEmpty:
return TelegramMediaMap(latitude: 0.0, longitude: 0.0, heading: nil, accuracyRadius: nil, geoPlace: nil, venue: venue, liveBroadcastingTimeout: liveBroadcastingTimeout)
return TelegramMediaMap(latitude: 0.0, longitude: 0.0, heading: nil, accuracyRadius: nil, geoPlace: nil, venue: venue, liveBroadcastingTimeout: liveBroadcastingTimeout, liveProximityNotificationRadius: liveProximityNotificationRadius)
}
}

View file

@ -11,7 +11,7 @@ public enum UpdatePinnedMessageError {
}
public enum PinnedMessageUpdate {
case pin(id: MessageId, silent: Bool)
case pin(id: MessageId, silent: Bool, forThisPeerOnlyIfPossible: Bool)
case clear(id: MessageId)
}
@ -52,11 +52,14 @@ public func requestUpdatePinnedMessage(account: Account, peerId: PeerId, update:
var flags: Int32 = 0
let messageId: Int32
switch update {
case let .pin(id, silent):
case let .pin(id, silent, forThisPeerOnlyIfPossible):
messageId = id.id
if silent {
flags |= (1 << 0)
}
if forThisPeerOnlyIfPossible {
flags |= (1 << 2)
}
case let .clear(id):
messageId = id.id
flags |= 1 << 1
@ -77,7 +80,7 @@ public func requestUpdatePinnedMessage(account: Account, peerId: PeerId, update:
if peerId.namespace == Namespaces.Peer.CloudChannel {
let messageId: MessageId
switch update {
case let .pin(id, _):
case let .pin(id, _, _):
messageId = id
case let .clear(id):
messageId = id

View file

@ -302,6 +302,32 @@ public struct ApplicationSpecificNotice {
}
}
public static func inlineBotLocationRequestStatus(accountManager: AccountManager, peerId: PeerId) -> Signal<Bool, NoError> {
return accountManager.noticeEntry(key: ApplicationSpecificNoticeKeys.inlineBotLocationRequestNotice(peerId: peerId))
|> map { view -> Bool in
guard let value = view.value as? ApplicationSpecificTimestampNotice else {
return false
}
if value.value == 0 {
return true
} else {
return false
}
}
}
public static func updateInlineBotLocationRequestState(accountManager: AccountManager, peerId: PeerId, timestamp: Int32) -> Signal<Bool, NoError> {
return accountManager.transaction { transaction -> Bool in
if let notice = transaction.getNotice(ApplicationSpecificNoticeKeys.inlineBotLocationRequestNotice(peerId: peerId)) as? ApplicationSpecificTimestampNotice, (notice.value == 0 || timestamp <= notice.value + 10 * 60) {
return false
}
transaction.setNotice(ApplicationSpecificNoticeKeys.inlineBotLocationRequestNotice(peerId: peerId), ApplicationSpecificTimestampNotice(value: timestamp))
return true
}
}
public static func setInlineBotLocationRequest(accountManager: AccountManager, peerId: PeerId, value: Int32) -> Signal<Void, NoError> {
return accountManager.transaction { transaction -> Void in
transaction.setNotice(ApplicationSpecificNoticeKeys.inlineBotLocationRequestNotice(peerId: peerId), ApplicationSpecificTimestampNotice(value: value))

View file

@ -77,6 +77,7 @@ public func chatBubbleActionButtonImage(fillColor: UIColor, strokeColor: UIColor
public final class PrincipalThemeEssentialGraphics {
public let chatMessageBackgroundIncomingMaskImage: UIImage
public let chatMessageBackgroundIncomingExtractedMaskImage: UIImage
public let chatMessageBackgroundIncomingImage: UIImage
public let chatMessageBackgroundIncomingExtractedImage: UIImage
public let chatMessageBackgroundIncomingOutlineImage: UIImage
@ -110,6 +111,7 @@ public final class PrincipalThemeEssentialGraphics {
public let chatMessageBackgroundIncomingMergedSideHighlightedImage: UIImage
public let chatMessageBackgroundOutgoingMaskImage: UIImage
public let chatMessageBackgroundOutgoingExtractedMaskImage: UIImage
public let chatMessageBackgroundOutgoingImage: UIImage
public let chatMessageBackgroundOutgoingExtractedImage: UIImage
public let chatMessageBackgroundOutgoingOutlineImage: UIImage
@ -248,12 +250,14 @@ public final class PrincipalThemeEssentialGraphics {
let emptyImage = UIImage()
if preview {
self.chatMessageBackgroundIncomingMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: UIColor.black, strokeColor: UIColor.clear, neighbors: .none, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true)
self.chatMessageBackgroundIncomingExtractedMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: UIColor.black, strokeColor: UIColor.clear, neighbors: .extracted, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true)
self.chatMessageBackgroundIncomingImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true)
self.chatMessageBackgroundIncomingExtractedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .extracted, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true)
self.chatMessageBackgroundIncomingOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true, onlyOutline: true)
self.chatMessageBackgroundIncomingExtractedOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .extracted, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true, onlyOutline: true)
self.chatMessageBackgroundIncomingShadowImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true, onlyShadow: true)
self.chatMessageBackgroundOutgoingMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: .black, strokeColor: .clear, neighbors: .none, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true)
self.chatMessageBackgroundOutgoingExtractedMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: .black, strokeColor: .clear, neighbors: .extracted, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true)
self.chatMessageBackgroundOutgoingImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true)
self.chatMessageBackgroundOutgoingExtractedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .extracted, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true)
self.chatMessageBackgroundOutgoingOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyOutline: true)
@ -350,6 +354,7 @@ public final class PrincipalThemeEssentialGraphics {
self.radialIndicatorFileIconOutgoing = emptyImage
} else {
self.chatMessageBackgroundIncomingMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: .black, strokeColor: .clear, neighbors: .none, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true)
self.chatMessageBackgroundIncomingExtractedMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: .black, strokeColor: .clear, neighbors: .extracted, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true)
self.chatMessageBackgroundIncomingImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true)
self.chatMessageBackgroundIncomingExtractedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .extracted, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true)
self.chatMessageBackgroundIncomingOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true, onlyOutline: true)
@ -378,6 +383,7 @@ public final class PrincipalThemeEssentialGraphics {
self.chatMessageBackgroundIncomingMergedBothHighlightedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: true, fillColor: incoming.highlightedFill, strokeColor: incoming.stroke, neighbors: .both, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout, extendedEdges: true)
self.chatMessageBackgroundOutgoingMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: .black, strokeColor: .clear, neighbors: .none, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true)
self.chatMessageBackgroundOutgoingExtractedMaskImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: .black, strokeColor: .clear, neighbors: .extracted, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true, extendedEdges: true)
self.chatMessageBackgroundOutgoingImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true)
self.chatMessageBackgroundOutgoingExtractedImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .extracted, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true)
self.chatMessageBackgroundOutgoingOutlineImage = messageBubbleImage(maxCornerRadius: maxCornerRadius, minCornerRadius: minCornerRadius, incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: outgoingKnockout, extendedEdges: true, onlyOutline: true)

View file

@ -217,7 +217,7 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
}
let textWithRanges: (String, [(Int, NSRange)])
if clippedText.isEmpty {
textWithRanges = strings.PUSH_PINNED_NOTEXT(authorName)
textWithRanges = strings.Message_PinnedGenericMessage(authorName)
} else {
textWithRanges = strings.Notification_PinnedTextMessage(authorName, clippedText)
}
@ -250,7 +250,7 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
attributedString = addAttributesToStringWithRanges(strings.Notification_PinnedQuizMessage(authorName), body: bodyAttributes, argumentAttributes: peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: [(0, message.author?.id)]))
}
case .deleted:
attributedString = addAttributesToStringWithRanges(strings.PUSH_PINNED_NOTEXT(authorName), body: bodyAttributes, argumentAttributes: peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: [(0, message.author?.id)]))
attributedString = addAttributesToStringWithRanges(strings.Message_PinnedGenericMessage(authorName), body: bodyAttributes, argumentAttributes: peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: [(0, message.author?.id)]))
}
case .joinedByLink:
attributedString = addAttributesToStringWithRanges(strings.Notification_JoinedGroupByLink(authorName), body: bodyAttributes, argumentAttributes: peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: [(0, message.author?.id)]))
@ -442,9 +442,13 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
attributedString = addAttributesToStringWithRanges(strings.Notification_Joined(authorName), body: bodyAttributes, argumentAttributes: peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: [(0, message.author?.id)]))
case .phoneNumberRequest:
attributedString = nil
case let .geoProximityReached(distance):
case let .geoProximityReached(fromId, toId, distance):
let distanceString = stringForDistance(strings: strings, distance: Double(distance))
attributedString = addAttributesToStringWithRanges(strings.Notification_ProximityReached(authorName, distanceString), body: bodyAttributes, argumentAttributes: peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: [(0, message.author?.id)]))
if toId == accountPeerId {
attributedString = addAttributesToStringWithRanges(strings.Notification_ProximityReachedYou(message.peers[fromId]?.displayTitle(strings: strings, displayOrder: nameDisplayOrder) ?? "", distanceString), body: bodyAttributes, argumentAttributes: peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: [(0, message.author?.id)]))
} else {
attributedString = addAttributesToStringWithRanges(strings.Notification_ProximityReached(message.peers[fromId]?.displayTitle(strings: strings, displayOrder: nameDisplayOrder) ?? "", distanceString, message.peers[toId]?.displayTitle(strings: strings, displayOrder: nameDisplayOrder) ?? ""), body: bodyAttributes, argumentAttributes: peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: [(0, message.author?.id), (2, toId)]))
}
case .unknown:
attributedString = nil
}

View file

@ -211,6 +211,8 @@ framework(
"//submodules/ChatInterfaceState:ChatInterfaceState",
"//submodules/AnimatedCountLabelNode:AnimatedCountLabelNode",
"//submodules/AnimatedAvatarSetNode:AnimatedAvatarSetNode",
"//submodules/SlotMachineAnimationNode:SlotMachineAnimationNode",
"//submodules/AnimatedNavigationStripeNode:AnimatedNavigationStripeNode",
],
frameworks = [
"$SDKROOT/System/Library/Frameworks/Foundation.framework",

View file

@ -208,6 +208,8 @@ swift_library(
"//submodules/ChatInterfaceState:ChatInterfaceState",
"//submodules/AnimatedCountLabelNode:AnimatedCountLabelNode",
"//submodules/AnimatedAvatarSetNode:AnimatedAvatarSetNode",
"//submodules/SlotMachineAnimationNode:SlotMachineAnimationNode",
"//submodules/AnimatedNavigationStripeNode:AnimatedNavigationStripeNode",
],
visibility = [
"//visibility:public",

View file

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "ic_menu_pinnedlist.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View file

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "ic_gps_filled.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View file

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "ic_gps_2.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -361,7 +361,7 @@ final class AuthorizedApplicationContext {
var processed = false
for media in firstMessage.media {
if let action = media as? TelegramMediaAction, case let .geoProximityReached(distance) = action.action {
if let action = media as? TelegramMediaAction, case let .geoProximityReached(fromId, toId, distance) = action.action {
strongSelf.context.sharedContext.openLocationScreen(context: strongSelf.context, messageId: firstMessage.id, navigationController: strongSelf.rootController)
processed = true
break

Some files were not shown because too many files have changed in this diff Show more