Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios

This commit is contained in:
overtake 2020-10-27 11:31:34 +04:00
commit d3df036229
68 changed files with 4609 additions and 5074 deletions

View File

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

View File

@ -5876,4 +5876,8 @@ Any member of this group will be able to see messages in the channel.";
"Location.LiveLocationRequired.Title" = "Share Location"; "Location.LiveLocationRequired.Title" = "Share Location";
"Location.LiveLocationRequired.Description" = "For the alert to work, please share your live location in this chat."; "Location.LiveLocationRequired.Description" = "For the alert to work, please share your live location in this chat.";
"Location.LiveLocationRequired.ShareLocation" = "Share Location";  "Location.LiveLocationRequired.ShareLocation" = "Share Location";
"Stats.Message.Views" = "Views";
"Stats.Message.PublicShares" = "Public Shares";
"Stats.Message.PrivateShares" = "Private Shares";

View File

@ -1,12 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?> <?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"> <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" orientation="portrait"> <device id="watch38"/>
<adaptation id="fullscreen"/>
</device>
<dependencies> <dependencies>
<deployment identifier="watchOS"/> <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17125"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14283.14"/> <plugIn identifier="com.apple.InterfaceBuilder.IBWatchKitPlugin" version="17034"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBWatchKitPlugin" version="14238.10"/>
</dependencies> </dependencies>
<scenes> <scenes>
<!--TGNeoConversationController--> <!--TGNeoConversationController-->
@ -1097,432 +1094,6 @@ contacts found.</string>
</objects> </objects>
<point key="canvasLocation" x="1130" y="463"/> <point key="canvasLocation" x="1130" y="463"/>
</scene> </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--> <!--Telegram-->
<scene sceneID="A9d-DL-l8r"> <scene sceneID="A9d-DL-l8r">
<objects> <objects>
@ -2000,4 +1571,20 @@ contacts found.</string>
</scene> </scene>
</scenes> </scenes>
<color key="tintColor" red="0.15550534427165985" green="0.57037848234176636" blue="0.8720671534538269" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <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> </document>

View File

