mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios
This commit is contained in:
commit
d3df036229
2
Makefile
2
Makefile
@ -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)
|
||||||
|
|
||||||
|
@ -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";
|
||||||
|
@ -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>
|
||||||
|
@ -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
|
||||||
|
@ -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 \
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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()
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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 {
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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) {
|
||||||
|
@ -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?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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) {
|
||||||
|
@ -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),
|
||||||
|
@ -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)
|
||||||
|
@ -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()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
@ -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")
|
||||||
|
@ -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))
|
||||||
|
@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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))
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -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
|
||||||
|
Binary file not shown.
@ -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) {
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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 {
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
@ -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()
|
||||||
|
|
||||||
|
@ -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 {
|
||||||
|
@ -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()
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
@ -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)))
|
||||||
|
@ -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]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -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 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
@ -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
|
Loading…
x
Reference in New Issue
Block a user