mirror of
https://github.com/TelegramMessenger/Telegram-iOS.git
synced 2026-07-06 03:33:41 +02:00
Merge branch 'master' into experiments/parallel-swiftmodule
This commit is contained in:
commit
eafbe0dc23
153 changed files with 6884 additions and 5870 deletions
2
Makefile
2
Makefile
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 \
|
||||
|
|
|
|||
16
submodules/AnimatedNavigationStripeNode/BUCK
Normal file
16
submodules/AnimatedNavigationStripeNode/BUCK
Normal 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",
|
||||
],
|
||||
)
|
||||
16
submodules/AnimatedNavigationStripeNode/BUILD
Normal file
16
submodules/AnimatedNavigationStripeNode/BUILD
Normal 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",
|
||||
],
|
||||
)
|
||||
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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)))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -95,6 +95,9 @@ public enum TabBarItemContextActionType {
|
|||
if !self.lockOrientation {
|
||||
self.lockedOrientation = nil
|
||||
}
|
||||
if let window = self.window {
|
||||
window.invalidateSupportedOrientations()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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?
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
277
submodules/LocationUI/Sources/LocationLiveListItem.swift
Normal file
277
submodules/LocationUI/Sources/LocationLiveListItem.swift
Normal 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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)))
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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)] = []) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
})
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
26
submodules/SlotMachineAnimationNode/BUCK
Normal file
26
submodules/SlotMachineAnimationNode/BUCK
Normal 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",
|
||||
],
|
||||
)
|
||||
26
submodules/SlotMachineAnimationNode/BUILD
Normal file
26
submodules/SlotMachineAnimationNode/BUILD
Normal 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",
|
||||
],
|
||||
)
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 []
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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) }
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -208,6 +208,8 @@ swift_library(
|
|||
"//submodules/ChatInterfaceState:ChatInterfaceState",
|
||||
"//submodules/AnimatedCountLabelNode:AnimatedCountLabelNode",
|
||||
"//submodules/AnimatedAvatarSetNode:AnimatedAvatarSetNode",
|
||||
"//submodules/SlotMachineAnimationNode:SlotMachineAnimationNode",
|
||||
"//submodules/AnimatedNavigationStripeNode:AnimatedNavigationStripeNode",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
|
|
|||
12
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/PinnedList.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/PinnedList.imageset/Contents.json
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "ic_menu_pinnedlist.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/PinnedList.imageset/ic_menu_pinnedlist.pdf
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/PinnedList.imageset/ic_menu_pinnedlist.pdf
vendored
Normal file
Binary file not shown.
12
submodules/TelegramUI/Images.xcassets/Location/TrackActiveIcon.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Location/TrackActiveIcon.imageset/Contents.json
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "ic_gps_filled.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
submodules/TelegramUI/Images.xcassets/Location/TrackActiveIcon.imageset/ic_gps_filled.pdf
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Location/TrackActiveIcon.imageset/ic_gps_filled.pdf
vendored
Normal file
Binary file not shown.
12
submodules/TelegramUI/Images.xcassets/Location/TrackHeadingIcon.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Location/TrackHeadingIcon.imageset/Contents.json
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "ic_gps_2.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
submodules/TelegramUI/Images.xcassets/Location/TrackHeadingIcon.imageset/ic_gps_2.pdf
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Location/TrackHeadingIcon.imageset/ic_gps_2.pdf
vendored
Normal file
Binary file not shown.
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.
|
|
@ -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
Loading…
Add table
Add a link
Reference in a new issue