@ -38,7 +38,7 @@
+ (UIColor *)colorForUserId:(int32_t)userId myUserId:(int32_t)myUserId + (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 + (UIColor *)colorForGroupId:(int64_t)groupId

View File

@ -54,9 +54,11 @@ BAZEL_OPTIONS=(\
--swiftcopt=-j${CORE_COUNT_MINUS_ONE} \ --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')") BAZEL_OPTIONS=("${BAZEL_OPTIONS[@]}" --disk_cache="$(echo $BAZEL_CACHE_DIR | sed -e 's/[\/&]/\\&/g')")
fi fi
"$TULSI" -- \ "$TULSI" -- \
--verbose \ --verbose \

View File

@ -95,7 +95,7 @@ private final class ContextControllerContentSourceImpl: ContextControllerContent
} }
} }
public class ChatListControllerImpl: TelegramBaseController, ChatListController, UIViewControllerPreviewingDelegate { public class ChatListControllerImpl: TelegramBaseController, ChatListController {
private var validLayout: ContainerViewLayout? private var validLayout: ContainerViewLayout?
public let context: AccountContext public let context: AccountContext
@ -1780,111 +1780,6 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
(self.navigationController as? NavigationController)?.pushViewController(controller) (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] { public override var keyShortcuts: [KeyShortcut] {
let strings = self.presentationData.strings let strings = self.presentationData.strings

View File

@ -14,18 +14,6 @@ import SearchBarNode
import SearchUI import SearchUI
import ContextUI 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 { enum ChatListContainerNodeFilter: Equatable {
case all case all
case filter(ChatListFilter) case filter(ChatListFilter)
@ -1027,7 +1015,7 @@ final class ChatListControllerNode: ASDisplayNode {
super.init() super.init()
self.setViewBlock({ self.setViewBlock({
return ChatListControllerNodeView() return UITracingLayerView()
}) })
self.backgroundColor = presentationData.theme.chatList.backgroundColor self.backgroundColor = presentationData.theme.chatList.backgroundColor
@ -1056,8 +1044,6 @@ final class ChatListControllerNode: ASDisplayNode {
override func didLoad() { override func didLoad() {
super.didLoad() super.didLoad()
(self.view as? ChatListControllerNodeView)?.controller = self.controller
let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:))) let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:)))
self.tapRecognizer = tapRecognizer self.tapRecognizer = tapRecognizer
self.view.addGestureRecognizer(tapRecognizer) self.view.addGestureRecognizer(tapRecognizer)

View File

@ -628,9 +628,9 @@ final class ChatListFilterTabContainerNode: ASDisplayNode {
self.selectedLineNode.image = generateImage(CGSize(width: 5.0, height: 3.0), rotatedContext: { size, context in self.selectedLineNode.image = generateImage(CGSize(width: 5.0, height: 3.0), rotatedContext: { size, context in
context.clear(CGRect(origin: CGPoint(), size: size)) context.clear(CGRect(origin: CGPoint(), size: size))
context.setFillColor(presentationData.theme.list.itemAccentColor.cgColor) context.setFillColor(presentationData.theme.list.itemAccentColor.cgColor)
context.fillEllipse(in: CGRect(origin: CGPoint(), size: CGSize(width: 3.0, height: 3.0))) context.fillEllipse(in: CGRect(origin: CGPoint(), size: CGSize(width: 4.0, height: 4.0)))
context.fillEllipse(in: CGRect(origin: CGPoint(x: size.width - 3.0, y: 0.0), size: CGSize(width: 3.0, height: 3.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: 1.5, y: 0.0, width: size.width - 3.0, height: 3.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)) context.fill(CGRect(x: 0.0, y: 2.0, width: size.width, height: 2.0))
})?.resizableImage(withCapInsets: UIEdgeInsets(top: 3.0, left: 3.0, bottom: 0.0, right: 3.0), resizingMode: .stretch) })?.resizableImage(withCapInsets: UIEdgeInsets(top: 3.0, left: 3.0, bottom: 0.0, right: 3.0), resizingMode: .stretch)
} }

View File

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

View File

@ -795,7 +795,13 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
} }
embeddedState = embeddedStateValue embeddedState = embeddedStateValue
summaryInfo = summaryInfoValue summaryInfo = summaryInfoValue
inputActivities = inputActivitiesValue
if let peerPresence = peerPresence as? TelegramUserPresence, case .present = peerPresence.status {
inputActivities = inputActivitiesValue
} else {
inputActivities = nil
}
isPeerGroup = false isPeerGroup = false
promoInfo = promoInfoValue promoInfo = promoInfoValue
displayAsMessage = displayAsMessageValue displayAsMessage = displayAsMessageValue
@ -1320,6 +1326,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
var inputActivitiesSize: CGSize? var inputActivitiesSize: CGSize?
var inputActivitiesApply: (() -> Void)? var inputActivitiesApply: (() -> Void)?
if let inputActivities = inputActivities, !inputActivities.isEmpty { 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) 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 inputActivitiesSize = size
inputActivitiesApply = apply inputActivitiesApply = apply

View File

@ -396,80 +396,6 @@ public class ContactsController: ViewController {
self.contactsNode.containerLayoutUpdated(layout, navigationBarHeight: self.navigationInsetHeight, actualNavigationBarHeight: self.navigationHeight, transition: transition) 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() { private func activateSearch() {
if self.displayNavigationBar { if self.displayNavigationBar {
if let searchContentNode = self.searchContentNode { if let searchContentNode = self.searchContentNode {

View File

@ -15,18 +15,6 @@ import SearchUI
import AppBundle import AppBundle
import ContextUI 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 { private final class ContextControllerContentSourceImpl: ContextControllerContentSource {
let controller: ViewController let controller: ViewController
weak var sourceNode: ASDisplayNode? weak var sourceNode: ASDisplayNode?
@ -108,7 +96,7 @@ final class ContactsControllerNode: ASDisplayNode {
super.init() super.init()
self.setViewBlock({ self.setViewBlock({
return ContactsControllerNodeView() return UITracingLayerView()
}) })
self.backgroundColor = self.presentationData.theme.chatList.backgroundColor self.backgroundColor = self.presentationData.theme.chatList.backgroundColor
@ -150,12 +138,6 @@ final class ContactsControllerNode: ASDisplayNode {
self.presentationDataDisposable?.dispose() self.presentationDataDisposable?.dispose()
} }
override func didLoad() {
super.didLoad()
(self.view as? ContactsControllerNodeView)?.controller = self.controller
}
private func updateThemeAndStrings() { private func updateThemeAndStrings() {
self.backgroundColor = self.presentationData.theme.chatList.backgroundColor self.backgroundColor = self.presentationData.theme.chatList.backgroundColor
self.searchDisplayController?.updatePresentationData(self.presentationData) self.searchDisplayController?.updatePresentationData(self.presentationData)

View File

@ -9,9 +9,9 @@ public enum DeviceLocationMode: Int32 {
private final class DeviceLocationSubscriber { private final class DeviceLocationSubscriber {
let id: Int32 let id: Int32
let mode: DeviceLocationMode let mode: DeviceLocationMode
let update: (CLLocationCoordinate2D, Double, Double?) -> Void let update: (CLLocation, Double?) -> Void
init(id: Int32, mode: DeviceLocationMode, update: @escaping (CLLocationCoordinate2D, Double, Double?) -> Void) { init(id: Int32, mode: DeviceLocationMode, update: @escaping (CLLocation, Double?) -> Void) {
self.id = id self.id = id
self.mode = mode self.mode = mode
self.update = update self.update = update
@ -39,7 +39,7 @@ public final class DeviceLocationManager: NSObject {
private var subscribers: [DeviceLocationSubscriber] = [] private var subscribers: [DeviceLocationSubscriber] = []
private var currentTopMode: DeviceLocationMode? private var currentTopMode: DeviceLocationMode?
private var currentLocation: (CLLocationCoordinate2D, Double)? private var currentLocation: CLLocation?
private var currentHeading: CLHeading? private var currentHeading: CLHeading?
public init(queue: Queue, log: ((String) -> Void)? = nil) { public init(queue: Queue, log: ((String) -> Void)? = nil) {
@ -56,12 +56,12 @@ public final class DeviceLocationManager: NSObject {
} }
self.manager.delegate = self self.manager.delegate = self
self.manager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters self.manager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
self.manager.distanceFilter = 10.0 self.manager.distanceFilter = 5.0
self.manager.activityType = .other self.manager.activityType = .other
self.manager.pausesLocationUpdatesAutomatically = false self.manager.pausesLocationUpdatesAutomatically = false
} }
public func push(mode: DeviceLocationMode, updated: @escaping (CLLocationCoordinate2D, Double, Double?) -> Void) -> Disposable { public func push(mode: DeviceLocationMode, updated: @escaping (CLLocation, Double?) -> Void) -> Disposable {
assert(self.queue.isCurrent()) assert(self.queue.isCurrent())
let id = self.nextSubscriberId let id = self.nextSubscriberId
@ -69,7 +69,7 @@ public final class DeviceLocationManager: NSObject {
self.subscribers.append(DeviceLocationSubscriber(id: id, mode: mode, update: updated)) self.subscribers.append(DeviceLocationSubscriber(id: id, mode: mode, update: updated))
if let currentLocation = self.currentLocation { if let currentLocation = self.currentLocation {
updated(currentLocation.0, currentLocation.1, self.currentHeading?.magneticHeading) updated(currentLocation, self.currentHeading?.magneticHeading)
} }
self.updateTopMode() self.updateTopMode()
@ -125,9 +125,9 @@ extension DeviceLocationManager: CLLocationManagerDelegate {
if let location = locations.first { if let location = locations.first {
if self.currentTopMode != nil { if self.currentTopMode != nil {
self.currentLocation = (location.coordinate, location.horizontalAccuracy) self.currentLocation = location
for subscriber in self.subscribers { for subscriber in self.subscribers {
subscriber.update(location.coordinate, location.horizontalAccuracy, self.currentHeading?.magneticHeading) subscriber.update(location, self.currentHeading?.magneticHeading)
} }
} }
} }
@ -140,7 +140,7 @@ extension DeviceLocationManager: CLLocationManagerDelegate {
self.currentHeading = newHeading self.currentHeading = newHeading
if let currentLocation = self.currentLocation { if let currentLocation = self.currentLocation {
for subscriber in self.subscribers { for subscriber in self.subscribers {
subscriber.update(currentLocation.0, currentLocation.1, newHeading.magneticHeading) subscriber.update(currentLocation, newHeading.magneticHeading)
} }
} }
} }
@ -150,8 +150,8 @@ extension DeviceLocationManager: CLLocationManagerDelegate {
public func currentLocationManagerCoordinate(manager: DeviceLocationManager, timeout timeoutValue: Double) -> Signal<CLLocationCoordinate2D?, NoError> { public func currentLocationManagerCoordinate(manager: DeviceLocationManager, timeout timeoutValue: Double) -> Signal<CLLocationCoordinate2D?, NoError> {
return ( return (
Signal { subscriber in Signal { subscriber in
let disposable = manager.push(mode: .precise, updated: { coordinate, _, _ in let disposable = manager.push(mode: .precise, updated: { location, _ in
subscriber.putNext(coordinate) subscriber.putNext(location.coordinate)
subscriber.putCompletion() subscriber.putCompletion()
}) })
return disposable return disposable

View File

@ -12,6 +12,9 @@ public enum DeviceMetrics: CaseIterable, Equatable {
case iPhone6Plus case iPhone6Plus
case iPhoneX case iPhoneX
case iPhoneXSMax case iPhoneXSMax
case iPhone12Mini
case iPhone12
case iPhone12ProMax
case iPad case iPad
case iPadPro10Inch case iPadPro10Inch
case iPadPro11Inch case iPadPro11Inch
@ -27,6 +30,9 @@ public enum DeviceMetrics: CaseIterable, Equatable {
.iPhone6Plus, .iPhone6Plus,
.iPhoneX, .iPhoneX,
.iPhoneXSMax, .iPhoneXSMax,
.iPhone12Mini,
.iPhone12,
.iPhone12ProMax,
.iPad, .iPad,
.iPadPro10Inch, .iPadPro10Inch,
.iPadPro11Inch, .iPadPro11Inch,
@ -89,6 +95,12 @@ public enum DeviceMetrics: CaseIterable, Equatable {
return CGSize(width: 375.0, height: 812.0) return CGSize(width: 375.0, height: 812.0)
case .iPhoneXSMax: case .iPhoneXSMax:
return CGSize(width: 414.0, height: 896.0) 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: case .iPad:
return CGSize(width: 768.0, height: 1024.0) return CGSize(width: 768.0, height: 1024.0)
case .iPadPro10Inch: case .iPadPro10Inch:
@ -104,7 +116,7 @@ public enum DeviceMetrics: CaseIterable, Equatable {
func safeInsets(inLandscape: Bool) -> UIEdgeInsets { func safeInsets(inLandscape: Bool) -> UIEdgeInsets {
switch self { 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) 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: default:
return UIEdgeInsets.zero return UIEdgeInsets.zero
@ -113,7 +125,7 @@ public enum DeviceMetrics: CaseIterable, Equatable {
func onScreenNavigationHeight(inLandscape: Bool, systemOnScreenNavigationHeight: CGFloat?) -> CGFloat? { func onScreenNavigationHeight(inLandscape: Bool, systemOnScreenNavigationHeight: CGFloat?) -> CGFloat? {
switch self { switch self {
case .iPhoneX, .iPhoneXSMax: case .iPhoneX, .iPhoneXSMax, .iPhone12Mini, .iPhone12, .iPhone12ProMax:
return inLandscape ? 21.0 : 34.0 return inLandscape ? 21.0 : 34.0
case .iPadPro3rdGen, .iPadPro11Inch: case .iPadPro3rdGen, .iPadPro11Inch:
return 21.0 return 21.0
@ -146,7 +158,7 @@ public enum DeviceMetrics: CaseIterable, Equatable {
var statusBarHeight: CGFloat { var statusBarHeight: CGFloat {
switch self { switch self {
case .iPhoneX, .iPhoneXSMax: case .iPhoneX, .iPhoneXSMax, .iPhone12Mini, .iPhone12, .iPhone12ProMax:
return 44.0 return 44.0
case .iPadPro11Inch, .iPadPro3rdGen: case .iPadPro11Inch, .iPadPro3rdGen:
return 24.0 return 24.0
@ -164,7 +176,7 @@ public enum DeviceMetrics: CaseIterable, Equatable {
return 162.0 return 162.0
case .iPhone6, .iPhone6Plus: case .iPhone6, .iPhone6Plus:
return 163.0 return 163.0
case .iPhoneX, .iPhoneXSMax: case .iPhoneX, .iPhoneXSMax, .iPhone12Mini, .iPhone12, .iPhone12ProMax:
return 172.0 return 172.0
case .iPad, .iPadPro10Inch: case .iPad, .iPadPro10Inch:
return 348.0 return 348.0
@ -183,9 +195,9 @@ public enum DeviceMetrics: CaseIterable, Equatable {
return 216.0 return 216.0
case .iPhone6Plus: case .iPhone6Plus:
return 226.0 return 226.0
case .iPhoneX: case .iPhoneX, .iPhone12Mini, .iPhone12:
return 291.0 return 291.0
case .iPhoneXSMax: case .iPhoneXSMax, .iPhone12ProMax:
return 302.0 return 302.0
case .iPad, .iPadPro10Inch: case .iPad, .iPadPro10Inch:
return 263.0 return 263.0
@ -204,7 +216,7 @@ public enum DeviceMetrics: CaseIterable, Equatable {
func predictiveInputHeight(inLandscape: Bool) -> CGFloat { func predictiveInputHeight(inLandscape: Bool) -> CGFloat {
if inLandscape { if inLandscape {
switch self { switch self {
case .iPhone4, .iPhone5, .iPhone6, .iPhone6Plus, .iPhoneX, .iPhoneXSMax: case .iPhone4, .iPhone5, .iPhone6, .iPhone6Plus, .iPhoneX, .iPhoneXSMax, .iPhone12Mini, .iPhone12, .iPhone12ProMax:
return 37.0 return 37.0
case .iPad, .iPadPro10Inch, .iPadPro11Inch, .iPadPro, .iPadPro3rdGen: case .iPad, .iPadPro10Inch, .iPadPro11Inch, .iPadPro, .iPadPro3rdGen:
return 50.0 return 50.0
@ -215,7 +227,7 @@ public enum DeviceMetrics: CaseIterable, Equatable {
switch self { switch self {
case .iPhone4, .iPhone5: case .iPhone4, .iPhone5:
return 37.0 return 37.0
case .iPhone6, .iPhoneX, .iPhoneXSMax: case .iPhone6, .iPhoneX, .iPhoneXSMax, .iPhone12Mini, .iPhone12, .iPhone12ProMax:
return 44.0 return 44.0
case .iPhone6Plus: case .iPhone6Plus:
return 45.0 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 { public var hasTopNotch: Bool {
switch self { switch self {
case .iPhoneX, .iPhoneXSMax: case .iPhoneX, .iPhoneXSMax, .iPhone12Mini, .iPhone12, .iPhone12ProMax:
return true return true
default: default:
return false return false

View File

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

View File

@ -45,6 +45,9 @@ public class TwoAxisStepBarsChartController: BaseLinesChartController {
private var prevoiusHorizontalStrideInterval: Int = 1 private var prevoiusHorizontalStrideInterval: Int = 1
public var hourly: Bool = false
public var min5: Bool = false
override public init(chartsCollection: ChartsCollection) { override public init(chartsCollection: ChartsCollection) {
self.initialChartCollection = chartsCollection self.initialChartCollection = chartsCollection
graphControllers = chartsCollection.chartValues.map { _ in GraphController() } graphControllers = chartsCollection.chartValues.map { _ in GraphController() }
@ -252,8 +255,19 @@ public class TwoAxisStepBarsChartController: BaseLinesChartController {
} }
func updateHorizontalLimits(horizontalRange: ClosedRange<CGFloat>, animated: Bool) { func updateHorizontalLimits(horizontalRange: ClosedRange<CGFloat>, animated: Bool) {
var scaleType: ChartScaleType = .day
if isZoomed {
scaleType = .minutes5
} else {
if self.hourly {
scaleType = .hour
} else if self.min5 {
scaleType = .minutes5
}
}
if let (stride, labels) = horizontalLimitsLabels(horizontalRange: horizontalRange, if let (stride, labels) = horizontalLimitsLabels(horizontalRange: horizontalRange,
scaleType: isZoomed ? .minutes5 : .day, scaleType: scaleType,
prevoiusHorizontalStrideInterval: prevoiusHorizontalStrideInterval) { prevoiusHorizontalStrideInterval: prevoiusHorizontalStrideInterval) {
self.horizontalScalesRenderer.setup(labels: labels, animated: animated) self.horizontalScalesRenderer.setup(labels: labels, animated: animated)
self.prevoiusHorizontalStrideInterval = stride self.prevoiusHorizontalStrideInterval = stride

View File

@ -16,6 +16,8 @@ public enum ChartType {
case step case step
case twoAxisStep case twoAxisStep
case hourlyStep case hourlyStep
case twoAxisHourlyStep
case twoAxis5MinStep
} }
public extension ChartTheme { public extension ChartTheme {
@ -90,6 +92,16 @@ public func createChartController(_ data: String, type: ChartType, getDetailsDat
case .hourlyStep: case .hourlyStep:
controller = StepBarsChartController(chartsCollection: collection, hourly: true) controller = StepBarsChartController(chartsCollection: collection, hourly: true)
controller.isZoomable = false controller.isZoomable = false
case .twoAxisHourlyStep:
let stepController = TwoAxisStepBarsChartController(chartsCollection: collection)
stepController.hourly = true
controller = stepController
controller.isZoomable = false
case .twoAxis5MinStep:
let stepController = TwoAxisStepBarsChartController(chartsCollection: collection)
stepController.min5 = true
controller = stepController
controller.isZoomable = false
} }
controller.getDetailsData = { date, completion in controller.getDetailsData = { date, completion in
getDetailsData(date, { detailsData in getDetailsData(date, { detailsData in

View File

@ -224,9 +224,6 @@ open class ItemListController: ViewController, KeyShortcutResponder, Presentable
} }
} }
public var previewItemWithTag: ((ItemListItemTag) -> UIViewController?)?
public var commitPreview: ((UIViewController) -> Void)?
public var willDisappear: ((Bool) -> Void)? public var willDisappear: ((Bool) -> Void)?
public var didDisappear: ((Bool) -> Void)? public var didDisappear: ((Bool) -> Void)?
@ -557,52 +554,11 @@ open class ItemListController: ViewController, KeyShortcutResponder, Presentable
public func afterLayout(_ f: @escaping () -> Void) { public func afterLayout(_ f: @escaping () -> Void) {
(self.displayNode as! ItemListControllerNode).afterLayout(f) (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) { public func clearItemNodesHighlight(animated: Bool = false) {
(self.displayNode as! ItemListControllerNode).listNode.clearHighlightAnimated(animated) (self.displayNode as! ItemListControllerNode).listNode.clearHighlightAnimated(animated)
} }
public func previewingCommit(_ viewControllerToCommit: UIViewController) {
self.commitPreview?(viewControllerToCommit)
}
public var keyShortcuts: [KeyShortcut] { public var keyShortcuts: [KeyShortcut] {
return [KeyShortcut(input: UIKeyCommand.inputEscape, action: { [weak self] in return [KeyShortcut(input: UIKeyCommand.inputEscape, action: { [weak self] in
if !(self?.navigationController?.topViewController is TabBarController) { if !(self?.navigationController?.topViewController is TabBarController) {

View File

@ -138,7 +138,7 @@ public final class ItemListNodeVisibleEntries: Sequence {
} }
} }
public final class ItemListControllerNodeView: UITracingLayerView, PreviewingHostView { public final class ItemListControllerNodeView: UITracingLayerView {
var onLayout: (() -> Void)? var onLayout: (() -> Void)?
init(controller: ItemListController?) { 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? weak var controller: ItemListController?
} }

View File

@ -107,9 +107,13 @@ public final class LiveLocationManagerImpl: LiveLocationManager {
if let strongSelf = self { if let strongSelf = self {
if value { if value {
let queue = strongSelf.queue let queue = strongSelf.queue
strongSelf.deviceLocationDisposable.set(strongSelf.locationManager.push(mode: .precise, updated: { coordinate, accuracyRadius, heading in strongSelf.deviceLocationDisposable.set(strongSelf.locationManager.push(mode: .precise, updated: { location, heading in
queue.async { queue.async {
self?.updateDeviceCoordinate(coordinate, accuracyRadius: accuracyRadius, heading: heading) var effectiveHeading = heading ?? location.course
if location.speed > 1.0 {
effectiveHeading = location.course
}
self?.updateDeviceCoordinate(location.coordinate, accuracyRadius: location.horizontalAccuracy, heading: effectiveHeading)
} }
})) }))
} else { } else {
@ -213,7 +217,7 @@ public final class LiveLocationManagerImpl: LiveLocationManager {
let ids = self.broadcastToMessageIds let ids = self.broadcastToMessageIds
let remainingIds = Atomic<Set<MessageId>>(value: Set(ids.keys)) let remainingIds = Atomic<Set<MessageId>>(value: Set(ids.keys))
for id in ids.keys { for id in ids.keys {
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) 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: heading.flatMap { Int32($0) }, proximityNotificationRadius: nil)
|> deliverOn(self.queue)).start(completed: { [weak self] in |> deliverOn(self.queue)).start(completed: { [weak self] in
if let strongSelf = self { if let strongSelf = self {
strongSelf.editMessageDisposables.set(nil, forKey: id) strongSelf.editMessageDisposables.set(nil, forKey: id)

View File

@ -105,7 +105,7 @@ final class LocationActionListItem: ListViewItem {
async { async {
let node = LocationActionListItemNode() let node = LocationActionListItemNode()
let makeLayout = node.asyncLayout() 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.contentSize = nodeLayout.contentSize
node.insets = nodeLayout.insets node.insets = nodeLayout.insets
@ -118,7 +118,7 @@ final class LocationActionListItem: ListViewItem {
if let nodeValue = node() as? LocationActionListItemNode { if let nodeValue = node() as? LocationActionListItemNode {
let layout = nodeValue.asyncLayout() let layout = nodeValue.asyncLayout()
async { 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 { Queue.mainQueue().async {
completion(nodeLayout, { info in completion(nodeLayout, { info in
apply().1(info) apply().1(info)

View File

@ -96,7 +96,9 @@ class LocationPinAnnotation: NSObject, MKAnnotation {
} }
var id: String { 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())" return "\(peer.id.toInt64())"
} else if let venueId = self.location?.venue?.id { } else if let venueId = self.location?.venue?.id {
return venueId return venueId
@ -257,8 +259,8 @@ class LocationPinAnnotationView: MKAnnotationView {
self.dotNode.isHidden = false self.dotNode.isHidden = false
self.backgroundNode.image = UIImage(bundleImageName: "Location/PinBackground") self.backgroundNode.image = UIImage(bundleImageName: "Location/PinBackground")
if let author = message.author, let peer = message.peers[author.id] { if let author = message.author {
self.setPeer(context: annotation.context, theme: annotation.theme, peer: peer) self.setPeer(context: annotation.context, theme: annotation.theme, peer: author)
} else if let selfPeer = annotation.selfPeer { } else if let selfPeer = annotation.selfPeer {
self.setPeer(context: annotation.context, theme: annotation.theme, peer: selfPeer) self.setPeer(context: annotation.context, theme: annotation.theme, peer: selfPeer)
} }

View File

@ -338,8 +338,14 @@ class LocationDistancePickerScreenNode: ViewControllerTracingNode, UIScrollViewD
private func updateDoneButtonTitle() { private func updateDoneButtonTitle() {
if let pickerView = self.pickerView { if let pickerView = self.pickerView {
let largeValue = unitValues[pickerView.selectedRow(inComponent: 0)] let selectedLargeRow = pickerView.selectedRow(inComponent: 0)
let smallValue = smallUnitValues[pickerView.selectedRow(inComponent: 1)] 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 var value = largeValue * 1000 + smallValue * 10
if !self.usesMetricSystem() { if !self.usesMetricSystem() {
@ -353,7 +359,7 @@ class LocationDistancePickerScreenNode: ViewControllerTracingNode, UIScrollViewD
self.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .immediate) 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.alpha = 0.0
self.doneButton.isUserInteractionEnabled = false self.doneButton.isUserInteractionEnabled = false
self.textNode.alpha = 1.0 self.textNode.alpha = 1.0

View File

@ -6,7 +6,10 @@ import Display
import SwiftSignalKit import SwiftSignalKit
import TelegramCore import TelegramCore
import SyncCore import SyncCore
import AccountContext
import TelegramPresentationData import TelegramPresentationData
import TelegramUIPreferences
import TelegramStringFormatting
import ItemListUI import ItemListUI
import LocationResources import LocationResources
import AppBundle import AppBundle
@ -15,15 +18,19 @@ import LiveLocationTimerNode
final class LocationLiveListItem: ListViewItem { final class LocationLiveListItem: ListViewItem {
let presentationData: ItemListPresentationData let presentationData: ItemListPresentationData
let account: Account let dateTimeFormat: PresentationDateTimeFormat
let nameDisplayOrder: PresentationPersonNameOrder
let context: AccountContext
let message: Message let message: Message
let distance: Double? let distance: Double?
let action: () -> Void let action: () -> Void
let longTapAction: () -> Void let longTapAction: () -> Void
public init(presentationData: ItemListPresentationData, account: Account, message: Message, distance: Double?, action: @escaping () -> Void, longTapAction: @escaping () -> 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.presentationData = presentationData
self.account = account self.dateTimeFormat = dateTimeFormat
self.nameDisplayOrder = nameDisplayOrder
self.context = context
self.message = message self.message = message
self.distance = distance self.distance = distance
self.action = action self.action = action
@ -92,6 +99,7 @@ final class LocationLiveListItemNode: ListViewItemNode {
self.highlightedBackgroundNode.isLayerBacked = true self.highlightedBackgroundNode.isLayerBacked = true
self.avatarNode = AvatarNode(font: avatarFont) self.avatarNode = AvatarNode(font: avatarFont)
self.avatarNode.isLayerBacked = !smartInvertColorsEnabled()
super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false) super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false)
@ -146,20 +154,39 @@ final class LocationLiveListItemNode: ListViewItemNode {
let leftInset: CGFloat = 65.0 + params.leftInset let leftInset: CGFloat = 65.0 + params.leftInset
let rightInset: CGFloat = params.rightInset let rightInset: CGFloat = params.rightInset
let verticalInset: CGFloat = 8.0 let verticalInset: CGFloat = 8.0
let iconSize: CGFloat = 40.0
let titleFont = Font.medium(item.presentationData.fontSize.itemListBaseFontSize) let titleFont = Font.medium(item.presentationData.fontSize.itemListBaseFontSize)
let subtitleFont = Font.regular(floor(item.presentationData.fontSize.itemListBaseFontSize * 14.0 / 17.0)) let subtitleFont = Font.regular(floor(item.presentationData.fontSize.itemListBaseFontSize * 14.0 / 17.0))
let titleAttributedString = NSAttributedString(string: "title", font: titleFont, textColor: item.presentationData.theme.list.itemPrimaryTextColor) var title: String = ""
let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: titleAttributedString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - rightInset - 15.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) 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()))
let subtitleAttributedString = NSAttributedString(string: "subtitle", font: subtitleFont, textColor: item.presentationData.theme.list.itemSecondaryTextColor) var updateTimestamp = item.message.timestamp
let (subtitleLayout, subtitleApply) = makeSubtitleLayout(TextNodeLayoutArguments(attributedString: subtitleAttributedString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - rightInset - 15.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) 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 titleSpacing: CGFloat = 1.0
let bottomInset: CGFloat = hasSeparator ? 0.0 : 4.0 let contentSize = CGSize(width: params.width, height: verticalInset * 2.0 + titleLayout.size.height + titleSpacing + subtitleLayout.size.height)
let contentSize = CGSize(width: params.width, height: verticalInset * 2.0 + titleLayout.size.height + titleSpacing + subtitleLayout.size.height + bottomInset)
let nodeLayout = ListViewItemNodeLayout(contentSize: contentSize, insets: UIEdgeInsets()) let nodeLayout = ListViewItemNodeLayout(contentSize: contentSize, insets: UIEdgeInsets())
return (nodeLayout, { [weak self] in return (nodeLayout, { [weak self] in
@ -168,7 +195,7 @@ final class LocationLiveListItemNode: ListViewItemNode {
updatedTheme = item.presentationData.theme updatedTheme = item.presentationData.theme
} }
return (nil, { _ in return (self?.avatarNode.ready, { _ in
if let strongSelf = self { if let strongSelf = self {
strongSelf.item = item strongSelf.item = item
strongSelf.layoutParams = params strongSelf.layoutParams = params
@ -199,32 +226,41 @@ final class LocationLiveListItemNode: ListViewItemNode {
let separatorHeight = UIScreenPixel let separatorHeight = UIScreenPixel
let topHighlightInset: CGFloat = separatorHeight let topHighlightInset: CGFloat = separatorHeight
let avatarSize: CGFloat = 40.0
// let iconNodeFrame = CGRect(origin: CGPoint(x: params.leftInset + 15.0, y: floorToScreenPixels((contentSize.height - bottomInset - iconSize) / 2.0)), size: CGSize(width: iconSize, height: iconSize)) if let peer = item.message.author {
// strongSelf.iconNode.frame = iconNodeFrame strongSelf.avatarNode.setPeer(context: item.context, theme: item.presentationData.theme, peer: peer, overrideImage: nil, emptyColor: item.presentationData.theme.list.mediaPlaceholderColor, synchronousLoad: false)
// strongSelf.venueIconNode.frame = iconNodeFrame }
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.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.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.frame = CGRect(origin: CGPoint(x: leftInset, y: nodeLayout.contentSize.height - separatorHeight), size: CGSize(width: nodeLayout.size.width, height: separatorHeight))
strongSelf.separatorNode.isHidden = !hasSeparator strongSelf.separatorNode.isHidden = !hasSeparator
// if let (beginTimestamp, timeout) = item.beginTimeAndTimeout { var liveBroadcastingTimeout: Int32 = 0
// let timerNode: ChatMessageLiveLocationTimerNode if let location = getLocation(from: item.message), let timeout = location.liveBroadcastingTimeout {
// if let current = strongSelf.timerNode { liveBroadcastingTimeout = timeout
// timerNode = current }
// } else {
// timerNode = ChatMessageLiveLocationTimerNode() let currentTimestamp = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)
// strongSelf.addSubnode(timerNode) if currentTimestamp < item.message.timestamp + liveBroadcastingTimeout {
// strongSelf.timerNode = timerNode let timerNode: ChatMessageLiveLocationTimerNode
// } if let current = strongSelf.timerNode {
// let timerSize = CGSize(width: 28.0, height: 28.0) timerNode = current
// timerNode.update(backgroundColor: item.presentationData.theme.list.itemAccentColor.withAlphaComponent(0.4), foregroundColor: item.presentationData.theme.list.itemAccentColor, textColor: item.presentationData.theme.list.itemAccentColor, beginTimestamp: beginTimestamp, timeout: timeout, strings: item.presentationData.strings) } else {
// timerNode.frame = CGRect(origin: CGPoint(x: contentSize.width - 16.0 - timerSize.width, y: floorToScreenPixels((contentSize.height - timerSize.height) / 2.0) - 2.0), size: timerSize) timerNode = ChatMessageLiveLocationTimerNode()
// } else if let timerNode = strongSelf.timerNode { strongSelf.addSubnode(timerNode)
// strongSelf.timerNode = nil strongSelf.timerNode = timerNode
// timerNode.removeFromSupernode() }
// } 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()
}
} }
}) })
}) })

View File

@ -548,7 +548,7 @@ final class LocationMapNode: ASDisplayNode, MKMapViewDelegate {
subscriber.putCompletion() subscriber.putCompletion()
return EmptyDisposable 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) return combineLatest(self.userLocation, annotationsPoll)
|> map { userLocation, annotations -> [Double] in |> map { userLocation, annotations -> [Double] in
@ -758,7 +758,7 @@ final class LocationMapNode: ASDisplayNode, MKMapViewDelegate {
} }
if let zoomRect = zoomRect { if let zoomRect = zoomRect {
let insets = UIEdgeInsets(top: 0.0, left: 80.0, bottom: 0.0, right: 80.0) let insets = UIEdgeInsets(top: 88.0, left: 80.0, bottom: 160.0, right: 80.0)
let fittedZoomRect = mapView.mapRectThatFits(zoomRect, edgePadding: insets) let fittedZoomRect = mapView.mapRectThatFits(zoomRect, edgePadding: insets)
mapView.setVisibleMapRect(fittedZoomRect, animated: animated) mapView.setVisibleMapRect(fittedZoomRect, animated: animated)
} }

View File

@ -884,6 +884,7 @@ final class LocationPickerControllerNode: ViewControllerTracingNode, CLLocationM
let optionsFrame = CGRect(x: 0.0, y: optionsOffset, width: layout.size.width, height: optionsHeight) let optionsFrame = CGRect(x: 0.0, y: optionsOffset, width: layout.size.width, height: optionsHeight)
transition.updateFrame(node: self.optionsNode, frame: optionsFrame) 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.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 { if let searchContainerNode = self.searchContainerNode {
searchContainerNode.frame = CGRect(origin: CGPoint(), size: layout.size) searchContainerNode.frame = CGRect(origin: CGPoint(), size: layout.size)

View File

@ -11,6 +11,7 @@ import ItemListUI
import ItemListVenueItem import ItemListVenueItem
import TelegramPresentationData import TelegramPresentationData
import TelegramStringFormatting import TelegramStringFormatting
import TelegramUIPreferences
import TelegramNotices import TelegramNotices
import AccountContext import AccountContext
import AppBundle import AppBundle
@ -48,7 +49,7 @@ private enum LocationViewEntryId: Hashable {
private enum LocationViewEntry: Comparable, Identifiable { private enum LocationViewEntry: Comparable, Identifiable {
case info(PresentationTheme, TelegramMediaMap, String?, Double?, Double?) case info(PresentationTheme, TelegramMediaMap, String?, Double?, Double?)
case toggleLiveLocation(PresentationTheme, String, String, CLLocationCoordinate2D?, 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 { var stableId: LocationViewEntryId {
switch self { switch self {
@ -56,7 +57,7 @@ private enum LocationViewEntry: Comparable, Identifiable {
return .info return .info
case .toggleLiveLocation: case .toggleLiveLocation:
return .toggleLiveLocation return .toggleLiveLocation
case let .liveLocation(_, message, _, _): case let .liveLocation(_, _, _, message, _, _):
return .liveLocation(message.stableId) return .liveLocation(message.stableId)
} }
} }
@ -75,8 +76,8 @@ private enum LocationViewEntry: Comparable, Identifiable {
} else { } else {
return false return false
} }
case let .liveLocation(lhsTheme, lhsMessage, lhsDistance, lhsIndex): case let .liveLocation(lhsTheme, lhsDateTimeFormat, lhsNameDisplayOrder, lhsMessage, lhsDistance, lhsIndex):
if case let .liveLocation(rhsTheme, rhsMessage, rhsDistance, rhsIndex) = rhs, lhsTheme === rhsTheme, areMessagesEqual(lhsMessage, rhsMessage), lhsDistance == rhsDistance, lhsIndex == rhsIndex { 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 return true
} else { } else {
return false return false
@ -100,17 +101,17 @@ private enum LocationViewEntry: Comparable, Identifiable {
case .liveLocation: case .liveLocation:
return true return true
} }
case let .liveLocation(_, _, _, lhsIndex): case let .liveLocation(_, _, _, _, _, lhsIndex):
switch rhs { switch rhs {
case .info, .toggleLiveLocation: case .info, .toggleLiveLocation:
return false return false
case let .liveLocation(_, _, _, rhsIndex): case let .liveLocation(_, _, _, _, _, rhsIndex):
return lhsIndex < rhsIndex return lhsIndex < rhsIndex
} }
} }
} }
func item(account: Account, presentationData: PresentationData, interaction: LocationViewInteraction?) -> ListViewItem { func item(context: AccountContext, presentationData: PresentationData, interaction: LocationViewInteraction?) -> ListViewItem {
switch self { switch self {
case let .info(_, location, address, distance, time): case let .info(_, location, address, distance, time):
let addressString: String? let addressString: String?
@ -126,7 +127,7 @@ private enum LocationViewEntry: Comparable, Identifiable {
distanceString = nil distanceString = nil
} }
let eta = time.flatMap { stringForEstimatedDuration(strings: presentationData.strings, eta: $0) } 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) interaction?.goToCoordinate(location.coordinate)
}, getDirections: { }, getDirections: {
interaction?.requestDirections() interaction?.requestDirections()
@ -138,7 +139,7 @@ private enum LocationViewEntry: Comparable, Identifiable {
} else { } else {
beginTimeAndTimeout = nil 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 { if beginTimeAndTimeout != nil {
interaction?.stopLiveLocation() interaction?.stopLiveLocation()
} else if let coordinate = coordinate { } else if let coordinate = coordinate {
@ -147,24 +148,18 @@ private enum LocationViewEntry: Comparable, Identifiable {
}, highlighted: { highlight in }, highlighted: { highlight in
interaction?.updateSendActionHighlight(highlight) interaction?.updateSendActionHighlight(highlight)
}) })
case let .liveLocation(_, message, distance, _): case let .liveLocation(_, dateTimeFormat, nameDisplayOrder, message, distance, _):
let distanceString: String? return LocationLiveListItem(presentationData: ItemListPresentationData(presentationData), dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, context: context, message: message, distance: distance, action: {}, longTapAction: {})
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 LocationLiveListItem(presentationData: ItemListPresentationData(presentationData), account: account, 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 (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries)
let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) } 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 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(account: account, 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) return LocationViewTransaction(deletions: deletions, insertions: insertions, updates: updates)
} }
@ -207,7 +202,6 @@ final class LocationViewControllerNode: ViewControllerTracingNode, CLLocationMan
private var disposable: Disposable? private var disposable: Disposable?
private var state: LocationViewState private var state: LocationViewState
private let statePromise: Promise<LocationViewState> private let statePromise: Promise<LocationViewState>
private var geocodingDisposable = MetaDisposable()
private var validLayout: (layout: ContainerViewLayout, navigationHeight: CGFloat)? private var validLayout: (layout: ContainerViewLayout, navigationHeight: CGFloat)?
private var listOffset: CGFloat? private var listOffset: CGFloat?
@ -276,7 +270,7 @@ final class LocationViewControllerNode: ViewControllerTracingNode, CLLocationMan
return messages return messages
} }
setupProximityNotificationImpl = { reset in setupProximityNotificationImpl = { [weak self] reset in
let _ = (liveLocations let _ = (liveLocations
|> take(1) |> take(1)
|> deliverOnMainQueue).start(next: { [weak self] messages in |> deliverOnMainQueue).start(next: { [weak self] messages in
@ -369,7 +363,10 @@ final class LocationViewControllerNode: ViewControllerTracingNode, CLLocationMan
timeout = nil timeout = nil
} }
entries.append(.toggleLiveLocation(presentationData.theme, title, subtitle, userLocation?.coordinate, beginTime, timeout)) if let channel = subject.author as? TelegramChannel, case .broadcast = channel.info, activeOwnLiveLocation == nil {
} else {
entries.append(.toggleLiveLocation(presentationData.theme, title, subtitle, userLocation?.coordinate, beginTime, timeout))
}
var sortedLiveLocations: [Message] = [] var sortedLiveLocations: [Message] = []
@ -390,6 +387,10 @@ final class LocationViewControllerNode: ViewControllerTracingNode, CLLocationMan
} }
for message in effectiveLiveLocations { for message in effectiveLiveLocations {
if let channel = message.peers[message.id.peerId] as? TelegramChannel, case .broadcast = channel.info, message.threadId != nil {
continue
}
var liveBroadcastingTimeout: Int32 = 0 var liveBroadcastingTimeout: Int32 = 0
if let location = getLocation(from: message), let timeout = location.liveBroadcastingTimeout { if let location = getLocation(from: message), let timeout = location.liveBroadcastingTimeout {
liveBroadcastingTimeout = timeout liveBroadcastingTimeout = timeout
@ -401,12 +402,12 @@ final class LocationViewControllerNode: ViewControllerTracingNode, CLLocationMan
let subjectLocation = CLLocation(latitude: location.latitude, longitude: location.longitude) let subjectLocation = CLLocation(latitude: location.latitude, longitude: location.longitude)
let distance = userLocation.flatMap { subjectLocation.distance(from: $0) } let distance = userLocation.flatMap { subjectLocation.distance(from: $0) }
// entries.append(.liveLocation(presentationData.theme, message, distance, index))
if message.localTags.contains(.OutgoingLiveLocation), let selfPeer = selfPeer { if message.localTags.contains(.OutgoingLiveLocation), let selfPeer = selfPeer {
userAnnotation = LocationPinAnnotation(context: context, theme: presentationData.theme, message: message, selfPeer: selfPeer, heading: location.heading) userAnnotation = LocationPinAnnotation(context: context, theme: presentationData.theme, message: message, selfPeer: selfPeer, heading: location.heading)
} else { } else {
annotations.append(LocationPinAnnotation(context: context, theme: presentationData.theme, message: message, selfPeer: selfPeer, heading: location.heading)) 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 index += 1
} }
@ -414,11 +415,14 @@ final class LocationViewControllerNode: ViewControllerTracingNode, CLLocationMan
if subject.id.peerId.namespace != Namespaces.Peer.CloudUser, proximityNotification == nil { if subject.id.peerId.namespace != Namespaces.Peer.CloudUser, proximityNotification == nil {
proximityNotification = false proximityNotification = false
} }
if let channel = subject.author as? TelegramChannel, case .broadcast = channel.info {
proximityNotification = nil
}
let previousEntries = previousEntries.swap(entries) let previousEntries = previousEntries.swap(entries)
let previousState = previousState.swap(state) 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.enqueueTransition(transition)
strongSelf.headerNode.updateState(mapMode: state.mapMode, trackingMode: state.trackingMode, 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)
@ -535,7 +539,6 @@ final class LocationViewControllerNode: ViewControllerTracingNode, CLLocationMan
deinit { deinit {
self.disposable?.dispose() self.disposable?.dispose()
self.geocodingDisposable.dispose()
self.locationManager.manager.stopUpdatingHeading() self.locationManager.manager.stopUpdatingHeading()
} }
@ -692,5 +695,6 @@ final class LocationViewControllerNode: ViewControllerTracingNode, CLLocationMan
let optionsFrame = CGRect(x: 0.0, y: optionsOffset, width: layout.size.width, height: optionsHeight) let optionsFrame = CGRect(x: 0.0, y: optionsOffset, width: layout.size.width, height: optionsHeight)
transition.updateFrame(node: self.optionsNode, frame: optionsFrame) 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.updateLayout(size: optionsFrame.size, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, transition: transition)
self.optionsNode.isUserInteractionEnabled = self.state.displayingMapModeOptions
} }
} }

View File

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

View File

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

View File

@ -591,8 +591,8 @@ private final class SemanticStatusNodeAppearanceContext {
self.cutout = cutout self.cutout = cutout
} }
func drawingState(transitionFraction: CGFloat) -> SemanticStatusNodeAppearanceDrawingState { func drawingState(backgroundTransitionFraction: CGFloat, foregroundTransitionFraction: CGFloat) -> SemanticStatusNodeAppearanceDrawingState {
return SemanticStatusNodeAppearanceDrawingState(transitionFraction: transitionFraction, background: self.background, foreground: self.foreground, backgroundImage: self.backgroundImage, overlayForeground: self.overlayForeground, cutout: self.cutout) 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 { func withUpdatedBackground(_ background: UIColor) -> SemanticStatusNodeAppearanceContext {
@ -617,7 +617,8 @@ private final class SemanticStatusNodeAppearanceContext {
} }
private final class SemanticStatusNodeAppearanceDrawingState { private final class SemanticStatusNodeAppearanceDrawingState {
let transitionFraction: CGFloat let backgroundTransitionFraction: CGFloat
let foregroundTransitionFraction: CGFloat
let background: UIColor let background: UIColor
let foreground: UIColor let foreground: UIColor
let backgroundImage: UIImage? let backgroundImage: UIImage?
@ -632,8 +633,9 @@ private final class SemanticStatusNodeAppearanceDrawingState {
} }
} }
init(transitionFraction: CGFloat, background: UIColor, foreground: UIColor, backgroundImage: UIImage?, overlayForeground: UIColor?, cutout: CGRect?) { init(backgroundTransitionFraction: CGFloat, foregroundTransitionFraction: CGFloat, background: UIColor, foreground: UIColor, backgroundImage: UIImage?, overlayForeground: UIColor?, cutout: CGRect?) {
self.transitionFraction = transitionFraction self.backgroundTransitionFraction = backgroundTransitionFraction
self.foregroundTransitionFraction = foregroundTransitionFraction
self.background = background self.background = background
self.foreground = foreground self.foreground = foreground
self.backgroundImage = backgroundImage self.backgroundImage = backgroundImage
@ -644,11 +646,12 @@ private final class SemanticStatusNodeAppearanceDrawingState {
func drawBackground(context: CGContext, size: CGSize) { func drawBackground(context: CGContext, size: CGSize) {
let bounds = CGRect(origin: CGPoint(), size: size) let bounds = CGRect(origin: CGPoint(), size: size)
context.setBlendMode(.normal)
if let backgroundImage = self.backgroundImage?.cgImage { if let backgroundImage = self.backgroundImage?.cgImage {
context.saveGState() context.saveGState()
context.translateBy(x: 0.0, y: bounds.height) context.translateBy(x: 0.0, y: bounds.height)
context.scaleBy(x: 1.0, y: -1.0) context.scaleBy(x: 1.0, y: -1.0)
context.setAlpha(self.transitionFraction) context.setAlpha(self.backgroundTransitionFraction)
context.draw(backgroundImage, in: bounds) context.draw(backgroundImage, in: bounds)
context.restoreGState() context.restoreGState()
} else { } else {
@ -659,7 +662,7 @@ private final class SemanticStatusNodeAppearanceDrawingState {
func drawForeground(context: CGContext, size: CGSize) { func drawForeground(context: CGContext, size: CGSize) {
if let cutout = self.cutout { if let cutout = self.cutout {
let size = CGSize(width: cutout.width * self.transitionFraction, height: cutout.height * self.transitionFraction) 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) 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.setBlendMode(.clear)
@ -740,13 +743,23 @@ public final class SemanticStatusNode: ASControlNode {
return self.appearanceContext.cutout return self.appearanceContext.cutout
} }
set { set {
if self.appearanceContext.cutout != newValue { self.setCutout(newValue, animated: false)
self.transitionContext = SemanticStatusNodeTransitionContext(startTime: CACurrentMediaTime(), duration: 0.18, previousStateContext: nil, previousAppearanceContext: self.appearanceContext, completion: {}) }
self.appearanceContext = self.appearanceContext.withUpdatedCutout(newValue) }
self.updateAnimations() public func setCutout(_ cutout: CGRect?, animated: Bool) {
self.setNeedsDisplay() 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()
} }
} }
@ -783,7 +796,7 @@ public final class SemanticStatusNode: ASControlNode {
let previousAppearanceContext = strongSelf.appearanceContext let previousAppearanceContext = strongSelf.appearanceContext
strongSelf.appearanceContext = strongSelf.appearanceContext.withUpdatedBackgroundImage(context?.generateImage()) strongSelf.appearanceContext = strongSelf.appearanceContext.withUpdatedBackgroundImage(context?.generateImage())
if CACurrentMediaTime() - start > 0.2 { if CACurrentMediaTime() - start > 0.3 {
strongSelf.transitionContext = SemanticStatusNodeTransitionContext(startTime: CACurrentMediaTime(), duration: 0.18, previousStateContext: nil, previousAppearanceContext: previousAppearanceContext, completion: {}) strongSelf.transitionContext = SemanticStatusNodeTransitionContext(startTime: CACurrentMediaTime(), duration: 0.18, previousStateContext: nil, previousAppearanceContext: previousAppearanceContext, completion: {})
strongSelf.updateAnimations() strongSelf.updateAnimations()
} }
@ -858,7 +871,8 @@ public final class SemanticStatusNode: ASControlNode {
override public func drawParameters(forAsyncLayer layer: _ASDisplayLayer) -> NSObjectProtocol? { override public func drawParameters(forAsyncLayer layer: _ASDisplayLayer) -> NSObjectProtocol? {
var transitionState: SemanticStatusNodeTransitionDrawingState? var transitionState: SemanticStatusNodeTransitionDrawingState?
var transitionFraction: CGFloat = 1.0 var transitionFraction: CGFloat = 1.0
var appearanceTransitionFraction: CGFloat = 1.0 var appearanceBackgroundTransitionFraction: CGFloat = 1.0
var appearanceForegroundTransitionFraction: CGFloat = 1.0
if let transitionContext = self.transitionContext { if let transitionContext = self.transitionContext {
let timestamp = CACurrentMediaTime() let timestamp = CACurrentMediaTime()
@ -868,13 +882,20 @@ public final class SemanticStatusNode: ASControlNode {
if let _ = transitionContext.previousStateContext { if let _ = transitionContext.previousStateContext {
transitionFraction = t transitionFraction = t
} }
if let _ = transitionContext.previousAppearanceContext { var foregroundTransitionFraction: CGFloat = 1.0
appearanceTransitionFraction = t 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(transitionFraction: 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(transitionState: transitionState, drawingState: self.stateContext.drawingState(transitionFraction: transitionFraction), appearanceState: self.appearanceContext.drawingState(transitionFraction: appearanceTransitionFraction)) 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) { @objc override public class func draw(_ bounds: CGRect, withParameters parameters: Any?, isCancelled: () -> Bool, isRasterizing: Bool) {

View File

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

View File

@ -1924,42 +1924,6 @@ public func settingsController(context: AccountContext, accountManager: AccountM
updateNotifyExceptions() updateNotifyExceptions()
updateActiveSessions() 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 controller.switchToAccount = { id in
let _ = (contextValue.get() let _ = (contextValue.get()
|> take(1) |> take(1)

View File

@ -385,8 +385,8 @@ public func themeAutoNightSettingsController(context: AccountContext) -> ViewCon
let forceUpdateLocation: () -> Void = { let forceUpdateLocation: () -> Void = {
let locationCoordinates = Signal<(Double, Double), NoError> { subscriber in let locationCoordinates = Signal<(Double, Double), NoError> { subscriber in
return context.sharedContext.locationManager!.push(mode: DeviceLocationMode.precise, updated: { coordinate, _, _ in return context.sharedContext.locationManager!.push(mode: DeviceLocationMode.precise, updated: { location, _ in
subscriber.putNext((coordinate.latitude, coordinate.longitude)) subscriber.putNext((location.coordinate.latitude, location.coordinate.longitude))
subscriber.putCompletion() subscriber.putCompletion()
}) })
} }

View File

@ -135,16 +135,15 @@ private enum StatsEntry: ItemListNodeEntry {
}) })
}, sectionId: self.section, style: .blocks) }, sectionId: self.section, style: .blocks)
case let .publicForward(_, _, _, _, message): case let .publicForward(_, _, _, _, message):
var views: Int = 0 var views: Int32 = 0
for attribute in message.attributes { for attribute in message.attributes {
if let viewsAttribute = attribute as? ViewCountMessageAttribute { if let viewsAttribute = attribute as? ViewCountMessageAttribute {
views = viewsAttribute.count views = Int32(viewsAttribute.count)
break break
} }
} }
var text: String = "" let text: String = presentationData.strings.Stats_MessageViews(views)
text += "\(views) views"
return ItemListPeerItem(presentationData: presentationData, dateTimeFormat: PresentationDateTimeFormat(timeFormat: .military, dateFormat: .dayFirst, dateSeparator: ".", decimalSeparator: ",", groupingSeparator: ""), nameDisplayOrder: .firstLast, context: arguments.context, peer: message.peers[message.id.peerId]!, height: .generic, aliasHandling: .standard, nameColor: .primary, nameStyle: .plain, presence: nil, text: .text(text), label: .none, editing: ItemListPeerItemEditing(editable: false, editing: false, revealed: nil), revealOptions: nil, switchValue: nil, enabled: true, highlighted: false, selectable: true, sectionId: self.section, action: { return ItemListPeerItem(presentationData: presentationData, dateTimeFormat: PresentationDateTimeFormat(timeFormat: .military, dateFormat: .dayFirst, dateSeparator: ".", decimalSeparator: ",", groupingSeparator: ""), nameDisplayOrder: .firstLast, context: arguments.context, peer: message.peers[message.id.peerId]!, height: .generic, aliasHandling: .standard, nameColor: .primary, nameStyle: .plain, presence: nil, text: .text(text), label: .none, editing: ItemListPeerItemEditing(editable: false, editing: false, revealed: nil), revealOptions: nil, switchValue: nil, enabled: true, highlighted: false, selectable: true, sectionId: self.section, action: {
arguments.openMessage(message.id) arguments.openMessage(message.id)
}, setPeerIdWithRevealedOptions: { _, _ in }, removePeer: { _ in }, toggleUpdated: nil, contextAction: nil) }, setPeerIdWithRevealedOptions: { _, _ in }, removePeer: { _ in }, toggleUpdated: nil, contextAction: nil)
@ -161,7 +160,17 @@ private func messageStatsControllerEntries(data: MessageStats?, messages: Search
if !data.interactionsGraph.isEmpty { if !data.interactionsGraph.isEmpty {
entries.append(.interactionsTitle(presentationData.theme, presentationData.strings.Stats_MessageInteractionsTitle.uppercased())) entries.append(.interactionsTitle(presentationData.theme, presentationData.strings.Stats_MessageInteractionsTitle.uppercased()))
entries.append(.interactionsGraph(presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, data.interactionsGraph, .twoAxisStep))
var chartType: ChartType
if data.interactionsGraphDelta == 3600 {
chartType = .twoAxisHourlyStep
} else if data.interactionsGraphDelta == 300 {
chartType = .twoAxis5MinStep
} else {
chartType = .twoAxisStep
}
entries.append(.interactionsGraph(presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, data.interactionsGraph, chartType))
} }
if let messages = messages, !messages.messages.isEmpty { if let messages = messages, !messages.messages.isEmpty {

View File

@ -168,11 +168,11 @@ class MessageStatsOverviewItemNode: ListViewItemNode {
rightValueLabelLayoutAndApply = makeRightValueLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.publicShares.flatMap { "\( compactNumericCountString(item.stats.forwards - Int($0)))" } ?? "", font: valueFont, textColor: item.presentationData.theme.list.itemPrimaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) 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 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) 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 { if let leftValueLabelLayout = leftValueLabelLayoutAndApply?.0, let leftTitleLabelLayout = leftTitleLabelLayoutAndApply?.0 {
strongSelf.leftValueLabel.frame = CGRect(origin: CGPoint(x: x, y: topInset), size: leftValueLabelLayout.size) 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) strongSelf.leftTitleLabel.frame = CGRect(origin: CGPoint(x: x, y: strongSelf.leftValueLabel.frame.maxY), size: leftTitleLabelLayout.size)

View File

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

View File

@ -2992,6 +2992,9 @@ func replayFinalState(accountManager: AccountManager, postbox: Postbox, accountP
} }
if upperId >= messageId.id { 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: .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)") Logger.shared.log("State", "adding hole for peer \(messageId.peerId), \(messageId.id) ... \(upperId)")
} else { } else {
Logger.shared.log("State", "not adding hole for peer \(messageId.peerId), \(upperId) >= \(messageId.id) = false") Logger.shared.log("State", "not adding hole for peer \(messageId.peerId), \(upperId) >= \(messageId.id) = false")

View File

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

View File

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

View File

@ -9,13 +9,13 @@ public struct MessageStats: Equatable {
public let views: Int public let views: Int
public let forwards: Int public let forwards: Int
public let interactionsGraph: StatsGraph public let interactionsGraph: StatsGraph
public let detailedInteractionsGraph: StatsGraph? public let interactionsGraphDelta: Int64
init(views: Int, forwards: Int, interactionsGraph: StatsGraph, detailedInteractionsGraph: StatsGraph?) { init(views: Int, forwards: Int, interactionsGraph: StatsGraph, interactionsGraphDelta: Int64) {
self.views = views self.views = views
self.forwards = forwards self.forwards = forwards
self.interactionsGraph = interactionsGraph self.interactionsGraph = interactionsGraph
self.detailedInteractionsGraph = detailedInteractionsGraph self.interactionsGraphDelta = interactionsGraphDelta
} }
public static func == (lhs: MessageStats, rhs: MessageStats) -> Bool { public static func == (lhs: MessageStats, rhs: MessageStats) -> Bool {
@ -28,14 +28,14 @@ public struct MessageStats: Equatable {
if lhs.interactionsGraph != rhs.interactionsGraph { if lhs.interactionsGraph != rhs.interactionsGraph {
return false return false
} }
if lhs.detailedInteractionsGraph != rhs.detailedInteractionsGraph { if lhs.interactionsGraphDelta != rhs.interactionsGraphDelta {
return false return false
} }
return true return true
} }
public func withUpdatedInteractionsGraph(_ interactionsGraph: StatsGraph) -> MessageStats { public func withUpdatedInteractionsGraph(_ interactionsGraph: StatsGraph) -> MessageStats {
return MessageStats(views: self.views, forwards: self.forwards, interactionsGraph: interactionsGraph, detailedInteractionsGraph: self.detailedInteractionsGraph) return MessageStats(views: self.views, forwards: self.forwards, interactionsGraph: interactionsGraph, interactionsGraphDelta: self.interactionsGraphDelta)
} }
} }
@ -86,16 +86,24 @@ private func requestMessageStats(postbox: Postbox, network: Network, datacenterI
|> mapToSignal { result -> Signal<MessageStats?, MTRpcError> in |> mapToSignal { result -> Signal<MessageStats?, MTRpcError> in
if case let .messageStats(apiViewsGraph) = result { if case let .messageStats(apiViewsGraph) = result {
let interactionsGraph = StatsGraph(apiStatsGraph: apiViewsGraph) let interactionsGraph = StatsGraph(apiStatsGraph: apiViewsGraph)
let timestamp = Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970) var interactionsGraphDelta: Int64 = 86400
if case let .Loaded(tokenValue, _) = interactionsGraph, let token = tokenValue, Int64(message.timestamp + 60 * 60 * 24 * 2) > Int64(timestamp) { if case let .Loaded(_, data) = interactionsGraph {
return requestGraph(network: network, datacenterId: datacenterId, token: token, x: 1601596800000) if let start = data.range(of: "[\"x\",") {
|> castError(MTRpcError.self) let substring = data.suffix(from: start.upperBound)
|> map { detailedGraph -> MessageStats? in if let end = substring.range(of: "],") {
return MessageStats(views: views, forwards: forwards, interactionsGraph: interactionsGraph, detailedInteractionsGraph: detailedGraph) let valuesString = substring.prefix(through: substring.index(before: end.lowerBound))
let values = valuesString.components(separatedBy: ",").compactMap { Int64($0) }
if values.count > 1 {
let first = values[0]
let second = values[1]
let delta = abs(second - first) / 1000
interactionsGraphDelta = delta
}
}
} }
} else {
return .single(MessageStats(views: views, forwards: forwards, interactionsGraph: interactionsGraph, detailedInteractionsGraph: nil))
} }
return .single(MessageStats(views: views, forwards: forwards, interactionsGraph: interactionsGraph, interactionsGraphDelta: interactionsGraphDelta))
} else { } else {
return .single(nil) return .single(nil)
} }

View File

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

View File

@ -442,12 +442,12 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
attributedString = addAttributesToStringWithRanges(strings.Notification_Joined(authorName), body: bodyAttributes, argumentAttributes: peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: [(0, message.author?.id)])) attributedString = addAttributesToStringWithRanges(strings.Notification_Joined(authorName), body: bodyAttributes, argumentAttributes: peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: [(0, message.author?.id)]))
case .phoneNumberRequest: case .phoneNumberRequest:
attributedString = nil attributedString = nil
case let .geoProximityReached(_, toId, distance): case let .geoProximityReached(fromId, toId, distance):
let distanceString = stringForDistance(strings: strings, distance: Double(distance)) let distanceString = stringForDistance(strings: strings, distance: Double(distance))
if toId == accountPeerId { if toId == accountPeerId {
attributedString = addAttributesToStringWithRanges(strings.Notification_ProximityReachedYou(authorName, distanceString), body: bodyAttributes, argumentAttributes: peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: [(0, message.author?.id)])) attributedString = addAttributesToStringWithRanges(strings.Notification_ProximityReachedYou(message.peers[fromId]?.displayTitle(strings: strings, displayOrder: nameDisplayOrder) ?? "", distanceString), body: bodyAttributes, argumentAttributes: peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: [(0, fromId)]))
} else { } else {
attributedString = addAttributesToStringWithRanges(strings.Notification_ProximityReached(authorName, distanceString, message.peers[toId]?.displayTitle(strings: strings, displayOrder: nameDisplayOrder) ?? ""), body: bodyAttributes, argumentAttributes: peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: [(0, message.author?.id), (2, toId)])) 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, fromId), (2, toId)]))
} }
case .unknown: case .unknown:
attributedString = nil attributedString = nil

View File

@ -8,25 +8,6 @@ import ContextUI
private let normalFont = avatarPlaceholderFont(size: 16.0) private let normalFont = avatarPlaceholderFont(size: 16.0)
private let smallFont = avatarPlaceholderFont(size: 12.0) private let smallFont = avatarPlaceholderFont(size: 12.0)
final class ChatAvatarNavigationNodeView: UIView, PreviewingHostView {
var previewingDelegate: PreviewingHostViewDelegate? {
return PreviewingHostViewDelegate(controllerForLocation: { [weak self] sourceView, point in
return self?.chatController?.avatarPreviewingController(from: sourceView)
}, commitController: { [weak self] controller in
self?.chatController?.previewingCommit(controller)
})
}
weak var chatController: ChatControllerImpl?
weak var targetNode: ChatAvatarNavigationNode?
override func layoutSubviews() {
super.layoutSubviews()
self.targetNode?.onLayout()
}
}
final class ChatAvatarNavigationNode: ASDisplayNode { final class ChatAvatarNavigationNode: ASDisplayNode {
private let containerNode: ContextControllerSourceNode private let containerNode: ContextControllerSourceNode
let avatarNode: AvatarNode let avatarNode: AvatarNode
@ -40,14 +21,6 @@ final class ChatAvatarNavigationNode: ASDisplayNode {
} }
} }
weak var chatController: ChatControllerImpl? {
didSet {
if self.isNodeLoaded {
(self.view as? ChatAvatarNavigationNodeView)?.chatController = self.chatController
}
}
}
var tapped: (() -> Void)? var tapped: (() -> Void)?
override init() { override init() {
@ -56,10 +29,6 @@ final class ChatAvatarNavigationNode: ASDisplayNode {
super.init() super.init()
self.setViewBlock({
return ChatAvatarNavigationNodeView()
})
self.containerNode.addSubnode(self.avatarNode) self.containerNode.addSubnode(self.avatarNode)
self.addSubnode(self.containerNode) self.addSubnode(self.containerNode)
@ -80,11 +49,6 @@ final class ChatAvatarNavigationNode: ASDisplayNode {
override func didLoad() { override func didLoad() {
super.didLoad() super.didLoad()
self.view.isOpaque = false self.view.isOpaque = false
(self.view as? ChatAvatarNavigationNodeView)?.targetNode = self
(self.view as? ChatAvatarNavigationNodeView)?.chatController = self.chatController
/*let tapRecognizer = TapLongTapOrDoubleTapGestureRecognizer(target: self, action: #selector(self.avatarTapGesture(_:)))
self.avatarNode.view.addGestureRecognizer(tapRecognizer)*/
} }
@objc private func avatarTapGesture(_ recognizer: TapLongTapOrDoubleTapGestureRecognizer) { @objc private func avatarTapGesture(_ recognizer: TapLongTapOrDoubleTapGestureRecognizer) {

View File

@ -2341,7 +2341,6 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
switch chatLocation { switch chatLocation {
case .peer, .replyThread: case .peer, .replyThread:
let avatarNode = ChatAvatarNavigationNode() let avatarNode = ChatAvatarNavigationNode()
avatarNode.chatController = self
avatarNode.contextAction = { [weak self] node, gesture in avatarNode.contextAction = { [weak self] node, gesture in
guard let strongSelf = self, let peer = strongSelf.presentationInterfaceState.renderedPeer?.chatMainPeer, peer.smallProfileImage != nil else { guard let strongSelf = self, let peer = strongSelf.presentationInterfaceState.renderedPeer?.chatMainPeer, peer.smallProfileImage != nil else {
return return
@ -6569,7 +6568,21 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
let inputTextPanelState = inputTextPanelStateForChatPresentationInterfaceState(temporaryChatPresentationInterfaceState, context: self.context) let inputTextPanelState = inputTextPanelStateForChatPresentationInterfaceState(temporaryChatPresentationInterfaceState, context: self.context)
var updatedChatPresentationInterfaceState = temporaryChatPresentationInterfaceState.updatedInputTextPanelState({ _ in return inputTextPanelState }) var updatedChatPresentationInterfaceState = temporaryChatPresentationInterfaceState.updatedInputTextPanelState({ _ in return inputTextPanelState })
let contextQueryUpdates = contextQueryResultStateForChatInterfacePresentationState(updatedChatPresentationInterfaceState, context: self.context, currentQueryStates: &self.contextQueryStates) let contextQueryUpdates = contextQueryResultStateForChatInterfacePresentationState(updatedChatPresentationInterfaceState, context: self.context, currentQueryStates: &self.contextQueryStates, requestBotLocationStatus: { [weak self] peerId in
guard let strongSelf = self else {
return
}
let _ = (ApplicationSpecificNotice.updateInlineBotLocationRequestState(accountManager: strongSelf.context.sharedContext.accountManager, peerId: peerId, timestamp: Int32(Date().timeIntervalSince1970 + 10 * 60))
|> deliverOnMainQueue).start(next: { value in
guard let strongSelf = self, value else {
return
}
strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: strongSelf.presentationData.strings.Conversation_ShareInlineBotLocationConfirmation, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {
}), TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {
let _ = ApplicationSpecificNotice.setInlineBotLocationRequest(accountManager: strongSelf.context.sharedContext.accountManager, peerId: peerId, value: 0).start()
})]), in: .window(.root))
})
})
for (kind, update) in contextQueryUpdates { for (kind, update) in contextQueryUpdates {
switch update { switch update {
@ -7858,6 +7871,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
let selfPeerId: PeerId let selfPeerId: PeerId
if let peer = peer as? TelegramChannel, case .broadcast = peer.info { if let peer = peer as? TelegramChannel, case .broadcast = peer.info {
selfPeerId = peer.id selfPeerId = peer.id
} else if let peer = peer as? TelegramChannel, case .group = peer.info, peer.hasPermission(.canBeAnonymous) {
selfPeerId = peer.id
} else { } else {
selfPeerId = self.context.account.peerId selfPeerId = self.context.account.peerId
} }
@ -8306,6 +8321,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
controller.dismissWithCommitAction() controller.dismissWithCommitAction()
} }
}) })
self.forEachController({ controller in
if let controller = controller as? UndoOverlayController {
controller.dismissWithCommitAction()
}
return true
})
let value: String? let value: String?
let emoji = dice.emoji.strippedEmoji let emoji = dice.emoji.strippedEmoji

View File

@ -24,20 +24,10 @@ final class VideoNavigationControllerDropContentItem: NavigationControllerDropCo
} }
} }
private final class ChatControllerNodeView: UITracingLayerView, WindowInputAccessoryHeightProvider, PreviewingHostView { private final class ChatControllerNodeView: UITracingLayerView, WindowInputAccessoryHeightProvider {
var inputAccessoryHeight: (() -> CGFloat)? var inputAccessoryHeight: (() -> CGFloat)?
var hitTestImpl: ((CGPoint, UIEvent?) -> UIView?)? var hitTestImpl: ((CGPoint, UIEvent?) -> UIView?)?
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: ChatControllerImpl?
func getWindowInputAccessoryHeight() -> CGFloat { func getWindowInputAccessoryHeight() -> CGFloat {
return self.inputAccessoryHeight?() ?? 0.0 return self.inputAccessoryHeight?() ?? 0.0
} }
@ -680,8 +670,6 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
return false return false
} }
(self.view as? ChatControllerNodeView)?.controller = self.controller
self.displayVideoUnmuteTipDisposable = (combineLatest(queue: Queue.mainQueue(), ApplicationSpecificNotice.getVolumeButtonToUnmute(accountManager: self.context.sharedContext.accountManager), self.historyNode.hasVisiblePlayableItemNodes, self.historyNode.isInteractivelyScrolling) self.displayVideoUnmuteTipDisposable = (combineLatest(queue: Queue.mainQueue(), ApplicationSpecificNotice.getVolumeButtonToUnmute(accountManager: self.context.sharedContext.accountManager), self.historyNode.hasVisiblePlayableItemNodes, self.historyNode.isInteractivelyScrolling)
|> mapToSignal { notice, hasVisiblePlayableItemNodes, isInteractivelyScrolling -> Signal<Bool, NoError> in |> mapToSignal { notice, hasVisiblePlayableItemNodes, isInteractivelyScrolling -> Signal<Bool, NoError> in
let display = !notice && hasVisiblePlayableItemNodes && !isInteractivelyScrolling let display = !notice && hasVisiblePlayableItemNodes && !isInteractivelyScrolling

View File

@ -601,9 +601,18 @@ func contextMenuForChatPresentationIntefaceState(chatPresentationInterfaceState:
var threadId: Int64? var threadId: Int64?
var threadMessageCount: Int = 0 var threadMessageCount: Int = 0
if case .peer = chatPresentationInterfaceState.chatLocation, let channel = chatPresentationInterfaceState.renderedPeer?.peer as? TelegramChannel, case .group = channel.info, let cachedData = cachedData as? CachedChannelData, case let .known(maybeValue) = cachedData.linkedDiscussionPeerId, let _ = maybeValue { if case .peer = chatPresentationInterfaceState.chatLocation, let channel = chatPresentationInterfaceState.renderedPeer?.peer as? TelegramChannel, case .group = channel.info {
if let value = messages[0].threadId { if let cachedData = cachedData as? CachedChannelData, case let .known(maybeValue) = cachedData.linkedDiscussionPeerId, let _ = maybeValue {
threadId = value if let value = messages[0].threadId {
threadId = value
} else {
for attribute in messages[0].attributes {
if let attribute = attribute as? ReplyThreadMessageAttribute, attribute.count > 0 {
threadId = makeMessageThreadId(messages[0].id)
threadMessageCount = Int(attribute.count)
}
}
}
} else { } else {
for attribute in messages[0].attributes { for attribute in messages[0].attributes {
if let attribute = attribute as? ReplyThreadMessageAttribute, attribute.count > 0 { if let attribute = attribute as? ReplyThreadMessageAttribute, attribute.count > 0 {
@ -612,13 +621,6 @@ func contextMenuForChatPresentationIntefaceState(chatPresentationInterfaceState:
} }
} }
} }
} else {
for attribute in messages[0].attributes {
if let attribute = attribute as? ReplyThreadMessageAttribute, attribute.count > 0 {
threadId = makeMessageThreadId(messages[0].id)
threadMessageCount = Int(attribute.count)
}
}
} }
if let _ = threadId, !isPinnedMessages { if let _ = threadId, !isPinnedMessages {
@ -853,7 +855,8 @@ func contextMenuForChatPresentationIntefaceState(chatPresentationInterfaceState:
views = attribute.count views = attribute.count
} }
} }
if views >= 100 {
if let cachedData = cachedData as? CachedChannelData, cachedData.flags.contains(.canViewStats), views >= 100 {
actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_ContextViewStats, icon: { theme in actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_ContextViewStats, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Statistics"), color: theme.actionSheet.primaryTextColor) return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Statistics"), color: theme.actionSheet.primaryTextColor)
}, action: { c, _ in }, action: { c, _ in
@ -1139,7 +1142,7 @@ func chatAvailableMessageActionsImpl(postbox: Postbox, accountPeerId: PeerId, me
if canDeleteGlobally { if canDeleteGlobally {
optionsMap[id]!.insert(.deleteGlobally) optionsMap[id]!.insert(.deleteGlobally)
} }
if user.botInfo != nil && !user.id.isReplies { if user.botInfo != nil && !user.id.isReplies && !isAction {
optionsMap[id]!.insert(.report) optionsMap[id]!.insert(.report)
} }
} else if let _ = peer as? TelegramSecretChat { } else if let _ = peer as? TelegramSecretChat {

View File

@ -11,6 +11,7 @@ import AccountContext
import Emoji import Emoji
import SearchPeerMembers import SearchPeerMembers
import DeviceLocationManager import DeviceLocationManager
import TelegramNotices
enum ChatContextQueryError { enum ChatContextQueryError {
case inlineBotLocationRequest(PeerId) case inlineBotLocationRequest(PeerId)
@ -21,7 +22,7 @@ enum ChatContextQueryUpdate {
case update(ChatPresentationInputQuery, Signal<(ChatPresentationInputQueryResult?) -> ChatPresentationInputQueryResult?, ChatContextQueryError>) case update(ChatPresentationInputQuery, Signal<(ChatPresentationInputQueryResult?) -> ChatPresentationInputQueryResult?, ChatContextQueryError>)
} }
func contextQueryResultStateForChatInterfacePresentationState(_ chatPresentationInterfaceState: ChatPresentationInterfaceState, context: AccountContext, currentQueryStates: inout [ChatPresentationInputQueryKind: (ChatPresentationInputQuery, Disposable)]) -> [ChatPresentationInputQueryKind: ChatContextQueryUpdate] { func contextQueryResultStateForChatInterfacePresentationState(_ chatPresentationInterfaceState: ChatPresentationInterfaceState, context: AccountContext, currentQueryStates: inout [ChatPresentationInputQueryKind: (ChatPresentationInputQuery, Disposable)], requestBotLocationStatus: @escaping (PeerId) -> Void) -> [ChatPresentationInputQueryKind: ChatContextQueryUpdate] {
guard let peer = chatPresentationInterfaceState.renderedPeer?.peer else { guard let peer = chatPresentationInterfaceState.renderedPeer?.peer else {
return [:] return [:]
} }
@ -43,7 +44,7 @@ func contextQueryResultStateForChatInterfacePresentationState(_ chatPresentation
for query in inputQueries { for query in inputQueries {
let previousQuery = currentQueryStates[query.kind]?.0 let previousQuery = currentQueryStates[query.kind]?.0
if previousQuery != query { if previousQuery != query {
let signal = updatedContextQueryResultStateForQuery(context: context, peer: peer, chatLocation: chatPresentationInterfaceState.chatLocation, inputQuery: query, previousQuery: previousQuery) let signal = updatedContextQueryResultStateForQuery(context: context, peer: peer, chatLocation: chatPresentationInterfaceState.chatLocation, inputQuery: query, previousQuery: previousQuery, requestBotLocationStatus: requestBotLocationStatus)
updates[query.kind] = .update(query, signal) updates[query.kind] = .update(query, signal)
} }
} }
@ -64,7 +65,7 @@ func contextQueryResultStateForChatInterfacePresentationState(_ chatPresentation
return updates return updates
} }
private func updatedContextQueryResultStateForQuery(context: AccountContext, peer: Peer, chatLocation: ChatLocation, inputQuery: ChatPresentationInputQuery, previousQuery: ChatPresentationInputQuery?) -> Signal<(ChatPresentationInputQueryResult?) -> ChatPresentationInputQueryResult?, ChatContextQueryError> { private func updatedContextQueryResultStateForQuery(context: AccountContext, peer: Peer, chatLocation: ChatLocation, inputQuery: ChatPresentationInputQuery, previousQuery: ChatPresentationInputQuery?, requestBotLocationStatus: @escaping (PeerId) -> Void) -> Signal<(ChatPresentationInputQueryResult?) -> ChatPresentationInputQueryResult?, ChatContextQueryError> {
switch inputQuery { switch inputQuery {
case let .emoji(query): case let .emoji(query):
var signal: Signal<(ChatPresentationInputQueryResult?) -> ChatPresentationInputQueryResult?, ChatContextQueryError> = .complete() var signal: Signal<(ChatPresentationInputQueryResult?) -> ChatPresentationInputQueryResult?, ChatContextQueryError> = .complete()
@ -245,10 +246,21 @@ private func updatedContextQueryResultStateForQuery(context: AccountContext, pee
|> castError(ChatContextQueryError.self) |> castError(ChatContextQueryError.self)
|> mapToSignal { peer -> Signal<(ChatPresentationInputQueryResult?) -> ChatPresentationInputQueryResult?, ChatContextQueryError> in |> mapToSignal { peer -> Signal<(ChatPresentationInputQueryResult?) -> ChatPresentationInputQueryResult?, ChatContextQueryError> in
if let user = peer as? TelegramUser, let botInfo = user.botInfo, let _ = botInfo.inlinePlaceholder { if let user = peer as? TelegramUser, let botInfo = user.botInfo, let _ = botInfo.inlinePlaceholder {
let contextResults = requestChatContextResults(account: context.account, botId: user.id, peerId: chatPeer.id, query: query, location: context.sharedContext.locationManager.flatMap { locationManager in let contextResults = requestChatContextResults(account: context.account, botId: user.id, peerId: chatPeer.id, query: query, location: context.sharedContext.locationManager.flatMap { locationManager -> Signal<(Double, Double)?, NoError> in
return currentLocationManagerCoordinate(manager: locationManager, timeout: 5.0) return `deferred` {
|> flatMap { coordinate -> (Double, Double) in Queue.mainQueue().async {
return (coordinate.latitude, coordinate.longitude) requestBotLocationStatus(user.id)
}
return ApplicationSpecificNotice.inlineBotLocationRequestStatus(accountManager: context.sharedContext.accountManager, peerId: user.id)
|> filter { $0 }
|> take(1)
|> mapToSignal { _ -> Signal<(Double, Double)?, NoError> in
return currentLocationManagerCoordinate(manager: locationManager, timeout: 5.0)
|> flatMap { coordinate -> (Double, Double) in
return (coordinate.latitude, coordinate.longitude)
}
}
} }
} ?? .single(nil), offset: "") } ?? .single(nil), offset: "")
|> mapError { error -> ChatContextQueryError in |> mapError { error -> ChatContextQueryError in

View File

@ -37,7 +37,7 @@ class ChatMessageActionBubbleContentNode: ChatMessageBubbleContentNode {
required init() { required init() {
self.labelNode = TextNode() self.labelNode = TextNode()
self.labelNode.isUserInteractionEnabled = false self.labelNode.isUserInteractionEnabled = false
self.labelNode.displaysAsynchronously = true self.labelNode.displaysAsynchronously = false
self.filledBackgroundNode = LinkHighlightingNode(color: .clear) self.filledBackgroundNode = LinkHighlightingNode(color: .clear)

View File

@ -20,6 +20,7 @@ import Emoji
import Markdown import Markdown
import ManagedAnimationNode import ManagedAnimationNode
import SlotMachineAnimationNode import SlotMachineAnimationNode
import UniversalMediaPlayer
private let nameFont = Font.medium(14.0) private let nameFont = Font.medium(14.0)
private let inlineBotPrefixFont = Font.regular(14.0) private let inlineBotPrefixFont = Font.regular(14.0)
@ -445,7 +446,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
} }
} }
func updateVisibility() { private func updateVisibility() {
guard let item = self.item else { guard let item = self.item else {
return return
} }
@ -1104,7 +1105,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
} }
} }
@objc func tapLongTapOrDoubleTapGesture(_ recognizer: TapLongTapOrDoubleTapGestureRecognizer) { @objc private func tapLongTapOrDoubleTapGesture(_ recognizer: TapLongTapOrDoubleTapGestureRecognizer) {
switch recognizer.state { switch recognizer.state {
case .ended: case .ended:
if let (gesture, location) = recognizer.lastRecognizedGestureAndLocation { if let (gesture, location) = recognizer.lastRecognizedGestureAndLocation {
@ -1217,7 +1218,9 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
} else if let _ = self.emojiFile { } else if let _ = self.emojiFile {
if let animationNode = self.animationNode as? AnimatedStickerNode { if let animationNode = self.animationNode as? AnimatedStickerNode {
var startTime: Signal<Double, NoError> var startTime: Signal<Double, NoError>
if animationNode.playIfNeeded() { var shouldPlay = false
if !animationNode.isPlaying {
shouldPlay = true
startTime = .single(0.0) startTime = .single(0.0)
} else { } else {
startTime = animationNode.status startTime = animationNode.status
@ -1229,30 +1232,62 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
let beatingHearts: [UInt32] = [0x2764, 0x1F90E, 0x1F9E1, 0x1F499, 0x1F49A, 0x1F49C, 0x1F49B, 0x1F5A4, 0x1F90D] let beatingHearts: [UInt32] = [0x2764, 0x1F90E, 0x1F9E1, 0x1F499, 0x1F49A, 0x1F49C, 0x1F49B, 0x1F5A4, 0x1F90D]
let peach = 0x1F351 let peach = 0x1F351
if let text = self.item?.message.text, let firstScalar = text.unicodeScalars.first, beatingHearts.contains(firstScalar.value) || firstScalar.value == peach { let appConfiguration = item.context.account.postbox.preferencesView(keys: [PreferencesKeys.appConfiguration])
return .optionalAction({ |> take(1)
let _ = startTime.start(next: { [weak self] time in |> map { view in
guard let strongSelf = self else { return view.values[PreferencesKeys.appConfiguration] as? AppConfiguration ?? .defaultValue
return }
}
if let text = self.item?.message.text, let firstScalar = text.unicodeScalars.first {
var haptic: EmojiHaptic if beatingHearts.contains(firstScalar.value) || firstScalar.value == peach {
if let current = strongSelf.haptic { if shouldPlay {
haptic = current animationNode.play()
} else { }
if beatingHearts.contains(firstScalar.value) { return .optionalAction({
haptic = HeartbeatHaptic() let _ = startTime.start(next: { [weak self] time in
} else { guard let strongSelf = self else {
haptic = PeachHaptic() return
} }
haptic.enabled = true
strongSelf.haptic = haptic var haptic: EmojiHaptic
} if let current = strongSelf.haptic {
if !haptic.active { haptic = current
haptic.start(time: time) } else {
if beatingHearts.contains(firstScalar.value) {
haptic = HeartbeatHaptic()
} else {
haptic = PeachHaptic()
}
haptic.enabled = true
strongSelf.haptic = haptic
}
if !haptic.active {
haptic.start(time: time)
}
})
})
} else {
return .optionalAction({
if shouldPlay {
let _ = (appConfiguration
|> deliverOnMainQueue).start(next: { [weak self] appConfiguration in
let emojiSounds = AnimatedEmojiSoundsConfiguration.with(appConfiguration: appConfiguration, account: item.context.account)
for (emoji, file) in emojiSounds.sounds {
if emoji.unicodeScalars.first == firstScalar {
let mediaManager = item.context.sharedContext.mediaManager
let mediaPlayer = MediaPlayer(audioSessionManager: mediaManager.audioSession, postbox: item.context.account.postbox, resourceReference: .standalone(resource: file.resource), streamable: .none, video: false, preferSoftwareDecoding: false, enableSound: true, fetchAutomatically: true)
mediaPlayer.togglePlayPause()
self?.mediaPlayer = mediaPlayer
animationNode.play()
break
}
}
})
} }
}) })
}) }
} }
} }
} }
@ -1268,7 +1303,9 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
return nil return nil
} }
@objc func shareButtonPressed() { private var mediaPlayer: MediaPlayer?
@objc private func shareButtonPressed() {
if let item = self.item { if let item = self.item {
if case .pinnedMessages = item.associatedData.subject { if case .pinnedMessages = item.associatedData.subject {
item.controllerInteraction.navigateToMessageStandalone(item.content.firstMessage.id) item.controllerInteraction.navigateToMessageStandalone(item.content.firstMessage.id)
@ -1299,7 +1336,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
} }
} }
@objc func swipeToReplyGesture(_ recognizer: ChatSwipeToReplyRecognizer) { @objc private func swipeToReplyGesture(_ recognizer: ChatSwipeToReplyRecognizer) {
switch recognizer.state { switch recognizer.state {
case .began: case .began:
self.currentSwipeToReplyTranslation = 0.0 self.currentSwipeToReplyTranslation = 0.0
@ -1512,3 +1549,39 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
self.contextSourceNode.contentNode.addSubnode(accessoryItemNode) self.contextSourceNode.contentNode.addSubnode(accessoryItemNode)
} }
} }
struct AnimatedEmojiSoundsConfiguration {
static var defaultValue: AnimatedEmojiSoundsConfiguration {
return AnimatedEmojiSoundsConfiguration(sounds: [:])
}
public let sounds: [String: TelegramMediaFile]
fileprivate init(sounds: [String: TelegramMediaFile]) {
self.sounds = sounds
}
static func with(appConfiguration: AppConfiguration, account: Account) -> AnimatedEmojiSoundsConfiguration {
if let data = appConfiguration.data, let values = data["emojies_sounds"] as? [String: Any] {
var sounds: [String: TelegramMediaFile] = [:]
for (key, value) in values {
if let dict = value as? [String: String], var fileReferenceString = dict["file_reference_base64"] {
fileReferenceString = fileReferenceString.replacingOccurrences(of: "-", with: "+")
fileReferenceString = fileReferenceString.replacingOccurrences(of: "_", with: "/")
while fileReferenceString.count % 4 != 0 {
fileReferenceString.append("=")
}
if let idString = dict["id"], let id = Int64(idString), let accessHashString = dict["access_hash"], let accessHash = Int64(accessHashString), let fileReference = Data(base64Encoded: fileReferenceString) {
let resource = CloudDocumentMediaResource(datacenterId: 1, fileId: id, accessHash: accessHash, size: nil, fileReference: fileReference, fileName: nil)
let file = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: resource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "audio/ogg", size: nil, attributes: [])
sounds[key] = file
}
}
}
return AnimatedEmojiSoundsConfiguration(sounds: sounds)
} else {
return .defaultValue
}
}
}

View File

@ -252,7 +252,7 @@ final class ChatMessageAttachedContentNode: ASDisplayNode {
self.textNode = TextNode() self.textNode = TextNode()
self.textNode.isUserInteractionEnabled = false self.textNode.isUserInteractionEnabled = false
self.textNode.displaysAsynchronously = true self.textNode.displaysAsynchronously = false
self.textNode.contentsScale = UIScreenScale self.textNode.contentsScale = UIScreenScale
self.textNode.contentMode = .topLeft self.textNode.contentMode = .topLeft

View File

@ -39,13 +39,13 @@ class ChatMessageCallBubbleContentNode: ChatMessageBubbleContentNode {
self.titleNode.isUserInteractionEnabled = false self.titleNode.isUserInteractionEnabled = false
self.titleNode.contentMode = .topLeft self.titleNode.contentMode = .topLeft
self.titleNode.contentsScale = UIScreenScale self.titleNode.contentsScale = UIScreenScale
self.titleNode.displaysAsynchronously = true self.titleNode.displaysAsynchronously = false
self.addSubnode(self.titleNode) self.addSubnode(self.titleNode)
self.labelNode.isUserInteractionEnabled = false self.labelNode.isUserInteractionEnabled = false
self.labelNode.contentMode = .topLeft self.labelNode.contentMode = .topLeft
self.labelNode.contentsScale = UIScreenScale self.labelNode.contentsScale = UIScreenScale
self.labelNode.displaysAsynchronously = true self.labelNode.displaysAsynchronously = false
self.addSubnode(self.labelNode) self.addSubnode(self.labelNode)
self.addSubnode(self.iconNode) self.addSubnode(self.iconNode)

View File

@ -173,7 +173,7 @@ class ChatMessageDateAndStatusNode: ASDisplayNode {
override init() { override init() {
self.dateNode = TextNode() self.dateNode = TextNode()
self.dateNode.isUserInteractionEnabled = false self.dateNode.isUserInteractionEnabled = false
self.dateNode.displaysAsynchronously = true self.dateNode.displaysAsynchronously = false
super.init() super.init()

View File

@ -94,17 +94,17 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
override init() { override init() {
self.titleNode = TextNode() self.titleNode = TextNode()
self.titleNode.displaysAsynchronously = true self.titleNode.displaysAsynchronously = false
self.titleNode.isUserInteractionEnabled = false self.titleNode.isUserInteractionEnabled = false
self.descriptionNode = TextNode() self.descriptionNode = TextNode()
self.descriptionNode.displaysAsynchronously = true self.descriptionNode.displaysAsynchronously = false
self.descriptionNode.isUserInteractionEnabled = false self.descriptionNode.isUserInteractionEnabled = false
self.descriptionMeasuringNode = TextNode() self.descriptionMeasuringNode = TextNode()
self.fetchingTextNode = ImmediateTextNode() self.fetchingTextNode = ImmediateTextNode()
self.fetchingTextNode.displaysAsynchronously = true self.fetchingTextNode.displaysAsynchronously = false
self.fetchingTextNode.isUserInteractionEnabled = false self.fetchingTextNode.isUserInteractionEnabled = false
self.fetchingTextNode.maximumNumberOfLines = 1 self.fetchingTextNode.maximumNumberOfLines = 1
self.fetchingTextNode.contentMode = .left self.fetchingTextNode.contentMode = .left
@ -112,7 +112,7 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
self.fetchingTextNode.isHidden = true self.fetchingTextNode.isHidden = true
self.fetchingCompactTextNode = ImmediateTextNode() self.fetchingCompactTextNode = ImmediateTextNode()
self.fetchingCompactTextNode.displaysAsynchronously = true self.fetchingCompactTextNode.displaysAsynchronously = false
self.fetchingCompactTextNode.isUserInteractionEnabled = false self.fetchingCompactTextNode.isUserInteractionEnabled = false
self.fetchingCompactTextNode.maximumNumberOfLines = 1 self.fetchingCompactTextNode.maximumNumberOfLines = 1
self.fetchingCompactTextNode.contentMode = .left self.fetchingCompactTextNode.contentMode = .left
@ -829,10 +829,14 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
wasCheck = true wasCheck = true
} }
if adjustedProgress.isEqual(to: 1.0), (message.flags.contains(.Unsent) || wasCheck) { if isAudio && !isVoice {
state = .check(appearance: nil) state = .play
} else { } else {
state = .progress(value: CGFloat(adjustedProgress), cancelEnabled: true, appearance: nil) if adjustedProgress.isEqual(to: 1.0), (message.flags.contains(.Unsent) || wasCheck) {
state = .check(appearance: nil)
} else {
state = .progress(value: CGFloat(adjustedProgress), cancelEnabled: true, appearance: nil)
}
} }
case .Local: case .Local:
if isAudio { if isAudio {
@ -897,7 +901,7 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
} }
} }
image = playerAlbumArt(postbox: context.account.postbox, fileReference: .message(message: MessageReference(message), media: file), albumArt: .init(thumbnailResource: ExternalMusicAlbumArtResource(title: title ?? "", performer: performer ?? "", isThumbnail: true), fullSizeResource: ExternalMusicAlbumArtResource(title: title ?? "", performer: performer ?? "", isThumbnail: false)), thumbnail: true, overlayColor: UIColor(white: 0.0, alpha: 0.3), drawPlaceholderWhenEmpty: false) image = playerAlbumArt(postbox: context.account.postbox, fileReference: .message(message: MessageReference(message), media: file), albumArt: .init(thumbnailResource: ExternalMusicAlbumArtResource(title: title ?? "", performer: performer ?? "", isThumbnail: true), fullSizeResource: ExternalMusicAlbumArtResource(title: title ?? "", performer: performer ?? "", isThumbnail: false)), thumbnail: true, overlayColor: UIColor(white: 0.0, alpha: 0.3), drawPlaceholderWhenEmpty: false, attemptSynchronously: !animated)
} }
let statusNode = SemanticStatusNode(backgroundNodeColor: backgroundNodeColor, foregroundNodeColor: foregroundNodeColor, image: image, overlayForegroundNodeColor: presentationData.theme.theme.chat.message.mediaOverlayControlColors.foregroundColor) let statusNode = SemanticStatusNode(backgroundNodeColor: backgroundNodeColor, foregroundNodeColor: foregroundNodeColor, image: image, overlayForegroundNodeColor: presentationData.theme.theme.chat.message.mediaOverlayControlColors.foregroundColor)
self.statusNode = statusNode self.statusNode = statusNode
@ -975,11 +979,10 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
let cutoutFrame = streamingCacheStatusFrame.insetBy(dx: -(1.0 + UIScreenPixel), dy: -(1.0 + UIScreenPixel)).offsetBy(dx: progressFrame.minX - 6.0, dy: progressFrame.minY) let cutoutFrame = streamingCacheStatusFrame.insetBy(dx: -(1.0 + UIScreenPixel), dy: -(1.0 + UIScreenPixel)).offsetBy(dx: progressFrame.minX - 6.0, dy: progressFrame.minY)
if streamingState == .none && self.selectionNode == nil { if streamingState == .none && self.selectionNode == nil {
self.statusNode?.cutout = nil self.statusNode?.setCutout(nil, animated: animated)
} else if let statusNode = self.statusNode, (self.iconNode?.isHidden ?? true) { } else if let statusNode = self.statusNode, (self.iconNode?.isHidden ?? true) {
self.statusNode?.cutout = cutoutFrame statusNode.setCutout(cutoutFrame, animated: true)
} }
if let (expandedString, compactString, font) = downloadingStrings { if let (expandedString, compactString, font) = downloadingStrings {

View File

@ -862,13 +862,13 @@ class ChatMessagePollBubbleContentNode: ChatMessageBubbleContentNode {
self.textNode.isUserInteractionEnabled = false self.textNode.isUserInteractionEnabled = false
self.textNode.contentMode = .topLeft self.textNode.contentMode = .topLeft
self.textNode.contentsScale = UIScreenScale self.textNode.contentsScale = UIScreenScale
self.textNode.displaysAsynchronously = true self.textNode.displaysAsynchronously = false
self.typeNode = TextNode() self.typeNode = TextNode()
self.typeNode.isUserInteractionEnabled = false self.typeNode.isUserInteractionEnabled = false
self.typeNode.contentMode = .topLeft self.typeNode.contentMode = .topLeft
self.typeNode.contentsScale = UIScreenScale self.typeNode.contentsScale = UIScreenScale
self.typeNode.displaysAsynchronously = true self.typeNode.displaysAsynchronously = false
self.avatarsNode = MergedAvatarsNode() self.avatarsNode = MergedAvatarsNode()
@ -876,7 +876,7 @@ class ChatMessagePollBubbleContentNode: ChatMessageBubbleContentNode {
self.votersNode.isUserInteractionEnabled = false self.votersNode.isUserInteractionEnabled = false
self.votersNode.contentMode = .topLeft self.votersNode.contentMode = .topLeft
self.votersNode.contentsScale = UIScreenScale self.votersNode.contentsScale = UIScreenScale
self.votersNode.displaysAsynchronously = true self.votersNode.displaysAsynchronously = false
var displaySolution: (() -> Void)? var displaySolution: (() -> Void)?
self.solutionButtonNode = SolutionButtonNode(pressed: { self.solutionButtonNode = SolutionButtonNode(pressed: {
@ -888,19 +888,19 @@ class ChatMessagePollBubbleContentNode: ChatMessageBubbleContentNode {
self.buttonSubmitInactiveTextNode.isUserInteractionEnabled = false self.buttonSubmitInactiveTextNode.isUserInteractionEnabled = false
self.buttonSubmitInactiveTextNode.contentMode = .topLeft self.buttonSubmitInactiveTextNode.contentMode = .topLeft
self.buttonSubmitInactiveTextNode.contentsScale = UIScreenScale self.buttonSubmitInactiveTextNode.contentsScale = UIScreenScale
self.buttonSubmitInactiveTextNode.displaysAsynchronously = true self.buttonSubmitInactiveTextNode.displaysAsynchronously = false
self.buttonSubmitActiveTextNode = TextNode() self.buttonSubmitActiveTextNode = TextNode()
self.buttonSubmitActiveTextNode.isUserInteractionEnabled = false self.buttonSubmitActiveTextNode.isUserInteractionEnabled = false
self.buttonSubmitActiveTextNode.contentMode = .topLeft self.buttonSubmitActiveTextNode.contentMode = .topLeft
self.buttonSubmitActiveTextNode.contentsScale = UIScreenScale self.buttonSubmitActiveTextNode.contentsScale = UIScreenScale
self.buttonSubmitActiveTextNode.displaysAsynchronously = true self.buttonSubmitActiveTextNode.displaysAsynchronously = false
self.buttonViewResultsTextNode = TextNode() self.buttonViewResultsTextNode = TextNode()
self.buttonViewResultsTextNode.isUserInteractionEnabled = false self.buttonViewResultsTextNode.isUserInteractionEnabled = false
self.buttonViewResultsTextNode.contentMode = .topLeft self.buttonViewResultsTextNode.contentMode = .topLeft
self.buttonViewResultsTextNode.contentsScale = UIScreenScale self.buttonViewResultsTextNode.contentsScale = UIScreenScale
self.buttonViewResultsTextNode.displaysAsynchronously = true self.buttonViewResultsTextNode.displaysAsynchronously = false
self.buttonNode = HighlightableButtonNode() self.buttonNode = HighlightableButtonNode()

View File

@ -28,7 +28,7 @@ class ChatMessageReplyInfoNode: ASDisplayNode {
override init() { override init() {
self.contentNode = ASDisplayNode() self.contentNode = ASDisplayNode()
self.contentNode.isUserInteractionEnabled = false self.contentNode.isUserInteractionEnabled = false
self.contentNode.displaysAsynchronously = true self.contentNode.displaysAsynchronously = false
self.contentNode.contentMode = .left self.contentNode.contentMode = .left
self.contentNode.contentsScale = UIScreenScale self.contentNode.contentsScale = UIScreenScale

View File

@ -22,7 +22,7 @@ class ChatMessageRestrictedBubbleContentNode: ChatMessageBubbleContentNode {
self.textNode.isUserInteractionEnabled = false self.textNode.isUserInteractionEnabled = false
self.textNode.contentMode = .topLeft self.textNode.contentMode = .topLeft
self.textNode.contentsScale = UIScreenScale self.textNode.contentsScale = UIScreenScale
self.textNode.displaysAsynchronously = true self.textNode.displaysAsynchronously = false
self.addSubnode(self.textNode) self.addSubnode(self.textNode)
} }

View File

@ -59,7 +59,7 @@ class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
self.textNode.isUserInteractionEnabled = false self.textNode.isUserInteractionEnabled = false
self.textNode.contentMode = .topLeft self.textNode.contentMode = .topLeft
self.textNode.contentsScale = UIScreenScale self.textNode.contentsScale = UIScreenScale
self.textNode.displaysAsynchronously = true self.textNode.displaysAsynchronously = false
self.addSubnode(self.textNode) self.addSubnode(self.textNode)
self.addSubnode(self.textAccessibilityOverlayNode) self.addSubnode(self.textAccessibilityOverlayNode)

View File

@ -317,7 +317,7 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode {
} else { } else {
titleStrings.append(.text(0, NSAttributedString(string: "\(strings.Conversation_PinnedMessage) ", font: Font.medium(15.0), textColor: theme.chat.inputPanel.panelControlAccentColor))) titleStrings.append(.text(0, NSAttributedString(string: "\(strings.Conversation_PinnedMessage) ", font: Font.medium(15.0), textColor: theme.chat.inputPanel.panelControlAccentColor)))
} }
} else if pinnedMessage.totalCount > 1 || pinnedMessage.index == pinnedMessage.totalCount - 1 { } else if pinnedMessage.totalCount > 1 && pinnedMessage.index != pinnedMessage.totalCount - 1 {
titleStrings.append(.text(0, NSAttributedString(string: "\(strings.Conversation_PinnedMessage)", font: Font.medium(15.0), textColor: theme.chat.inputPanel.panelControlAccentColor))) titleStrings.append(.text(0, NSAttributedString(string: "\(strings.Conversation_PinnedMessage)", font: Font.medium(15.0), textColor: theme.chat.inputPanel.panelControlAccentColor)))
titleStrings.append(.text(1, NSAttributedString(string: " #", font: Font.medium(15.0), textColor: theme.chat.inputPanel.panelControlAccentColor))) titleStrings.append(.text(1, NSAttributedString(string: " #", font: Font.medium(15.0), textColor: theme.chat.inputPanel.panelControlAccentColor)))
titleStrings.append(.number(pinnedMessage.index + 1, NSAttributedString(string: "\(pinnedMessage.index + 1)", font: Font.medium(15.0), textColor: theme.chat.inputPanel.panelControlAccentColor))) titleStrings.append(.number(pinnedMessage.index + 1, NSAttributedString(string: "\(pinnedMessage.index + 1)", font: Font.medium(15.0), textColor: theme.chat.inputPanel.panelControlAccentColor)))

View File

@ -414,10 +414,22 @@ final class ChatReportPeerTitlePanelNode: ChatTitleAccessoryPanelNode {
var nextButtonOrigin = maxInset + floor((areaWidth - updatedButtonsWidth) / 2.0) var nextButtonOrigin = maxInset + floor((areaWidth - updatedButtonsWidth) / 2.0)
let buttonWidth = floor(updatedButtonsWidth / CGFloat(self.buttons.count)) let buttonWidth = floor(updatedButtonsWidth / CGFloat(self.buttons.count))
for (_, view) in self.buttons {
view.frame = CGRect(origin: CGPoint(x: nextButtonOrigin, y: 0.0), size: CGSize(width: buttonWidth, height: panelHeight)) var buttonFrames: [CGRect] = []
for _ in 0 ..< self.buttons.count {
buttonFrames.append(CGRect(origin: CGPoint(x: nextButtonOrigin, y: 0.0), size: CGSize(width: buttonWidth, height: panelHeight)))
nextButtonOrigin += buttonWidth nextButtonOrigin += buttonWidth
} }
if buttonFrames[buttonFrames.count - 1].maxX >= width - 20.0 {
for i in 0 ..< buttonFrames.count {
buttonFrames[i].origin.x -= 16.0
}
}
for i in 0 ..< self.buttons.count {
self.buttons[i].1.frame = buttonFrames[i]
}
} }
} }

View File

@ -931,7 +931,7 @@ private func infoItems(data: PeerInfoScreenData?, context: AccountContext, prese
interaction.requestLayout() interaction.requestLayout()
})) }))
} else if let about = cachedData.about, !about.isEmpty { } else if let about = cachedData.about, !about.isEmpty {
items[.peerInfo]!.append(PeerInfoScreenLabeledValueItem(id: 0, label: user.botInfo == nil ? presentationData.strings.Profile_About : presentationData.strings.Profile_BotInfo, text: about, textColor: .primary, textBehavior: .multiLine(maxLines: 100, enabledEntities: []), action: nil, longTapAction: bioContextAction, linkItemAction: bioLinkAction, requestLayout: { items[.peerInfo]!.append(PeerInfoScreenLabeledValueItem(id: 0, label: user.botInfo == nil ? presentationData.strings.Profile_About : presentationData.strings.Profile_BotInfo, text: about, textColor: .primary, textBehavior: .multiLine(maxLines: 100, enabledEntities: enabledBioEntities), action: nil, longTapAction: bioContextAction, linkItemAction: bioLinkAction, requestLayout: {
interaction.requestLayout() interaction.requestLayout()
})) }))
} }
@ -3159,9 +3159,6 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
self.controller?.present(shareController, in: .window(.root)) self.controller?.present(shareController, in: .window(.root))
} }
private let groupCallDisposable = MetaDisposable()
private var groupCall: GroupCallContext?
private func requestCall(isVideo: Bool) { private func requestCall(isVideo: Bool) {
guard let peer = self.data?.peer as? TelegramUser, let cachedUserData = self.data?.cachedData as? CachedUserData else { guard let peer = self.data?.peer as? TelegramUser, let cachedUserData = self.data?.cachedData as? CachedUserData else {
return return

View File

@ -108,7 +108,12 @@ final class PeerSelectionControllerNode: ASDisplayNode {
} }
self.chatListNode.contentOffsetChanged = { [weak self] offset in self.chatListNode.contentOffsetChanged = { [weak self] offset in
self?.contentOffsetChanged?(offset) guard let strongSelf = self else {
return
}
if strongSelf.chatListNode.supernode != nil {
strongSelf.contentOffsetChanged?(offset)
}
} }
self.chatListNode.contentScrollingEnded = { [weak self] listView in self.chatListNode.contentScrollingEnded = { [weak self] listView in
@ -293,7 +298,7 @@ final class PeerSelectionControllerNode: ASDisplayNode {
if self.chatListNode.supernode != nil { if self.chatListNode.supernode != nil {
self.chatListNode.scrollToPosition(.top) self.chatListNode.scrollToPosition(.top)
} else if let contactListNode = self.contactListNode, contactListNode.supernode != nil { } else if let contactListNode = self.contactListNode, contactListNode.supernode != nil {
contactListNode.scrollToTop() //contactListNode.scrollToTop()
} }
} }
@ -331,7 +336,12 @@ final class PeerSelectionControllerNode: ASDisplayNode {
} }
} }
contactListNode.contentOffsetChanged = { [weak self] offset in contactListNode.contentOffsetChanged = { [weak self] offset in
self?.contentOffsetChanged?(offset) guard let strongSelf = self else {
return
}
if strongSelf.contactListNode?.supernode != nil {
strongSelf.contentOffsetChanged?(offset)
}
} }
contactListNode.contentScrollingEnded = { [weak self] listView in contactListNode.contentScrollingEnded = { [weak self] listView in

View File

@ -45,34 +45,45 @@ private final class PrefetchManagerImpl {
} }
|> distinctUntilChanged |> distinctUntilChanged
let orderedPreloadMedia = account.viewTracker.orderedPreloadMedia let appConfiguration = account.postbox.preferencesView(keys: [PreferencesKeys.appConfiguration])
|> mapToSignal { orderedPreloadMedia in |> take(1)
return loadedStickerPack(postbox: account.postbox, network: account.network, reference: .animatedEmoji, forceActualized: false) |> map { view in
|> map { result -> [PrefetchMediaItem] in return view.values[PreferencesKeys.appConfiguration] as? AppConfiguration ?? .defaultValue
let chatHistoryMediaItems = orderedPreloadMedia.map { PrefetchMediaItem.chatHistory($0) } }
switch result {
case let .result(_, items, _): let orderedPreloadMedia = combineLatest(account.viewTracker.orderedPreloadMedia, loadedStickerPack(postbox: account.postbox, network: account.network, reference: .animatedEmoji, forceActualized: false), appConfiguration)
var animatedEmojiStickers: [String: StickerPackItem] = [:] |> map { orderedPreloadMedia, stickerPack, appConfiguration -> [PrefetchMediaItem] in
for case let item as StickerPackItem in items { let emojiSounds = AnimatedEmojiSoundsConfiguration.with(appConfiguration: appConfiguration, account: account)
if let emoji = item.getStringRepresentationsOfIndexKeys().first { let chatHistoryMediaItems = orderedPreloadMedia.map { PrefetchMediaItem.chatHistory($0) }
animatedEmojiStickers[emoji.basicEmoji.0] = item var stickerItems: [PrefetchMediaItem] = []
switch stickerPack {
case let .result(_, items, _):
var animatedEmojiStickers: [String: StickerPackItem] = [:]
for case let item as StickerPackItem in items {
if let emoji = item.getStringRepresentationsOfIndexKeys().first {
animatedEmojiStickers[emoji.basicEmoji.0] = item
}
}
let popularEmoji = ["\u{2764}", "👍", "😳", "😒", "🥳"]
for emoji in popularEmoji {
if let sticker = animatedEmojiStickers[emoji] {
if let _ = account.postbox.mediaBox.completedResourcePath(sticker.file.resource) {
} else {
stickerItems.append(.animatedEmojiSticker(sticker.file))
} }
} }
var stickerItems: [PrefetchMediaItem] = [] }
let popularEmoji = ["\u{2764}", "👍", "😳", "😒", "🥳"] default:
for emoji in popularEmoji { break
if let sticker = animatedEmojiStickers[emoji] {
if let _ = account.postbox.mediaBox.completedResourcePath(sticker.file.resource) {
} else {
stickerItems.append(.animatedEmojiSticker(sticker.file))
}
}
}
return stickerItems + chatHistoryMediaItems
default:
return chatHistoryMediaItems
}
} }
var prefetchItems: [PrefetchMediaItem] = []
prefetchItems.append(contentsOf: chatHistoryMediaItems)
prefetchItems.append(contentsOf: stickerItems)
prefetchItems.append(contentsOf: emojiSounds.sounds.values.map { .animatedEmojiSticker($0) })
return prefetchItems
} }
self.listDisposable = (combineLatest(orderedPreloadMedia, sharedContext.automaticMediaDownloadSettings, networkType) self.listDisposable = (combineLatest(orderedPreloadMedia, sharedContext.automaticMediaDownloadSettings, networkType)

View File

@ -61,7 +61,9 @@ final class WidgetDataContext {
} }
if #available(iOSApplicationExtension 14.0, iOS 14.0, *) { if #available(iOSApplicationExtension 14.0, iOS 14.0, *) {
#if arch(arm64) || arch(i386) || arch(x86_64)
WidgetCenter.shared.reloadAllTimelines() WidgetCenter.shared.reloadAllTimelines()
#endif
} }
}) })
@ -78,7 +80,9 @@ final class WidgetDataContext {
} }
if #available(iOSApplicationExtension 14.0, iOS 14.0, *) { if #available(iOSApplicationExtension 14.0, iOS 14.0, *) {
#if arch(arm64) || arch(i386) || arch(x86_64)
WidgetCenter.shared.reloadAllTimelines() WidgetCenter.shared.reloadAllTimelines()
#endif
} }
}) })

View File

@ -2,7 +2,7 @@ import Foundation
import SwiftSignalKit import SwiftSignalKit
import TgVoipWebrtc import TgVoipWebrtc
private final class ContextQueueImpl: NSObject, OngoingCallThreadLocalContextQueueWebrtc { /*private final class ContextQueueImpl: NSObject, OngoingCallThreadLocalContextQueueWebrtc {
private let queue: Queue private let queue: Queue
init(queue: Queue) { init(queue: Queue) {
@ -1467,3 +1467,4 @@ public final class GroupCallContext {
} }
} }
} }
*/

View File

@ -1,7 +1,7 @@
#import <TgVoipWebrtc/GroupCallThreadLocalContext.h> #import <TgVoipWebrtc/GroupCallThreadLocalContext.h>
#import "group/GroupInstanceImpl.h" /*#import "group/GroupInstanceImpl.h"
@interface GroupCallThreadLocalContext () { @interface GroupCallThreadLocalContext () {
id<OngoingCallThreadLocalContextQueueWebrtc> _queue; id<OngoingCallThreadLocalContextQueueWebrtc> _queue;
@ -55,3 +55,4 @@
} }
@end @end
*/

@ -1 +1 @@
Subproject commit 4f7501d281b851e6302b2a2d7298c733eee82414 Subproject commit 64f96a1b4fcfb8afdb0fb7749082cb42cdad7901